[canvaskit] Expose getShadowLocalBounds
Bug: skia:11146
Change-Id: Ib08a96c8d0ca0f4ca2b489b3793e45f642cb231f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/353034
Reviewed-by: Yegor Jbanov <yjbanov@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/modules/canvaskit/CHANGELOG.md b/modules/canvaskit/CHANGELOG.md
index 2d9cce3..75d1397 100644
--- a/modules/canvaskit/CHANGELOG.md
+++ b/modules/canvaskit/CHANGELOG.md
@@ -8,6 +8,7 @@
### Added
- Constants for the shadow flags. Of note, some of these values can be used on previous releases.
+ - `getShadowLocalBounds()` to estimate the bounds of the shadows drawn by `Canvas.drawShadow`.
### Breaking
- `MakeImprovedNoise` is removed.
diff --git a/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts b/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts
index bd45380..adb659c 100644
--- a/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts
+++ b/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts
@@ -351,13 +351,19 @@
const tf = fm.makeTypefaceFromData(buff1); // $ExpectType Typeface
}
-function globalTests(CK: CanvasKit) {
+function globalTests(CK: CanvasKit, path?: Path) {
+ if (!path) {
+ return;
+ }
const ctx = CK.currentContext();
CK.setCurrentContext(ctx);
CK.deleteContext(ctx);
const n = CK.getDecodeCacheLimitBytes();
const u = CK.getDecodeCacheUsedBytes();
CK.setDecodeCacheLimitBytes(1000);
+ const matr = CK.Matrix.rotated(Math.PI / 6);
+ const p = CK.getShadowLocalBounds(matr, path, [0, 0, 1], [500, 500, 20], 20,
+ CK.ShadowDirectionalLight | CK.ShadowGeometricOnly | CK.ShadowDirectionalLight);
}
function paintTests(CK: CanvasKit, colorFilter?: ColorFilter, imageFilter?: ImageFilter,
diff --git a/modules/canvaskit/canvaskit/types/index.d.ts b/modules/canvaskit/canvaskit/types/index.d.ts
index 83960f8..0bfc99d 100644
--- a/modules/canvaskit/canvaskit/types/index.d.ts
+++ b/modules/canvaskit/canvaskit/types/index.d.ts
@@ -124,6 +124,26 @@
RRectXY(rect: InputRect, rx: number, ry: number): RRect;
/**
+ * Generate bounding box for shadows relative to path. Includes both the ambient and spot
+ * shadow bounds. This pairs with Canvas.drawShadow().
+ * See SkShadowUtils.h for more details.
+ * @param ctm - Current transformation matrix to device space.
+ * @param path - The occluder used to generate the shadows.
+ * @param zPlaneParams - Values for the plane function which returns the Z offset of the
+ * occluder from the canvas based on local x and y values (the current
+ * matrix is not applied).
+ * @param lightPos - The 3D position of the light relative to the canvas plane. This is
+ * independent of the canvas's current matrix.
+ * @param lightRadius - The radius of the disc light.
+ * @param flags - See SkShadowFlags.h; 0 means use default options.
+ * @param dstRect - if provided, the bounds will be copied into this rect instead of allocating
+ * a new one.
+ * @returns The bounding rectangle or null if it could not be computed.
+ */
+ getShadowLocalBounds(ctm: InputMatrix, path: Path, zPlaneParams: Vector3, lightPos: Vector3,
+ lightRadius: number, flags: number, dstRect?: Rect): Rect | null;
+
+ /**
* Malloc returns a TypedArray backed by the C++ memory of the
* given length. It should only be used by advanced users who
* can manage memory and initialize values properly. When used
diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp
index 1f5f60a..edf2de3 100644
--- a/modules/canvaskit/canvaskit_bindings.cpp
+++ b/modules/canvaskit/canvaskit_bindings.cpp
@@ -781,6 +781,16 @@
return SkImage::MakeRasterData(info, pixelData, rowBytes);
}), allow_raw_pointers());
+ function("_getShadowLocalBounds", optional_override([](
+ uintptr_t /* float* */ ctmPtr, const SkPath& path,
+ const SkPoint3& zPlaneParams, const SkPoint3& lightPos, SkScalar lightRadius,
+ uint32_t flags, uintptr_t /* SkRect* */ outPtr) -> bool {
+ OptionalMatrix ctm(ctmPtr);
+ SkRect* outputBounds = reinterpret_cast<SkRect*>(outPtr);
+ return SkShadowUtils::GetLocalBounds(ctm, path, zPlaneParams, lightPos, lightRadius,
+ flags, outputBounds);
+ }));
+
#ifdef SK_SERIALIZE_SKP
function("_MakePicture", optional_override([](uintptr_t /* unint8_t* */ dPtr,
size_t bytes)->sk_sp<SkPicture> {
diff --git a/modules/canvaskit/externs.js b/modules/canvaskit/externs.js
index 6508b56..451041c 100644
--- a/modules/canvaskit/externs.js
+++ b/modules/canvaskit/externs.js
@@ -70,7 +70,7 @@
parseColorString: function() {},
setCurrentContext: function() {},
setDecodeCacheLimitBytes: function() {},
-
+ getShadowLocalBounds: function() {},
// Defined by emscripten.
createContext: function() {},
@@ -84,6 +84,7 @@
_decodeAnimatedImage: function() {},
_decodeImage: function() {},
_drawShapedText: function() {},
+ _getShadowLocalBounds: function() {},
// The testing object is meant to expose internal functions
// for more fine-grained testing, e.g. parseColor
diff --git a/modules/canvaskit/interface.js b/modules/canvaskit/interface.js
index 7a5e4c9..9c78e05 100644
--- a/modules/canvaskit/interface.js
+++ b/modules/canvaskit/interface.js
@@ -1149,7 +1149,8 @@
this._drawRect(rPtr, paint);
};
- CanvasKit.Canvas.prototype.drawShadow = function(path, zPlaneParams, lightPos, lightRadius, ambientColor, spotColor, flags) {
+ CanvasKit.Canvas.prototype.drawShadow = function(path, zPlaneParams, lightPos, lightRadius,
+ ambientColor, spotColor, flags) {
var ambiPtr = copyColorToWasmNoScratch(ambientColor);
var spotPtr = copyColorToWasmNoScratch(spotColor);
this._drawShadow(path, zPlaneParams, lightPos, lightRadius, ambiPtr, spotPtr, flags);
@@ -1157,6 +1158,22 @@
freeArraysThatAreNotMallocedByUsers(spotPtr, spotColor);
};
+ CanvasKit.getShadowLocalBounds = function(ctm, path, zPlaneParams, lightPos, lightRadius,
+ flags, optOutputRect) {
+ var ctmPtr = copy3x3MatrixToWasm(ctm);
+ var ok = this._getShadowLocalBounds(ctmPtr, path, zPlaneParams, lightPos, lightRadius,
+ flags, _scratchRectPtr);
+ if (!ok) {
+ return null;
+ }
+ var ta = _scratchRect['toTypedArray']();
+ if (optOutputRect) {
+ optOutputRect.set(ta);
+ return optOutputRect;
+ }
+ return ta.slice();
+ };
+
// getLocalToDevice returns a 4x4 matrix.
CanvasKit.Canvas.prototype.getLocalToDevice = function() {
// _getLocalToDevice will copy the values into the pointer.
diff --git a/modules/canvaskit/tests/core.spec.js b/modules/canvaskit/tests/core.spec.js
index 929f4d3..b723683 100644
--- a/modules/canvaskit/tests/core.spec.js
+++ b/modules/canvaskit/tests/core.spec.js
@@ -927,12 +927,23 @@
CanvasKit.BLACK, CanvasKit.MAGENTA, 0);
canvas.drawText('Default Flags', 5, 250, textPaint, textFont);
+ const bounds = CanvasKit.getShadowLocalBounds(CanvasKit.Matrix.identity(),
+ path, zPlaneParams, lightPos, lightRadius, 0);
+ expectTypedArraysToEqual(bounds, Float32Array.of(-3.64462, -12.67541, 245.50, 242.59164));
+
canvas.translate(250, 250);
canvas.drawShadow(path, zPlaneParams, lightPos, lightRadius,
CanvasKit.BLACK, CanvasKit.MAGENTA,
CanvasKit.ShadowTransparentOccluder | CanvasKit.ShadowGeometricOnly | CanvasKit.ShadowDirectionalLight);
canvas.drawText('All Flags', 5, 250, textPaint, textFont);
+ const outBounds = new Float32Array(4);
+ CanvasKit.getShadowLocalBounds(CanvasKit.Matrix.rotated(Math.PI / 6),
+ path, zPlaneParams, lightPos, lightRadius,
+ CanvasKit.ShadowTransparentOccluder | CanvasKit.ShadowGeometricOnly | CanvasKit.ShadowDirectionalLight,
+ outBounds);
+ expectTypedArraysToEqual(outBounds, Float32Array.of(1.52207, -6.35035, 264.03445, 261.83294));
+
path.delete();
textFont.delete();
textPaint.delete();