diff --git a/experimental/pathkit/compile.sh b/experimental/pathkit/compile.sh index 2f66588cc0..fc43301d9a 100755 --- a/experimental/pathkit/compile.sh +++ b/experimental/pathkit/compile.sh @@ -44,7 +44,7 @@ fi # Use -O0 for larger builds (but generally quicker) # Use -Oz for (much slower, but smaller/faster) production builds export EMCC_CLOSURE_ARGS="--externs $BASE_DIR/helper_externs.js " -RELEASE_CONF="-Oz --closure 1 -s EVAL_CTORS=1" +RELEASE_CONF="-Oz --closure 1 -s EVAL_CTORS=1 --llvm-lto 3" if [[ $@ == *test* ]]; then echo "Building a Testing/Profiling build" RELEASE_CONF="-O2 --profiling -DPATHKIT_TESTING -DSK_RELEASE" @@ -73,12 +73,19 @@ mkdir -p $BUILD_DIR em++ $RELEASE_CONF -std=c++14 \ -Iinclude/config \ -Iinclude/core \ +-Iinclude/effects \ +-Iinclude/gpu \ -Iinclude/pathops \ -Iinclude/private \ -Iinclude/utils \ -Isrc/core \ +-Isrc/gpu \ +-Isrc/shaders \ +-Isrc/opts \ +-Isrc/utils \ --bind \ --pre-js $BASE_DIR/helper.js \ +-DWEB_ASSEMBLY=1 \ -fno-rtti -fno-exceptions -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0 \ $WASM_CONF \ -s MODULARIZE=1 \ @@ -91,22 +98,40 @@ $WASM_CONF \ -s STRICT=1 \ $OUTPUT \ $BASE_DIR/pathkit_wasm_bindings.cpp \ +src/core/SkAnalyticEdge.cpp \ src/core/SkArenaAlloc.cpp \ +src/core/SkEdge.cpp \ +src/core/SkEdgeBuilder.cpp \ +src/core/SkEdgeClipper.cpp \ +src/core/SkFDot6Constants.cpp \ +src/core/SkFlattenable.cpp \ src/core/SkGeometry.cpp \ +src/core/SkLineClipper.cpp \ src/core/SkMallocPixelRef.cpp \ src/core/SkMath.cpp \ src/core/SkMatrix.cpp \ +src/core/SkOpts.cpp \ +src/core/SkPaint.cpp \ src/core/SkPath.cpp \ +src/core/SkPathEffect.cpp \ +src/core/SkPathMeasure.cpp \ src/core/SkPathRef.cpp \ src/core/SkPoint.cpp \ +src/core/SkRRect.cpp \ src/core/SkRect.cpp \ src/core/SkStream.cpp \ src/core/SkString.cpp \ src/core/SkStringUtils.cpp \ +src/core/SkStroke.cpp \ +src/core/SkStrokeRec.cpp \ +src/core/SkStrokerPriv.cpp \ src/core/SkUtils.cpp \ +src/effects/SkDashPathEffect.cpp \ +src/effects/SkTrimPathEffect.cpp \ src/pathops/*.cpp \ src/ports/SkDebug_stdio.cpp \ src/ports/SkMemory_malloc.cpp \ +src/utils/SkDashPath.cpp \ src/utils/SkParse.cpp \ src/utils/SkParsePath.cpp \ src/utils/SkUTF.cpp diff --git a/experimental/pathkit/karma-docker.conf.js b/experimental/pathkit/karma-docker.conf.js index 50397cc6db..4695fd4dcd 100644 --- a/experimental/pathkit/karma-docker.conf.js +++ b/experimental/pathkit/karma-docker.conf.js @@ -14,6 +14,10 @@ module.exports = function(config) { 'tests/*.spec.js' ], + proxies: { + "/pathkit/": "/base/npm-wasm/bin/test/" + }, + // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter diff --git a/experimental/pathkit/karma.conf.js b/experimental/pathkit/karma.conf.js index bbb5667676..c3104b44cd 100644 --- a/experimental/pathkit/karma.conf.js +++ b/experimental/pathkit/karma.conf.js @@ -14,6 +14,10 @@ module.exports = function(config) { 'tests/*.spec.js' ], + proxies: { + "/pathkit/": "/base/npm-wasm/bin/test/" + }, + // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter diff --git a/experimental/pathkit/npm-wasm/example.html b/experimental/pathkit/npm-wasm/example.html index c1fa8272b4..ec097b947e 100644 --- a/experimental/pathkit/npm-wasm/example.html +++ b/experimental/pathkit/npm-wasm/example.html @@ -30,6 +30,14 @@ +

Has various Path Effects

+ + + + + + + diff --git a/experimental/pathkit/npm-wasm/package.json b/experimental/pathkit/npm-wasm/package.json index daba5dbace..b3e42b439b 100644 --- a/experimental/pathkit/npm-wasm/package.json +++ b/experimental/pathkit/npm-wasm/package.json @@ -1,6 +1,6 @@ { "name": "experimental-pathkit-wasm", - "version": "0.1.4", + "version": "0.1.7", "description": "A WASM version of Skia's PathOps toolkit", "main": "bin/pathkit.js", "homepage": "https://github.com/google/skia/tree/master/experimental/pathkit", diff --git a/experimental/pathkit/pathkit_wasm_bindings.cpp b/experimental/pathkit/pathkit_wasm_bindings.cpp index 5587cadc0d..5909e39f2d 100644 --- a/experimental/pathkit/pathkit_wasm_bindings.cpp +++ b/experimental/pathkit/pathkit_wasm_bindings.cpp @@ -5,14 +5,17 @@ * found in the LICENSE file. */ +#include "SkDashPathEffect.h" #include "SkFloatBits.h" #include "SkFloatingPoint.h" #include "SkMatrix.h" +#include "SkPaint.h" #include "SkParsePath.h" #include "SkPath.h" #include "SkPathOps.h" #include "SkRect.h" #include "SkString.h" +#include "SkTrimPathEffect.h" #include #include @@ -22,13 +25,16 @@ 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 val (e.g. null) could also be +// SkPath, but in an error case, something of type null (which is val) could also be // returned; -using SkPathOrVal = emscripten::val; +using SkPathOrNull = emscripten::val; +// Self-documenting for when we return a string +using JSString = emscripten::val; // ================================================================================= // Creating/Exporting Paths with cmd arrays @@ -60,16 +66,9 @@ emscripten::val EMSCRIPTEN_KEEPALIVE ToCmds(const SkPath& path) { cmd.call("push", QUAD, 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); - cmd.call("push", MOVE, quads[0].x(), quads[0].y()); - cmds.call("push", cmd); - cmd = emscripten::val::array(); - cmd.call("push", QUAD, quads[1].x(), quads[1].y(), quads[2].x(), quads[2].y()); - cmds.call("push", cmd); - cmd = emscripten::val::array(); - cmd.call("push", QUAD, quads[3].x(), quads[3].y(), quads[4].x(), quads[4].y()); + cmd.call("push", CONIC, + pts[1].x(), pts[1].y(), + pts[2].x(), pts[2].y(), iter.conicWeight()); break; case SkPath::kCubic_Verb: cmd.call("push", CUBIC, @@ -99,7 +98,7 @@ emscripten::val EMSCRIPTEN_KEEPALIVE ToCmds(const SkPath& path) { // 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. -SkPathOrVal EMSCRIPTEN_KEEPALIVE FromCmds(uintptr_t /* float* */ cptr, int numCmds) { +SkPathOrNull EMSCRIPTEN_KEEPALIVE FromCmds(uintptr_t /* float* */ cptr, int numCmds) { const auto* cmds = reinterpret_cast(cptr); SkPath path; float x1, y1, x2, y2, x3, y3; @@ -158,7 +157,7 @@ SkPath EMSCRIPTEN_KEEPALIVE NewPath() { // SVG things //======================================================================================== -emscripten::val EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) { +JSString EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) { SkString s; SkParsePath::ToSVGString(path, &s); // Wrapping it in val automatically turns it into a JS string. @@ -169,7 +168,7 @@ emscripten::val EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) { } -SkPathOrVal EMSCRIPTEN_KEEPALIVE FromSVGString(std::string str) { +SkPathOrNull EMSCRIPTEN_KEEPALIVE FromSVGString(std::string str) { SkPath path; if (SkParsePath::FromSVGString(str.c_str(), &path)) { return emscripten::val(path); @@ -181,7 +180,7 @@ SkPathOrVal EMSCRIPTEN_KEEPALIVE FromSVGString(std::string str) { // PATHOP things //======================================================================================== -SkPathOrVal EMSCRIPTEN_KEEPALIVE SimplifyPath(const SkPath& path) { +SkPathOrNull EMSCRIPTEN_KEEPALIVE SimplifyPath(const SkPath& path) { SkPath simple; if (Simplify(path, &simple)) { return emscripten::val(simple); @@ -189,7 +188,7 @@ SkPathOrVal EMSCRIPTEN_KEEPALIVE SimplifyPath(const SkPath& path) { return emscripten::val::null(); } -SkPathOrVal EMSCRIPTEN_KEEPALIVE ApplyPathOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) { +SkPathOrNull EMSCRIPTEN_KEEPALIVE ApplyPathOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) { SkPath path; if (Op(pathOne, pathTwo, op, &path)) { return emscripten::val(path); @@ -197,7 +196,7 @@ SkPathOrVal EMSCRIPTEN_KEEPALIVE ApplyPathOp(const SkPath& pathOne, const SkPath return emscripten::val::null(); } -SkPathOrVal EMSCRIPTEN_KEEPALIVE ResolveBuilder(SkOpBuilder& builder) { +SkPathOrNull EMSCRIPTEN_KEEPALIVE ResolveBuilder(SkOpBuilder& builder) { SkPath path; if (builder.resolve(&path)) { return emscripten::val(path); @@ -228,7 +227,6 @@ void EMSCRIPTEN_KEEPALIVE ToCanvas(const SkPath& path, emscripten::val /* Path2D SkPoint quads[5]; // approximate with 2^1=2 quads. SkPath::ConvertConicToQuads(pts[0], pts[1], pts[2], iter.conicWeight(), quads, 1); - ctx.call("moveTo", quads[0].x(), quads[0].y()); ctx.call("quadraticCurveTo", quads[1].x(), quads[1].y(), quads[2].x(), quads[2].y()); ctx.call("quadraticCurveTo", quads[3].x(), quads[3].y(), quads[4].x(), quads[4].y()); break; @@ -326,6 +324,70 @@ void Path2DAddPath(SkPath& orig, const SkPath& newPath, orig.addPath(newPath, m); } +JSString GetCanvasFillType(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 +//======================================================================================== + +SkPathOrNull PathEffectDash(const SkPath& path, SkScalar on, SkScalar off, SkScalar phase) { + SkPath output; + SkScalar intervals[] = { on, off }; + auto pe = SkDashPathEffect::Make(intervals, 2, phase); + if (!pe) { + SkDebugf("Invalid args to dash()\n"); + return emscripten::val::null(); + } + if (pe->filterPath(&output, path, nullptr, nullptr)) { + return emscripten::val(output); + } + SkDebugf("Could not make dashed path\n"); + return emscripten::val::null(); +} + +SkPathOrNull PathEffectTrim(const SkPath& path, SkScalar startT, SkScalar stopT, bool isComplement) { + SkPath output; + 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 emscripten::val::null(); + } + if (pe->filterPath(&output, path, nullptr, nullptr)) { + return emscripten::val(output); + } + SkDebugf("Could not trim path\n"); + return emscripten::val::null(); +} + +SkPathOrNull PathEffectTrim(const SkPath& path, SkScalar startT, SkScalar stopT) { + return PathEffectTrim(path, startT, stopT, false); +} + +SkPathOrNull PathEffectStroke(const SkPath& path, SkScalar width, SkPaint::Join join, SkPaint::Cap cap) { + SkPath output; + SkPaint p; + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeCap(cap); + p.setStrokeJoin(join); + p.setStrokeWidth(width); + + if (p.getFillPath(path, &output)) { + return emscripten::val(output); + } + SkDebugf("Could not stroke path\n"); + return emscripten::val::null(); +} + //======================================================================================== // Testing things //======================================================================================== @@ -384,6 +446,8 @@ EMSCRIPTEN_BINDINGS(skia) { .function("addPath", select_overload(&Path2DAddPath)) .function("close", &SkPath::close) + .function("conicTo", + select_overload(&SkPath::conicTo)) .function("cubicTo", select_overload(&SkPath::cubicTo)) .function("quadTo", @@ -392,9 +456,16 @@ EMSCRIPTEN_BINDINGS(skia) { // Extra features .function("setFillType", &SkPath::setFillType) .function("getFillType", &SkPath::getFillType) + .function("getCanvasFillType", &GetCanvasFillType) .function("getBounds", &SkPath::getBounds) .function("computeTightBounds", &SkPath::computeTightBounds) + // PathEffects + .function("dash", &PathEffectDash) + .function("trim", select_overload(&PathEffectTrim)) + .function("trim", select_overload(&PathEffectTrim)) + .function("stroke", &PathEffectStroke) + // PathOps .function("simplify", &SimplifyPath) .function("op", &ApplyPathOp) @@ -407,6 +478,7 @@ EMSCRIPTEN_BINDINGS(skia) { #ifdef PATHKIT_TESTING .function("dump", select_overload(&SkPath::dump)) + .function("dumpHex", select_overload(&SkPath::dumpHex)) #endif ; @@ -444,6 +516,7 @@ EMSCRIPTEN_BINDINGS(skia) { constant("MOVE_VERB", MOVE); constant("LINE_VERB", LINE); constant("QUAD_VERB", QUAD); + constant("CONIC_VERB", CONIC); constant("CUBIC_VERB", CUBIC); constant("CLOSE_VERB", CLOSE); @@ -458,12 +531,20 @@ EMSCRIPTEN_BINDINGS(skia) { function("MakeLTRBRect", &SkRect::MakeLTRB); - // coming soon - Stroke + // Stroke + enum_("StrokeJoin") + .value("MITER", SkPaint::Join::kMiter_Join) + .value("ROUND", SkPaint::Join::kRound_Join) + .value("BEVEL", SkPaint::Join::kBevel_Join); + + enum_("StrokeCap") + .value("BUTT", SkPaint::Cap::kButt_Cap) + .value("ROUND", SkPaint::Cap::kRound_Cap) + .value("SQUARE", SkPaint::Cap::kSquare_Cap); + // coming soon - Matrix - // coming soon - Trim - // Test Utils function("SkBits2FloatUnsigned", &SkBits2FloatUnsigned); } diff --git a/experimental/pathkit/tests/path.spec.js b/experimental/pathkit/tests/path.spec.js index 660ade5e70..0cafc384cf 100644 --- a/experimental/pathkit/tests/path.spec.js +++ b/experimental/pathkit/tests/path.spec.js @@ -2,12 +2,12 @@ describe('PathKit\'s Path Behavior', function() { // Note, don't try to print the PathKit object - it can cause Karma/Jasmine to lock up. var PathKit = null; - const LoadPathKit = new Promise(function(resolve, reject){ + const LoadPathKit = new Promise(function(resolve, reject) { if (PathKit) { resolve(); } else { PathKitInit({ - locateFile: (file) => '/base/npm-wasm/bin/test/'+file, + locateFile: (file) => '/pathkit/'+file, }).then((_PathKit) => { PathKit = _PathKit; resolve(); @@ -31,6 +31,10 @@ describe('PathKit\'s Path Behavior', function() { }); }); + function bits2float(str) { + return PathKit.SkBits2FloatUnsigned(parseInt(str)) + } + it('has getBounds() and computeTightBounds()', function(done){ LoadPathKit.then(() => { // Based on PathOpsTightBoundsIllBehaved @@ -39,12 +43,36 @@ describe('PathKit\'s Path Behavior', function() { path.quadraticCurveTo(4, 3, 2, 2); expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(1, 1, 4, 3)); expect(path.computeTightBounds()).toEqual(PathKit.MakeLTRBRect(1, 1, - PathKit.SkBits2FloatUnsigned(parseInt("0x40333334")), // 2.8 - PathKit.SkBits2FloatUnsigned(parseInt("0x40155556")))); // 2.3333333 + bits2float("0x40333334"), // 2.8 + bits2float("0x40155556"))); // 2.3333333 path.delete(); done(); }); }); + it('does NOT approximates conics when dumping as toCmds', function(done){ + LoadPathKit.then(() => { + let path = PathKit.NewPath(); + path.moveTo(20, 120); + path.arc(20, 120, 18, 0, 1.75 * Math.PI); + path.lineTo(20, 120); + + let expectedCmds = [ + [PathKit.MOVE_VERB, 20, 120], + [PathKit.LINE_VERB, 38, 120], + [PathKit.CONIC_VERB, 38, 138, 20, 138, bits2float("0x3f3504f3)")], // 0.707107f + [PathKit.CONIC_VERB, 2, 138, 2, 120, bits2float("0x3f3504f3)")], // 0.707107f + [PathKit.CONIC_VERB, 2, 102, 20, 102, bits2float("0x3f3504f3)")], // 0.707107f + [PathKit.CONIC_VERB, bits2float("0x41dba58e"), 102, bits2float("0x4202e962"), bits2float("0x42d68b4d"), bits2float("0x3f6c8361")], // 27.4558, 102, 32.7279, 107.272, 0.92388 + [PathKit.LINE_VERB, 20, 120], + ]; + let actual = path.toCmds(); + expect(actual).toEqual(expectedCmds); + + path.delete(); + done(); + }); + }); + }); diff --git a/experimental/pathkit/tests/path2d.spec.js b/experimental/pathkit/tests/path2d.spec.js index 6e1cf95cb5..303bf879c9 100644 --- a/experimental/pathkit/tests/path2d.spec.js +++ b/experimental/pathkit/tests/path2d.spec.js @@ -10,12 +10,12 @@ describe('PathKit\'s Path2D API', function() { // Note, don't try to print the PathKit object - it can cause Karma/Jasmine to lock up. var PathKit = null; - const LoadPathKit = new Promise(function(resolve, reject){ + const LoadPathKit = new Promise(function(resolve, reject) { if (PathKit) { resolve(); } else { PathKitInit({ - locateFile: (file) => '/base/npm-wasm/bin/test/'+file, + locateFile: (file) => '/pathkit/'+file, }).then((_PathKit) => { PathKit = _PathKit; resolve(); @@ -23,7 +23,7 @@ describe('PathKit\'s Path2D API', function() { } }); - it('can do everything in the Path2D API w/o crashing', function(done){ + it('can do everything in the Path2D API w/o crashing', function(done) { LoadPathKit.then(() => { // This is taken from example.html let path = PathKit.NewPath(); @@ -79,4 +79,29 @@ describe('PathKit\'s Path2D API', function() { }); }); -}); \ No newline at end of file + it('approximates arcs (conics) with quads', function(done) { + LoadPathKit.then(() => { + let path = PathKit.NewPath(); + path.moveTo(20, 120); + path.arc(20, 120, 18, 0, 1.75 * Math.PI); + path.lineTo(20, 120); + + let canvas = document.createElement('canvas'); + container.appendChild(canvas); + let canvasCtx = canvas.getContext('2d'); + + spyOn(canvasCtx, 'quadraticCurveTo'); + + canvasCtx.beginPath(); + path.toCanvas(canvasCtx); + canvasCtx.stroke(); + // No need to check the whole path, as that's more what the + // gold correctness tests are for (can account for changes we make + // to the approximation algorithms). + expect(canvasCtx.quadraticCurveTo).toHaveBeenCalled(); + path.delete(); + done(); + }); + }); + +}); diff --git a/experimental/pathkit/tests/pathops.spec.js b/experimental/pathkit/tests/pathops.spec.js index c9a3d54a35..3a9228ed53 100644 --- a/experimental/pathkit/tests/pathops.spec.js +++ b/experimental/pathkit/tests/pathops.spec.js @@ -77,12 +77,12 @@ describe('PathKit\'s PathOps Behavior', function() { var PathKit = null; var PATHOP_MAP = {}; var FILLTYPE_MAP = {}; - const LoadPathKit = new Promise(function(resolve, reject){ + const LoadPathKit = new Promise(function(resolve, reject) { if (PathKit) { resolve(); } else { PathKitInit({ - locateFile: (file) => '/base/npm-wasm/bin/test/'+file, + locateFile: (file) => '/pathkit/'+file, }).then((_PathKit) => { PathKit = _PathKit; PATHOP_MAP = { @@ -152,6 +152,9 @@ describe('PathKit\'s PathOps Behavior', function() { // Do a tolerant match. let diff = diffPaths(expected, combined); if (test.expectMatch === 'yes'){ + // Check fill type + expect(combined.getFillType().value).toEqual(getFillType(test.fillTypeOut).value); + // diff should be null if the paths are identical (modulo rounding) if (diff) { expect(`[${testName}] ${diff}`).toBe(''); addSVG('[PathOps] ' + testName, expected, combined, diff); @@ -202,6 +205,9 @@ describe('PathKit\'s PathOps Behavior', function() { // Do a tolerant match. let diff = diffPaths(expected, simplified); if (test.expectMatch === 'yes'){ + // Check fill type + expect(simplified.getFillType().value).toEqual(getFillType(test.fillTypeOut).value); + // diff should be null if the paths are identical (modulo rounding) if (diff) { expect(`[${testName}] ${diff}`).toBe(''); addSVG('[Simplify] ' + testName, expected, simplified, diff); @@ -223,4 +229,4 @@ describe('PathKit\'s PathOps Behavior', function() { }); }); }); -}); \ No newline at end of file +}); diff --git a/experimental/pathkit/tests/svg.spec.js b/experimental/pathkit/tests/svg.spec.js index f9d1b02e13..f4c30077c4 100644 --- a/experimental/pathkit/tests/svg.spec.js +++ b/experimental/pathkit/tests/svg.spec.js @@ -2,12 +2,12 @@ describe('PathKit\'s SVG Behavior', function() { // Note, don't try to print the PathKit object - it can cause Karma/Jasmine to lock up. var PathKit = null; - const LoadPathKit = new Promise(function(resolve, reject){ + const LoadPathKit = new Promise(function(resolve, reject) { if (PathKit) { resolve(); } else { PathKitInit({ - locateFile: (file) => '/base/npm-wasm/bin/test/'+file, + locateFile: (file) => '/pathkit/'+file, }).then((_PathKit) => { PathKit = _PathKit; resolve(); @@ -15,7 +15,7 @@ describe('PathKit\'s SVG Behavior', function() { } }); - it('can create a path from an SVG string', function(done){ + it('can create a path from an SVG string', function(done) { LoadPathKit.then(() => { //.This is a parallelagram from // https://upload.wikimedia.org/wikipedia/commons/e/e7/Simple_parallelogram.svg @@ -37,7 +37,7 @@ describe('PathKit\'s SVG Behavior', function() { }); }); - it('can create an SVG string from a path', function(done){ + it('can create an SVG string from a path', function(done) { LoadPathKit.then(() => { let cmds = [[PathKit.MOVE_VERB, 205, 5], [PathKit.LINE_VERB, 795, 5], @@ -56,7 +56,7 @@ describe('PathKit\'s SVG Behavior', function() { }); }); - it('can create an SVG string from hex values', function(done){ + it('can create an SVG string from hex values', function(done) { LoadPathKit.then(() => { let cmds = [[PathKit.MOVE_VERB, "0x15e80300", "0x400004dc"], // 9.37088e-26f, 2.0003f [PathKit.LINE_VERB, 795, 5], @@ -74,7 +74,7 @@ describe('PathKit\'s SVG Behavior', function() { }); }); - it('should have input and the output be the same', function(done){ + it('should have input and the output be the same', function(done) { LoadPathKit.then(() => { let testCases = [ 'M0 0L1075 0L1075 242L0 242L0 0Z' @@ -92,4 +92,20 @@ describe('PathKit\'s SVG Behavior', function() { }); }); + it('approximates arcs (conics) with quads', function(done) { + LoadPathKit.then(() => { + let path = PathKit.NewPath(); + path.moveTo(20, 120); + path.arc(20, 120, 18, 0, 1.75 * Math.PI); + path.lineTo(20, 120); + let svgStr = path.toSVGString(); + // Q stands for quad. No need to check the whole path, as that's more + // what the gold correctness tests are for (can account for changes we make + // to the approximation algorithms). + expect(svgStr).toContain('Q'); + path.delete(); + done(); + }); + }); + }); diff --git a/src/core/SkFlattenable.cpp b/src/core/SkFlattenable.cpp index c8766f88be..3ccbadd20e 100644 --- a/src/core/SkFlattenable.cpp +++ b/src/core/SkFlattenable.cpp @@ -101,6 +101,11 @@ static void report_no_entries(const char* functionName) { #endif SkFlattenable::Factory SkFlattenable::NameToFactory(const char name[]) { +#ifdef WEB_ASSEMBLY + // Should not be reachable by WebAssembly Code. + SkASSERT(false); + return nullptr; +#else InitializeFlattenablesIfNeeded(); SkASSERT(std::is_sorted(gEntries, gEntries + gCount, EntryComparator())); #ifdef SK_DEBUG @@ -110,9 +115,15 @@ SkFlattenable::Factory SkFlattenable::NameToFactory(const char name[]) { if (pair.first == pair.second) return nullptr; return pair.first->fFactory; +#endif } bool SkFlattenable::NameToType(const char name[], SkFlattenable::Type* type) { +#ifdef WEB_ASSEMBLY + // Should not be reachable by WebAssembly Code. + SkASSERT(false); + return false; +#else SkASSERT(type); InitializeFlattenablesIfNeeded(); SkASSERT(std::is_sorted(gEntries, gEntries + gCount, EntryComparator())); @@ -124,9 +135,15 @@ bool SkFlattenable::NameToType(const char name[], SkFlattenable::Type* type) { return false; *type = pair.first->fType; return true; +#endif } const char* SkFlattenable::FactoryToName(Factory fact) { +#ifdef WEB_ASSEMBLY + // Should not be reachable by WebAssembly Code. + SkASSERT(false); + return nullptr; +#else InitializeFlattenablesIfNeeded(); #ifdef SK_DEBUG report_no_entries(__FUNCTION__); @@ -138,6 +155,7 @@ const char* SkFlattenable::FactoryToName(Factory fact) { } } return nullptr; +#endif } /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp index aa3803a95c..a2434099f6 100644 --- a/src/effects/SkDashPathEffect.cpp +++ b/src/effects/SkDashPathEffect.cpp @@ -374,6 +374,11 @@ void SkDashImpl::flatten(SkWriteBuffer& buffer) const { } sk_sp SkDashImpl::CreateProc(SkReadBuffer& buffer) { +#ifdef WEB_ASSEMBLY + // Should not be reachable by WebAssembly Code. + SkASSERT(false); + return nullptr; +#else const SkScalar phase = buffer.readScalar(); uint32_t count = buffer.getArrayCount(); @@ -387,6 +392,7 @@ sk_sp SkDashImpl::CreateProc(SkReadBuffer& buffer) { return SkDashPathEffect::Make(intervals.get(), SkToInt(count), phase); } return nullptr; +#endif } ////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/effects/SkTrimPathEffect.cpp b/src/effects/SkTrimPathEffect.cpp index 8c3f56e2ba..3a8d0944cb 100644 --- a/src/effects/SkTrimPathEffect.cpp +++ b/src/effects/SkTrimPathEffect.cpp @@ -89,12 +89,18 @@ void SkTrimPE::flatten(SkWriteBuffer& buffer) const { } sk_sp SkTrimPE::CreateProc(SkReadBuffer& buffer) { +#ifdef WEB_ASSEMBLY + // Should not be reachable by WebAssembly Code. + SkASSERT(false); + return nullptr; +#else const auto start = buffer.readScalar(), stop = buffer.readScalar(); const auto mode = buffer.readUInt(); return SkTrimPathEffect::Make(start, stop, (mode & 1) ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal); +#endif } //////////////////////////////////////////////////////////////////////////////////////////////////