1 /*
2 * Copyright 2018 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkBlendMode.h"
9 #include "SkBlurTypes.h"
10 #include "SkCanvas.h"
11 #include "SkColor.h"
12 #include "SkCornerPathEffect.h"
13 #include "SkDashPathEffect.h"
14 #include "SkData.h"
15 #include "SkDiscretePathEffect.h"
16 #include "SkEncodedImageFormat.h"
17 #include "SkFilterQuality.h"
18 #include "SkFont.h"
19 #include "SkFontMgr.h"
20 #include "SkFontMgrPriv.h"
21 #include "SkFontTypes.h"
22 #include "SkGradientShader.h"
23 #include "SkImage.h"
24 #include "SkImageInfo.h"
25 #include "SkImageShader.h"
26 #include "SkMakeUnique.h"
27 #include "SkMaskFilter.h"
28 #include "SkPaint.h"
29 #include "SkParsePath.h"
30 #include "SkPath.h"
31 #include "SkPathEffect.h"
32 #include "SkPathOps.h"
33 #include "SkScalar.h"
34 #include "SkShader.h"
35 #include "SkShadowUtils.h"
36 #include "SkShaper.h"
37 #include "SkString.h"
38 #include "SkStrokeRec.h"
39 #include "SkSurface.h"
40 #include "SkSurfaceProps.h"
41 #include "SkTextBlob.h"
42 #include "SkTrimPathEffect.h"
43 #include "SkTypeface.h"
44 #include "SkTypes.h"
45 #include "SkVertices.h"
46
47 #include <iostream>
48 #include <string>
49
50 #include "WasmAliases.h"
51 #include <emscripten.h>
52 #include <emscripten/bind.h>
53
54 #if SK_SUPPORT_GPU
55 #include "GrBackendSurface.h"
56 #include "GrContext.h"
57 #include "GrGLInterface.h"
58 #include "GrGLTypes.h"
59
60 #include <GL/gl.h>
61 #include <emscripten/html5.h>
62 #endif
63
64 // Aliases for less typing
65 using BoneIndices = SkVertices::BoneIndices;
66 using BoneWeights = SkVertices::BoneWeights;
67 using Bone = SkVertices::Bone;
68
69 struct SimpleMatrix {
70 SkScalar scaleX, skewX, transX;
71 SkScalar skewY, scaleY, transY;
72 SkScalar pers0, pers1, pers2;
73 };
74
toSkMatrix(const SimpleMatrix & sm)75 SkMatrix toSkMatrix(const SimpleMatrix& sm) {
76 return SkMatrix::MakeAll(sm.scaleX, sm.skewX , sm.transX,
77 sm.skewY , sm.scaleY, sm.transY,
78 sm.pers0 , sm.pers1 , sm.pers2);
79 }
80
toSimpleSkMatrix(const SkMatrix & sm)81 SimpleMatrix toSimpleSkMatrix(const SkMatrix& sm) {
82 SimpleMatrix m {sm[0], sm[1], sm[2],
83 sm[3], sm[4], sm[5],
84 sm[6], sm[7], sm[8]};
85 return m;
86 }
87
88 struct SimpleImageInfo {
89 int width;
90 int height;
91 SkColorType colorType;
92 SkAlphaType alphaType;
93 // TODO color spaces?
94 };
95
toSkImageInfo(const SimpleImageInfo & sii)96 SkImageInfo toSkImageInfo(const SimpleImageInfo& sii) {
97 return SkImageInfo::Make(sii.width, sii.height, sii.colorType, sii.alphaType);
98 }
99
100 #if SK_SUPPORT_GPU
MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)101 sk_sp<GrContext> MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)
102 {
103 EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current(context);
104 if (r < 0) {
105 printf("failed to make webgl context current %d\n", r);
106 return nullptr;
107 }
108 // setup GrContext
109 auto interface = GrGLMakeNativeInterface();
110 // setup contexts
111 sk_sp<GrContext> grContext(GrContext::MakeGL(interface));
112 return grContext;
113 }
114
MakeOnScreenGLSurface(sk_sp<GrContext> grContext,int width,int height)115 sk_sp<SkSurface> MakeOnScreenGLSurface(sk_sp<GrContext> grContext, int width, int height) {
116 glClearColor(0, 0, 0, 0);
117 glClearStencil(0);
118 glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
119
120
121 // Wrap the frame buffer object attached to the screen in a Skia render
122 // target so Skia can render to it
123 GrGLint buffer;
124 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &buffer);
125 GrGLFramebufferInfo info;
126 info.fFBOID = (GrGLuint) buffer;
127 SkColorType colorType;
128
129 info.fFormat = GL_RGBA8;
130 colorType = kRGBA_8888_SkColorType;
131
132 GrBackendRenderTarget target(width, height, 0, 8, info);
133
134 sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(grContext.get(), target,
135 kBottomLeft_GrSurfaceOrigin,
136 colorType, nullptr, nullptr));
137 return surface;
138 }
139
MakeRenderTarget(sk_sp<GrContext> grContext,int width,int height)140 sk_sp<SkSurface> MakeRenderTarget(sk_sp<GrContext> grContext, int width, int height) {
141 SkImageInfo info = SkImageInfo::MakeN32(width, height, SkAlphaType::kPremul_SkAlphaType);
142
143 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(grContext.get(),
144 SkBudgeted::kYes,
145 info, 0,
146 kBottomLeft_GrSurfaceOrigin,
147 nullptr, true));
148 return surface;
149 }
150
MakeRenderTarget(sk_sp<GrContext> grContext,SimpleImageInfo sii)151 sk_sp<SkSurface> MakeRenderTarget(sk_sp<GrContext> grContext, SimpleImageInfo sii) {
152 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(grContext.get(),
153 SkBudgeted::kYes,
154 toSkImageInfo(sii), 0,
155 kBottomLeft_GrSurfaceOrigin,
156 nullptr, true));
157 return surface;
158 }
159 #endif
160
161
162 //========================================================================================
163 // Path things
164 //========================================================================================
165
166 // All these Apply* methods are simple wrappers to avoid returning an object.
167 // The default WASM bindings produce code that will leak if a return value
168 // isn't assigned to a JS variable and has delete() called on it.
169 // These Apply methods, combined with the smarter binding code allow for chainable
170 // commands that don't leak if the return value is ignored (i.e. when used intuitively).
171
ApplyAddArc(SkPath & orig,const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle)172 void ApplyAddArc(SkPath& orig, const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
173 orig.addArc(oval, startAngle, sweepAngle);
174 }
175
ApplyAddPath(SkPath & orig,const SkPath & newPath,SkScalar scaleX,SkScalar skewX,SkScalar transX,SkScalar skewY,SkScalar scaleY,SkScalar transY,SkScalar pers0,SkScalar pers1,SkScalar pers2,bool extendPath)176 void ApplyAddPath(SkPath& orig, const SkPath& newPath,
177 SkScalar scaleX, SkScalar skewX, SkScalar transX,
178 SkScalar skewY, SkScalar scaleY, SkScalar transY,
179 SkScalar pers0, SkScalar pers1, SkScalar pers2,
180 bool extendPath) {
181 SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
182 skewY , scaleY, transY,
183 pers0 , pers1 , pers2);
184 orig.addPath(newPath, m, extendPath ? SkPath::kExtend_AddPathMode :
185 SkPath::kAppend_AddPathMode);
186 }
187
ApplyAddRect(SkPath & path,SkScalar left,SkScalar top,SkScalar right,SkScalar bottom,bool ccw)188 void ApplyAddRect(SkPath& path, SkScalar left, SkScalar top,
189 SkScalar right, SkScalar bottom, bool ccw) {
190 path.addRect(left, top, right, bottom,
191 ccw ? SkPath::Direction::kCCW_Direction :
192 SkPath::Direction::kCW_Direction);
193 }
194
ApplyAddRoundRect(SkPath & path,SkScalar left,SkScalar top,SkScalar right,SkScalar bottom,uintptr_t rPtr,bool ccw)195 void ApplyAddRoundRect(SkPath& path, SkScalar left, SkScalar top,
196 SkScalar right, SkScalar bottom, uintptr_t /* SkScalar* */ rPtr,
197 bool ccw) {
198 // See comment below for uintptr_t explanation
199 const SkScalar* radii = reinterpret_cast<const SkScalar*>(rPtr);
200 path.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom), radii,
201 ccw ? SkPath::Direction::kCCW_Direction : SkPath::Direction::kCW_Direction);
202 }
203
204
ApplyArcTo(SkPath & p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar radius)205 void ApplyArcTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
206 SkScalar radius) {
207 p.arcTo(x1, y1, x2, y2, radius);
208 }
209
ApplyArcToAngle(SkPath & p,SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool forceMoveTo)210 void ApplyArcToAngle(SkPath& p, SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) {
211 p.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
212 }
213
ApplyClose(SkPath & p)214 void ApplyClose(SkPath& p) {
215 p.close();
216 }
217
ApplyConicTo(SkPath & p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar w)218 void ApplyConicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
219 SkScalar w) {
220 p.conicTo(x1, y1, x2, y2, w);
221 }
222
ApplyCubicTo(SkPath & p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2,SkScalar x3,SkScalar y3)223 void ApplyCubicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
224 SkScalar x3, SkScalar y3) {
225 p.cubicTo(x1, y1, x2, y2, x3, y3);
226 }
227
ApplyLineTo(SkPath & p,SkScalar x,SkScalar y)228 void ApplyLineTo(SkPath& p, SkScalar x, SkScalar y) {
229 p.lineTo(x, y);
230 }
231
ApplyMoveTo(SkPath & p,SkScalar x,SkScalar y)232 void ApplyMoveTo(SkPath& p, SkScalar x, SkScalar y) {
233 p.moveTo(x, y);
234 }
235
ApplyReset(SkPath & p)236 void ApplyReset(SkPath& p) {
237 p.reset();
238 }
239
ApplyRewind(SkPath & p)240 void ApplyRewind(SkPath& p) {
241 p.rewind();
242 }
243
ApplyQuadTo(SkPath & p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)244 void ApplyQuadTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
245 p.quadTo(x1, y1, x2, y2);
246 }
247
ApplyTransform(SkPath & orig,SkScalar scaleX,SkScalar skewX,SkScalar transX,SkScalar skewY,SkScalar scaleY,SkScalar transY,SkScalar pers0,SkScalar pers1,SkScalar pers2)248 void ApplyTransform(SkPath& orig,
249 SkScalar scaleX, SkScalar skewX, SkScalar transX,
250 SkScalar skewY, SkScalar scaleY, SkScalar transY,
251 SkScalar pers0, SkScalar pers1, SkScalar pers2) {
252 SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
253 skewY , scaleY, transY,
254 pers0 , pers1 , pers2);
255 orig.transform(m);
256 }
257
ApplySimplify(SkPath & path)258 bool EMSCRIPTEN_KEEPALIVE ApplySimplify(SkPath& path) {
259 return Simplify(path, &path);
260 }
261
ApplyPathOp(SkPath & pathOne,const SkPath & pathTwo,SkPathOp op)262 bool EMSCRIPTEN_KEEPALIVE ApplyPathOp(SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
263 return Op(pathOne, pathTwo, op, &pathOne);
264 }
265
ToSVGString(const SkPath & path)266 JSString EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) {
267 SkString s;
268 SkParsePath::ToSVGString(path, &s);
269 return emscripten::val(s.c_str());
270 }
271
MakePathFromSVGString(std::string str)272 SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromSVGString(std::string str) {
273 SkPath path;
274 if (SkParsePath::FromSVGString(str.c_str(), &path)) {
275 return emscripten::val(path);
276 }
277 return emscripten::val::null();
278 }
279
MakePathFromOp(const SkPath & pathOne,const SkPath & pathTwo,SkPathOp op)280 SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
281 SkPath out;
282 if (Op(pathOne, pathTwo, op, &out)) {
283 return emscripten::val(out);
284 }
285 return emscripten::val::null();
286 }
287
CopyPath(const SkPath & a)288 SkPath EMSCRIPTEN_KEEPALIVE CopyPath(const SkPath& a) {
289 SkPath copy(a);
290 return copy;
291 }
292
Equals(const SkPath & a,const SkPath & b)293 bool EMSCRIPTEN_KEEPALIVE Equals(const SkPath& a, const SkPath& b) {
294 return a == b;
295 }
296
297 // =================================================================================
298 // Creating/Exporting Paths with cmd arrays
299 // =================================================================================
300
301 static const int MOVE = 0;
302 static const int LINE = 1;
303 static const int QUAD = 2;
304 static const int CONIC = 3;
305 static const int CUBIC = 4;
306 static const int CLOSE = 5;
307
308 template <typename VisitFunc>
VisitPath(const SkPath & p,VisitFunc && f)309 void VisitPath(const SkPath& p, VisitFunc&& f) {
310 SkPath::RawIter iter(p);
311 SkPoint pts[4];
312 SkPath::Verb verb;
313 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
314 f(verb, pts, iter);
315 }
316 }
317
ToCmds(const SkPath & path)318 JSArray EMSCRIPTEN_KEEPALIVE ToCmds(const SkPath& path) {
319 JSArray cmds = emscripten::val::array();
320
321 VisitPath(path, [&cmds](SkPath::Verb verb, const SkPoint pts[4], SkPath::RawIter iter) {
322 JSArray cmd = emscripten::val::array();
323 switch (verb) {
324 case SkPath::kMove_Verb:
325 cmd.call<void>("push", MOVE, pts[0].x(), pts[0].y());
326 break;
327 case SkPath::kLine_Verb:
328 cmd.call<void>("push", LINE, pts[1].x(), pts[1].y());
329 break;
330 case SkPath::kQuad_Verb:
331 cmd.call<void>("push", QUAD, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
332 break;
333 case SkPath::kConic_Verb:
334 cmd.call<void>("push", CONIC,
335 pts[1].x(), pts[1].y(),
336 pts[2].x(), pts[2].y(), iter.conicWeight());
337 break;
338 case SkPath::kCubic_Verb:
339 cmd.call<void>("push", CUBIC,
340 pts[1].x(), pts[1].y(),
341 pts[2].x(), pts[2].y(),
342 pts[3].x(), pts[3].y());
343 break;
344 case SkPath::kClose_Verb:
345 cmd.call<void>("push", CLOSE);
346 break;
347 case SkPath::kDone_Verb:
348 SkASSERT(false);
349 break;
350 }
351 cmds.call<void>("push", cmd);
352 });
353 return cmds;
354 }
355
356 // This type signature is a mess, but it's necessary. See, we can't use "bind" (EMSCRIPTEN_BINDINGS)
357 // and pointers to primitive types (Only bound types like SkPoint). We could if we used
358 // cwrap (see https://becominghuman.ai/passing-and-returning-webassembly-array-parameters-a0f572c65d97)
359 // but that requires us to stick to C code and, AFAIK, doesn't allow us to return nice things like
360 // SkPath or SkOpBuilder.
361 //
362 // So, basically, if we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primative pointers
363 // in our function type signatures. (this gives an error message like "Cannot call foo due to unbound
364 // types Pi, Pf"). But, we can just pretend they are numbers and cast them to be pointers and
365 // the compiler is happy.
MakePathFromCmds(uintptr_t cptr,int numCmds)366 SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromCmds(uintptr_t /* float* */ cptr, int numCmds) {
367 const auto* cmds = reinterpret_cast<const float*>(cptr);
368 SkPath path;
369 float x1, y1, x2, y2, x3, y3;
370
371 // if there are not enough arguments, bail with the path we've constructed so far.
372 #define CHECK_NUM_ARGS(n) \
373 if ((i + n) > numCmds) { \
374 SkDebugf("Not enough args to match the verbs. Saw %d commands\n", numCmds); \
375 return emscripten::val::null(); \
376 }
377
378 for(int i = 0; i < numCmds;){
379 switch (sk_float_floor2int(cmds[i++])) {
380 case MOVE:
381 CHECK_NUM_ARGS(2);
382 x1 = cmds[i++], y1 = cmds[i++];
383 path.moveTo(x1, y1);
384 break;
385 case LINE:
386 CHECK_NUM_ARGS(2);
387 x1 = cmds[i++], y1 = cmds[i++];
388 path.lineTo(x1, y1);
389 break;
390 case QUAD:
391 CHECK_NUM_ARGS(4);
392 x1 = cmds[i++], y1 = cmds[i++];
393 x2 = cmds[i++], y2 = cmds[i++];
394 path.quadTo(x1, y1, x2, y2);
395 break;
396 case CONIC:
397 CHECK_NUM_ARGS(5);
398 x1 = cmds[i++], y1 = cmds[i++];
399 x2 = cmds[i++], y2 = cmds[i++];
400 x3 = cmds[i++]; // weight
401 path.conicTo(x1, y1, x2, y2, x3);
402 break;
403 case CUBIC:
404 CHECK_NUM_ARGS(6);
405 x1 = cmds[i++], y1 = cmds[i++];
406 x2 = cmds[i++], y2 = cmds[i++];
407 x3 = cmds[i++], y3 = cmds[i++];
408 path.cubicTo(x1, y1, x2, y2, x3, y3);
409 break;
410 case CLOSE:
411 path.close();
412 break;
413 default:
414 SkDebugf(" path: UNKNOWN command %f, aborting dump...\n", cmds[i-1]);
415 return emscripten::val::null();
416 }
417 }
418
419 #undef CHECK_NUM_ARGS
420
421 return emscripten::val(path);
422 }
423
424 //========================================================================================
425 // Path Effects
426 //========================================================================================
427
ApplyDash(SkPath & path,SkScalar on,SkScalar off,SkScalar phase)428 bool ApplyDash(SkPath& path, SkScalar on, SkScalar off, SkScalar phase) {
429 SkScalar intervals[] = { on, off };
430 auto pe = SkDashPathEffect::Make(intervals, 2, phase);
431 if (!pe) {
432 SkDebugf("Invalid args to dash()\n");
433 return false;
434 }
435 SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
436 if (pe->filterPath(&path, path, &rec, nullptr)) {
437 return true;
438 }
439 SkDebugf("Could not make dashed path\n");
440 return false;
441 }
442
ApplyTrim(SkPath & path,SkScalar startT,SkScalar stopT,bool isComplement)443 bool ApplyTrim(SkPath& path, SkScalar startT, SkScalar stopT, bool isComplement) {
444 auto mode = isComplement ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal;
445 auto pe = SkTrimPathEffect::Make(startT, stopT, mode);
446 if (!pe) {
447 SkDebugf("Invalid args to trim(): startT and stopT must be in [0,1]\n");
448 return false;
449 }
450 SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
451 if (pe->filterPath(&path, path, &rec, nullptr)) {
452 return true;
453 }
454 SkDebugf("Could not trim path\n");
455 return false;
456 }
457
458 struct StrokeOpts {
459 // Default values are set in interface.js which allows clients
460 // to set any number of them. Otherwise, the binding code complains if
461 // any are omitted.
462 SkScalar width;
463 SkScalar miter_limit;
464 SkPaint::Join join;
465 SkPaint::Cap cap;
466 float precision;
467 };
468
ApplyStroke(SkPath & path,StrokeOpts opts)469 bool ApplyStroke(SkPath& path, StrokeOpts opts) {
470 SkPaint p;
471 p.setStyle(SkPaint::kStroke_Style);
472 p.setStrokeCap(opts.cap);
473 p.setStrokeJoin(opts.join);
474 p.setStrokeWidth(opts.width);
475 p.setStrokeMiter(opts.miter_limit);
476
477 return p.getFillPath(path, &path, nullptr, opts.precision);
478 }
479
480 // to map from raw memory to a uint8array
getSkDataBytes(const SkData * data)481 Uint8Array getSkDataBytes(const SkData *data) {
482 return Uint8Array(typed_memory_view(data->size(), data->bytes()));
483 }
484
485 // Text Shaping abstraction
486
487 struct ShapedTextOpts {
488 SkFont font;
489 bool leftToRight;
490 std::string text;
491 SkScalar width;
492 };
493
494 std::unique_ptr<SkShaper> shaper;
495
do_shaping(const ShapedTextOpts & opts,SkPoint * pt)496 static sk_sp<SkTextBlob> do_shaping(const ShapedTextOpts& opts, SkPoint* pt) {
497 SkTextBlobBuilderRunHandler builder(opts.text.c_str());
498 if (!shaper) {
499 shaper = SkShaper::Make();
500 }
501 *pt = shaper->shape(&builder, opts.font, opts.text.c_str(),
502 opts.text.length(), opts.leftToRight,
503 {0, 0}, opts.width);
504 return builder.makeBlob();
505 }
506
507 class ShapedText {
508 public:
ShapedText(ShapedTextOpts opts)509 ShapedText(ShapedTextOpts opts) : fOpts(opts) {}
510
getBounds()511 SkRect getBounds() {
512 this->init();
513 return SkRect::MakeLTRB(0, 0, fOpts.width, fPoint.y());
514 }
515
blob()516 SkTextBlob* blob() {
517 this->init();
518 return fBlob.get();
519 }
520 private:
521 const ShapedTextOpts fOpts;
522 SkPoint fPoint;
523 sk_sp<SkTextBlob> fBlob;
524
init()525 void init() {
526 if (!fBlob) {
527 fBlob = do_shaping(fOpts, &fPoint);
528 }
529 }
530 };
531
drawShapedText(SkCanvas & canvas,ShapedText st,SkScalar x,SkScalar y,SkPaint paint)532 void drawShapedText(SkCanvas& canvas, ShapedText st, SkScalar x,
533 SkScalar y, SkPaint paint) {
534 canvas.drawTextBlob(st.blob(), x, y, paint);
535 }
536
537 // These objects have private destructors / delete mthods - I don't think
538 // we need to do anything other than tell emscripten to do nothing.
539 namespace emscripten {
540 namespace internal {
541 template<typename ClassType>
542 void raw_destructor(ClassType *);
543
544 template<>
raw_destructor(SkData * ptr)545 void raw_destructor<SkData>(SkData *ptr) {
546 }
547
548 template<>
raw_destructor(SkTypeface * ptr)549 void raw_destructor<SkTypeface>(SkTypeface *ptr) {
550 }
551
552 template<>
raw_destructor(SkVertices * ptr)553 void raw_destructor<SkVertices>(SkVertices *ptr) {
554 }
555
556 template<>
raw_destructor(SkTextBlob * ptr)557 void raw_destructor<SkTextBlob>(SkTextBlob *ptr) {
558 }
559 }
560 }
561
562 // Some timesignatures below have uintptr_t instead of a pointer to a primative
563 // type (e.g. SkScalar). This is necessary because we can't use "bind" (EMSCRIPTEN_BINDINGS)
564 // and pointers to primitive types (Only bound types like SkPoint). We could if we used
565 // cwrap (see https://becominghuman.ai/passing-and-returning-webassembly-array-parameters-a0f572c65d97)
566 // but that requires us to stick to C code and, AFAIK, doesn't allow us to return nice things like
567 // SkPath or SkCanvas.
568 //
569 // So, basically, if we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primative pointers
570 // in our function type signatures. (this gives an error message like "Cannot call foo due to unbound
571 // types Pi, Pf"). But, we can just pretend they are numbers and cast them to be pointers and
572 // the compiler is happy.
EMSCRIPTEN_BINDINGS(Skia)573 EMSCRIPTEN_BINDINGS(Skia) {
574 #if SK_SUPPORT_GPU
575 function("currentContext", &emscripten_webgl_get_current_context);
576 function("setCurrentContext", &emscripten_webgl_make_context_current);
577 function("MakeGrContext", &MakeGrContext);
578 function("MakeOnScreenGLSurface", &MakeOnScreenGLSurface);
579 function("MakeRenderTarget", select_overload<sk_sp<SkSurface>(sk_sp<GrContext>, int, int)>(&MakeRenderTarget));
580 function("MakeRenderTarget", select_overload<sk_sp<SkSurface>(sk_sp<GrContext>, SimpleImageInfo)>(&MakeRenderTarget));
581
582 constant("gpu", true);
583 #endif
584 function("_decodeImage", optional_override([](uintptr_t /* uint8_t* */ iptr,
585 size_t length)->sk_sp<SkImage> {
586 uint8_t* imgData = reinterpret_cast<uint8_t*>(iptr);
587 sk_sp<SkData> bytes = SkData::MakeFromMalloc(imgData, length);
588 return SkImage::MakeFromEncoded(std::move(bytes));
589 }), allow_raw_pointers());
590 function("_getRasterDirectSurface", optional_override([](const SimpleImageInfo ii,
591 uintptr_t /* uint8_t* */ pPtr,
592 size_t rowBytes)->sk_sp<SkSurface> {
593 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
594 SkImageInfo imageInfo = toSkImageInfo(ii);
595 return SkSurface::MakeRasterDirect(imageInfo, pixels, rowBytes, nullptr);
596 }), allow_raw_pointers());
597 function("_getRasterN32PremulSurface", optional_override([](int width, int height)->sk_sp<SkSurface> {
598 return SkSurface::MakeRasterN32Premul(width, height, nullptr);
599 }), allow_raw_pointers());
600
601 function("getSkDataBytes", &getSkDataBytes, allow_raw_pointers());
602 function("MakeSkCornerPathEffect", &SkCornerPathEffect::Make, allow_raw_pointers());
603 function("MakeSkDiscretePathEffect", &SkDiscretePathEffect::Make, allow_raw_pointers());
604 function("MakeBlurMaskFilter", optional_override([](SkBlurStyle style, SkScalar sigma, bool respectCTM)->sk_sp<SkMaskFilter> {
605 // Adds a little helper because emscripten doesn't expose default params.
606 return SkMaskFilter::MakeBlur(style, sigma, respectCTM);
607 }), allow_raw_pointers());
608 function("_MakePathFromCmds", &MakePathFromCmds);
609 function("MakePathFromOp", &MakePathFromOp);
610 function("MakePathFromSVGString", &MakePathFromSVGString);
611
612 // These won't be called directly, there's a JS helper to deal with typed arrays.
613 function("_MakeSkDashPathEffect", optional_override([](uintptr_t /* float* */ cptr, int count, SkScalar phase)->sk_sp<SkPathEffect> {
614 // See comment above for uintptr_t explanation
615 const float* intervals = reinterpret_cast<const float*>(cptr);
616 return SkDashPathEffect::Make(intervals, count, phase);
617 }), allow_raw_pointers());
618 function("_MakeImage", optional_override([](SimpleImageInfo ii,
619 uintptr_t /* uint8_t* */ pPtr, int plen,
620 size_t rowBytes)->sk_sp<SkImage> {
621 // See comment above for uintptr_t explanation
622 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
623 SkImageInfo info = toSkImageInfo(ii);
624 sk_sp<SkData> pixelData = SkData::MakeFromMalloc(pixels, plen);
625
626 return SkImage::MakeRasterData(info, pixelData, rowBytes);
627 }), allow_raw_pointers());
628 // Allow localMatrix to be optional, so we have 2 declarations of these shaders
629 function("_MakeImageShader", optional_override([](sk_sp<SkImage> img,
630 SkShader::TileMode tx, SkShader::TileMode ty,
631 bool clampAsIfUnpremul)->sk_sp<SkShader> {
632 return SkImageShader::Make(img, tx, ty, nullptr, clampAsIfUnpremul);
633 }), allow_raw_pointers());
634 function("_MakeImageShader", optional_override([](sk_sp<SkImage> img,
635 SkShader::TileMode tx, SkShader::TileMode ty,
636 bool clampAsIfUnpremul, const SimpleMatrix& lm)->sk_sp<SkShader> {
637 SkMatrix localMatrix = toSkMatrix(lm);
638
639 return SkImageShader::Make(img, tx, ty, &localMatrix, clampAsIfUnpremul);
640 }), allow_raw_pointers());
641 function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
642 uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
643 int count, SkShader::TileMode mode, uint32_t flags)->sk_sp<SkShader> {
644 SkPoint points[] = { start, end };
645 // See comment above for uintptr_t explanation
646 const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
647 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
648
649 return SkGradientShader::MakeLinear(points, colors, positions, count,
650 mode, flags, nullptr);
651 }), allow_raw_pointers());
652 function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
653 uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
654 int count, SkShader::TileMode mode, uint32_t flags,
655 const SimpleMatrix& lm)->sk_sp<SkShader> {
656 SkPoint points[] = { start, end };
657 // See comment above for uintptr_t explanation
658 const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
659 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
660
661 SkMatrix localMatrix = toSkMatrix(lm);
662
663 return SkGradientShader::MakeLinear(points, colors, positions, count,
664 mode, flags, &localMatrix);
665 }), allow_raw_pointers());
666 function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
667 uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
668 int count, SkShader::TileMode mode, uint32_t flags)->sk_sp<SkShader> {
669 // See comment above for uintptr_t explanation
670 const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
671 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
672
673 return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
674 mode, flags, nullptr);
675 }), allow_raw_pointers());
676 function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
677 uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
678 int count, SkShader::TileMode mode, uint32_t flags,
679 const SimpleMatrix& lm)->sk_sp<SkShader> {
680 // See comment above for uintptr_t explanation
681 const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
682 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
683
684 SkMatrix localMatrix = toSkMatrix(lm);
685 return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
686 mode, flags, &localMatrix);
687 }), allow_raw_pointers());
688 function("_MakeTwoPointConicalGradientShader", optional_override([](
689 SkPoint start, SkScalar startRadius,
690 SkPoint end, SkScalar endRadius,
691 uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
692 int count, SkShader::TileMode mode, uint32_t flags)->sk_sp<SkShader> {
693 // See comment above for uintptr_t explanation
694 const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
695 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
696
697 return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
698 colors, positions, count, mode,
699 flags, nullptr);
700 }), allow_raw_pointers());
701 function("_MakeTwoPointConicalGradientShader", optional_override([](
702 SkPoint start, SkScalar startRadius,
703 SkPoint end, SkScalar endRadius,
704 uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
705 int count, SkShader::TileMode mode, uint32_t flags,
706 const SimpleMatrix& lm)->sk_sp<SkShader> {
707 // See comment above for uintptr_t explanation
708 const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
709 const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
710
711 SkMatrix localMatrix = toSkMatrix(lm);
712 return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
713 colors, positions, count, mode,
714 flags, &localMatrix);
715 }), allow_raw_pointers());
716
717 function("_MakeSkVertices", optional_override([](SkVertices::VertexMode mode, int vertexCount,
718 uintptr_t /* SkPoint* */ pPtr, uintptr_t /* SkPoint* */ tPtr,
719 uintptr_t /* SkColor* */ cPtr,
720 uintptr_t /* BoneIndices* */ biPtr, uintptr_t /* BoneWeights* */ bwPtr,
721 int indexCount, uintptr_t /* uint16_t * */ iPtr,
722 bool isVolatile)->sk_sp<SkVertices> {
723 // See comment above for uintptr_t explanation
724 const SkPoint* positions = reinterpret_cast<const SkPoint*>(pPtr);
725 const SkPoint* texs = reinterpret_cast<const SkPoint*>(tPtr);
726 const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
727 const BoneIndices* boneIndices = reinterpret_cast<const BoneIndices*>(biPtr);
728 const BoneWeights* boneWeights = reinterpret_cast<const BoneWeights*>(bwPtr);
729 const uint16_t* indices = reinterpret_cast<const uint16_t*>(iPtr);
730
731 return SkVertices::MakeCopy(mode, vertexCount, positions, texs, colors,
732 boneIndices, boneWeights, indexCount, indices, isVolatile);
733 }), allow_raw_pointers());
734
735 #if SK_SUPPORT_GPU
736 class_<GrContext>("GrContext")
737 .smart_ptr<sk_sp<GrContext>>("sk_sp<GrContext>");
738 #endif
739
740 class_<SkCanvas>("SkCanvas")
741 .constructor<>()
742 .function("clear", optional_override([](SkCanvas& self, JSColor color)->void {
743 // JS side gives us a signed int instead of an unsigned int for color
744 // Add a optional_override to change it out.
745 self.clear(SkColor(color));
746 }))
747 .function("clipPath", select_overload<void (const SkPath&, SkClipOp, bool)>(&SkCanvas::clipPath))
748 .function("clipRect", select_overload<void (const SkRect&, SkClipOp, bool)>(&SkCanvas::clipRect))
749 .function("concat", optional_override([](SkCanvas& self, const SimpleMatrix& m) {
750 self.concat(toSkMatrix(m));
751 }))
752 .function("drawArc", &SkCanvas::drawArc)
753 .function("drawImage", select_overload<void (const sk_sp<SkImage>&, SkScalar, SkScalar, const SkPaint*)>(&SkCanvas::drawImage), allow_raw_pointers())
754 .function("drawImageRect", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
755 SkRect src, SkRect dst,
756 const SkPaint* paint, bool fastSample)->void {
757 self.drawImageRect(image, src, dst, paint,
758 fastSample ? SkCanvas::kFast_SrcRectConstraint :
759 SkCanvas::kStrict_SrcRectConstraint);
760 }), allow_raw_pointers())
761 .function("drawLine", select_overload<void (SkScalar, SkScalar, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawLine))
762 .function("drawOval", &SkCanvas::drawOval)
763 .function("drawPaint", &SkCanvas::drawPaint)
764 .function("drawPath", &SkCanvas::drawPath)
765 .function("drawRect", &SkCanvas::drawRect)
766 .function("drawRoundRect", &SkCanvas::drawRoundRect)
767 .function("drawShadow", optional_override([](SkCanvas& self, const SkPath& path,
768 const SkPoint3& zPlaneParams,
769 const SkPoint3& lightPos, SkScalar lightRadius,
770 JSColor ambientColor, JSColor spotColor,
771 uint32_t flags) {
772 SkShadowUtils::DrawShadow(&self, path, zPlaneParams, lightPos, lightRadius,
773 SkColor(ambientColor), SkColor(spotColor), flags);
774 }))
775 .function("_drawShapedText", &drawShapedText)
776 .function("_drawSimpleText", optional_override([](SkCanvas& self, uintptr_t /* char* */ sptr,
777 size_t len, SkScalar x, SkScalar y, const SkFont& font,
778 const SkPaint& paint) {
779 // See comment above for uintptr_t explanation
780 const char* str = reinterpret_cast<const char*>(sptr);
781
782 self.drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint);
783 }))
784 .function("drawTextBlob", select_overload<void (const sk_sp<SkTextBlob>&, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawTextBlob))
785 .function("drawVertices", select_overload<void (const sk_sp<SkVertices>&, SkBlendMode, const SkPaint&)>(&SkCanvas::drawVertices))
786 .function("flush", &SkCanvas::flush)
787 .function("getTotalMatrix", optional_override([](const SkCanvas& self)->SimpleMatrix {
788 SkMatrix m = self.getTotalMatrix();
789 return toSimpleSkMatrix(m);
790 }))
791 .function("makeSurface", optional_override([](SkCanvas& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
792 return self.makeSurface(toSkImageInfo(sii), nullptr);
793 }), allow_raw_pointers())
794 .function("_readPixels", optional_override([](SkCanvas& self, SimpleImageInfo di,
795 uintptr_t /* uint8_t* */ pPtr,
796 size_t dstRowBytes, int srcX, int srcY) {
797 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
798 SkImageInfo dstInfo = toSkImageInfo(di);
799
800 return self.readPixels(dstInfo, pixels, dstRowBytes, srcX, srcY);
801 }))
802 .function("restore", &SkCanvas::restore)
803 .function("restoreToCount", &SkCanvas::restoreToCount)
804 .function("rotate", select_overload<void (SkScalar, SkScalar, SkScalar)>(&SkCanvas::rotate))
805 .function("save", &SkCanvas::save)
806 .function("saveLayer", select_overload<int (const SkRect&, const SkPaint*)>(&SkCanvas::saveLayer),
807 allow_raw_pointers())
808 .function("scale", &SkCanvas::scale)
809 .function("skew", &SkCanvas::skew)
810 .function("translate", &SkCanvas::translate)
811 .function("_writePixels", optional_override([](SkCanvas& self, SimpleImageInfo di,
812 uintptr_t /* uint8_t* */ pPtr,
813 size_t srcRowBytes, int dstX, int dstY) {
814 uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
815 SkImageInfo dstInfo = toSkImageInfo(di);
816
817 return self.writePixels(dstInfo, pixels, srcRowBytes, dstX, dstY);
818 }))
819 ;
820
821 class_<SkData>("SkData")
822 .smart_ptr<sk_sp<SkData>>("sk_sp<SkData>>")
823 .function("size", &SkData::size);
824
825 class_<SkFont>("SkFont")
826 .constructor<>()
827 .constructor<sk_sp<SkTypeface>>()
828 .constructor<sk_sp<SkTypeface>, SkScalar>()
829 .constructor<sk_sp<SkTypeface>, SkScalar, SkScalar, SkScalar>()
830 .function("getScaleX", &SkFont::getScaleX)
831 .function("getSize", &SkFont::getSize)
832 .function("getSkewX", &SkFont::getSkewX)
833 .function("getTypeface", &SkFont::getTypeface, allow_raw_pointers())
834 .function("measureText", optional_override([](SkFont& self, std::string text) {
835 // TODO(kjlubick): This does not work well for non-ascii
836 // Need to maybe add a helper in interface.js that supports UTF-8
837 // Otherwise, go with std::wstring and set UTF-32 encoding.
838 return self.measureText(text.c_str(), text.length(), SkTextEncoding::kUTF8);
839 }))
840 .function("setScaleX", &SkFont::setScaleX)
841 .function("setSize", &SkFont::setSize)
842 .function("setSkewX", &SkFont::setSkewX)
843 .function("setTypeface", &SkFont::setTypeface, allow_raw_pointers());
844
845 class_<ShapedText>("ShapedText")
846 .constructor<ShapedTextOpts>()
847 .function("getBounds", &ShapedText::getBounds);
848
849 class_<SkFontMgr>("SkFontMgr")
850 .smart_ptr<sk_sp<SkFontMgr>>("sk_sp<SkFontMgr>")
851 .class_function("RefDefault", &SkFontMgr::RefDefault)
852 #ifdef SK_DEBUG
853 .function("dumpFamilies", optional_override([](SkFontMgr& self) {
854 int numFam = self.countFamilies();
855 SkDebugf("There are %d font families\n");
856 for (int i = 0 ; i< numFam; i++) {
857 SkString s;
858 self.getFamilyName(i, &s);
859 SkDebugf("\t%s", s.c_str());
860 }
861 }))
862 #endif
863 .function("countFamilies", &SkFontMgr::countFamilies)
864 .function("_makeTypefaceFromData", optional_override([](SkFontMgr& self,
865 uintptr_t /* uint8_t* */ fPtr,
866 int flen)->sk_sp<SkTypeface> {
867 // See comment above for uintptr_t explanation
868 uint8_t* font = reinterpret_cast<uint8_t*>(fPtr);
869 sk_sp<SkData> fontData = SkData::MakeFromMalloc(font, flen);
870
871 return self.makeFromData(fontData);
872 }), allow_raw_pointers());
873
874 class_<SkImage>("SkImage")
875 .smart_ptr<sk_sp<SkImage>>("sk_sp<SkImage>")
876 .function("height", &SkImage::height)
877 .function("width", &SkImage::width)
878 .function("_encodeToData", select_overload<sk_sp<SkData>()const>(&SkImage::encodeToData))
879 .function("_encodeToDataWithFormat", select_overload<sk_sp<SkData>(SkEncodedImageFormat encodedImageFormat, int quality)const>(&SkImage::encodeToData));
880
881 class_<SkMaskFilter>("SkMaskFilter")
882 .smart_ptr<sk_sp<SkMaskFilter>>("sk_sp<SkMaskFilter>");
883
884 class_<SkPaint>("SkPaint")
885 .constructor<>()
886 .function("copy", optional_override([](const SkPaint& self)->SkPaint {
887 SkPaint p(self);
888 return p;
889 }))
890 .function("getBlendMode", &SkPaint::getBlendMode)
891 .function("getColor", optional_override([](SkPaint& self)->JSColor {
892 // JS side gives us a signed int instead of an unsigned int for color
893 // Add a optional_override to change it out.
894 return JSColor(self.getColor());
895 }))
896 .function("getFilterQuality", &SkPaint::getFilterQuality)
897 .function("getStrokeCap", &SkPaint::getStrokeCap)
898 .function("getStrokeJoin", &SkPaint::getStrokeJoin)
899 .function("getStrokeMiter", &SkPaint::getStrokeMiter)
900 .function("getStrokeWidth", &SkPaint::getStrokeWidth)
901 .function("setAntiAlias", &SkPaint::setAntiAlias)
902 .function("setBlendMode", &SkPaint::setBlendMode)
903 .function("setColor", optional_override([](SkPaint& self, JSColor color)->void {
904 // JS side gives us a signed int instead of an unsigned int for color
905 // Add a optional_override to change it out.
906 self.setColor(SkColor(color));
907 }))
908 .function("setFilterQuality", &SkPaint::setFilterQuality)
909 .function("setMaskFilter", &SkPaint::setMaskFilter)
910 .function("setPathEffect", &SkPaint::setPathEffect)
911 .function("setShader", &SkPaint::setShader)
912 .function("setStrokeCap", &SkPaint::setStrokeCap)
913 .function("setStrokeJoin", &SkPaint::setStrokeJoin)
914 .function("setStrokeMiter", &SkPaint::setStrokeMiter)
915 .function("setStrokeWidth", &SkPaint::setStrokeWidth)
916 .function("setStyle", &SkPaint::setStyle);
917
918 class_<SkPathEffect>("SkPathEffect")
919 .smart_ptr<sk_sp<SkPathEffect>>("sk_sp<SkPathEffect>");
920
921 class_<SkPath>("SkPath")
922 .constructor<>()
923 .constructor<const SkPath&>()
924 .function("_addArc", &ApplyAddArc)
925 // interface.js has 3 overloads of addPath
926 .function("_addPath", &ApplyAddPath)
927 // interface.js has 4 overloads of addRect
928 .function("_addRect", &ApplyAddRect)
929 // interface.js has 4 overloads of addRoundRect
930 .function("_addRoundRect", &ApplyAddRoundRect)
931 .function("_arcTo", &ApplyArcTo)
932 .function("_arcTo", &ApplyArcToAngle)
933 .function("_close", &ApplyClose)
934 .function("_conicTo", &ApplyConicTo)
935 .function("countPoints", &SkPath::countPoints)
936 .function("contains", &SkPath::contains)
937 .function("_cubicTo", &ApplyCubicTo)
938 .function("getPoint", &SkPath::getPoint)
939 .function("isEmpty", &SkPath::isEmpty)
940 .function("isVolatile", &SkPath::isVolatile)
941 .function("_lineTo", &ApplyLineTo)
942 .function("_moveTo", &ApplyMoveTo)
943 .function("reset", &ApplyReset)
944 .function("rewind", &ApplyRewind)
945 .function("_quadTo", &ApplyQuadTo)
946 .function("setIsVolatile", &SkPath::setIsVolatile)
947 .function("_transform", select_overload<void(SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&ApplyTransform))
948
949 // PathEffects
950 .function("_dash", &ApplyDash)
951 .function("_trim", &ApplyTrim)
952 .function("_stroke", &ApplyStroke)
953
954 // PathOps
955 .function("_simplify", &ApplySimplify)
956 .function("_op", &ApplyPathOp)
957
958 // Exporting
959 .function("toSVGString", &ToSVGString)
960 .function("toCmds", &ToCmds)
961
962 .function("setFillType", &SkPath::setFillType)
963 .function("getFillType", &SkPath::getFillType)
964 .function("getBounds", &SkPath::getBounds)
965 .function("computeTightBounds", &SkPath::computeTightBounds)
966 .function("equals", &Equals)
967 .function("copy", &CopyPath)
968 #ifdef SK_DEBUG
969 .function("dump", select_overload<void() const>(&SkPath::dump))
970 .function("dumpHex", select_overload<void() const>(&SkPath::dumpHex))
971 #endif
972 ;
973
974 class_<SkShader>("SkShader")
975 .smart_ptr<sk_sp<SkShader>>("sk_sp<SkShader>");
976
977 class_<SkSurface>("SkSurface")
978 .smart_ptr<sk_sp<SkSurface>>("sk_sp<SkSurface>")
979 .function("_flush", select_overload<void()>(&SkSurface::flush))
980 .function("getCanvas", &SkSurface::getCanvas, allow_raw_pointers())
981 .function("height", &SkSurface::height)
982 .function("makeImageSnapshot", select_overload<sk_sp<SkImage>()>(&SkSurface::makeImageSnapshot))
983 .function("makeImageSnapshot", select_overload<sk_sp<SkImage>(const SkIRect& bounds)>(&SkSurface::makeImageSnapshot))
984 .function("makeSurface", optional_override([](SkSurface& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
985 return self.makeSurface(toSkImageInfo(sii));
986 }), allow_raw_pointers())
987 .function("width", &SkSurface::width);
988
989 class_<SkTextBlob>("SkTextBlob")
990 .smart_ptr<sk_sp<SkTextBlob>>("sk_sp<SkTextBlob>>")
991 .class_function("_MakeFromText", optional_override([](uintptr_t /* char* */ sptr,
992 size_t len, const SkFont& font,
993 SkTextEncoding encoding)->sk_sp<SkTextBlob> {
994 // See comment above for uintptr_t explanation
995 const char* str = reinterpret_cast<const char*>(sptr);
996 return SkTextBlob::MakeFromText(str, len, font, encoding);
997 }), allow_raw_pointers());
998
999
1000 class_<SkTypeface>("SkTypeface")
1001 .smart_ptr<sk_sp<SkTypeface>>("sk_sp<SkTypeface>");
1002
1003 class_<SkVertices>("SkVertices")
1004 .smart_ptr<sk_sp<SkVertices>>("sk_sp<SkVertices>")
1005 .function("_applyBones", optional_override([](SkVertices& self, uintptr_t /* Bone* */ bptr, int boneCount)->sk_sp<SkVertices> {
1006 // See comment above for uintptr_t explanation
1007 const Bone* bones = reinterpret_cast<const Bone*>(bptr);
1008 return self.applyBones(bones, boneCount);
1009 }))
1010 .function("bounds", &SkVertices::bounds)
1011 .function("mode", &SkVertices::mode)
1012 .function("uniqueID", &SkVertices::uniqueID)
1013 #ifdef SK_DEBUG
1014 .function("dumpPositions", optional_override([](SkVertices& self)->void {
1015 auto pos = self.positions();
1016 for(int i = 0; i< self.vertexCount(); i++) {
1017 SkDebugf("position[%d] = (%f, %f)\n", i, pos[i].x(), pos[i].y());
1018 }
1019 }))
1020 #endif
1021 .function("vertexCount", &SkVertices::vertexCount);
1022
1023 enum_<SkAlphaType>("AlphaType")
1024 .value("Opaque", SkAlphaType::kOpaque_SkAlphaType)
1025 .value("Premul", SkAlphaType::kPremul_SkAlphaType)
1026 .value("Unpremul", SkAlphaType::kUnpremul_SkAlphaType);
1027
1028 enum_<SkBlendMode>("BlendMode")
1029 .value("Clear", SkBlendMode::kClear)
1030 .value("Src", SkBlendMode::kSrc)
1031 .value("Dst", SkBlendMode::kDst)
1032 .value("SrcOver", SkBlendMode::kSrcOver)
1033 .value("DstOver", SkBlendMode::kDstOver)
1034 .value("SrcIn", SkBlendMode::kSrcIn)
1035 .value("DstIn", SkBlendMode::kDstIn)
1036 .value("SrcOut", SkBlendMode::kSrcOut)
1037 .value("DstOut", SkBlendMode::kDstOut)
1038 .value("SrcATop", SkBlendMode::kSrcATop)
1039 .value("DstATop", SkBlendMode::kDstATop)
1040 .value("Xor", SkBlendMode::kXor)
1041 .value("Plus", SkBlendMode::kPlus)
1042 .value("Modulate", SkBlendMode::kModulate)
1043 .value("Screen", SkBlendMode::kScreen)
1044 .value("Overlay", SkBlendMode::kOverlay)
1045 .value("Darken", SkBlendMode::kDarken)
1046 .value("Lighten", SkBlendMode::kLighten)
1047 .value("ColorDodge", SkBlendMode::kColorDodge)
1048 .value("ColorBurn", SkBlendMode::kColorBurn)
1049 .value("HardLight", SkBlendMode::kHardLight)
1050 .value("SoftLight", SkBlendMode::kSoftLight)
1051 .value("Difference", SkBlendMode::kDifference)
1052 .value("Exclusion", SkBlendMode::kExclusion)
1053 .value("Multiply", SkBlendMode::kMultiply)
1054 .value("Hue", SkBlendMode::kHue)
1055 .value("Saturation", SkBlendMode::kSaturation)
1056 .value("Color", SkBlendMode::kColor)
1057 .value("Luminosity", SkBlendMode::kLuminosity);
1058
1059 enum_<SkBlurStyle>("BlurStyle")
1060 .value("Normal", SkBlurStyle::kNormal_SkBlurStyle)
1061 .value("Solid", SkBlurStyle::kSolid_SkBlurStyle)
1062 .value("Outer", SkBlurStyle::kOuter_SkBlurStyle)
1063 .value("Inner", SkBlurStyle::kInner_SkBlurStyle);
1064
1065 enum_<SkClipOp>("ClipOp")
1066 .value("Difference", SkClipOp::kDifference)
1067 .value("Intersect", SkClipOp::kIntersect);
1068
1069 enum_<SkColorType>("ColorType")
1070 .value("Alpha_8", SkColorType::kAlpha_8_SkColorType)
1071 .value("RGB_565", SkColorType::kRGB_565_SkColorType)
1072 .value("ARGB_4444", SkColorType::kARGB_4444_SkColorType)
1073 .value("RGBA_8888", SkColorType::kRGBA_8888_SkColorType)
1074 .value("RGB_888x", SkColorType::kRGB_888x_SkColorType)
1075 .value("BGRA_8888", SkColorType::kBGRA_8888_SkColorType)
1076 .value("RGBA_1010102", SkColorType::kRGBA_1010102_SkColorType)
1077 .value("RGB_101010x", SkColorType::kRGB_101010x_SkColorType)
1078 .value("Gray_8", SkColorType::kGray_8_SkColorType)
1079 .value("RGBA_F16", SkColorType::kRGBA_F16_SkColorType)
1080 .value("RGBA_F32", SkColorType::kRGBA_F32_SkColorType);
1081
1082 enum_<SkPath::FillType>("FillType")
1083 .value("Winding", SkPath::FillType::kWinding_FillType)
1084 .value("EvenOdd", SkPath::FillType::kEvenOdd_FillType)
1085 .value("InverseWinding", SkPath::FillType::kInverseWinding_FillType)
1086 .value("InverseEvenOdd", SkPath::FillType::kInverseEvenOdd_FillType);
1087
1088 enum_<SkFilterQuality>("FilterQuality")
1089 .value("None", SkFilterQuality::kNone_SkFilterQuality)
1090 .value("Low", SkFilterQuality::kLow_SkFilterQuality)
1091 .value("Medium", SkFilterQuality::kMedium_SkFilterQuality)
1092 .value("High", SkFilterQuality::kHigh_SkFilterQuality);
1093
1094 enum_<SkEncodedImageFormat>("ImageFormat")
1095 .value("PNG", SkEncodedImageFormat::kPNG)
1096 .value("JPEG", SkEncodedImageFormat::kJPEG);
1097
1098 enum_<SkPaint::Style>("PaintStyle")
1099 .value("Fill", SkPaint::Style::kFill_Style)
1100 .value("Stroke", SkPaint::Style::kStroke_Style)
1101 .value("StrokeAndFill", SkPaint::Style::kStrokeAndFill_Style);
1102
1103 enum_<SkPathOp>("PathOp")
1104 .value("Difference", SkPathOp::kDifference_SkPathOp)
1105 .value("Intersect", SkPathOp::kIntersect_SkPathOp)
1106 .value("Union", SkPathOp::kUnion_SkPathOp)
1107 .value("XOR", SkPathOp::kXOR_SkPathOp)
1108 .value("ReverseDifference", SkPathOp::kReverseDifference_SkPathOp);
1109
1110 enum_<SkPaint::Cap>("StrokeCap")
1111 .value("Butt", SkPaint::Cap::kButt_Cap)
1112 .value("Round", SkPaint::Cap::kRound_Cap)
1113 .value("Square", SkPaint::Cap::kSquare_Cap);
1114
1115 enum_<SkPaint::Join>("StrokeJoin")
1116 .value("Miter", SkPaint::Join::kMiter_Join)
1117 .value("Round", SkPaint::Join::kRound_Join)
1118 .value("Bevel", SkPaint::Join::kBevel_Join);
1119
1120 enum_<SkTextEncoding>("TextEncoding")
1121 .value("UTF8", SkTextEncoding::kUTF8)
1122 .value("UTF16", SkTextEncoding::kUTF16)
1123 .value("UTF32", SkTextEncoding::kUTF32)
1124 .value("GlyphID", SkTextEncoding::kGlyphID);
1125
1126 enum_<SkShader::TileMode>("TileMode")
1127 .value("Clamp", SkShader::TileMode::kClamp_TileMode)
1128 .value("Repeat", SkShader::TileMode::kRepeat_TileMode)
1129 .value("Mirror", SkShader::TileMode::kMirror_TileMode)
1130 // Decal mode only works in the SW backend, not WebGl (yet).
1131 .value("Decal", SkShader::TileMode::kDecal_TileMode);
1132
1133 enum_<SkVertices::VertexMode>("VertexMode")
1134 .value("Triangles", SkVertices::VertexMode::kTriangles_VertexMode)
1135 .value("TrianglesStrip", SkVertices::VertexMode::kTriangleStrip_VertexMode)
1136 .value("TriangleFan", SkVertices::VertexMode::kTriangleFan_VertexMode);
1137
1138
1139 // A value object is much simpler than a class - it is returned as a JS
1140 // object and does not require delete().
1141 // https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#value-types
1142 value_object<ShapedTextOpts>("ShapedTextOpts")
1143 .field("font", &ShapedTextOpts::font)
1144 .field("leftToRight", &ShapedTextOpts::leftToRight)
1145 .field("text", &ShapedTextOpts::text)
1146 .field("width", &ShapedTextOpts::width);
1147
1148 value_object<SkRect>("SkRect")
1149 .field("fLeft", &SkRect::fLeft)
1150 .field("fTop", &SkRect::fTop)
1151 .field("fRight", &SkRect::fRight)
1152 .field("fBottom", &SkRect::fBottom);
1153
1154 value_object<SkIRect>("SkIRect")
1155 .field("fLeft", &SkIRect::fLeft)
1156 .field("fTop", &SkIRect::fTop)
1157 .field("fRight", &SkIRect::fRight)
1158 .field("fBottom", &SkIRect::fBottom);
1159
1160 value_object<SimpleImageInfo>("SkImageInfo")
1161 .field("width", &SimpleImageInfo::width)
1162 .field("height", &SimpleImageInfo::height)
1163 .field("colorType", &SimpleImageInfo::colorType)
1164 .field("alphaType", &SimpleImageInfo::alphaType);
1165
1166 // SkPoints can be represented by [x, y]
1167 value_array<SkPoint>("SkPoint")
1168 .element(&SkPoint::fX)
1169 .element(&SkPoint::fY);
1170
1171 // SkPoint3s can be represented by [x, y, z]
1172 value_array<SkPoint3>("SkPoint3")
1173 .element(&SkPoint3::fX)
1174 .element(&SkPoint3::fY)
1175 .element(&SkPoint3::fZ);
1176
1177 // {"w": Number, "h", Number}
1178 value_object<SkSize>("SkSize")
1179 .field("w", &SkSize::fWidth)
1180 .field("h", &SkSize::fHeight);
1181
1182 value_object<SkISize>("SkISize")
1183 .field("w", &SkISize::fWidth)
1184 .field("h", &SkISize::fHeight);
1185
1186 value_object<StrokeOpts>("StrokeOpts")
1187 .field("width", &StrokeOpts::width)
1188 .field("miter_limit", &StrokeOpts::miter_limit)
1189 .field("join", &StrokeOpts::join)
1190 .field("cap", &StrokeOpts::cap)
1191 .field("precision", &StrokeOpts::precision);
1192
1193 // Allows clients to supply a 1D array of 9 elements and the bindings
1194 // will automatically turn it into a 3x3 2D matrix.
1195 // e.g. path.transform([0,1,2,3,4,5,6,7,8])
1196 // This is likely simpler for the client than exposing SkMatrix
1197 // directly and requiring them to do a lot of .delete().
1198 value_array<SimpleMatrix>("SkMatrix")
1199 .element(&SimpleMatrix::scaleX)
1200 .element(&SimpleMatrix::skewX)
1201 .element(&SimpleMatrix::transX)
1202
1203 .element(&SimpleMatrix::skewY)
1204 .element(&SimpleMatrix::scaleY)
1205 .element(&SimpleMatrix::transY)
1206
1207 .element(&SimpleMatrix::pers0)
1208 .element(&SimpleMatrix::pers1)
1209 .element(&SimpleMatrix::pers2);
1210
1211 constant("TRANSPARENT", (JSColor) SK_ColorTRANSPARENT);
1212 constant("RED", (JSColor) SK_ColorRED);
1213 constant("BLUE", (JSColor) SK_ColorBLUE);
1214 constant("YELLOW", (JSColor) SK_ColorYELLOW);
1215 constant("CYAN", (JSColor) SK_ColorCYAN);
1216 constant("BLACK", (JSColor) SK_ColorBLACK);
1217 constant("WHITE", (JSColor) SK_ColorWHITE);
1218 // TODO(?)
1219
1220 constant("MOVE_VERB", MOVE);
1221 constant("LINE_VERB", LINE);
1222 constant("QUAD_VERB", QUAD);
1223 constant("CONIC_VERB", CONIC);
1224 constant("CUBIC_VERB", CUBIC);
1225 constant("CLOSE_VERB", CLOSE);
1226 }
1227