blob: 5587cadc0d34ccda229eee88f6d6e568b5d02246 [file] [log] [blame]
Kevin Lubick22647d02018-07-06 14:31:23 -04001/*
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
Kevin Lubick92eaa3c2018-07-16 21:00:52 -04008#include "SkFloatBits.h"
Kevin Lubick22647d02018-07-06 14:31:23 -04009#include "SkFloatingPoint.h"
Kevin Lubick641bf872018-08-06 14:49:39 -040010#include "SkMatrix.h"
Kevin Lubick22647d02018-07-06 14:31:23 -040011#include "SkParsePath.h"
12#include "SkPath.h"
13#include "SkPathOps.h"
Kevin Lubick641bf872018-08-06 14:49:39 -040014#include "SkRect.h"
Kevin Lubick22647d02018-07-06 14:31:23 -040015#include "SkString.h"
16
17#include <emscripten/emscripten.h>
18#include <emscripten/bind.h>
19
20using namespace emscripten;
21
22static const int MOVE = 0;
23static const int LINE = 1;
24static const int QUAD = 2;
25static const int CUBIC = 4;
26static const int CLOSE = 5;
27
Kevin Lubick5f0e3a12018-08-07 11:30:12 -040028// Just for self-documenting purposes where the main thing being returned is an
29// SkPath, but in an error case, something of type val (e.g. null) could also be
30// returned;
31using SkPathOrVal = emscripten::val;
32
Kevin Lubick22647d02018-07-06 14:31:23 -040033// =================================================================================
Kevin Lubick641bf872018-08-06 14:49:39 -040034// Creating/Exporting Paths with cmd arrays
Kevin Lubick22647d02018-07-06 14:31:23 -040035// =================================================================================
36
Florin Malitae1824da2018-07-12 10:33:39 -040037template <typename VisitFunc>
38void VisitPath(const SkPath& p, VisitFunc&& f) {
39 SkPath::RawIter iter(p);
Kevin Lubick22647d02018-07-06 14:31:23 -040040 SkPoint pts[4];
41 SkPath::Verb verb;
Florin Malitae1824da2018-07-12 10:33:39 -040042 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
Kevin Lubick641bf872018-08-06 14:49:39 -040043 f(verb, pts, iter);
Kevin Lubick22647d02018-07-06 14:31:23 -040044 }
45}
46
Kevin Lubick641bf872018-08-06 14:49:39 -040047emscripten::val EMSCRIPTEN_KEEPALIVE ToCmds(const SkPath& path) {
Kevin Lubick5f0e3a12018-08-07 11:30:12 -040048 emscripten::val cmds = emscripten::val::array();
Kevin Lubick22647d02018-07-06 14:31:23 -040049
Kevin Lubick641bf872018-08-06 14:49:39 -040050 VisitPath(path, [&cmds](SkPath::Verb verb, const SkPoint pts[4], SkPath::RawIter iter) {
Kevin Lubick5f0e3a12018-08-07 11:30:12 -040051 emscripten::val cmd = emscripten::val::array();
Kevin Lubick22647d02018-07-06 14:31:23 -040052 switch (verb) {
Florin Malitae1824da2018-07-12 10:33:39 -040053 case SkPath::kMove_Verb:
54 cmd.call<void>("push", MOVE, pts[0].x(), pts[0].y());
55 break;
56 case SkPath::kLine_Verb:
57 cmd.call<void>("push", LINE, pts[1].x(), pts[1].y());
58 break;
59 case SkPath::kQuad_Verb:
60 cmd.call<void>("push", QUAD, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
61 break;
62 case SkPath::kConic_Verb:
Kevin Lubick641bf872018-08-06 14:49:39 -040063 SkPoint quads[5];
64 // approximate with 2^1=2 quads.
65 SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(), quads, 1);
66 cmd.call<void>("push", MOVE, quads[0].x(), quads[0].y());
67 cmds.call<void>("push", cmd);
68 cmd = emscripten::val::array();
69 cmd.call<void>("push", QUAD, quads[1].x(), quads[1].y(), quads[2].x(), quads[2].y());
70 cmds.call<void>("push", cmd);
71 cmd = emscripten::val::array();
72 cmd.call<void>("push", QUAD, quads[3].x(), quads[3].y(), quads[4].x(), quads[4].y());
Florin Malitae1824da2018-07-12 10:33:39 -040073 break;
74 case SkPath::kCubic_Verb:
75 cmd.call<void>("push", CUBIC,
76 pts[1].x(), pts[1].y(),
77 pts[2].x(), pts[2].y(),
78 pts[3].x(), pts[3].y());
79 break;
80 case SkPath::kClose_Verb:
81 cmd.call<void>("push", CLOSE);
82 break;
83 case SkPath::kDone_Verb:
84 SkASSERT(false);
85 break;
Kevin Lubick22647d02018-07-06 14:31:23 -040086 }
87 cmds.call<void>("push", cmd);
Florin Malitae1824da2018-07-12 10:33:39 -040088 });
Kevin Lubick22647d02018-07-06 14:31:23 -040089 return cmds;
90}
91
92// This type signature is a mess, but it's necessary. See, we can't use "bind" (EMSCRIPTEN_BINDINGS)
93// and pointers to primitive types (Only bound types like SkPoint). We could if we used
94// cwrap (see https://becominghuman.ai/passing-and-returning-webassembly-array-parameters-a0f572c65d97)
95// but that requires us to stick to C code and, AFAIK, doesn't allow us to return nice things like
96// SkPath or SkOpBuilder.
97//
98// So, basically, if we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primative pointers
99// in our function type signatures. (this gives an error message like "Cannot call foo due to unbound
100// types Pi, Pf"). But, we can just pretend they are numbers and cast them to be pointers and
101// the compiler is happy.
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400102SkPathOrVal EMSCRIPTEN_KEEPALIVE FromCmds(uintptr_t /* float* */ cptr, int numCmds) {
Florin Malitae1824da2018-07-12 10:33:39 -0400103 const auto* cmds = reinterpret_cast<const float*>(cptr);
Kevin Lubick22647d02018-07-06 14:31:23 -0400104 SkPath path;
105 float x1, y1, x2, y2, x3, y3;
106
107 // if there are not enough arguments, bail with the path we've constructed so far.
108 #define CHECK_NUM_ARGS(n) \
109 if ((i + n) > numCmds) { \
110 SkDebugf("Not enough args to match the verbs. Saw %d commands\n", numCmds); \
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400111 return emscripten::val::null(); \
Kevin Lubick22647d02018-07-06 14:31:23 -0400112 }
113
114 for(int i = 0; i < numCmds;){
115 switch (sk_float_floor2int(cmds[i++])) {
116 case MOVE:
117 CHECK_NUM_ARGS(2);
118 x1 = cmds[i++], y1 = cmds[i++];
119 path.moveTo(x1, y1);
120 break;
121 case LINE:
122 CHECK_NUM_ARGS(2);
123 x1 = cmds[i++], y1 = cmds[i++];
124 path.lineTo(x1, y1);
125 break;
126 case QUAD:
127 CHECK_NUM_ARGS(4);
128 x1 = cmds[i++], y1 = cmds[i++];
129 x2 = cmds[i++], y2 = cmds[i++];
130 path.quadTo(x1, y1, x2, y2);
131 break;
132 case CUBIC:
133 CHECK_NUM_ARGS(6);
134 x1 = cmds[i++], y1 = cmds[i++];
135 x2 = cmds[i++], y2 = cmds[i++];
136 x3 = cmds[i++], y3 = cmds[i++];
137 path.cubicTo(x1, y1, x2, y2, x3, y3);
138 break;
139 case CLOSE:
140 path.close();
141 break;
142 default:
143 SkDebugf(" path: UNKNOWN command %f, aborting dump...\n", cmds[i-1]);
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400144 return emscripten::val::null();
Kevin Lubick22647d02018-07-06 14:31:23 -0400145 }
146 }
147
148 #undef CHECK_NUM_ARGS
149
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400150 return emscripten::val(path);
Kevin Lubick22647d02018-07-06 14:31:23 -0400151}
152
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400153SkPath EMSCRIPTEN_KEEPALIVE NewPath() {
154 return SkPath();
155}
156
Kevin Lubick22647d02018-07-06 14:31:23 -0400157//========================================================================================
Kevin Lubick641bf872018-08-06 14:49:39 -0400158// SVG things
Kevin Lubick22647d02018-07-06 14:31:23 -0400159//========================================================================================
160
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400161emscripten::val EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) {
Kevin Lubick22647d02018-07-06 14:31:23 -0400162 SkString s;
163 SkParsePath::ToSVGString(path, &s);
164 // Wrapping it in val automatically turns it into a JS string.
165 // Not too sure on performance implications, but is is simpler than
166 // returning a raw pointer to const char * and then using
167 // Pointer_stringify() on the calling side.
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400168 return emscripten::val(s.c_str());
Kevin Lubick22647d02018-07-06 14:31:23 -0400169}
170
171
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400172SkPathOrVal EMSCRIPTEN_KEEPALIVE FromSVGString(std::string str) {
Kevin Lubick22647d02018-07-06 14:31:23 -0400173 SkPath path;
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400174 if (SkParsePath::FromSVGString(str.c_str(), &path)) {
175 return emscripten::val(path);
176 }
177 return emscripten::val::null();
Kevin Lubick22647d02018-07-06 14:31:23 -0400178}
179
180//========================================================================================
Kevin Lubick641bf872018-08-06 14:49:39 -0400181// PATHOP things
Kevin Lubick22647d02018-07-06 14:31:23 -0400182//========================================================================================
183
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400184SkPathOrVal EMSCRIPTEN_KEEPALIVE SimplifyPath(const SkPath& path) {
Kevin Lubick22647d02018-07-06 14:31:23 -0400185 SkPath simple;
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400186 if (Simplify(path, &simple)) {
187 return emscripten::val(simple);
188 }
189 return emscripten::val::null();
Kevin Lubick22647d02018-07-06 14:31:23 -0400190}
191
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400192SkPathOrVal EMSCRIPTEN_KEEPALIVE ApplyPathOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
Kevin Lubick22647d02018-07-06 14:31:23 -0400193 SkPath path;
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400194 if (Op(pathOne, pathTwo, op, &path)) {
195 return emscripten::val(path);
196 }
197 return emscripten::val::null();
Kevin Lubick22647d02018-07-06 14:31:23 -0400198}
199
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400200SkPathOrVal EMSCRIPTEN_KEEPALIVE ResolveBuilder(SkOpBuilder& builder) {
Kevin Lubick22647d02018-07-06 14:31:23 -0400201 SkPath path;
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400202 if (builder.resolve(&path)) {
203 return emscripten::val(path);
204 }
205 return emscripten::val::null();
Kevin Lubick22647d02018-07-06 14:31:23 -0400206}
207
208//========================================================================================
Kevin Lubick641bf872018-08-06 14:49:39 -0400209// Canvas things
Kevin Lubick22647d02018-07-06 14:31:23 -0400210//========================================================================================
211
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400212void EMSCRIPTEN_KEEPALIVE ToCanvas(const SkPath& path, emscripten::val /* Path2D or Canvas*/ ctx) {
Kevin Lubick22647d02018-07-06 14:31:23 -0400213 SkPath::Iter iter(path, false);
214 SkPoint pts[4];
215 SkPath::Verb verb;
216 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
217 switch (verb) {
218 case SkPath::kMove_Verb:
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400219 ctx.call<void>("moveTo", pts[0].x(), pts[0].y());
Kevin Lubick22647d02018-07-06 14:31:23 -0400220 break;
221 case SkPath::kLine_Verb:
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400222 ctx.call<void>("lineTo", pts[1].x(), pts[1].y());
Kevin Lubick22647d02018-07-06 14:31:23 -0400223 break;
224 case SkPath::kQuad_Verb:
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400225 ctx.call<void>("quadraticCurveTo", pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
Kevin Lubick22647d02018-07-06 14:31:23 -0400226 break;
227 case SkPath::kConic_Verb:
Kevin Lubick641bf872018-08-06 14:49:39 -0400228 SkPoint quads[5];
229 // approximate with 2^1=2 quads.
230 SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(), quads, 1);
231 ctx.call<void>("moveTo", quads[0].x(), quads[0].y());
232 ctx.call<void>("quadraticCurveTo", quads[1].x(), quads[1].y(), quads[2].x(), quads[2].y());
233 ctx.call<void>("quadraticCurveTo", quads[3].x(), quads[3].y(), quads[4].x(), quads[4].y());
Kevin Lubick22647d02018-07-06 14:31:23 -0400234 break;
235 case SkPath::kCubic_Verb:
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400236 ctx.call<void>("bezierCurveTo", pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y(),
Kevin Lubick22647d02018-07-06 14:31:23 -0400237 pts[3].x(), pts[3].y());
238 break;
239 case SkPath::kClose_Verb:
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400240 ctx.call<void>("closePath");
Kevin Lubick22647d02018-07-06 14:31:23 -0400241 break;
242 case SkPath::kDone_Verb:
243 break;
244 }
245 }
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400246}
247
248emscripten::val JSPath2D = emscripten::val::global("Path2D");
249
Kevin Lubick641bf872018-08-06 14:49:39 -0400250emscripten::val EMSCRIPTEN_KEEPALIVE ToPath2D(const SkPath& path) {
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400251 emscripten::val retVal = JSPath2D.new_();
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400252 ToCanvas(path, retVal);
Kevin Lubick22647d02018-07-06 14:31:23 -0400253 return retVal;
254}
255
Kevin Lubick641bf872018-08-06 14:49:39 -0400256// ======================================================================================
257// Path2D API things
258// ======================================================================================
259void Path2DAddRect(SkPath& path, SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
260 path.addRect(x, y, x+width, y+height);
261}
262
263void Path2DAddArc(SkPath& path, SkScalar x, SkScalar y, SkScalar radius,
264 SkScalar startAngle, SkScalar endAngle, bool ccw) {
265 SkPath temp;
266 SkRect bounds = SkRect::MakeLTRB(x-radius, y-radius, x+radius, y+radius);
267 const auto sweep = SkRadiansToDegrees(endAngle - startAngle) - 360 * ccw;
268 temp.addArc(bounds, SkRadiansToDegrees(startAngle), sweep);
269 path.addPath(temp, SkPath::kExtend_AddPathMode);
270}
271
272void Path2DAddArc(SkPath& path, SkScalar x, SkScalar y, SkScalar radius,
273 SkScalar startAngle, SkScalar endAngle) {
274 Path2DAddArc(path, x, y, radius, startAngle, endAngle, false);
275}
276
277void Path2DAddEllipse(SkPath& path, SkScalar x, SkScalar y, SkScalar radiusX, SkScalar radiusY,
278 SkScalar rotation, SkScalar startAngle, SkScalar endAngle, bool ccw) {
279 // This is easiest to do by making a new path and then extending the current path
280 // (this properly catches the cases of if there's a moveTo before this call or not).
281 SkRect bounds = SkRect::MakeLTRB(x-radiusX, y-radiusY, x+radiusX, y+radiusY);
282 SkPath temp;
283 const auto sweep = SkRadiansToDegrees(endAngle - startAngle) - (360 * ccw);
284 temp.addArc(bounds, SkRadiansToDegrees(startAngle), sweep);
285
286 SkMatrix m;
287 m.setRotate(SkRadiansToDegrees(rotation), x, y);
288 path.addPath(temp, m, SkPath::kExtend_AddPathMode);
289}
290
291void Path2DAddEllipse(SkPath& path, SkScalar x, SkScalar y, SkScalar radiusX, SkScalar radiusY,
292 SkScalar rotation, SkScalar startAngle, SkScalar endAngle) {
293 Path2DAddEllipse(path, x, y, radiusX, radiusY, rotation, startAngle, endAngle, false);
294}
295
296void Path2DAddPath(SkPath& orig, const SkPath& newPath) {
297 orig.addPath(newPath);
298}
299
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400300void Path2DAddPath(SkPath& orig, const SkPath& newPath, emscripten::val /* SVGMatrix*/ t) {
Kevin Lubick641bf872018-08-06 14:49:39 -0400301 SkMatrix m = SkMatrix::MakeAll(
302 t["a"].as<SkScalar>(), t["c"].as<SkScalar>(), t["e"].as<SkScalar>(),
303 t["b"].as<SkScalar>(), t["d"].as<SkScalar>(), t["f"].as<SkScalar>(),
304 0 , 0 , 1);
305 orig.addPath(newPath, m);
306}
307
308// Mimics the order of SVGMatrix, just w/o the SVG Matrix
309// This order is scaleX, skewY, skewX, scaleY, transX, transY
310// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform#Transform_functions
311void Path2DAddPath(SkPath& orig, const SkPath& newPath, SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar e, SkScalar f) {
312 SkMatrix m = SkMatrix::MakeAll(a, c, e,
313 b, d, f,
314 0, 0, 1);
315 orig.addPath(newPath, m);
316}
317
318// Allows for full matix control.
319void Path2DAddPath(SkPath& orig, const SkPath& newPath,
320 SkScalar scaleX, SkScalar skewX, SkScalar transX,
321 SkScalar skewY, SkScalar scaleY, SkScalar transY,
322 SkScalar pers0, SkScalar pers1, SkScalar pers2) {
323 SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
324 skewY , scaleY, transY,
325 pers0 , pers1 , pers2);
326 orig.addPath(newPath, m);
327}
328
Kevin Lubick92eaa3c2018-07-16 21:00:52 -0400329//========================================================================================
Kevin Lubick644d8e72018-08-09 13:58:04 -0400330// Testing things
Kevin Lubick92eaa3c2018-07-16 21:00:52 -0400331//========================================================================================
332
Kevin Lubick644d8e72018-08-09 13:58:04 -0400333// The use case for this is on the JS side is something like:
334// PathKit.SkBits2FloatUnsigned(parseInt("0xc0a00000"))
335// to have precise float values for tests. In the C++ tests, we can use SkBits2Float because
336// it takes int32_t, but the JS parseInt basically returns an unsigned int. So, we add in
337// this helper which casts for us on the way to SkBits2Float.
338float SkBits2FloatUnsigned(uint32_t floatAsBits) {
339 return SkBits2Float((int32_t) floatAsBits);
Kevin Lubick92eaa3c2018-07-16 21:00:52 -0400340}
341
Kevin Lubick22647d02018-07-06 14:31:23 -0400342// Binds the classes to the JS
Kevin Lubick641bf872018-08-06 14:49:39 -0400343//
344// See https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#non-member-functions-on-the-javascript-prototype
345// for more on binding non-member functions to the JS object, allowing us to rewire
346// various functions. That is, we can make the SkPath we expose appear to have methods
347// that the original SkPath does not, like rect(x, y, width, height) and toPath2D().
348//
349// An important detail for binding non-member functions is that the first argument
350// must be SkPath& (the reference part is very important).
Kevin Lubick22647d02018-07-06 14:31:23 -0400351EMSCRIPTEN_BINDINGS(skia) {
352 class_<SkPath>("SkPath")
353 .constructor<>()
354
Kevin Lubick641bf872018-08-06 14:49:39 -0400355 // Path2D API
356 .function("addPath",
357 select_overload<void(SkPath&, const SkPath&)>(&Path2DAddPath))
358 .function("addPath",
Kevin Lubick5f0e3a12018-08-07 11:30:12 -0400359 select_overload<void(SkPath&, const SkPath&, emscripten::val)>(&Path2DAddPath))
Kevin Lubick641bf872018-08-06 14:49:39 -0400360 .function("arc",
361 select_overload<void(SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&Path2DAddArc))
362 .function("arc",
363 select_overload<void(SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, bool)>(&Path2DAddArc))
364 .function("arcTo",
365 select_overload<void(SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&SkPath::arcTo))
366 .function("bezierCurveTo",
367 select_overload<void(SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&SkPath::cubicTo))
368 .function("closePath", &SkPath::close)
369 .function("ellipse",
370 select_overload<void(SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&Path2DAddEllipse))
371 .function("ellipse",
372 select_overload<void(SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, bool)>(&Path2DAddEllipse))
Kevin Lubick22647d02018-07-06 14:31:23 -0400373 .function("lineTo",
374 select_overload<void(SkScalar, SkScalar)>(&SkPath::lineTo))
Kevin Lubick641bf872018-08-06 14:49:39 -0400375 .function("moveTo",
376 select_overload<void(SkScalar, SkScalar)>(&SkPath::moveTo))
377 .function("quadraticCurveTo",
Kevin Lubick22647d02018-07-06 14:31:23 -0400378 select_overload<void(SkScalar, SkScalar, SkScalar, SkScalar)>(&SkPath::quadTo))
Kevin Lubick641bf872018-08-06 14:49:39 -0400379 .function("rect", &Path2DAddRect)
380
381 // Some shorthand helpers, to mirror SkPath.cpp's API
382 .function("addPath",
383 select_overload<void(SkPath&, const SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&Path2DAddPath))
384 .function("addPath",
385 select_overload<void(SkPath&, const SkPath&, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&Path2DAddPath))
386 .function("close", &SkPath::close)
Kevin Lubick22647d02018-07-06 14:31:23 -0400387 .function("cubicTo",
388 select_overload<void(SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&SkPath::cubicTo))
Kevin Lubick641bf872018-08-06 14:49:39 -0400389 .function("quadTo",
390 select_overload<void(SkScalar, SkScalar, SkScalar, SkScalar)>(&SkPath::quadTo))
391
Kevin Lubick644d8e72018-08-09 13:58:04 -0400392 // Extra features
393 .function("setFillType", &SkPath::setFillType)
394 .function("getFillType", &SkPath::getFillType)
395 .function("getBounds", &SkPath::getBounds)
396 .function("computeTightBounds", &SkPath::computeTightBounds)
397
Kevin Lubick641bf872018-08-06 14:49:39 -0400398 // PathOps
399 .function("simplify", &SimplifyPath)
400 .function("op", &ApplyPathOp)
401
402 // Exporting
403 .function("toCmds", &ToCmds)
404 .function("toPath2D", &ToPath2D)
405 .function("toCanvas", &ToCanvas)
406 .function("toSVGString", &ToSVGString)
407
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400408#ifdef PATHKIT_TESTING
409 .function("dump", select_overload<void() const>(&SkPath::dump))
410#endif
411 ;
Kevin Lubick22647d02018-07-06 14:31:23 -0400412
413 class_<SkOpBuilder>("SkOpBuilder")
414 .constructor<>()
415
Kevin Lubick641bf872018-08-06 14:49:39 -0400416 .function("add", &SkOpBuilder::add)
417 .function("resolve", &ResolveBuilder);
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400418
419 // Without these function() bindings, the function would be exposed but oblivious to
420 // our types (e.g. SkPath)
421
422 // Import
423 function("FromSVGString", &FromSVGString);
424 function("FromCmds", &FromCmds);
425 function("NewPath", &NewPath);
426 // Path2D is opaque, so we can't read in from it.
427
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400428 // PathOps
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400429 function("ApplyPathOp", &ApplyPathOp);
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400430
431 enum_<SkPathOp>("PathOp")
432 .value("DIFFERENCE", SkPathOp::kDifference_SkPathOp)
433 .value("INTERSECT", SkPathOp::kIntersect_SkPathOp)
434 .value("UNION", SkPathOp::kUnion_SkPathOp)
435 .value("XOR", SkPathOp::kXOR_SkPathOp)
436 .value("REVERSE_DIFFERENCE", SkPathOp::kReverseDifference_SkPathOp);
437
Kevin Lubick644d8e72018-08-09 13:58:04 -0400438 enum_<SkPath::FillType>("FillType")
439 .value("WINDING", SkPath::FillType::kWinding_FillType)
440 .value("EVENODD", SkPath::FillType::kEvenOdd_FillType)
441 .value("INVERSE_WINDING", SkPath::FillType::kInverseWinding_FillType)
442 .value("INVERSE_EVENODD", SkPath::FillType::kInverseEvenOdd_FillType);
443
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400444 constant("MOVE_VERB", MOVE);
445 constant("LINE_VERB", LINE);
446 constant("QUAD_VERB", QUAD);
447 constant("CUBIC_VERB", CUBIC);
448 constant("CLOSE_VERB", CLOSE);
449
Kevin Lubick644d8e72018-08-09 13:58:04 -0400450 // A value object is much simpler than a class - it is returned as a JS
451 // object and does not require delete().
452 // https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#value-types
453 value_object<SkRect>("SkRect")
454 .field("fLeft", &SkRect::fLeft)
455 .field("fTop", &SkRect::fTop)
456 .field("fRight", &SkRect::fRight)
457 .field("fBottom", &SkRect::fBottom);
458
459 function("MakeLTRBRect", &SkRect::MakeLTRB);
460
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400461 // coming soon - Stroke
462
463 // coming soon - Matrix
464
Kevin Lubick644d8e72018-08-09 13:58:04 -0400465 // coming soon - Trim
Kevin Lubicke1b36fe2018-08-02 11:30:33 -0400466
Kevin Lubick644d8e72018-08-09 13:58:04 -0400467 // Test Utils
468 function("SkBits2FloatUnsigned", &SkBits2FloatUnsigned);
Kevin Lubick22647d02018-07-06 14:31:23 -0400469}