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