123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- /*
- * Copyright 2018 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "include/core/SkCubicMap.h"
- #include "include/core/SkMatrix.h"
- #include "include/core/SkPaint.h"
- #include "include/core/SkPath.h"
- #include "include/core/SkRect.h"
- #include "include/core/SkString.h"
- #include "include/core/SkStrokeRec.h"
- #include "include/effects/SkDashPathEffect.h"
- #include "include/effects/SkTrimPathEffect.h"
- #include "include/pathops/SkPathOps.h"
- #include "include/private/SkFloatBits.h"
- #include "include/private/SkFloatingPoint.h"
- #include "include/utils/SkParsePath.h"
- #include "src/core/SkPaintDefaults.h"
- #include <emscripten/emscripten.h>
- #include <emscripten/bind.h>
- using namespace emscripten;
- static const int MOVE = 0;
- static const int LINE = 1;
- static const int QUAD = 2;
- static const int CONIC = 3;
- static const int CUBIC = 4;
- static const int CLOSE = 5;
- // Just for self-documenting purposes where the main thing being returned is an
- // SkPath, but in an error case, something of type null (which is val) could also be
- // returned;
- using SkPathOrNull = emscripten::val;
- // Self-documenting for when we return a string
- using JSString = emscripten::val;
- using JSArray = emscripten::val;
- // =================================================================================
- // Creating/Exporting Paths with cmd arrays
- // =================================================================================
- template <typename VisitFunc>
- void VisitPath(const SkPath& p, VisitFunc&& f) {
- SkPath::RawIter iter(p);
- SkPoint pts[4];
- SkPath::Verb verb;
- while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
- f(verb, pts, iter);
- }
- }
- JSArray EMSCRIPTEN_KEEPALIVE ToCmds(const SkPath& path) {
- JSArray cmds = emscripten::val::array();
- VisitPath(path, [&cmds](SkPath::Verb verb, const SkPoint pts[4], SkPath::RawIter iter) {
- JSArray cmd = emscripten::val::array();
- switch (verb) {
- case SkPath::kMove_Verb:
- cmd.call<void>("push", MOVE, pts[0].x(), pts[0].y());
- break;
- case SkPath::kLine_Verb:
- cmd.call<void>("push", LINE, pts[1].x(), pts[1].y());
- break;
- case SkPath::kQuad_Verb:
- cmd.call<void>("push", QUAD, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
- break;
- case SkPath::kConic_Verb:
- cmd.call<void>("push", CONIC,
- pts[1].x(), pts[1].y(),
- pts[2].x(), pts[2].y(), iter.conicWeight());
- break;
- case SkPath::kCubic_Verb:
- cmd.call<void>("push", CUBIC,
- pts[1].x(), pts[1].y(),
- pts[2].x(), pts[2].y(),
- pts[3].x(), pts[3].y());
- break;
- case SkPath::kClose_Verb:
- cmd.call<void>("push", CLOSE);
- break;
- case SkPath::kDone_Verb:
- SkASSERT(false);
- break;
- }
- cmds.call<void>("push", cmd);
- });
- return cmds;
- }
- // This type signature is a mess, but it's necessary. See, we can't use "bind" (EMSCRIPTEN_BINDINGS)
- // and pointers to primitive types (Only bound types like SkPoint). We could if we used
- // cwrap (see https://becominghuman.ai/passing-and-returning-webassembly-array-parameters-a0f572c65d97)
- // but that requires us to stick to C code and, AFAIK, doesn't allow us to return nice things like
- // SkPath or SkOpBuilder.
- //
- // So, basically, if we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primative pointers
- // in our function type signatures. (this gives an error message like "Cannot call foo due to unbound
- // types Pi, Pf"). But, we can just pretend they are numbers and cast them to be pointers and
- // the compiler is happy.
- SkPathOrNull EMSCRIPTEN_KEEPALIVE FromCmds(uintptr_t /* float* */ cptr, int numCmds) {
- const auto* cmds = reinterpret_cast<const float*>(cptr);
- SkPath path;
- float x1, y1, x2, y2, x3, y3;
- // if there are not enough arguments, bail with the path we've constructed so far.
- #define CHECK_NUM_ARGS(n) \
- if ((i + n) > numCmds) { \
- SkDebugf("Not enough args to match the verbs. Saw %d commands\n", numCmds); \
- return emscripten::val::null(); \
- }
- for(int i = 0; i < numCmds;){
- switch (sk_float_floor2int(cmds[i++])) {
- case MOVE:
- CHECK_NUM_ARGS(2);
- x1 = cmds[i++], y1 = cmds[i++];
- path.moveTo(x1, y1);
- break;
- case LINE:
- CHECK_NUM_ARGS(2);
- x1 = cmds[i++], y1 = cmds[i++];
- path.lineTo(x1, y1);
- break;
- case QUAD:
- CHECK_NUM_ARGS(4);
- x1 = cmds[i++], y1 = cmds[i++];
- x2 = cmds[i++], y2 = cmds[i++];
- path.quadTo(x1, y1, x2, y2);
- break;
- case CONIC:
- CHECK_NUM_ARGS(5);
- x1 = cmds[i++], y1 = cmds[i++];
- x2 = cmds[i++], y2 = cmds[i++];
- x3 = cmds[i++]; // weight
- path.conicTo(x1, y1, x2, y2, x3);
- break;
- case CUBIC:
- CHECK_NUM_ARGS(6);
- x1 = cmds[i++], y1 = cmds[i++];
- x2 = cmds[i++], y2 = cmds[i++];
- x3 = cmds[i++], y3 = cmds[i++];
- path.cubicTo(x1, y1, x2, y2, x3, y3);
- break;
- case CLOSE:
- path.close();
- break;
- default:
- SkDebugf(" path: UNKNOWN command %f, aborting dump...\n", cmds[i-1]);
- return emscripten::val::null();
- }
- }
- #undef CHECK_NUM_ARGS
- return emscripten::val(path);
- }
- SkPath EMSCRIPTEN_KEEPALIVE NewPath() {
- return SkPath();
- }
- SkPath EMSCRIPTEN_KEEPALIVE CopyPath(const SkPath& a) {
- SkPath copy(a);
- return copy;
- }
- bool EMSCRIPTEN_KEEPALIVE Equals(const SkPath& a, const SkPath& b) {
- return a == b;
- }
- //========================================================================================
- // Path things
- //========================================================================================
- // All these Apply* methods are simple wrappers to avoid returning an object.
- // The default WASM bindings produce code that will leak if a return value
- // isn't assigned to a JS variable and has delete() called on it.
- // These Apply methods, combined with the smarter binding code allow for chainable
- // commands that don't leak if the return value is ignored (i.e. when used intuitively).
- void ApplyArcTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
- SkScalar radius) {
- p.arcTo(x1, y1, x2, y2, radius);
- }
- void ApplyClose(SkPath& p) {
- p.close();
- }
- void ApplyConicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
- SkScalar w) {
- p.conicTo(x1, y1, x2, y2, w);
- }
- void ApplyCubicTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
- SkScalar x3, SkScalar y3) {
- p.cubicTo(x1, y1, x2, y2, x3, y3);
- }
- void ApplyLineTo(SkPath& p, SkScalar x, SkScalar y) {
- p.lineTo(x, y);
- }
- void ApplyMoveTo(SkPath& p, SkScalar x, SkScalar y) {
- p.moveTo(x, y);
- }
- void ApplyQuadTo(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
- p.quadTo(x1, y1, x2, y2);
- }
- //========================================================================================
- // SVG things
- //========================================================================================
- JSString EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) {
- SkString s;
- SkParsePath::ToSVGString(path, &s);
- // Wrapping it in val automatically turns it into a JS string.
- // Not too sure on performance implications, but is is simpler than
- // returning a raw pointer to const char * and then using
- // UTF8ToString() on the calling side.
- return emscripten::val(s.c_str());
- }
- SkPathOrNull EMSCRIPTEN_KEEPALIVE FromSVGString(std::string str) {
- SkPath path;
- if (SkParsePath::FromSVGString(str.c_str(), &path)) {
- return emscripten::val(path);
- }
- return emscripten::val::null();
- }
- //========================================================================================
- // PATHOP things
- //========================================================================================
- bool EMSCRIPTEN_KEEPALIVE ApplySimplify(SkPath& path) {
- return Simplify(path, &path);
- }
- bool EMSCRIPTEN_KEEPALIVE ApplyPathOp(SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
- return Op(pathOne, pathTwo, op, &pathOne);
- }
- SkPathOrNull EMSCRIPTEN_KEEPALIVE MakeFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
- SkPath out;
- if (Op(pathOne, pathTwo, op, &out)) {
- return emscripten::val(out);
- }
- return emscripten::val::null();
- }
- SkPathOrNull EMSCRIPTEN_KEEPALIVE ResolveBuilder(SkOpBuilder& builder) {
- SkPath path;
- if (builder.resolve(&path)) {
- return emscripten::val(path);
- }
- return emscripten::val::null();
- }
- //========================================================================================
- // Canvas things
- //========================================================================================
- void EMSCRIPTEN_KEEPALIVE ToCanvas(const SkPath& path, emscripten::val /* Path2D or Canvas*/ ctx) {
- SkPath::Iter iter(path, false);
- SkPoint pts[4];
- SkPath::Verb verb;
- while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
- switch (verb) {
- case SkPath::kMove_Verb:
- ctx.call<void>("moveTo", pts[0].x(), pts[0].y());
- break;
- case SkPath::kLine_Verb:
- ctx.call<void>("lineTo", pts[1].x(), pts[1].y());
- break;
- case SkPath::kQuad_Verb:
- ctx.call<void>("quadraticCurveTo", pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
- break;
- case SkPath::kConic_Verb:
- SkPoint quads[5];
- // approximate with 2^1=2 quads.
- SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(), quads, 1);
- ctx.call<void>("quadraticCurveTo", quads[1].x(), quads[1].y(), quads[2].x(), quads[2].y());
- ctx.call<void>("quadraticCurveTo", quads[3].x(), quads[3].y(), quads[4].x(), quads[4].y());
- break;
- case SkPath::kCubic_Verb:
- ctx.call<void>("bezierCurveTo", pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y(),
- pts[3].x(), pts[3].y());
- break;
- case SkPath::kClose_Verb:
- ctx.call<void>("closePath");
- break;
- case SkPath::kDone_Verb:
- break;
- }
- }
- }
- emscripten::val JSPath2D = emscripten::val::global("Path2D");
- emscripten::val EMSCRIPTEN_KEEPALIVE ToPath2D(const SkPath& path) {
- emscripten::val retVal = JSPath2D.new_();
- ToCanvas(path, retVal);
- return retVal;
- }
- // ======================================================================================
- // Path2D API things
- // ======================================================================================
- void ApplyAddRect(SkPath& path, SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
- path.addRect(x, y, x+width, y+height);
- }
- void ApplyAddArc(SkPath& path, SkScalar x, SkScalar y, SkScalar radius,
- SkScalar startAngle, SkScalar endAngle, bool ccw) {
- SkPath temp;
- SkRect bounds = SkRect::MakeLTRB(x-radius, y-radius, x+radius, y+radius);
- const auto sweep = SkRadiansToDegrees(endAngle - startAngle) - 360 * ccw;
- temp.addArc(bounds, SkRadiansToDegrees(startAngle), sweep);
- path.addPath(temp, SkPath::kExtend_AddPathMode);
- }
- void ApplyEllipse(SkPath& path, SkScalar x, SkScalar y, SkScalar radiusX, SkScalar radiusY,
- SkScalar rotation, SkScalar startAngle, SkScalar endAngle, bool ccw) {
- // This is easiest to do by making a new path and then extending the current path
- // (this properly catches the cases of if there's a moveTo before this call or not).
- SkRect bounds = SkRect::MakeLTRB(x-radiusX, y-radiusY, x+radiusX, y+radiusY);
- SkPath temp;
- const auto sweep = SkRadiansToDegrees(endAngle - startAngle) - (360 * ccw);
- temp.addArc(bounds, SkRadiansToDegrees(startAngle), sweep);
- SkMatrix m;
- m.setRotate(SkRadiansToDegrees(rotation), x, y);
- path.addPath(temp, m, SkPath::kExtend_AddPathMode);
- }
- // Allows for full matix control.
- void ApplyAddPath(SkPath& orig, const SkPath& newPath,
- SkScalar scaleX, SkScalar skewX, SkScalar transX,
- SkScalar skewY, SkScalar scaleY, SkScalar transY,
- SkScalar pers0, SkScalar pers1, SkScalar pers2) {
- SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
- skewY , scaleY, transY,
- pers0 , pers1 , pers2);
- orig.addPath(newPath, m);
- }
- JSString GetFillTypeString(const SkPath& path) {
- if (path.getFillType() == SkPath::FillType::kWinding_FillType) {
- return emscripten::val("nonzero");
- } else if (path.getFillType() == SkPath::FillType::kEvenOdd_FillType) {
- return emscripten::val("evenodd");
- } else {
- SkDebugf("warning: can't translate inverted filltype to HTML Canvas\n");
- return emscripten::val("nonzero"); //Use default
- }
- }
- //========================================================================================
- // Path Effects
- //========================================================================================
- bool ApplyDash(SkPath& path, SkScalar on, SkScalar off, SkScalar phase) {
- SkScalar intervals[] = { on, off };
- auto pe = SkDashPathEffect::Make(intervals, 2, phase);
- if (!pe) {
- SkDebugf("Invalid args to dash()\n");
- return false;
- }
- SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
- if (pe->filterPath(&path, path, &rec, nullptr)) {
- return true;
- }
- SkDebugf("Could not make dashed path\n");
- return false;
- }
- bool ApplyTrim(SkPath& path, SkScalar startT, SkScalar stopT, bool isComplement) {
- auto mode = isComplement ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal;
- auto pe = SkTrimPathEffect::Make(startT, stopT, mode);
- if (!pe) {
- SkDebugf("Invalid args to trim(): startT and stopT must be in [0,1]\n");
- return false;
- }
- SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
- if (pe->filterPath(&path, path, &rec, nullptr)) {
- return true;
- }
- SkDebugf("Could not trim path\n");
- return false;
- }
- struct StrokeOpts {
- // Default values are set in chaining.js which allows clients
- // to set any number of them. Otherwise, the binding code complains if
- // any are omitted.
- SkScalar width;
- SkScalar miter_limit;
- SkPaint::Join join;
- SkPaint::Cap cap;
- };
- bool ApplyStroke(SkPath& path, StrokeOpts opts) {
- SkPaint p;
- p.setStyle(SkPaint::kStroke_Style);
- p.setStrokeCap(opts.cap);
- p.setStrokeJoin(opts.join);
- p.setStrokeWidth(opts.width);
- p.setStrokeMiter(opts.miter_limit);
- return p.getFillPath(path, &path);
- }
- //========================================================================================
- // Matrix things
- //========================================================================================
- struct SimpleMatrix {
- SkScalar scaleX, skewX, transX;
- SkScalar skewY, scaleY, transY;
- SkScalar pers0, pers1, pers2;
- };
- SkMatrix toSkMatrix(const SimpleMatrix& sm) {
- return SkMatrix::MakeAll(sm.scaleX, sm.skewX , sm.transX,
- sm.skewY , sm.scaleY, sm.transY,
- sm.pers0 , sm.pers1 , sm.pers2);
- }
- void ApplyTransform(SkPath& orig, const SimpleMatrix& sm) {
- orig.transform(toSkMatrix(sm));
- }
- void ApplyTransform(SkPath& orig,
- SkScalar scaleX, SkScalar skewX, SkScalar transX,
- SkScalar skewY, SkScalar scaleY, SkScalar transY,
- SkScalar pers0, SkScalar pers1, SkScalar pers2) {
- SkMatrix m = SkMatrix::MakeAll(scaleX, skewX , transX,
- skewY , scaleY, transY,
- pers0 , pers1 , pers2);
- orig.transform(m);
- }
- //========================================================================================
- // Testing things
- //========================================================================================
- // The use case for this is on the JS side is something like:
- // PathKit.SkBits2FloatUnsigned(parseInt("0xc0a00000"))
- // to have precise float values for tests. In the C++ tests, we can use SkBits2Float because
- // it takes int32_t, but the JS parseInt basically returns an unsigned int. So, we add in
- // this helper which casts for us on the way to SkBits2Float.
- float SkBits2FloatUnsigned(uint32_t floatAsBits) {
- return SkBits2Float((int32_t) floatAsBits);
- }
- // Binds the classes to the JS
- //
- // See https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#non-member-functions-on-the-javascript-prototype
- // for more on binding non-member functions to the JS object, allowing us to rewire
- // various functions. That is, we can make the SkPath we expose appear to have methods
- // that the original SkPath does not, like rect(x, y, width, height) and toPath2D().
- //
- // An important detail for binding non-member functions is that the first argument
- // must be SkPath& (the reference part is very important).
- //
- // Note that we can't expose default or optional arguments, but we can have multiple
- // declarations of the same function that take different amounts of arguments.
- // For example, see _transform
- // Additionally, we are perfectly happy to handle default arguments and function
- // overloads in the JS glue code (see chaining.js::addPath() for an example).
- EMSCRIPTEN_BINDINGS(skia) {
- class_<SkPath>("SkPath")
- .constructor<>()
- .constructor<const SkPath&>()
- // Path2D API
- .function("_addPath", &ApplyAddPath)
- // 3 additional overloads of addPath are handled in JS bindings
- .function("_arc", &ApplyAddArc)
- .function("_arcTo", &ApplyArcTo)
- //"bezierCurveTo" alias handled in JS bindings
- .function("_close", &ApplyClose)
- //"closePath" alias handled in JS bindings
- .function("_conicTo", &ApplyConicTo)
- .function("_cubicTo", &ApplyCubicTo)
- .function("_ellipse", &ApplyEllipse)
- .function("_lineTo", &ApplyLineTo)
- .function("_moveTo", &ApplyMoveTo)
- // "quadraticCurveTo" alias handled in JS bindings
- .function("_quadTo", &ApplyQuadTo)
- .function("_rect", &ApplyAddRect)
- // Extra features
- .function("setFillType", &SkPath::setFillType)
- .function("getFillType", &SkPath::getFillType)
- .function("getFillTypeString", &GetFillTypeString)
- .function("getBounds", &SkPath::getBounds)
- .function("computeTightBounds", &SkPath::computeTightBounds)
- .function("equals", &Equals)
- .function("copy", &CopyPath)
- // PathEffects
- .function("_dash", &ApplyDash)
- .function("_trim", &ApplyTrim)
- .function("_stroke", &ApplyStroke)
- // Matrix
- .function("_transform", select_overload<void(SkPath& orig, const SimpleMatrix& sm)>(&ApplyTransform))
- .function("_transform", select_overload<void(SkPath& orig, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&ApplyTransform))
- // PathOps
- .function("_simplify", &ApplySimplify)
- .function("_op", &ApplyPathOp)
- // Exporting
- .function("toCmds", &ToCmds)
- .function("toPath2D", &ToPath2D)
- .function("toCanvas", &ToCanvas)
- .function("toSVGString", &ToSVGString)
- #ifdef PATHKIT_TESTING
- .function("dump", select_overload<void() const>(&SkPath::dump))
- .function("dumpHex", select_overload<void() const>(&SkPath::dumpHex))
- #endif
- ;
- class_<SkOpBuilder>("SkOpBuilder")
- .constructor<>()
- .function("add", &SkOpBuilder::add)
- .function("make", &ResolveBuilder)
- .function("resolve", &ResolveBuilder);
- // Without these function() bindings, the function would be exposed but oblivious to
- // our types (e.g. SkPath)
- // Import
- function("FromSVGString", &FromSVGString);
- function("NewPath", &NewPath);
- function("NewPath", &CopyPath);
- // FromCmds is defined in helper.js to make use of TypedArrays transparent.
- function("_FromCmds", &FromCmds);
- // Path2D is opaque, so we can't read in from it.
- // PathOps
- function("MakeFromOp", &MakeFromOp);
- enum_<SkPathOp>("PathOp")
- .value("DIFFERENCE", SkPathOp::kDifference_SkPathOp)
- .value("INTERSECT", SkPathOp::kIntersect_SkPathOp)
- .value("UNION", SkPathOp::kUnion_SkPathOp)
- .value("XOR", SkPathOp::kXOR_SkPathOp)
- .value("REVERSE_DIFFERENCE", SkPathOp::kReverseDifference_SkPathOp);
- enum_<SkPath::FillType>("FillType")
- .value("WINDING", SkPath::FillType::kWinding_FillType)
- .value("EVENODD", SkPath::FillType::kEvenOdd_FillType)
- .value("INVERSE_WINDING", SkPath::FillType::kInverseWinding_FillType)
- .value("INVERSE_EVENODD", SkPath::FillType::kInverseEvenOdd_FillType);
- constant("MOVE_VERB", MOVE);
- constant("LINE_VERB", LINE);
- constant("QUAD_VERB", QUAD);
- constant("CONIC_VERB", CONIC);
- constant("CUBIC_VERB", CUBIC);
- constant("CLOSE_VERB", CLOSE);
- // A value object is much simpler than a class - it is returned as a JS
- // object and does not require delete().
- // https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#value-types
- value_object<SkRect>("SkRect")
- .field("fLeft", &SkRect::fLeft)
- .field("fTop", &SkRect::fTop)
- .field("fRight", &SkRect::fRight)
- .field("fBottom", &SkRect::fBottom);
- function("LTRBRect", &SkRect::MakeLTRB);
- // Stroke
- enum_<SkPaint::Join>("StrokeJoin")
- .value("MITER", SkPaint::Join::kMiter_Join)
- .value("ROUND", SkPaint::Join::kRound_Join)
- .value("BEVEL", SkPaint::Join::kBevel_Join);
- enum_<SkPaint::Cap>("StrokeCap")
- .value("BUTT", SkPaint::Cap::kButt_Cap)
- .value("ROUND", SkPaint::Cap::kRound_Cap)
- .value("SQUARE", SkPaint::Cap::kSquare_Cap);
- value_object<StrokeOpts>("StrokeOpts")
- .field("width", &StrokeOpts::width)
- .field("miter_limit", &StrokeOpts::miter_limit)
- .field("join", &StrokeOpts::join)
- .field("cap", &StrokeOpts::cap);
- // Matrix
- // Allows clients to supply a 1D array of 9 elements and the bindings
- // will automatically turn it into a 3x3 2D matrix.
- // e.g. path.transform([0,1,2,3,4,5,6,7,8])
- // This is likely simpler for the client than exposing SkMatrix
- // directly and requiring them to do a lot of .delete().
- value_array<SimpleMatrix>("SkMatrix")
- .element(&SimpleMatrix::scaleX)
- .element(&SimpleMatrix::skewX)
- .element(&SimpleMatrix::transX)
- .element(&SimpleMatrix::skewY)
- .element(&SimpleMatrix::scaleY)
- .element(&SimpleMatrix::transY)
- .element(&SimpleMatrix::pers0)
- .element(&SimpleMatrix::pers1)
- .element(&SimpleMatrix::pers2);
- value_array<SkPoint>("SkPoint")
- .element(&SkPoint::fX)
- .element(&SkPoint::fY);
- // Not intended for external clients to call directly.
- // See helper.js for the client-facing implementation.
- class_<SkCubicMap>("_SkCubicMap")
- .constructor<SkPoint, SkPoint>()
- .function("computeYFromX", &SkCubicMap::computeYFromX)
- .function("computePtFromT", &SkCubicMap::computeFromT);
- // Test Utils
- function("SkBits2FloatUnsigned", &SkBits2FloatUnsigned);
- }
|