[canvaskit] Expose AsWinding
Bug: skia:11858
Change-Id: Iedbc2333779f2fac5029779bae44da48d8dd7b8f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/430956
Reviewed-by: Ben Wagner <bungeman@google.com>
diff --git a/modules/canvaskit/CHANGELOG.md b/modules/canvaskit/CHANGELOG.md
index afcc3bb..7258b2e 100644
--- a/modules/canvaskit/CHANGELOG.md
+++ b/modules/canvaskit/CHANGELOG.md
@@ -6,9 +6,13 @@
## [Unreleased]
+### Added
+ - `Path.makeAsWinding` has been added to convert paths with an EvenOdd FillType to the
+ equivalent area using the Winding FillType.
+
### Breaking
- - Paint.getBlendMode() has been removed.
- - Canvas.drawImageAtCurrentFrame() has been removed.
+ - `Paint.getBlendMode()` has been removed.
+ - `Canvas.drawImageAtCurrentFrame()` has been removed.
- FilterQuality enum removed -- pass FilterOptions | CubicResampler instead.
## [0.28.1] - 2021-06-28
diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp
index 3898aad..ef5538d 100644
--- a/modules/canvaskit/canvaskit_bindings.cpp
+++ b/modules/canvaskit/canvaskit_bindings.cpp
@@ -329,6 +329,14 @@
}
return emscripten::val::null();
}
+
+SkPathOrNull MakeAsWinding(const SkPath& self) {
+ SkPath out;
+ if (AsWinding(self, &out)) {
+ return emscripten::val(out);
+ }
+ return emscripten::val::null();
+}
#endif
JSString ToSVGString(const SkPath& path) {
@@ -1511,6 +1519,7 @@
// PathOps
.function("_simplify", &ApplySimplify)
.function("_op", &ApplyPathOp)
+ .function("makeAsWinding", &MakeAsWinding)
#endif
// Exporting
.function("toSVGString", &ToSVGString)
diff --git a/modules/canvaskit/externs.js b/modules/canvaskit/externs.js
index c9e8980..3c7e5d6 100644
--- a/modules/canvaskit/externs.js
+++ b/modules/canvaskit/externs.js
@@ -529,6 +529,7 @@
getFillType: function() {},
isEmpty: function() {},
isVolatile: function() {},
+ makeAsWinding: function() {},
reset: function() {},
rewind: function() {},
setFillType: function() {},
diff --git a/modules/canvaskit/npm_build/types/canvaskit-wasm-tests.ts b/modules/canvaskit/npm_build/types/canvaskit-wasm-tests.ts
index c3e1adb..d27c73a 100644
--- a/modules/canvaskit/npm_build/types/canvaskit-wasm-tests.ts
+++ b/modules/canvaskit/npm_build/types/canvaskit-wasm-tests.ts
@@ -429,6 +429,7 @@
const p4 = CK.Path.MakeFromVerbsPointsWeights([CK.CONIC_VERB], points, [2.3]);
const p5 = CK.Path.MakeFromOp(p4, p2!, CK.PathOp.ReverseDifference); // $ExpectType Path | null
const p6 = CK.Path.MakeFromSVGString('M 205,5 L 795,5 z'); // $ExpectType Path | null
+ const p7 = p3.makeAsWinding(); // $ExpectType Path | null
const someRect = CK.LTRBRect(10, 20, 30, 40);
// Making sure arrays are accepted as rrects.
diff --git a/modules/canvaskit/npm_build/types/index.d.ts b/modules/canvaskit/npm_build/types/index.d.ts
index af8904a..fafe8b0 100644
--- a/modules/canvaskit/npm_build/types/index.d.ts
+++ b/modules/canvaskit/npm_build/types/index.d.ts
@@ -2318,7 +2318,15 @@
lineTo(x: number, y: number): Path;
/**
- * Adds begininning of contour at the given point.
+ * Returns a new path that covers the same area as the original path, but with the
+ * Winding FillType. This may re-draw some contours in the path as counter-clockwise
+ * instead of clockwise to achieve that effect. If such a transformation cannot
+ * be done, null is returned.
+ */
+ makeAsWinding(): Path | null;
+
+ /**
+ * Adds beginning of contour at the given point.
* Returns the modified path for easier chaining.
* @param x
* @param y
diff --git a/modules/canvaskit/tests/path.spec.js b/modules/canvaskit/tests/path.spec.js
index 944038c..8388c67 100644
--- a/modules/canvaskit/tests/path.spec.js
+++ b/modules/canvaskit/tests/path.spec.js
@@ -488,4 +488,93 @@
path.delete();
paint.delete();
});
+
+ gm('winding_example', (canvas) => {
+ // Inspired by https://fiddle.skia.org/c/@Path_FillType_a
+ const path = new CanvasKit.Path();
+ // Draw overlapping rects on top
+ path.addRect(CanvasKit.LTRBRect(10, 10, 30, 30), false);
+ path.addRect(CanvasKit.LTRBRect(20, 20, 40, 40), false);
+ // Draw overlapping rects on bottom, with different direction lines.
+ path.addRect(CanvasKit.LTRBRect(10, 60, 30, 80), false);
+ path.addRect(CanvasKit.LTRBRect(20, 70, 40, 90), true);
+
+ expect(path.getFillType()).toEqual(CanvasKit.FillType.Winding);
+
+ // Draw the two rectangles on the left side.
+ const paint = new CanvasKit.Paint();
+ paint.setStyle(CanvasKit.PaintStyle.Stroke);
+ canvas.drawPath(path, paint);
+
+ const clipRect = CanvasKit.LTRBRect(0, 0, 51, 100);
+ paint.setStyle(CanvasKit.PaintStyle.Fill);
+
+ for (const fillType of [CanvasKit.FillType.Winding, CanvasKit.FillType.EvenOdd]) {
+ canvas.translate(51, 0);
+ canvas.save();
+ canvas.clipRect(clipRect, CanvasKit.ClipOp.Intersect, false);
+ path.setFillType(fillType);
+ canvas.drawPath(path, paint);
+ canvas.restore();
+ }
+
+ path.delete();
+ paint.delete();
+ });
+
+ gm('as_winding', (canvas) => {
+ const evenOddPath = new CanvasKit.Path();
+ // Draw overlapping rects
+ evenOddPath.addRect(CanvasKit.LTRBRect(10, 10, 70, 70), false);
+ evenOddPath.addRect(CanvasKit.LTRBRect(30, 30, 50, 50), false);
+ evenOddPath.setFillType(CanvasKit.FillType.EvenOdd);
+
+ const evenOddCmds = evenOddPath.toCmds();
+ expect(evenOddCmds).toEqual(Float32Array.of(
+ CanvasKit.MOVE_VERB, 10, 10,
+ CanvasKit.LINE_VERB, 70, 10,
+ CanvasKit.LINE_VERB, 70, 70,
+ CanvasKit.LINE_VERB, 10, 70,
+ CanvasKit.CLOSE_VERB,
+ CanvasKit.MOVE_VERB, 30, 30, // This contour is drawn
+ CanvasKit.LINE_VERB, 50, 30, // clockwise, as specified.
+ CanvasKit.LINE_VERB, 50, 50,
+ CanvasKit.LINE_VERB, 30, 50,
+ CanvasKit.CLOSE_VERB
+ ));
+
+ const windingPath = evenOddPath.makeAsWinding();
+
+ expect(windingPath.getFillType()).toBe(CanvasKit.FillType.Winding);
+ const windingCmds = windingPath.toCmds();
+ expect(windingCmds).toEqual(Float32Array.of(
+ CanvasKit.MOVE_VERB, 10, 10,
+ CanvasKit.LINE_VERB, 70, 10,
+ CanvasKit.LINE_VERB, 70, 70,
+ CanvasKit.LINE_VERB, 10, 70,
+ CanvasKit.CLOSE_VERB,
+ CanvasKit.MOVE_VERB, 30, 50, // This contour has been
+ CanvasKit.LINE_VERB, 50, 50, // re-drawn counter-clockwise
+ CanvasKit.LINE_VERB, 50, 30, // so that it covers the same
+ CanvasKit.LINE_VERB, 30, 30, // area, but with the winding fill type.
+ CanvasKit.CLOSE_VERB
+ ));
+
+ const paint = new CanvasKit.Paint();
+ paint.setStyle(CanvasKit.PaintStyle.Fill);
+ const font = new CanvasKit.Font(null, 20);
+
+ canvas.drawText('Original path (even odd)', 5, 20, paint, font);
+ canvas.translate(0, 50);
+ canvas.drawPath(evenOddPath, paint);
+
+ canvas.translate(300, 0);
+ canvas.drawPath(windingPath, paint);
+
+ canvas.translate(0, -50);
+ canvas.drawText('makeAsWinding path', 5, 20, paint, font);
+
+ evenOddPath.delete();
+ windingPath.delete();
+ });
});