[PathKit] Add various path effects
API Changes (nothing should be breaking):
- Exposes conic, although all output formats (SVG, Canvas) need conics
to be approximated with quads. Tests have been added to verify this
happens.
- Add .dash(), .trim(), .stroke() and examples for them.
- Expose Stroke enums (StrokeJoin, StrokeCap)
Adds tests for the cubic part and clean up a few spacing things.
There are some changes to the C++ code to simplify the build -
otherwise, I need to appease the linker and add add in a bunch
of files that may or may not get optimized out. Best make them
not even be compiled, just to make sure.
Bug: skia:8216
Change-Id: I1da3aaab1891f14a5b3dc01bb6523b4fd9a87b04
Reviewed-on: https://skia-review.googlesource.com/146650
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/experimental/pathkit/tests/path.spec.js b/experimental/pathkit/tests/path.spec.js
index 660ade5..0cafc38 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 @@
});
});
+ function bits2float(str) {
+ return PathKit.SkBits2FloatUnsigned(parseInt(str))
+ }
+
it('has getBounds() and computeTightBounds()', function(done){
LoadPathKit.then(() => {
// Based on PathOpsTightBoundsIllBehaved
@@ -39,12 +43,36 @@
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 6e1cf95..303bf87 100644
--- a/experimental/pathkit/tests/path2d.spec.js
+++ b/experimental/pathkit/tests/path2d.spec.js
@@ -10,12 +10,12 @@
// 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 @@
}
});
- 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 @@
});
});
-});
\ 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 c9a3d54..3a9228e 100644
--- a/experimental/pathkit/tests/pathops.spec.js
+++ b/experimental/pathkit/tests/pathops.spec.js
@@ -77,12 +77,12 @@
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 @@
// 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 @@
// 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 @@
});
});
});
-});
\ No newline at end of file
+});
diff --git a/experimental/pathkit/tests/svg.spec.js b/experimental/pathkit/tests/svg.spec.js
index f9d1b02..f4c3007 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 @@
}
});
- 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 @@
});
});
- 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 @@
});
});
- 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 @@
});
});
- 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 @@
});
});
+ 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();
+ });
+ });
+
});