pathkit_wasm_bindings.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  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. #include "include/core/SkCubicMap.h"
  8. #include "include/core/SkMatrix.h"
  9. #include "include/core/SkPaint.h"
  10. #include "include/core/SkPath.h"
  11. #include "include/core/SkRect.h"
  12. #include "include/core/SkString.h"
  13. #include "include/core/SkStrokeRec.h"
  14. #include "include/effects/SkDashPathEffect.h"
  15. #include "include/effects/SkTrimPathEffect.h"
  16. #include "include/pathops/SkPathOps.h"
  17. #include "include/private/SkFloatBits.h"
  18. #include "include/private/SkFloatingPoint.h"
  19. #include "include/utils/SkParsePath.h"
  20. #include "src/core/SkPaintDefaults.h"
  21. #include <emscripten/emscripten.h>
  22. #include <emscripten/bind.h>
  23. using namespace emscripten;
  24. static const int MOVE = 0;
  25. static const int LINE = 1;
  26. static const int QUAD = 2;
  27. static const int CONIC = 3;
  28. static const int CUBIC = 4;
  29. static const int CLOSE = 5;
  30. // Just for self-documenting purposes where the main thing being returned is an
  31. // SkPath, but in an error case, something of type null (which is val) could also be
  32. // returned;
  33. using SkPathOrNull = emscripten::val;
  34. // Self-documenting for when we return a string
  35. using JSString = emscripten::val;
  36. using JSArray = emscripten::val;
  37. // =================================================================================
  38. // Creating/Exporting Paths with cmd arrays
  39. // =================================================================================
  40. template <typename VisitFunc>
  41. void VisitPath(const SkPath& p, VisitFunc&& f) {
  42. SkPath::RawIter iter(p);
  43. SkPoint pts[4];
  44. SkPath::Verb verb;
  45. while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
  46. f(verb, pts, iter);
  47. }
  48. }
  49. JSArray EMSCRIPTEN_KEEPALIVE ToCmds(const SkPath& path) {
  50. JSArray cmds = emscripten::val::array();
  51. VisitPath(path, [&cmds](SkPath::Verb verb, const SkPoint pts[4], SkPath::RawIter iter) {
  52. JSArray cmd = emscripten::val::array();
  53. switch (verb) {
  54. case SkPath::kMove_Verb:
  55. cmd.call<void>("push", MOVE, pts[0].x(), pts[0].y());
  56. break;
  57. case SkPath::kLine_Verb:
  58. cmd.call<void>("push", LINE, pts[1].x(), pts[1].y());
  59. break;
  60. case SkPath::kQuad_Verb:
  61. cmd.call<void>("push", QUAD, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
  62. break;
  63. case SkPath::kConic_Verb:
  64. cmd.call<void>("push", CONIC,
  65. pts[1].x(), pts[1].y(),
  66. pts[2].x(), pts[2].y(), iter.conicWeight());
  67. break;
  68. case SkPath::kCubic_Verb:
  69. cmd.call<void>("push", CUBIC,
  70. pts[1].x(), pts[1].y(),
  71. pts[2].x(), pts[2].y(),
  72. pts[3].x(), pts[3].y());
  73. break;
  74. case SkPath::kClose_Verb:
  75. cmd.call<void>("push", CLOSE);
  76. break;
  77. case SkPath::kDone_Verb:
  78. SkASSERT(false);
  79. break;
  80. }
  81. cmds.call<void>("push", cmd);
  82. });
  83. return cmds;
  84. }
  85. // This type signature is a mess, but it's necessary. See, we can't use "bind" (EMSCRIPTEN_BINDINGS)
  86. // and pointers to primitive types (Only bound types like SkPoint). We could if we used
  87. // cwrap (see https://becominghuman.ai/passing-and-returning-webassembly-array-parameters-a0f572c65d97)
  88. // but that requires us to stick to C code and, AFAIK, doesn't allow us to return nice things like
  89. // SkPath or SkOpBuilder.
  90. //
  91. // So, basically, if we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primative pointers
  92. // in our function type signatures. (this gives an error message like "Cannot call foo due to unbound
  93. // types Pi, Pf"). But, we can just pretend they are numbers and cast them to be pointers and
  94. // the compiler is happy.
  95. SkPathOrNull EMSCRIPTEN_KEEPALIVE FromCmds(uintptr_t /* float* */ cptr, int numCmds) {
  96. const auto* cmds = reinterpret_cast<const float*>(cptr);
  97. SkPath path;
  98. float x1, y1, x2, y2, x3, y3;
  99. // if there are not enough arguments, bail with the path we've constructed so far.
  100. #define CHECK_NUM_ARGS(n) \
  101. if ((i + n) > numCmds) { \
  102. SkDebugf("Not enough args to match the verbs. Saw %d commands\n", numCmds); \
  103. return emscripten::val::null(); \
  104. }
  105. for(int i = 0; i < numCmds;){
  106. switch (sk_float_floor2int(cmds[i++])) {
  107. case MOVE:
  108. CHECK_NUM_ARGS(2);
  109. x1 = cmds[i++], y1 = cmds[i++];
  110. path.moveTo(x1, y1);
  111. break;
  112. case LINE:
  113. CHECK_NUM_ARGS(2);
  114. x1 = cmds[i++], y1 = cmds[i++];
  115. path.lineTo(x1, y1);
  116. break;
  117. case QUAD:
  118. CHECK_NUM_ARGS(4);
  119. x1 = cmds[i++], y1 = cmds[i++];
  120. x2 = cmds[i++], y2 = cmds[i++];
  121. path.quadTo(x1, y1, x2, y2);
  122. break;
  123. case CONIC:
  124. CHECK_NUM_ARGS(5);
  125. x1 = cmds[i++], y1 = cmds[i++];
  126. x2 = cmds[i++], y2 = cmds[i++];
  127. x3 = cmds[i++]; // weight
  128. path.conicTo(x1, y1, x2, y2, x3);
  129. break;
  130. case CUBIC:
  131. CHECK_NUM_ARGS(6);
  132. x1 = cmds[i++], y1 = cmds[i++];
  133. x2 = cmds[i++], y2 = cmds[i++];
  134. x3 = cmds[i++], y3 = cmds[i++];
  135. path.cubicTo(x1, y1, x2, y2, x3, y3);
  136. break;
  137. case CLOSE:
  138. path.close();
  139. break;
  140. default:
  141. SkDebugf(" path: UNKNOWN command %f, aborting dump...\n", cmds[i-1]);
  142. return emscripten::val::null();
  143. }
  144. }
  145. #undef CHECK_NUM_ARGS
  146. return emscripten::val(path);
  147. }
  148. SkPath EMSCRIPTEN_KEEPALIVE NewPath() {
  149. return SkPath();
  150. }
  151. SkPath EMSCRIPTEN_KEEPALIVE CopyPath(const SkPath& a) {
  152. SkPath copy(a);
  153. return copy;
  154. }
  155. bool EMSCRIPTEN_KEEPALIVE Equals(const SkPath& a, const SkPath& b) {
  156. return a == b;
  157. }
  158. //========================================================================================
  159. // Path things
  160. //========================================================================================
  161. // All these Apply* methods are simple wrappers to avoid returning an object.
  162. // The default WASM bindings produce code that will leak if a return value
  163. // isn't assigned to a JS variable and has delete() called on it.
  164. // These Apply methods, combined with the smarter binding code allow for chainable
  165. // commands that don't leak if the return value is ignored (i.e. when used intuitively).
  166. void ApplyArcTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
  167. SkScalar radius) {
  168. p.arcTo(x1, y1, x2, y2, radius);
  169. }
  170. void ApplyClose(SkPath& p) {
  171. p.close();
  172. }
  173. void ApplyConicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
  174. SkScalar w) {
  175. p.conicTo(x1, y1, x2, y2, w);
  176. }
  177. void ApplyCubicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
  178. SkScalar x3, SkScalar y3) {
  179. p.cubicTo(x1, y1, x2, y2, x3, y3);
  180. }
  181. void ApplyLineTo(SkPath& p, SkScalar x, SkScalar y) {
  182. p.lineTo(x, y);
  183. }
  184. void ApplyMoveTo(SkPath& p, SkScalar x, SkScalar y) {
  185. p.moveTo(x, y);
  186. }
  187. void ApplyQuadTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
  188. p.quadTo(x1, y1, x2, y2);
  189. }
  190. //========================================================================================
  191. // SVG things
  192. //========================================================================================
  193. JSString EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) {
  194. SkString s;
  195. SkParsePath::ToSVGString(path, &s);
  196. // Wrapping it in val automatically turns it into a JS string.
  197. // Not too sure on performance implications, but is is simpler than
  198. // returning a raw pointer to const char * and then using
  199. // UTF8ToString() on the calling side.
  200. return emscripten::val(s.c_str());
  201. }
  202. SkPathOrNull EMSCRIPTEN_KEEPALIVE FromSVGString(std::string str) {
  203. SkPath path;
  204. if (SkParsePath::FromSVGString(str.c_str(), &path)) {
  205. return emscripten::val(path);
  206. }
  207. return emscripten::val::null();
  208. }
  209. //========================================================================================
  210. // PATHOP things
  211. //========================================================================================
  212. bool EMSCRIPTEN_KEEPALIVE ApplySimplify(SkPath& path) {
  213. return Simplify(path, &path);
  214. }
  215. bool EMSCRIPTEN_KEEPALIVE ApplyPathOp(SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
  216. return Op(pathOne, pathTwo, op, &pathOne);
  217. }
  218. SkPathOrNull EMSCRIPTEN_KEEPALIVE MakeFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
  219. SkPath out;
  220. if (Op(pathOne, pathTwo, op, &out)) {
  221. return emscripten::val(out);
  222. }
  223. return emscripten::val::null();
  224. }
  225. SkPathOrNull EMSCRIPTEN_KEEPALIVE ResolveBuilder(SkOpBuilder& builder) {
  226. SkPath path;
  227. if (builder.resolve(&path)) {
  228. return emscripten::val(path);
  229. }
  230. return emscripten::val::null();
  231. }
  232. //========================================================================================
  233. // Canvas things
  234. //========================================================================================
  235. void EMSCRIPTEN_KEEPALIVE ToCanvas(const SkPath& path, emscripten::val /* Path2D or Canvas*/ ctx) {
  236. SkPath::Iter iter(path, false);
  237. SkPoint pts[4];
  238. SkPath::Verb verb;
  239. while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
  240. switch (verb) {
  241. case SkPath::kMove_Verb:
  242. ctx.call<void>("moveTo", pts[0].x(), pts[0].y());
  243. break;
  244. case SkPath::kLine_Verb:
  245. ctx.call<void>("lineTo", pts[1].x(), pts[1].y());
  246. break;
  247. case SkPath::kQuad_Verb:
  248. ctx.call<void>("quadraticCurveTo", pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
  249. break;
  250. case SkPath::kConic_Verb:
  251. SkPoint quads[5];
  252. // approximate with 2^1=2 quads.
  253. SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(), quads, 1);
  254. ctx.call<void>("quadraticCurveTo", quads[1].x(), quads[1].y(), quads[2].x(), quads[2].y());
  255. ctx.call<void>("quadraticCurveTo", quads[3].x(), quads[3].y(), quads[4].x(), quads[4].y());
  256. break;
  257. case SkPath::kCubic_Verb:
  258. ctx.call<void>("bezierCurveTo", pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y(),
  259. pts[3].x(), pts[3].y());
  260. break;
  261. case SkPath::kClose_Verb:
  262. ctx.call<void>("closePath");
  263. break;
  264. case SkPath::kDone_Verb:
  265. break;
  266. }
  267. }
  268. }
  269. emscripten::val JSPath2D = emscripten::val::global("Path2D");
  270. emscripten::val EMSCRIPTEN_KEEPALIVE ToPath2D(const SkPath& path) {
  271. emscripten::val retVal = JSPath2D.new_();
  272. ToCanvas(path, retVal);
  273. return retVal;
  274. }
  275. // ======================================================================================
  276. // Path2D API things
  277. // ======================================================================================
  278. void ApplyAddRect(SkPath& path, SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
  279. path.addRect(x, y, x+width, y+height);
  280. }
  281. void ApplyAddArc(SkPath& path, SkScalar x, SkScalar y, SkScalar radius,
  282. SkScalar startAngle, SkScalar endAngle, bool ccw) {
  283. SkPath temp;
  284. SkRect bounds = SkRect::MakeLTRB(x-radius, y-radius, x+radius, y+radius);
  285. const auto sweep = SkRadiansToDegrees(endAngle - startAngle) - 360 * ccw;
  286. temp.addArc(bounds, SkRadiansToDegrees(startAngle), sweep);
  287. path.addPath(temp, SkPath::kExtend_AddPathMode);
  288. }
  289. void ApplyEllipse(SkPath& path, SkScalar x, SkScalar y, SkScalar radiusX, SkScalar radiusY,
  290. SkScalar rotation, SkScalar startAngle, SkScalar endAngle, bool ccw) {
  291. // This is easiest to do by making a new path and then extending the current path
  292. // (this properly catches the cases of if there's a moveTo before this call or not).
  293. SkRect bounds = SkRect::MakeLTRB(x-radiusX, y-radiusY, x+radiusX, y+radiusY);
  294. SkPath temp;
  295. const auto sweep = SkRadiansToDegrees(endAngle - startAngle) - (360 * ccw);
  296. temp.addArc(bounds, SkRadiansToDegrees(startAngle), sweep);
  297. SkMatrix m;
  298. m.setRotate(SkRadiansToDegrees(rotation), x, y);
  299. path.addPath(temp, m, SkPath::kExtend_AddPathMode);
  300. }
  301. // Allows for full matix control.
  302. void ApplyAddPath(SkPath& orig, const SkPath& newPath,
  303. SkScalar scaleX, SkScalar skewX, SkScalar transX,
  304. SkScalar skewY, SkScalar scaleY, SkScalar transY,
  305. SkScalar pers0, SkScalar pers1, SkScalar pers2) {
  306. SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
  307. skewY , scaleY, transY,
  308. pers0 , pers1 , pers2);
  309. orig.addPath(newPath, m);
  310. }
  311. JSString GetFillTypeString(const SkPath& path) {
  312. if (path.getFillType() == SkPath::FillType::kWinding_FillType) {
  313. return emscripten::val("nonzero");
  314. } else if (path.getFillType() == SkPath::FillType::kEvenOdd_FillType) {
  315. return emscripten::val("evenodd");
  316. } else {
  317. SkDebugf("warning: can't translate inverted filltype to HTML Canvas\n");
  318. return emscripten::val("nonzero"); //Use default
  319. }
  320. }
  321. //========================================================================================
  322. // Path Effects
  323. //========================================================================================
  324. bool ApplyDash(SkPath& path, SkScalar on, SkScalar off, SkScalar phase) {
  325. SkScalar intervals[] = { on, off };
  326. auto pe = SkDashPathEffect::Make(intervals, 2, phase);
  327. if (!pe) {
  328. SkDebugf("Invalid args to dash()\n");
  329. return false;
  330. }
  331. SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
  332. if (pe->filterPath(&path, path, &rec, nullptr)) {
  333. return true;
  334. }
  335. SkDebugf("Could not make dashed path\n");
  336. return false;
  337. }
  338. bool ApplyTrim(SkPath& path, SkScalar startT, SkScalar stopT, bool isComplement) {
  339. auto mode = isComplement ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal;
  340. auto pe = SkTrimPathEffect::Make(startT, stopT, mode);
  341. if (!pe) {
  342. SkDebugf("Invalid args to trim(): startT and stopT must be in [0,1]\n");
  343. return false;
  344. }
  345. SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
  346. if (pe->filterPath(&path, path, &rec, nullptr)) {
  347. return true;
  348. }
  349. SkDebugf("Could not trim path\n");
  350. return false;
  351. }
  352. struct StrokeOpts {
  353. // Default values are set in chaining.js which allows clients
  354. // to set any number of them. Otherwise, the binding code complains if
  355. // any are omitted.
  356. SkScalar width;
  357. SkScalar miter_limit;
  358. SkPaint::Join join;
  359. SkPaint::Cap cap;
  360. };
  361. bool ApplyStroke(SkPath& path, StrokeOpts opts) {
  362. SkPaint p;
  363. p.setStyle(SkPaint::kStroke_Style);
  364. p.setStrokeCap(opts.cap);
  365. p.setStrokeJoin(opts.join);
  366. p.setStrokeWidth(opts.width);
  367. p.setStrokeMiter(opts.miter_limit);
  368. return p.getFillPath(path, &path);
  369. }
  370. //========================================================================================
  371. // Matrix things
  372. //========================================================================================
  373. struct SimpleMatrix {
  374. SkScalar scaleX, skewX, transX;
  375. SkScalar skewY, scaleY, transY;
  376. SkScalar pers0, pers1, pers2;
  377. };
  378. SkMatrix toSkMatrix(const SimpleMatrix& sm) {
  379. return SkMatrix::MakeAll(sm.scaleX, sm.skewX , sm.transX,
  380. sm.skewY , sm.scaleY, sm.transY,
  381. sm.pers0 , sm.pers1 , sm.pers2);
  382. }
  383. void ApplyTransform(SkPath& orig, const SimpleMatrix& sm) {
  384. orig.transform(toSkMatrix(sm));
  385. }
  386. void ApplyTransform(SkPath& orig,
  387. SkScalar scaleX, SkScalar skewX, SkScalar transX,
  388. SkScalar skewY, SkScalar scaleY, SkScalar transY,
  389. SkScalar pers0, SkScalar pers1, SkScalar pers2) {
  390. SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
  391. skewY , scaleY, transY,
  392. pers0 , pers1 , pers2);
  393. orig.transform(m);
  394. }
  395. //========================================================================================
  396. // Testing things
  397. //========================================================================================
  398. // The use case for this is on the JS side is something like:
  399. // PathKit.SkBits2FloatUnsigned(parseInt("0xc0a00000"))
  400. // to have precise float values for tests. In the C++ tests, we can use SkBits2Float because
  401. // it takes int32_t, but the JS parseInt basically returns an unsigned int. So, we add in
  402. // this helper which casts for us on the way to SkBits2Float.
  403. float SkBits2FloatUnsigned(uint32_t floatAsBits) {
  404. return SkBits2Float((int32_t) floatAsBits);
  405. }
  406. // Binds the classes to the JS
  407. //
  408. // See https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#non-member-functions-on-the-javascript-prototype
  409. // for more on binding non-member functions to the JS object, allowing us to rewire
  410. // various functions. That is, we can make the SkPath we expose appear to have methods
  411. // that the original SkPath does not, like rect(x, y, width, height) and toPath2D().
  412. //
  413. // An important detail for binding non-member functions is that the first argument
  414. // must be SkPath& (the reference part is very important).
  415. //
  416. // Note that we can't expose default or optional arguments, but we can have multiple
  417. // declarations of the same function that take different amounts of arguments.
  418. // For example, see _transform
  419. // Additionally, we are perfectly happy to handle default arguments and function
  420. // overloads in the JS glue code (see chaining.js::addPath() for an example).
  421. EMSCRIPTEN_BINDINGS(skia) {
  422. class_<SkPath>("SkPath")
  423. .constructor<>()
  424. .constructor<const SkPath&>()
  425. // Path2D API
  426. .function("_addPath", &ApplyAddPath)
  427. // 3 additional overloads of addPath are handled in JS bindings
  428. .function("_arc", &ApplyAddArc)
  429. .function("_arcTo", &ApplyArcTo)
  430. //"bezierCurveTo" alias handled in JS bindings
  431. .function("_close", &ApplyClose)
  432. //"closePath" alias handled in JS bindings
  433. .function("_conicTo", &ApplyConicTo)
  434. .function("_cubicTo", &ApplyCubicTo)
  435. .function("_ellipse", &ApplyEllipse)
  436. .function("_lineTo", &ApplyLineTo)
  437. .function("_moveTo", &ApplyMoveTo)
  438. // "quadraticCurveTo" alias handled in JS bindings
  439. .function("_quadTo", &ApplyQuadTo)
  440. .function("_rect", &ApplyAddRect)
  441. // Extra features
  442. .function("setFillType", &SkPath::setFillType)
  443. .function("getFillType", &SkPath::getFillType)
  444. .function("getFillTypeString", &GetFillTypeString)
  445. .function("getBounds", &SkPath::getBounds)
  446. .function("computeTightBounds", &SkPath::computeTightBounds)
  447. .function("equals", &Equals)
  448. .function("copy", &CopyPath)
  449. // PathEffects
  450. .function("_dash", &ApplyDash)
  451. .function("_trim", &ApplyTrim)
  452. .function("_stroke", &ApplyStroke)
  453. // Matrix
  454. .function("_transform", select_overload<void(SkPath& orig, const SimpleMatrix& sm)>(&ApplyTransform))
  455. .function("_transform", select_overload<void(SkPath& orig, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&ApplyTransform))
  456. // PathOps
  457. .function("_simplify", &ApplySimplify)
  458. .function("_op", &ApplyPathOp)
  459. // Exporting
  460. .function("toCmds", &ToCmds)
  461. .function("toPath2D", &ToPath2D)
  462. .function("toCanvas", &ToCanvas)
  463. .function("toSVGString", &ToSVGString)
  464. #ifdef PATHKIT_TESTING
  465. .function("dump", select_overload<void() const>(&SkPath::dump))
  466. .function("dumpHex", select_overload<void() const>(&SkPath::dumpHex))
  467. #endif
  468. ;
  469. class_<SkOpBuilder>("SkOpBuilder")
  470. .constructor<>()
  471. .function("add", &SkOpBuilder::add)
  472. .function("make", &ResolveBuilder)
  473. .function("resolve", &ResolveBuilder);
  474. // Without these function() bindings, the function would be exposed but oblivious to
  475. // our types (e.g. SkPath)
  476. // Import
  477. function("FromSVGString", &FromSVGString);
  478. function("NewPath", &NewPath);
  479. function("NewPath", &CopyPath);
  480. // FromCmds is defined in helper.js to make use of TypedArrays transparent.
  481. function("_FromCmds", &FromCmds);
  482. // Path2D is opaque, so we can't read in from it.
  483. // PathOps
  484. function("MakeFromOp", &MakeFromOp);
  485. enum_<SkPathOp>("PathOp")
  486. .value("DIFFERENCE", SkPathOp::kDifference_SkPathOp)
  487. .value("INTERSECT", SkPathOp::kIntersect_SkPathOp)
  488. .value("UNION", SkPathOp::kUnion_SkPathOp)
  489. .value("XOR", SkPathOp::kXOR_SkPathOp)
  490. .value("REVERSE_DIFFERENCE", SkPathOp::kReverseDifference_SkPathOp);
  491. enum_<SkPath::FillType>("FillType")
  492. .value("WINDING", SkPath::FillType::kWinding_FillType)
  493. .value("EVENODD", SkPath::FillType::kEvenOdd_FillType)
  494. .value("INVERSE_WINDING", SkPath::FillType::kInverseWinding_FillType)
  495. .value("INVERSE_EVENODD", SkPath::FillType::kInverseEvenOdd_FillType);
  496. constant("MOVE_VERB", MOVE);
  497. constant("LINE_VERB", LINE);
  498. constant("QUAD_VERB", QUAD);
  499. constant("CONIC_VERB", CONIC);
  500. constant("CUBIC_VERB", CUBIC);
  501. constant("CLOSE_VERB", CLOSE);
  502. // A value object is much simpler than a class - it is returned as a JS
  503. // object and does not require delete().
  504. // https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#value-types
  505. value_object<SkRect>("SkRect")
  506. .field("fLeft", &SkRect::fLeft)
  507. .field("fTop", &SkRect::fTop)
  508. .field("fRight", &SkRect::fRight)
  509. .field("fBottom", &SkRect::fBottom);
  510. function("LTRBRect", &SkRect::MakeLTRB);
  511. // Stroke
  512. enum_<SkPaint::Join>("StrokeJoin")
  513. .value("MITER", SkPaint::Join::kMiter_Join)
  514. .value("ROUND", SkPaint::Join::kRound_Join)
  515. .value("BEVEL", SkPaint::Join::kBevel_Join);
  516. enum_<SkPaint::Cap>("StrokeCap")
  517. .value("BUTT", SkPaint::Cap::kButt_Cap)
  518. .value("ROUND", SkPaint::Cap::kRound_Cap)
  519. .value("SQUARE", SkPaint::Cap::kSquare_Cap);
  520. value_object<StrokeOpts>("StrokeOpts")
  521. .field("width", &StrokeOpts::width)
  522. .field("miter_limit", &StrokeOpts::miter_limit)
  523. .field("join", &StrokeOpts::join)
  524. .field("cap", &StrokeOpts::cap);
  525. // Matrix
  526. // Allows clients to supply a 1D array of 9 elements and the bindings
  527. // will automatically turn it into a 3x3 2D matrix.
  528. // e.g. path.transform([0,1,2,3,4,5,6,7,8])
  529. // This is likely simpler for the client than exposing SkMatrix
  530. // directly and requiring them to do a lot of .delete().
  531. value_array<SimpleMatrix>("SkMatrix")
  532. .element(&SimpleMatrix::scaleX)
  533. .element(&SimpleMatrix::skewX)
  534. .element(&SimpleMatrix::transX)
  535. .element(&SimpleMatrix::skewY)
  536. .element(&SimpleMatrix::scaleY)
  537. .element(&SimpleMatrix::transY)
  538. .element(&SimpleMatrix::pers0)
  539. .element(&SimpleMatrix::pers1)
  540. .element(&SimpleMatrix::pers2);
  541. value_array<SkPoint>("SkPoint")
  542. .element(&SkPoint::fX)
  543. .element(&SkPoint::fY);
  544. // Not intended for external clients to call directly.
  545. // See helper.js for the client-facing implementation.
  546. class_<SkCubicMap>("_SkCubicMap")
  547. .constructor<SkPoint, SkPoint>()
  548. .function("computeYFromX", &SkCubicMap::computeYFromX)
  549. .function("computePtFromT", &SkCubicMap::computeFromT);
  550. // Test Utils
  551. function("SkBits2FloatUnsigned", &SkBits2FloatUnsigned);
  552. }