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