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