From f8823b5726004f593c322595405fdefe86719101 Mon Sep 17 00:00:00 2001 From: Kevin Lubick Date: Thu, 3 Sep 2020 10:02:10 -0400 Subject: [PATCH] Reland "[canvaskit] Change SkRects to be arrays, not objects." This is a reland of bdc214a50e854fabb62bda126977b63e298dea9f The CPU version of SkottieWasm is timing out for reasons unknown, but the GPU version is happy. I think we can get rid of the CPU version of the job since it has been more or less superseded by the SkottieFrames one (and the latter is more stable). Original change's description: > [canvaskit] Change SkRects to be arrays, not objects. > > This changes several APIs, so there are lots of breaking > notes in the Changelog. > > This made the "draw 100 colored regions" benchmark about > 20% faster (1ms -> .8ms). > > In theory, rendering should stay the same. > > Change-Id: Ib80b15e2d980ad5d568fff4460d2b529766c1b36 > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/312491 > Reviewed-by: Nathaniel Nifong Change-Id: I674aba85ecfb30b72e94cbaf89b2d97bfae3b7a4 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/315142 Reviewed-by: Nathaniel Nifong --- infra/bots/README.recipes.md | 4 +- .../unrecognized_builder.json | 2 +- .../recipes/perf_skottiewasm_lottieweb.py | 1 + modules/canvaskit/CHANGELOG.md | 30 ++- modules/canvaskit/canvaskit/example.html | 24 +- modules/canvaskit/canvaskit/extra.html | 34 +-- modules/canvaskit/canvaskit_bindings.cpp | 218 ++++++++---------- modules/canvaskit/cpu.js | 6 +- modules/canvaskit/externs.js | 167 +++++++------- modules/canvaskit/font.js | 13 ++ modules/canvaskit/helper.js | 73 +++--- modules/canvaskit/htmlcanvas/path2d.js | 7 +- modules/canvaskit/interface.js | 196 +++++++++------- modules/canvaskit/skottie.js | 39 +++- modules/canvaskit/skottie_bindings.cpp | 32 ++- modules/canvaskit/tests/canvas.spec.js | 25 +- modules/canvaskit/tests/core.spec.js | 2 +- modules/canvaskit/tests/font.spec.js | 16 +- modules/canvaskit/tests/path.spec.js | 21 +- modules/canvaskit/tests/skottie.spec.js | 21 +- .../skottie-frames.html | 7 +- tools/skottie-wasm-perf/Makefile | 8 +- .../skottie-wasm-perf/skottie-wasm-perf.html | 166 +++++++------ tools/skottie-wasm-perf/skottie-wasm-perf.js | 4 +- 24 files changed, 621 insertions(+), 495 deletions(-) diff --git a/infra/bots/README.recipes.md b/infra/bots/README.recipes.md index 54463cfa78..5117dba124 100644 --- a/infra/bots/README.recipes.md +++ b/infra/bots/README.recipes.md @@ -417,9 +417,9 @@ Run DM on lottie files with tracing turned on and then parse the output. [DEPS](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#12): [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/python][recipe_engine/recipe_modules/python], [recipe\_engine/step][recipe_engine/recipe_modules/step], [recipe\_engine/time][recipe_engine/recipe_modules/time], [checkout](#recipe_modules-checkout), [env](#recipe_modules-env), [flavor](#recipe_modules-flavor), [infra](#recipe_modules-infra), [run](#recipe_modules-run), [vars](#recipe_modules-vars) -— **def [RunSteps](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#83)(api):** +— **def [RunSteps](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#84)(api):** -— **def [parse\_trace](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#208)(trace_json, lottie_filename, api, renderer):** +— **def [parse\_trace](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#209)(trace_json, lottie_filename, api, renderer):** parse_trace parses the specified trace JSON. diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/unrecognized_builder.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/unrecognized_builder.json index 19cdea9783..85b3e72aae 100644 --- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/unrecognized_builder.json +++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/unrecognized_builder.json @@ -35,7 +35,7 @@ " arg_names, **additional_args)", " File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in _invoke_with_properties", " return callable_obj(*props, **additional_args)", - " File \"RECIPE_REPO[skia]/infra/bots/recipes/perf_skottiewasm_lottieweb.py\", line 128, in RunSteps", + " File \"RECIPE_REPO[skia]/infra/bots/recipes/perf_skottiewasm_lottieweb.py\", line 129, in RunSteps", " raise Exception('Could not recognize the buildername %s' % buildername)", "Exception: Could not recognize the buildername Perf-Debian10-none-GCE-CPU-AVX2-x86_64-Release-All-Unrecognized" ] diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.py b/infra/bots/recipes/perf_skottiewasm_lottieweb.py index d098e74076..42fb8c01fa 100644 --- a/infra/bots/recipes/perf_skottiewasm_lottieweb.py +++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.py @@ -69,6 +69,7 @@ SKOTTIE_WASM_EXCLUDE = [ 'streetby_loading.json', 'streetby_test_loading.json', # Times out + 'an_endless_hike_on_a_tiny_world_.json', # times out on CPU 'beetle.json', ] diff --git a/modules/canvaskit/CHANGELOG.md b/modules/canvaskit/CHANGELOG.md index 1522d4ef81..0c9b0a808b 100644 --- a/modules/canvaskit/CHANGELOG.md +++ b/modules/canvaskit/CHANGELOG.md @@ -7,12 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Breaking + - SkRect are no longer returned from `CanvasKit.LTRBRect`, `CanvasKit.XYWHRect` nor + are accepted as JS objects. Instead, the format is 4 floats in either an array, a + Float32Array or a piece of memory returned by CanvasKit.Malloc. These floats are the + left, top, right, bottom numbers of the rectangle. + - SkIRect (Rectangles with Integer values) are no longer accepted as JS objects. + Instead, the format is 4 ints in either an array, an Int32Array or a piece of memory + returned by CanvasKit.Malloc. These ints are the left, top, right, bottom numbers of + the rectangle. - SkRRect (Rectangles with rounded corners) are no longer returned from `CanvasKit.RRectXY` - nor accepted as JS objects. Instead, the format is 12 floats in either an array, a + nor are accepted as JS objects. Instead, the format is 12 floats in either an array, a Float32Array or a piece of memory returned by CanvasKit.Malloc. The first 4 floats are the left, top, right, bottom numbers of the rectangle and then 4 sets of points starting in the upper left corner and going clockwise. This change allows for faster transfer between JS and WASM code. + - `SkPath.addRoundRect` has been replaced with `SkPath.addRRect`. The same functionality + can be had with the `CanvasKit.RRectXY` helper. + - `SkPath.addRect` no longer accepts 4 floats as separate arguments. It only accepts + an SkRect (an array/Float32Array of 4 floats) and an optional boolean for + determining clockwise or counter-clockwise directionality. + - The order of `SkCanvas.saveLayer` arguments is slightly different (more consistent). + It is now `paint, bounds, backdrop, flags` ### Changed - We now compile CanvasKit with emsdk 2.0.0 when testing and deploying to npm. @@ -20,6 +35,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The signature of `main` used with SkSL passed to `CanvasKit.SkRuntimeEffect.Make` has changed. There is no longer an `inout half4 color` parameter, effects must return their color instead. Valid signatures are now `half4 main()` or `half4 main(float2 coord)`. + - `SkPath.getBounds`, `SkShapedText.getBounds`, and `SkVertices.bounds` now + take an optional argument. If a Float32Array with length 4 or greater is + provided, the bounds will be copied into this array instead of allocating + a new one. + +### Removed + - `SkCanvas.drawRoundRect` has been removed in favor of `SkCanvas.drawRRect` + The same functionality can be had with the `CanvasKit.RRectXY` helper. + - `SkPath.arcTo` which had been deprecated in favor of `SkPath.arcToOval`, + `SkPath.arcToRotated`, `SkPath.arcToTangent`. + +### Added + - `CanvasKit.LTRBiRect` and `CanvasKit.XYWHiRect` as helpers to create SkIRects. ## [0.17.3] - 2020-08-05 diff --git a/modules/canvaskit/canvaskit/example.html b/modules/canvaskit/canvaskit/example.html index 68f343f05d..87d661a2ac 100644 --- a/modules/canvaskit/canvaskit/example.html +++ b/modules/canvaskit/canvaskit/example.html @@ -219,7 +219,7 @@ path.lineTo(36, 148); path.moveTo(150, 180); - path.arcTo(150, 100, 50, 200, 20); + path.arcToTangent(150, 100, 50, 200, 20); path.lineTo(160, 160); path.moveTo(20, 120); @@ -227,14 +227,14 @@ canvas.drawPath(path, paint); - let rrect = new CanvasKit.SkPath() - .addRoundRect(100, 10, 140, 62, - 10, 4, true); + const rrect = CanvasKit.RRectXY([100, 10, 140, 62], 10, 4); - canvas.drawPath(rrect, paint); + const rrectPath = new CanvasKit.SkPath().addRRect(rrect, true); + canvas.drawPath(rrectPath, paint); + + rrectPath.delete(); path.delete(); - rrect.delete(); paint.delete(); } // Intentionally just draw frame once @@ -964,10 +964,10 @@ canvas.drawText(shapedText, textBoxX, textBoxY, textPaint); const bounds = shapedText.getBounds(); - bounds.fLeft += textBoxX; - bounds.fRight += textBoxX; - bounds.fTop += textBoxY; - bounds.fBottom += textBoxY + bounds[0] += textBoxX; // left + bounds[2] += textBoxX; // right + bounds[1] += textBoxY; // top + bounds[3] += textBoxY // bottom canvas.drawRect(bounds, paint); const SHAPE_TEST_TEXT = 'VAVAVAVAVAFIfi'; @@ -1072,9 +1072,9 @@ fontPaint.setAntiAlias(true); const arc = new CanvasKit.SkPath(); - arc.arcTo(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true); + arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true); arc.lineTo(210, 140); - arc.arcTo(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true); + arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true); const str = 'This téxt should follow the curve across contours...'; const textBlob = CanvasKit.SkTextBlob.MakeOnPath(str, arc, font); diff --git a/modules/canvaskit/canvaskit/extra.html b/modules/canvaskit/canvaskit/extra.html index 6b75a6efc7..4acd8703d2 100644 --- a/modules/canvaskit/canvaskit/extra.html +++ b/modules/canvaskit/canvaskit/extra.html @@ -81,12 +81,12 @@ p.lifetime = 3 + rand(p.seed); p.vel.y = -50; } - + void update(inout Particle p) { float w = mix(15, 3, p.age); p.pos.x = sin(radians(p.age * 320)) * mix(25, 10, p.age) + mix(-w, w, rand(p.seed)); if (rand(p.seed) < 0.5) { p.pos.x = -p.pos.x; } - + p.color.g = (mix(75, 220, p.age) + mix(-30, 30, rand(p.seed))) / 255; } ` @@ -122,10 +122,10 @@ // Examples requiring external resources. // Set bounds to fix the 4:3 resolution of the legos Promise.all([ckLoaded, loadLegoJSON]).then(([ck, jsonstr]) => { - SkottieExample(ck, 'sk_legos', jsonstr, {fLeft: -50, fTop: 0, fRight: 350, fBottom: 300}); + SkottieExample(ck, 'sk_legos', jsonstr, [-50, 0, 350, 300]); }); // Re-size to fit - let fullBounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500}; + let fullBounds = [0, 0, 500, 500]; Promise.all([ckLoaded, loadDrinksJSON]).then(([ck, jsonstr]) => { SkottieExample(ck, 'sk_drinks', jsonstr, fullBounds); }); @@ -146,6 +146,11 @@ Promise.all([ckLoaded, loadSkp]).then((results) => {SkpExample(...results)}); Promise.all([ckLoaded, loadBrickTex, loadBrickBump, loadFont]).then((results) => {Camera3D(...results)}); + const rectLeft = 0; + const rectTop = 1; + const rectRight = 2; + const rectBottom = 3; + function SkottieExample(CanvasKit, id, jsonStr, bounds, assets) { if (!CanvasKit || !jsonStr) { return; @@ -154,7 +159,7 @@ const duration = animation.duration() * 1000; const size = animation.size(); let c = document.getElementById(id); - bounds = bounds || {fLeft: 0, fTop: 0, fRight: size.w, fBottom: size.h}; + bounds = bounds || CanvasKit.LTRBRect(0, 0, size.w, size.h); // Basic managed animation test. if (id === 'sk_drinks') { @@ -172,8 +177,8 @@ function drawFrame(canvas) { let seek = ((Date.now() - firstFrame) / duration) % 1.0; let damage = animation.seek(seek); - // TODO: SkRect.isEmpty()? - if (damage.fRight > damage.fLeft && damage.fBottom > damage.fTop) { + + if (damage[rectRight] > damage[rectLeft] && damage[rectBottom] > damage[rectTop]) { canvas.clear(CanvasKit.WHITE); animation.render(canvas, bounds); } @@ -181,7 +186,6 @@ } surface.requestAnimationFrame(drawFrame); - //animation.delete(); return surface; } @@ -523,7 +527,7 @@ float dp = dot(plane_norm, light_dir); float scale = min(ambient + max(dp, 0), 1); - color = sample(color_map, p) * half4(float4(scale, scale, scale, 1)); + color = sample(color_map, p) * half4(float4(scale, scale, scale, 1)); } `; @@ -830,7 +834,7 @@ // then covering with semitransparent background color. if (lastImage) { canvas.drawImage(lastImage, 0, 0, null); - canvas.drawColor(CanvasKit.Color(171, 244, 255, 0.1)); // sky blue, almost transparent + canvas.drawColor(CanvasKit.Color(171, 244, 255, 0.1)); // sky blue, almost transparent } else { canvas.clear(CanvasKit.Color(171, 244, 255)); // sky blue, opaque } @@ -855,8 +859,8 @@ let r = letter['r']; // rotate about the center of the glyph's rect. rotationPoint = [ - margin + r.fLeft + (r.fRight - r.fLeft) / 2, - marginTop + r.fTop + (r.fBottom - r.fTop) / 2, + margin + r[rectLeft] + (r[rectRight] - r[rectLeft]) / 2, + marginTop + r[rectTop] + (r[rectBottom] - r[rectTop]) / 2, 0 ]; distanceFromPointer = CanvasKit.SkVector.dist(pointer, rotationPoint.slice(0, 2)); @@ -870,7 +874,7 @@ CanvasKit.SkM44.rotated([0,1,0], distanceFromPointer * -0.035), CanvasKit.SkM44.translated(CanvasKit.SkVector.mulScalar(rotationPoint, -1)), )); - canvas.drawParagraph(letter['para'], margin + r.fLeft, marginTop + r.fTop); + canvas.drawParagraph(letter['para'], margin + r[rectLeft], marginTop + r[rectTop]); i++; canvas.restore(); } @@ -907,9 +911,9 @@ paint.setColor(red, CanvasKit.SkColorSpace.ADOBE_RGB); canvas.drawPaint(paint); paint.setColor(red, CanvasKit.SkColorSpace.DISPLAY_P3); - canvas.drawRoundRect(CanvasKit.LTRBRect(50, 50, 250, 250), 30, 30, paint); + canvas.drawRRect(CanvasKit.RRectXY([50, 50, 250, 250], 30, 30), paint); paint.setColor(red, CanvasKit.SkColorSpace.SRGB); - canvas.drawRoundRect(CanvasKit.LTRBRect(100, 100, 200, 200), 30, 30, paint); + canvas.drawRRect(CanvasKit.RRectXY([100, 100, 200, 200], 30, 30), paint); surface.flush(); surface.delete(); diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp index c229ce9863..6c9fd29792 100644 --- a/modules/canvaskit/canvaskit_bindings.cpp +++ b/modules/canvaskit/canvaskit_bindings.cpp @@ -223,15 +223,6 @@ sk_sp MakeRenderTarget(sk_sp grContext, SimpleImageInfo si // isn't assigned to a JS variable and has delete() called on it. // These Apply methods, combined with the smarter binding code allow for chainable // commands that don't leak if the return value is ignored (i.e. when used intuitively). - -void ApplyAddArc(SkPath& orig, const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) { - orig.addArc(oval, startAngle, sweepAngle); -} - -void ApplyAddOval(SkPath& orig, const SkRect& oval, bool ccw, unsigned start) { - orig.addOval(oval, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW, start); -} - void ApplyAddPath(SkPath& orig, const SkPath& newPath, SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, @@ -244,29 +235,11 @@ void ApplyAddPath(SkPath& orig, const SkPath& newPath, SkPath::kAppend_AddPathMode); } -void ApplyAddRect(SkPath& path, SkScalar left, SkScalar top, - SkScalar right, SkScalar bottom, bool ccw) { - path.addRect(left, top, right, bottom, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW); -} - -void ApplyAddRoundRect(SkPath& path, SkScalar left, SkScalar top, - SkScalar right, SkScalar bottom, uintptr_t /* SkScalar* */ rPtr, - bool ccw) { - // See comment below for uintptr_t explanation - const SkScalar* radii = reinterpret_cast(rPtr); - path.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom), radii, - ccw ? SkPathDirection::kCCW : SkPathDirection::kCW); -} - void ApplyArcToTangent(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) { p.arcTo(x1, y1, x2, y2, radius); } -void ApplyArcToOval(SkPath& p, SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) { - p.arcTo(oval, startAngle, sweepAngle, forceMoveTo); -} - void ApplyArcToArcSize(SkPath& orig, SkScalar rx, SkScalar ry, SkScalar xAxisRotate, bool useSmallArc, bool ccw, SkScalar x, SkScalar y) { auto arcSize = useSmallArc ? SkPath::ArcSize::kSmall_ArcSize : SkPath::ArcSize::kLarge_ArcSize; @@ -687,16 +660,6 @@ void drawShapedText(SkCanvas& canvas, ShapedText st, SkScalar x, } #endif //SK_NO_FONTS -int saveLayerRec(SkCanvas& canvas, const SkPaint* paint, - const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) { - return canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, paint, backdrop, flags)); -} - -int saveLayerRecBounds(SkCanvas& canvas, const SkPaint* paint, const SkImageFilter* backdrop, - SkCanvas::SaveLayerFlags flags, const SkRect& bounds) { - return canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds, paint, backdrop, flags)); -} - // This is simpler than dealing with an SkPoint and SkVector struct PosTan { SkScalar px, py, tx, ty; @@ -829,7 +792,6 @@ EMSCRIPTEN_BINDINGS(Skia) { function("_MakeImage", optional_override([](SimpleImageInfo ii, uintptr_t /* uint8_t* */ pPtr, int plen, size_t rowBytes)->sk_sp { - // See comment above for uintptr_t explanation uint8_t* pixels = reinterpret_cast(pPtr); SkImageInfo info = toSkImageInfo(ii); sk_sp pixelData = SkData::MakeFromMalloc(pixels, plen); @@ -847,7 +809,6 @@ EMSCRIPTEN_BINDINGS(Skia) { uintptr_t /* SkScalar* */ mPtr, sk_sp colorSpace)->sk_sp { SkPoint points[] = { start, end }; - // See comment above for uintptr_t explanation const SkScalar* positions = reinterpret_cast(pPtr); OptionalMatrix localMatrix(mPtr); @@ -867,7 +828,6 @@ EMSCRIPTEN_BINDINGS(Skia) { #ifdef SK_SERIALIZE_SKP function("_MakeSkPicture", optional_override([](uintptr_t /* unint8_t* */ dPtr, size_t bytes)->sk_sp { - // See comment above for uintptr_t explanation uint8_t* d = reinterpret_cast(dPtr); sk_sp data = SkData::MakeFromMalloc(d, bytes); @@ -880,7 +840,6 @@ EMSCRIPTEN_BINDINGS(Skia) { int count, SkTileMode mode, uint32_t flags, uintptr_t /* SkScalar* */ mPtr, sk_sp colorSpace)->sk_sp { - // See comment above for uintptr_t explanation const SkScalar* positions = reinterpret_cast(pPtr); OptionalMatrix localMatrix(mPtr); if (colorType == SkColorType::kRGBA_F32_SkColorType) { @@ -904,7 +863,6 @@ EMSCRIPTEN_BINDINGS(Skia) { uint32_t flags, uintptr_t /* SkScalar* */ mPtr, sk_sp colorSpace)->sk_sp { - // See comment above for uintptr_t explanation const SkScalar* positions = reinterpret_cast(pPtr); OptionalMatrix localMatrix(mPtr); if (colorType == SkColorType::kRGBA_F32_SkColorType) { @@ -930,7 +888,6 @@ EMSCRIPTEN_BINDINGS(Skia) { int count, SkTileMode mode, uint32_t flags, uintptr_t /* SkScalar* */ mPtr, sk_sp colorSpace)->sk_sp { - // See comment above for uintptr_t explanation const SkScalar* positions = reinterpret_cast(pPtr); OptionalMatrix localMatrix(mPtr); @@ -997,21 +954,27 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("_clipRRect", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, SkClipOp op, bool doAntiAlias) { self.clipRRect(ptrToSkRRect(fPtr), op, doAntiAlias); })) - .function("clipRect", select_overload(&SkCanvas::clipRect)) + .function("_clipRect", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, SkClipOp op, bool doAntiAlias) { + const SkRect* rect = reinterpret_cast(fPtr); + self.clipRect(*rect, op, doAntiAlias); + })) .function("_concat", optional_override([](SkCanvas& self, uintptr_t /* SkScalar* */ mPtr) { - // See comment above for uintptr_t explanation //TODO(skbug.com/10108): make the JS side be column major. const SkScalar* sixteenMatrixValues = reinterpret_cast(mPtr); SkM44 m = SkM44::RowMajor(sixteenMatrixValues); self.concat(m); })) - .function("drawArc", &SkCanvas::drawArc) + .function("_drawArc", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, + SkScalar startAngle, SkScalar sweepAngle, + bool useCenter, const SkPaint& paint) { + const SkRect* oval = reinterpret_cast(fPtr); + self.drawArc(*oval, startAngle, sweepAngle, useCenter, paint); + })) // _drawAtlas takes an array of SkColor. There is no SkColor4f override. .function("_drawAtlas", optional_override([](SkCanvas& self, const sk_sp& atlas, uintptr_t /* SkRSXform* */ xptr, uintptr_t /* SkRect* */ rptr, uintptr_t /* SkColor* */ cptr, int count, SkBlendMode mode, const SkPaint* paint)->void { - // See comment above for uintptr_t explanation const SkRSXform* dstXforms = reinterpret_cast(xptr); const SkRect* srcRects = reinterpret_cast(rptr); const SkColor* colors = nullptr; @@ -1033,7 +996,7 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("drawColorInt", optional_override([](SkCanvas& self, SkColor color, SkBlendMode mode) { self.drawColor(color, mode); })) - .function("_drawDRRect",optional_override([](SkCanvas& self, uintptr_t /* float* */ outerPtr, + .function("_drawDRRect", optional_override([](SkCanvas& self, uintptr_t /* float* */ outerPtr, uintptr_t /* float* */ innerPtr, const SkPaint& paint) { self.drawDRRect(ptrToSkRRect(outerPtr), ptrToSkRRect(innerPtr), paint); })) @@ -1042,43 +1005,54 @@ EMSCRIPTEN_BINDINGS(Skia) { self.drawDrawable(aImg.get(), x, y); }), allow_raw_pointers()) .function("drawImage", select_overload&, SkScalar, SkScalar, const SkPaint*)>(&SkCanvas::drawImage), allow_raw_pointers()) - .function("drawImageNine", optional_override([](SkCanvas& self, const sk_sp& image, - SkIRect center, SkRect dst, - const SkPaint* paint)->void { - self.drawImageNine(image, center, dst, paint); + .function("_drawImageNine", optional_override([](SkCanvas& self, const sk_sp& image, + uintptr_t /* int* */ centerPtr, uintptr_t /* float* */ dstPtr, + const SkPaint* paint)->void { + const SkIRect* center = reinterpret_cast(centerPtr); + const SkRect* dst = reinterpret_cast(dstPtr); + + self.drawImageNine(image, *center, *dst, paint); }), allow_raw_pointers()) - .function("drawImageRect", optional_override([](SkCanvas& self, const sk_sp& image, - SkRect src, SkRect dst, - const SkPaint* paint, bool fastSample)->void { - self.drawImageRect(image, src, dst, paint, - fastSample ? SkCanvas::kFast_SrcRectConstraint : + .function("_drawImageRect", optional_override([](SkCanvas& self, const sk_sp& image, + uintptr_t /* float* */ srcPtr, uintptr_t /* float* */ dstPtr, + const SkPaint* paint, bool fastSample)->void { + const SkRect* src = reinterpret_cast(srcPtr); + const SkRect* dst = reinterpret_cast(dstPtr); + self.drawImageRect(image, *src, *dst, paint, + fastSample ? SkCanvas::kFast_SrcRectConstraint: SkCanvas::kStrict_SrcRectConstraint); }), allow_raw_pointers()) .function("drawLine", select_overload(&SkCanvas::drawLine)) - .function("drawOval", &SkCanvas::drawOval) + .function("_drawOval", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, + const SkPaint paint)->void { + const SkRect* oval = reinterpret_cast(fPtr); + self.drawOval(*oval, paint); + })) .function("drawPaint", &SkCanvas::drawPaint) #ifdef SK_INCLUDE_PARAGRAPH .function("drawParagraph", optional_override([](SkCanvas& self, skia::textlayout::Paragraph* p, - SkScalar x, SkScalar y) { + SkScalar x, SkScalar y) { p->paint(&self, x, y); }), allow_raw_pointers()) #endif .function("drawPath", &SkCanvas::drawPath) // Of note, picture is *not* what is colloquially thought of as a "picture", what we call // a bitmap. An SkPicture is a series of draw commands. - .function("drawPicture", select_overload&)>(&SkCanvas::drawPicture)) + .function("drawPicture", select_overload&)>(&SkCanvas::drawPicture)) .function("_drawPoints", optional_override([](SkCanvas& self, SkCanvas::PointMode mode, uintptr_t /* SkPoint* */ pptr, int count, SkPaint paint)->void { - // See comment above for uintptr_t explanation const SkPoint* pts = reinterpret_cast(pptr); self.drawPoints(mode, count, pts, paint); })) .function("_drawRRect",optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, const SkPaint& paint) { self.drawRRect(ptrToSkRRect(fPtr), paint); })) - .function("drawRect", &SkCanvas::drawRect) - .function("drawRoundRect", &SkCanvas::drawRoundRect) + .function("_drawRect", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, + const SkPaint paint)->void { + const SkRect* rect = reinterpret_cast(fPtr); + self.drawRect(*rect, paint); + })) .function("_drawShadow", optional_override([](SkCanvas& self, const SkPath& path, const SkPoint3& zPlaneParams, const SkPoint3& lightPos, SkScalar lightRadius, @@ -1095,7 +1069,6 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("_drawSimpleText", optional_override([](SkCanvas& self, uintptr_t /* char* */ sptr, size_t len, SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) { - // See comment above for uintptr_t explanation const char* str = reinterpret_cast(sptr); self.drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint); @@ -1109,7 +1082,6 @@ EMSCRIPTEN_BINDINGS(Skia) { // an awkward moment where we malloc something here and "just know" to free it on the // JS side. .function("_getTotalMatrix", optional_override([](const SkCanvas& self, uintptr_t /* uint8_t* */ mPtr) { - // See comment above for uintptr_t explanation SkScalar* nineMatrixValues = reinterpret_cast(mPtr); if (!nineMatrixValues) { return; // matrix cannot be null @@ -1147,25 +1119,17 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("restoreToCount", &SkCanvas::restoreToCount) .function("rotate", select_overload(&SkCanvas::rotate)) .function("save", &SkCanvas::save) - // 1 param (only the paint) - .function("saveLayer", optional_override([](SkCanvas& self, const SkPaint* p) { - return self.saveLayer(nullptr, p); + .function("_saveLayer", optional_override([](SkCanvas& self, const SkPaint* p, uintptr_t /* float* */ fPtr, + const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags)->int { + SkRect* bounds = reinterpret_cast(fPtr); + return self.saveLayer(SkCanvas::SaveLayerRec(bounds, p, backdrop, flags)); }), allow_raw_pointers()) - // 2 params - .function("saveLayer", select_overload(&SkCanvas::saveLayer), - allow_raw_pointers()) - // 3 params (effectively with SaveLayerRec, but no bounds) - .function("saveLayer", saveLayerRec, allow_raw_pointers()) - // 4 params (effectively with SaveLayerRec) - .function("saveLayer", saveLayerRecBounds, allow_raw_pointers()) - .function("scale", &SkCanvas::scale) .function("skew", &SkCanvas::skew) .function("translate", &SkCanvas::translate) .function("_writePixels", optional_override([](SkCanvas& self, SimpleImageInfo di, uintptr_t /* uint8_t* */ pPtr, size_t srcRowBytes, int dstX, int dstY) { - // See comment above for uintptr_t explanation uint8_t* pixels = reinterpret_cast(pPtr); SkImageInfo dstInfo = toSkImageInfo(di); @@ -1278,14 +1242,17 @@ EMSCRIPTEN_BINDINGS(Skia) { class_("ShapedText") .constructor() - .function("getBounds", &ShapedText::getBounds); + .function("_getBounds", optional_override([](ShapedText& self, + uintptr_t /* float* */ fPtr)->void { + SkRect* output = reinterpret_cast(fPtr); + output[0] = self.getBounds(); + })); class_("SkFontMgr") .smart_ptr>("sk_sp") .class_function("_fromData", optional_override([](uintptr_t /* uint8_t** */ dPtr, uintptr_t /* size_t* */ sPtr, int numFonts)->sk_sp { - // See comment above for uintptr_t explanation auto datas = reinterpret_cast(dPtr); auto sizes = reinterpret_cast(sPtr); @@ -1315,7 +1282,6 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("_makeTypefaceFromData", optional_override([](SkFontMgr& self, uintptr_t /* uint8_t* */ fPtr, int flen)->sk_sp { - // See comment above for uintptr_t explanation uint8_t* font = reinterpret_cast(fPtr); sk_sp fontData = SkData::MakeFromMalloc(font, flen); @@ -1338,10 +1304,9 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("_readPixels", optional_override([](sk_sp self, SimpleImageInfo sii, uintptr_t /* uint8_t* */ pPtr, size_t dstRowBytes, int srcX, int srcY)->bool { - // See comment above for uintptr_t explanation uint8_t* pixels = reinterpret_cast(pPtr); SkImageInfo ii = toSkImageInfo(sii); - // TODO: Migrate CanvasKit API to require DirectContext arg here. + // TODO(adlai) Migrate CanvasKit API to require DirectContext arg here. GrDirectContext* dContext = nullptr; #ifdef SK_GL dContext = GrAsDirectContext(as_IB(self.get())->context()); @@ -1353,12 +1318,10 @@ EMSCRIPTEN_BINDINGS(Skia) { .smart_ptr>("sk_sp") .class_function("MakeBlur", optional_override([](SkScalar sigmaX, SkScalar sigmaY, SkTileMode tileMode, sk_sp input)->sk_sp { - // Emscripten does not like default args nor SkIRect* much return SkImageFilters::Blur(sigmaX, sigmaY, tileMode, input); })) .class_function("MakeColorFilter", optional_override([](sk_sp cf, - sk_sp input)->sk_sp { - // Emscripten does not like default args nor SkIRect* much + sk_sp input)->sk_sp { return SkImageFilters::ColorFilter(cf, input); })) .class_function("MakeCompose", &SkImageFilters::Compose) @@ -1439,7 +1402,6 @@ EMSCRIPTEN_BINDINGS(Skia) { .class_function("MakeCorner", &SkCornerPathEffect::Make) .class_function("_MakeDash", optional_override([](uintptr_t /* float* */ cptr, int count, SkScalar phase)->sk_sp { - // See comment above for uintptr_t explanation const float* intervals = reinterpret_cast(cptr); return SkDashPathEffect::Make(intervals, count, phase); }), allow_raw_pointers()) @@ -1450,23 +1412,44 @@ EMSCRIPTEN_BINDINGS(Skia) { .constructor() .class_function("_MakeFromCmds", &MakePathFromCmds) .class_function("_MakeFromVerbsPointsWeights", &MakePathFromVerbsPointsWeights) - .function("_addArc", &ApplyAddArc) + .function("_addArc", optional_override([](SkPath& self, + uintptr_t /* float* */ fPtr, + SkScalar startAngle, SkScalar sweepAngle)->void { + const SkRect* oval = reinterpret_cast(fPtr); + self.addArc(*oval, startAngle, sweepAngle); + })) + .function("_addOval", optional_override([](SkPath& self, + uintptr_t /* float* */ fPtr, + bool ccw, unsigned start)->void { + const SkRect* oval = reinterpret_cast(fPtr); + self.addOval(*oval, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW, start); + })) // interface.js has 3 overloads of addPath - .function("_addOval", &ApplyAddOval) .function("_addPath", &ApplyAddPath) .function("_addPoly", optional_override([](SkPath& self, - uintptr_t /* SkPoint* */ pptr, + uintptr_t /* SkPoint* */ fPtr, int count, bool close)->void { - // See comment above for uintptr_t explanation - const SkPoint* pts = reinterpret_cast(pptr); + const SkPoint* pts = reinterpret_cast(fPtr); self.addPoly(pts, count, close); })) - // interface.js has 4 overloads of addRect - .function("_addRect", &ApplyAddRect) - // interface.js has 4 overloads of addRoundRect - .function("_addRoundRect", &ApplyAddRoundRect) + .function("_addRect", optional_override([](SkPath& self, + uintptr_t /* float* */ fPtr, + bool ccw)->void { + const SkRect* rect = reinterpret_cast(fPtr); + self.addRect(*rect, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW); + })) + .function("_addRRect", optional_override([](SkPath& self, + uintptr_t /* float* */ fPtr, + bool ccw)->void { + self.addRRect(ptrToSkRRect(fPtr), ccw ? SkPathDirection::kCCW : SkPathDirection::kCW); + })) .function("_addVerbsPointsWeights", &PathAddVerbsPointsWeights) - .function("_arcToOval", &ApplyArcToOval) + .function("_arcToOval", optional_override([](SkPath& self, + uintptr_t /* float* */ fPtr, SkScalar startAngle, + SkScalar sweepAngle, bool forceMoveTo)->void { + const SkRect* oval = reinterpret_cast(fPtr); + self.arcTo(*oval, startAngle, sweepAngle, forceMoveTo); + })) .function("_arcToRotated", &ApplyArcToArcSize) .function("_arcToTangent", ApplyArcToTangent) .function("_close", &ApplyClose) @@ -1507,7 +1490,11 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("setFillType", select_overload(&SkPath::setFillType)) .function("getFillType", &SkPath::getFillType) - .function("getBounds", &SkPath::getBounds) + .function("_getBounds", optional_override([](SkPath& self, + uintptr_t /* float* */ fPtr)->void { + SkRect* output = reinterpret_cast(fPtr); + output[0] = self.getBounds(); + })) .function("computeTightBounds", &SkPath::computeTightBounds) .function("equals", &Equals) .function("copy", &CopyPath) @@ -1543,9 +1530,10 @@ EMSCRIPTEN_BINDINGS(Skia) { class_("SkPictureRecorder") .constructor<>() - .function("beginRecording", optional_override([](SkPictureRecorder& self, - const SkRect& bounds) -> SkCanvas* { - return self.beginRecording(bounds); + .function("_beginRecording", optional_override([](SkPictureRecorder& self, + uintptr_t /* float* */ fPtr) -> SkCanvas* { + SkRect* bounds = reinterpret_cast(fPtr); + return self.beginRecording(*bounds, nullptr); }), allow_raw_pointers()) .function("finishRecordingAsPicture", optional_override([](SkPictureRecorder& self) -> sk_sp { @@ -1590,7 +1578,6 @@ EMSCRIPTEN_BINDINGS(Skia) { })) .function("_makeShader", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque, uintptr_t /* SkScalar* */ mPtr)->sk_sp { - // See comment above for uintptr_t explanation void* inputData = reinterpret_cast(fPtr); sk_sp inputs = SkData::MakeFromMalloc(inputData, fLen); @@ -1600,7 +1587,6 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("_makeShaderWithChildren", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque, uintptr_t /** SkShader*[] */cPtrs, size_t cLen, uintptr_t /* SkScalar* */ mPtr)->sk_sp { - // See comment above for uintptr_t explanation void* inputData = reinterpret_cast(fPtr); sk_sp inputs = SkData::MakeFromMalloc(inputData, fLen); @@ -1627,8 +1613,13 @@ EMSCRIPTEN_BINDINGS(Skia) { return {ii.width(), ii.height(), ii.colorType(), ii.alphaType(), ii.refColorSpace()}; })) .function("height", &SkSurface::height) - .function("makeImageSnapshot", select_overload()>(&SkSurface::makeImageSnapshot)) - .function("makeImageSnapshot", select_overload(const SkIRect& bounds)>(&SkSurface::makeImageSnapshot)) + .function("_makeImageSnapshot", optional_override([](SkSurface& self, uintptr_t /* int* */ iPtr)->sk_sp { + SkIRect* bounds = reinterpret_cast(iPtr); + if (!bounds) { + return self.makeImageSnapshot(); + } + return self.makeImageSnapshot(*bounds); + })) .function("makeSurface", optional_override([](SkSurface& self, SimpleImageInfo sii)->sk_sp { return self.makeSurface(toSkImageInfo(sii)); }), allow_raw_pointers()) @@ -1654,7 +1645,6 @@ EMSCRIPTEN_BINDINGS(Skia) { size_t strBtyes, uintptr_t /* SkRSXform* */ xptr, const SkFont& font)->sk_sp { - // See comment above for uintptr_t explanation const char* str = reinterpret_cast(sptr); const SkRSXform* xforms = reinterpret_cast(xptr); @@ -1662,7 +1652,6 @@ EMSCRIPTEN_BINDINGS(Skia) { }), allow_raw_pointers()) .class_function("_MakeFromText", optional_override([](uintptr_t /* char* */ sptr, size_t len, const SkFont& font)->sk_sp { - // See comment above for uintptr_t explanation const char* str = reinterpret_cast(sptr); return SkTextBlob::MakeFromText(str, len, font, SkTextEncoding::kUTF8); }), allow_raw_pointers()); @@ -1673,7 +1662,11 @@ EMSCRIPTEN_BINDINGS(Skia) { class_("SkVertices") .smart_ptr>("sk_sp") - .function("bounds", &SkVertices::bounds) + .function("_bounds", optional_override([](SkVertices& self, + uintptr_t /* float* */ fPtr)->void { + SkRect* output = reinterpret_cast(fPtr); + output[0] = self.bounds(); + })) .function("uniqueID", &SkVertices::uniqueID); // Not intended to be called directly by clients @@ -1844,17 +1837,6 @@ EMSCRIPTEN_BINDINGS(Skia) { .field("text", &ShapedTextOpts::text) .field("width", &ShapedTextOpts::width); #endif - value_object("SkRect") - .field("fLeft", &SkRect::fLeft) - .field("fTop", &SkRect::fTop) - .field("fRight", &SkRect::fRight) - .field("fBottom", &SkRect::fBottom); - - value_object("SkIRect") - .field("fLeft", &SkIRect::fLeft) - .field("fTop", &SkIRect::fTop) - .field("fRight", &SkIRect::fRight) - .field("fBottom", &SkIRect::fBottom); value_object("SkImageInfo") .field("width", &SimpleImageInfo::width) diff --git a/modules/canvaskit/cpu.js b/modules/canvaskit/cpu.js index 229a6c7355..52144e00fc 100644 --- a/modules/canvaskit/cpu.js +++ b/modules/canvaskit/cpu.js @@ -77,9 +77,9 @@ this._canvas.getContext('2d').putImageData(imageData, 0, 0); } else { this._canvas.getContext('2d').putImageData(imageData, 0, 0, - dirtyRect.fLeft, dirtyRect.fTop, - dirtyRect.fRight - dirtyRect.fLeft, - dirtyRect.fBottom - dirtyRect.fTop); + dirtyRect[0], dirtyRect[1], + dirtyRect[2] - dirtyRect[0], + dirtyRect[3] - dirtyRect[1]); } } }; diff --git a/modules/canvaskit/externs.js b/modules/canvaskit/externs.js index e70d8c7224..9cd13bba8c 100644 --- a/modules/canvaskit/externs.js +++ b/modules/canvaskit/externs.js @@ -27,11 +27,10 @@ var CanvasKit = { Color: function() {}, Color4f: function() {}, ColorAsInt: function() {}, - /** @return {CanvasKit.SkRect} */ LTRBRect: function() {}, - /** @return {CanvasKit.SkRect} */ XYWHRect: function() {}, - /** @return {CanvasKit.SkRRect} */ + LTRBiRect: function() {}, + XYWHiRect: function() {}, RRectXY: function() {}, /** @return {ImageData} */ ImageData: function() {}, @@ -101,6 +100,13 @@ var CanvasKit = { // Objects and properties on CanvasKit + Animation: { + prototype: { + render: function() {}, + }, + _render: function() {}, + }, + GrContext: { // public API (from C++ bindings) getResourceCacheLimitBytes: function() {}, @@ -111,8 +117,14 @@ var CanvasKit = { ManagedAnimation: { prototype: { + render: function() {}, + seek: function() {}, + seekFrame: function() {}, setColor: function() {}, }, + _render: function() {}, + _seek: function() {}, + _seekFrame: function() {}, }, Paragraph: { @@ -168,8 +180,11 @@ var CanvasKit = { SkRectBuilder: function() {}, ShapedText: { - // public API (from C++ bindings) - getBounds: function() {}, + prototype: { + getBounds: function() {}, + }, + // private API (from C++ bindings) + _getBounds: function() {}, }, SkAnimatedImage: { @@ -185,22 +200,15 @@ var CanvasKit = { SkCanvas: { // public API (from C++ bindings) clipPath: function() {}, - clipRect: function() {}, drawAnimatedImage: function() {}, - drawArc: function() {}, drawCircle: function() {}, drawColorInt: function() {}, drawImage: function() {}, - drawImageNine: function() {}, - drawImageRect: function() {}, drawLine: function() {}, - drawOval: function() {}, drawPaint: function() {}, drawParagraph: function() {}, drawPath: function() {}, drawPicture: function() {}, - drawRect: function() {}, - drawRoundRect: function() {}, drawText: function() {}, drawTextBlob: function() {}, drawVertices: function() {}, @@ -213,7 +221,6 @@ var CanvasKit = { restoreToCount: function() {}, rotate: function() {}, save: function() {}, - saveLayer: function() {}, scale: function() {}, skew: function() {}, translate: function() {}, @@ -221,13 +228,19 @@ var CanvasKit = { prototype: { clear: function() {}, clipRRect: function() {}, + clipRect: function() {}, concat44: function() {}, // deprecated concat: function() {}, + drawArc: function() {}, drawAtlas: function() {}, drawColor: function() {}, drawColorComponents: function() {}, drawDRRect: function() {}, + drawImageNine: function() {}, + drawImageRect: function() {}, + drawOval: function() {}, drawPoints: function() {}, + drawRect: function() {}, drawRRect: function() {}, drawShadow: function() {}, drawText: function() {}, @@ -235,17 +248,24 @@ var CanvasKit = { getLocalToDevice: function() {}, getTotalMatrix: function() {}, readPixels: function() {}, + saveLayer: function() {}, writePixels : function() {}, }, // private API _clear: function() {}, _clipRRect: function() {}, + _clipRect: function() {}, _concat: function() {}, + _drawArc: function() {}, _drawAtlas: function() {}, _drawColor: function() {}, _drawDRRect: function() {}, + _drawImageNine: function() {}, + _drawImageRect: function() {}, + _drawOval: function() {}, _drawPoints: function() {}, + _drawRect: function() {}, _drawRRect: function() {}, _drawShadow: function() {}, _drawSimpleText: function() {}, @@ -253,6 +273,7 @@ var CanvasKit = { _getLocalToDevice: function() {}, _getTotalMatrix: function() {}, _readPixels: function() {}, + _saveLayer: function() {}, _writePixels: function() {}, delete: function() {}, }, @@ -480,21 +501,53 @@ var CanvasKit = { toCmds: function() {}, toSVGString: function() {}, + prototype: { + addArc: function() {}, + addOval: function() {}, + addPath: function() {}, + addPoly: function() {}, + addRect: function() {}, + addRRect: function() {}, + addVerbsPointsWeights: function() {}, + arc: function() {}, + arcToOval: function() {}, + arcToRotated: function() {}, + arcToTangent: function() {}, + close: function() {}, + conicTo: function() {}, + cubicTo: function() {}, + dash: function() {}, + lineTo: function() {}, + moveTo: function() {}, + offset: function() {}, + op: function() {}, + quadTo: function() {}, + rArcTo: function() {}, + rConicTo: function() {}, + rCubicTo: function() {}, + rect: function() {}, + rLineTo: function() {}, + rMoveTo: function() {}, + rQuadTo: function() {}, + simplify: function() {}, + stroke: function() {}, + transform: function() {}, + trim: function() {}, + }, + // private API _MakeFromCmds: function() {}, _MakeFromVerbsPointsWeights: function() {}, _addArc: function() {}, _addOval: function() {}, _addPath: function() {}, - _addRect: function() {}, _addPoly: function() {}, - _addRoundRect: function() {}, + _addRect: function() {}, + _addRRect: function() {}, _addVerbsPointsWeights: function() {}, - _arc: function() {}, - _arcTo: function() {}, _arcToOval: function() {}, - _arcToTangent: function() {}, _arcToRotated: function() {}, + _arcToTangent: function() {}, _close: function() {}, _conicTo: function() {}, _cubicTo: function() {}, @@ -506,10 +559,10 @@ var CanvasKit = { _rArcTo: function() {}, _rConicTo: function() {}, _rCubicTo: function() {}, + _rect: function() {}, _rLineTo: function() {}, _rMoveTo: function() {}, _rQuadTo: function() {}, - _rect: function() {}, _simplify: function() {}, _stroke: function() {}, _transform: function() {}, @@ -532,27 +585,11 @@ var CanvasKit = { }, SkPictureRecorder: { - beginRecording: function() {}, finishRecordingAsPicture: function() {}, - }, - - SkRect: { - fLeft: {}, - fTop: {}, - fRight: {}, - fBottom: {}, - }, - - SkRRect: { - rect: {}, - rx1: {}, - ry1: {}, - rx2: {}, - ry2: {}, - rx3: {}, - ry3: {}, - rx4: {}, - ry4: {}, + prototype: { + beginRecording: function() {}, + }, + _beginRecording: function() {}, }, SkShader: { @@ -574,17 +611,22 @@ var CanvasKit = { /** @return {CanvasKit.SkCanvas} */ getCanvas: function() {}, imageInfo: function() {}, - /** @return {CanvasKit.SkImage} */ - makeImageSnapshot: function() {}, + makeSurface: function() {}, sampleCnt: function() {}, reportBackendTypeIsGPU: function() {}, grContext: {}, openGLversion: {}, + prototype: { + /** @return {CanvasKit.SkImage} */ + makeImageSnapshot: function() {}, + }, + // private API _flush: function() {}, _getRasterN32PremulSurface: function() {}, + _makeImageSnapshot: function() {}, delete: function() {}, }, @@ -613,10 +655,14 @@ var CanvasKit = { SkVertices: { // public API (from C++ bindings) - bounds: function() {}, - mode: function() {}, uniqueID: function() {}, - vertexCount: function() {}, + + prototype: { + bounds: function() {}, + }, + // private API (from C++ bindings) + + _bounds: function() {}, }, _SkVerticesBuilder: { @@ -899,39 +945,6 @@ var CanvasKit = { // unless they go on the prototype. CanvasKit.Paragraph.prototype.getRectsForRange = function() {}; -CanvasKit.SkPath.prototype.addArc = function() {}; -CanvasKit.SkPath.prototype.addOval = function() {}; -CanvasKit.SkPath.prototype.addPath = function() {}; -CanvasKit.SkPath.prototype.addPoly = function() {}; -CanvasKit.SkPath.prototype.addRect = function() {}; -CanvasKit.SkPath.prototype.addRoundRect = function() {}; -CanvasKit.SkPath.prototype.addVerbsPointsWeights = function() {}; -CanvasKit.SkPath.prototype.arc = function() {}; -CanvasKit.SkPath.prototype.arcTo = function() {}; -CanvasKit.SkPath.prototype.arcToOval = function() {}; -CanvasKit.SkPath.prototype.arcToTangent = function() {}; -CanvasKit.SkPath.prototype.arcToRotated = function() {}; -CanvasKit.SkPath.prototype.close = function() {}; -CanvasKit.SkPath.prototype.conicTo = function() {}; -CanvasKit.SkPath.prototype.cubicTo = function() {}; -CanvasKit.SkPath.prototype.dash = function() {}; -CanvasKit.SkPath.prototype.lineTo = function() {}; -CanvasKit.SkPath.prototype.moveTo = function() {}; -CanvasKit.SkPath.prototype.offset = function() {}; -CanvasKit.SkPath.prototype.op = function() {}; -CanvasKit.SkPath.prototype.quadTo = function() {}; -CanvasKit.SkPath.prototype.rArcTo = function() {}; -CanvasKit.SkPath.prototype.rConicTo = function() {}; -CanvasKit.SkPath.prototype.rCubicTo = function() {}; -CanvasKit.SkPath.prototype.rLineTo = function() {}; -CanvasKit.SkPath.prototype.rMoveTo = function() {}; -CanvasKit.SkPath.prototype.rQuadTo = function() {}; -CanvasKit.SkPath.prototype.rect = function() {}; -CanvasKit.SkPath.prototype.simplify = function() {}; -CanvasKit.SkPath.prototype.stroke = function() {}; -CanvasKit.SkPath.prototype.transform = function() {}; -CanvasKit.SkPath.prototype.trim = function() {}; - CanvasKit.SkPicture.prototype.saveAsFile = function() {}; CanvasKit.SkSurface.prototype.dispose = function() {}; diff --git a/modules/canvaskit/font.js b/modules/canvaskit/font.js index 845a8c2f03..eb591275ff 100644 --- a/modules/canvaskit/font.js +++ b/modules/canvaskit/font.js @@ -94,6 +94,19 @@ CanvasKit._extraInitializations.push(function() { return font; } + // Clients can pass in a Float32Array with length 4 to this and the results + // will be copied into that array. Otherwise, a new TypedArray will be allocated + // and returned. + CanvasKit.ShapedText.prototype.getBounds = function(optionalOutputArray) { + this._getBounds(_scratchRectPtr); + var ta = _scratchRect['toTypedArray'](); + if (optionalOutputArray) { + optionalOutputArray.set(ta); + return optionalOutputArray; + } + return ta.slice(); + } + CanvasKit.SkTextBlob.MakeOnPath = function(str, path, font, initialOffset) { if (!str || !str.length) { SkDebug('ignoring 0 length string'); diff --git a/modules/canvaskit/helper.js b/modules/canvaskit/helper.js index 54c777aad3..d65f4319c2 100644 --- a/modules/canvaskit/helper.js +++ b/modules/canvaskit/helper.js @@ -43,31 +43,31 @@ CanvasKit.Color4f = function(r, g, b, a) { // Color constants use property getters to prevent other code from accidentally // changing them. -Object.defineProperty(CanvasKit, "TRANSPARENT", { +Object.defineProperty(CanvasKit, 'TRANSPARENT', { get: function() { return CanvasKit.Color4f(0, 0, 0, 0); } }); -Object.defineProperty(CanvasKit, "BLACK", { +Object.defineProperty(CanvasKit, 'BLACK', { get: function() { return CanvasKit.Color4f(0, 0, 0, 1); } }); -Object.defineProperty(CanvasKit, "WHITE", { +Object.defineProperty(CanvasKit, 'WHITE', { get: function() { return CanvasKit.Color4f(1, 1, 1, 1); } }); -Object.defineProperty(CanvasKit, "RED", { +Object.defineProperty(CanvasKit, 'RED', { get: function() { return CanvasKit.Color4f(1, 0, 0, 1); } }); -Object.defineProperty(CanvasKit, "GREEN", { +Object.defineProperty(CanvasKit, 'GREEN', { get: function() { return CanvasKit.Color4f(0, 1, 0, 1); } }); -Object.defineProperty(CanvasKit, "BLUE", { +Object.defineProperty(CanvasKit, 'BLUE', { get: function() { return CanvasKit.Color4f(0, 0, 1, 1); } }); -Object.defineProperty(CanvasKit, "YELLOW", { +Object.defineProperty(CanvasKit, 'YELLOW', { get: function() { return CanvasKit.Color4f(1, 1, 0, 1); } }); -Object.defineProperty(CanvasKit, "CYAN", { +Object.defineProperty(CanvasKit, 'CYAN', { get: function() { return CanvasKit.Color4f(0, 1, 1, 1); } }); -Object.defineProperty(CanvasKit, "MAGENTA", { +Object.defineProperty(CanvasKit, 'MAGENTA', { get: function() { return CanvasKit.Color4f(1, 0, 1, 1); } }); @@ -207,9 +207,9 @@ function degreesToRadians(deg) { // See https://stackoverflow.com/a/31090240 // This contraption keeps closure from minifying away the check -// if btoa is defined *and* prevents runtime "btoa" or "window" is not defined. +// if btoa is defined *and* prevents runtime 'btoa' or 'window' is not defined. // Defined outside any scopes to make it available in all files. -var isNode = !(new Function("try {return this===window;}catch(e){ return false;}")()); +var isNode = !(new Function('try {return this===window;}catch(e){ return false;}')()); function almostEqual(floata, floatb) { return Math.abs(floata - floatb) < 0.00001; @@ -218,7 +218,7 @@ function almostEqual(floata, floatb) { var nullptr = 0; // emscripten doesn't like to take null as uintptr_t // arr can be a normal JS array or a TypedArray -// dest is a string like "HEAPU32" that specifies the type the src array +// dest is a string like 'HEAPU32' that specifies the type the src array // should be copied into. // ptr can be optionally provided if the memory was already allocated. function copy1dArray(arr, dest, ptr) { @@ -290,15 +290,15 @@ function copyFlexibleColorArray(colors) { colorType: CanvasKit.ColorType.RGBA_F32, } if (colors instanceof Float32Array) { - result.colorPtr = copy1dArray(colors, "HEAPF32"); + result.colorPtr = copy1dArray(colors, 'HEAPF32'); result.count = colors.length / 4; } else if (colors instanceof Uint32Array) { - result.colorPtr = copy1dArray(colors, "HEAPU32"); + result.colorPtr = copy1dArray(colors, 'HEAPU32'); result.colorType = CanvasKit.ColorType.RGBA_8888; } else if (colors instanceof Array && colors[0] instanceof Float32Array) { - result.colorPtr = copy2dArray(colors, "HEAPF32"); + result.colorPtr = copy2dArray(colors, 'HEAPF32'); } else { throw('Invalid argument to copyFlexibleColorArray, Not a color array '+typeof(colors)); } @@ -326,9 +326,8 @@ function copy3x3MatrixToWasm(matr) { if (matr.length !== 6 && matr.length !== 9) { throw 'invalid matrix size'; } - // This should be an array or typed array. - // have to divide the pointer by 4 to "cast" it from bytes to float. - var mPtr = copy1dArray(matr, "HEAPF32", _scratch3x3MatrixPtr); + // matr should be an array or typed array. + var mPtr = copy1dArray(matr, 'HEAPF32', _scratch3x3MatrixPtr); if (matr.length === 6) { // Overwrite the last 3 floats with the default perspective. The divide // by 4 casts the pointer into a float pointer. @@ -365,9 +364,8 @@ function copy4x4MatrixToWasm(matr) { throw 'invalid matrix size'; } if (matr.length === 16) { - // This should be an array or typed array. - // have to divide the pointer by 4 to "cast" it from bytes to float. - return copy1dArray(matr, "HEAPF32", _scratch4x4MatrixPtr); + // matr should be an array or typed array. + return copy1dArray(matr, 'HEAPF32', _scratch4x4MatrixPtr); } // Upscale the row-major 3x3 or 3x2 matrix into a 4x4 row-major matrix // TODO(skbug.com/10108) This will need to change when we convert our @@ -430,7 +428,7 @@ function copy4x4MatrixFromWasm(matrPtr) { // typedArrays, then we should return a typed array here too. var rv = new Array(16); for (var i = 0; i < 16; i++) { - rv[i] = CanvasKit.HEAPF32[matrPtr/4 + i]; // divide by 4 to "cast" to float. + rv[i] = CanvasKit.HEAPF32[matrPtr/4 + i]; // divide by 4 to cast to float. } return rv; } @@ -439,7 +437,7 @@ var _scratchColorPtr = nullptr; var _scratchColor; // the result from CanvasKit.Malloc function copyColorToWasm(color4f, ptr) { - return copy1dArray(color4f, "HEAPF32", ptr || _scratchColorPtr); + return copy1dArray(color4f, 'HEAPF32', ptr || _scratchColorPtr); } function copyColorComponentsToWasm(r, g, b, a) { @@ -453,18 +451,35 @@ function copyColorComponentsToWasm(r, g, b, a) { function copyColorToWasmNoScratch(color4f) { // TODO(kjlubick): accept 4 floats or int color - return copy1dArray(color4f, "HEAPF32"); + return copy1dArray(color4f, 'HEAPF32'); } // copies the four floats at the given pointer in a js Float32Array function copyColorFromWasm(colorPtr) { var rv = new Float32Array(4); for (var i = 0; i < 4; i++) { - rv[i] = CanvasKit.HEAPF32[colorPtr/4 + i]; // divide by 4 to "cast" to float. + rv[i] = CanvasKit.HEAPF32[colorPtr/4 + i]; // divide by 4 to cast to float. } return rv; } +// These will be initialized after loading. +var _scratchRect; +var _scratchRectPtr = nullptr; + +var _scratchRect2; +var _scratchRect2Ptr = nullptr; + +function copyRectToWasm(fourFloats, ptr) { + return copy1dArray(fourFloats, 'HEAPF32', ptr || _scratchRectPtr); +} + +var _scratchIRect; +var _scratchIRectPtr = nullptr; +function copyIRectToWasm(fourInts, ptr) { + return copy1dArray(fourInts, 'HEAP32', ptr || _scratchIRectPtr); +} + // These will be initialized after loading. var _scratchRRect; var _scratchRRectPtr = nullptr; @@ -474,7 +489,7 @@ var _scratchRRect2Ptr = nullptr; function copyRRectToWasm(twelveFloats, ptr) { - return copy1dArray(twelveFloats, "HEAPF32", ptr || _scratchRRectPtr); + return copy1dArray(twelveFloats, 'HEAPF32', ptr || _scratchRRectPtr); } // Caching the Float32Arrays can save having to reallocate them @@ -517,7 +532,7 @@ function loadCmdsTypedArray(arr) { } } - var ptr = copy1dArray(ta, "HEAPF32"); + var ptr = copy1dArray(ta, 'HEAPF32'); return [ptr, len]; } @@ -607,7 +622,7 @@ CanvasKit.FourFloatArrayHelper.prototype.build = function() { if (this._ptr) { return this._ptr; } - this._ptr = copy1dArray(this._floats, "HEAPF32"); + this._ptr = copy1dArray(this._floats, 'HEAPF32'); return this._ptr; } @@ -680,7 +695,7 @@ CanvasKit.OneUIntArrayHelper.prototype.build = function() { if (this._ptr) { return this._ptr; } - this._ptr = copy1dArray(this._uints, "HEAPU32"); + this._ptr = copy1dArray(this._uints, 'HEAPU32'); return this._ptr; } diff --git a/modules/canvaskit/htmlcanvas/path2d.js b/modules/canvaskit/htmlcanvas/path2d.js index d44dee67fe..12db9e6623 100644 --- a/modules/canvaskit/htmlcanvas/path2d.js +++ b/modules/canvaskit/htmlcanvas/path2d.js @@ -35,7 +35,7 @@ function closePath(skpath) { } // Check to see if we are not just a single point var bounds = skpath.getBounds(); - if ((bounds.fBottom - bounds.fTop) || (bounds.fRight - bounds.fLeft)) { + if ((bounds[3] - bounds[1]) || (bounds[2] - bounds[0])) { skpath.close(); } } @@ -134,11 +134,12 @@ function quadraticCurveTo(skpath, cpx, cpy, x, y) { } function rect(skpath, x, y, width, height) { - if (!allAreFinite([x, y, width, height])) { + var rect = CanvasKit.XYWHRect(x, y, width, height); + if (!allAreFinite(rect)) { return; } // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect - skpath.addRect(x, y, x+width, y+height); + skpath.addRect(rect); } function Path2D(path) { diff --git a/modules/canvaskit/interface.js b/modules/canvaskit/interface.js index 3b44742ec8..1b835aa27a 100644 --- a/modules/canvaskit/interface.js +++ b/modules/canvaskit/interface.js @@ -23,6 +23,15 @@ CanvasKit.onRuntimeInitialized = function() { _scratchRRect2 = CanvasKit.Malloc(Float32Array, 12); // 4 scalars for rrect, 8 for radii. _scratchRRect2Ptr = _scratchRRect2['byteOffset']; + _scratchRect = CanvasKit.Malloc(Float32Array, 4); + _scratchRectPtr = _scratchRect['byteOffset']; + + _scratchRect2 = CanvasKit.Malloc(Float32Array, 4); + _scratchRect2Ptr = _scratchRect2['byteOffset']; + + _scratchIRect = CanvasKit.Malloc(Int32Array, 4); + _scratchIRectPtr = _scratchIRect['byteOffset']; + // Create single copies of all three supported color spaces // These are sk_sp CanvasKit.SkColorSpace.SRGB = CanvasKit.SkColorSpace._MakeSRGB(); @@ -466,8 +475,8 @@ CanvasKit.onRuntimeInitialized = function() { CanvasKit.SkM44.setupCamera = function(area, zscale, cam) { var camera = CanvasKit.SkM44.lookat(cam['eye'], cam['coa'], cam['up']); var perspective = CanvasKit.SkM44.perspective(cam['near'], cam['far'], cam['angle']); - var center = [(area.fLeft + area.fRight)/2, (area.fTop + area.fBottom)/2, 0]; - var viewScale = [(area.fRight - area.fLeft)/2, (area.fBottom - area.fTop)/2, zscale]; + var center = [(area[0] + area[2])/2, (area[1] + area[3])/2, 0]; + var viewScale = [(area[2] - area[0])/2, (area[3] - area[1])/2, zscale]; var viewport = CanvasKit.SkM44.multiply( CanvasKit.SkM44.translated(center), CanvasKit.SkM44.scaled(viewScale)); @@ -590,7 +599,8 @@ CanvasKit.onRuntimeInitialized = function() { CanvasKit.SkPath.prototype.addArc = function(oval, startAngle, sweepAngle) { // see arc() for the HTMLCanvas version // note input angles are degrees. - this._addArc(oval, startAngle, sweepAngle); + var oPtr = copyRectToWasm(oval); + this._addArc(oPtr, startAngle, sweepAngle); return this; }; @@ -598,7 +608,8 @@ CanvasKit.onRuntimeInitialized = function() { if (startIndex === undefined) { startIndex = 1; } - this._addOval(oval, !!isCCW, startIndex); + var oPtr = copyRectToWasm(oval); + this._addOval(oPtr, !!isCCW, startIndex); return this; }; @@ -664,58 +675,15 @@ CanvasKit.onRuntimeInitialized = function() { return this; }; - CanvasKit.SkPath.prototype.addRect = function() { - // Takes 1, 2, 4 or 5 args - // - SkRect - // - SkRect, isCCW - // - left, top, right, bottom - // - left, top, right, bottom, isCCW - if (arguments.length === 1 || arguments.length === 2) { - var r = arguments[0]; - var ccw = arguments[1] || false; - this._addRect(r.fLeft, r.fTop, r.fRight, r.fBottom, ccw); - } else if (arguments.length === 4 || arguments.length === 5) { - var a = arguments; - this._addRect(a[0], a[1], a[2], a[3], a[4] || false); - } else { - SkDebug('addRect expected to take 1, 2, 4, or 5 args. Got ' + arguments.length); - return null; - } + CanvasKit.SkPath.prototype.addRect = function(rect, isCCW) { + var rPtr = copyRectToWasm(rect); + this._addRect(rPtr, !!isCCW); return this; }; - CanvasKit.SkPath.prototype.addRoundRect = function() { - // Takes 3, 4, 6 or 7 args - // - SkRect, radii (an array of 8 numbers), ccw - // - SkRect, rx, ry, ccw - // - left, top, right, bottom, radii, ccw - // - left, top, right, bottom, rx, ry, ccw - var args = arguments; - if (args.length === 3 || args.length === 6) { - var radii = args[args.length-2]; - } else if (args.length === 4 || args.length === 7){ - // duplicate the given (rx, ry) pairs for each corner. - var rx = args[args.length-3]; - var ry = args[args.length-2]; - var radii = [rx, ry, rx, ry, rx, ry, rx, ry]; - } else { - SkDebug('addRoundRect expected to take 3, 4, 6, or 7 args. Got ' + args.length); - return null; - } - if (radii.length !== 8) { - SkDebug('addRoundRect needs 8 radii provided. Got ' + radii.length); - return null; - } - var rptr = copy1dArray(radii, 'HEAPF32'); - if (args.length === 3 || args.length === 4) { - var r = args[0]; - var ccw = args[args.length - 1]; - this._addRoundRect(r.fLeft, r.fTop, r.fRight, r.fBottom, rptr, ccw); - } else if (args.length === 6 || args.length === 7) { - var a = args; - this._addRoundRect(a[0], a[1], a[2], a[3], rptr, ccw); - } - freeArraysThatAreNotMallocedByUsers(rptr, radii); + CanvasKit.SkPath.prototype.addRRect = function(rrect, isCCW) { + var rPtr = copyRRectToWasm(rrect); + this._addRRect(rPtr, !!isCCW); return this; }; @@ -744,32 +712,13 @@ CanvasKit.onRuntimeInitialized = function() { return this; }; - // Deprecated, use one of the three variants below depending on how many args you were calling it with. - CanvasKit.SkPath.prototype.arcTo = function() { - // takes 4, 5 or 7 args - // - 5 x1, y1, x2, y2, radius - // - 4 oval (as Rect), startAngle, sweepAngle, forceMoveTo - // - 7 rx, ry, xAxisRotate, useSmallArc, isCCW, x, y - var args = arguments; - if (args.length === 5) { - this._arcToTangent(args[0], args[1], args[2], args[3], args[4]); - } else if (args.length === 4) { - this._arcToOval(args[0], args[1], args[2], args[3]); - } else if (args.length === 7) { - this._arcToRotated(args[0], args[1], args[2], !!args[3], !!args[4], args[5], args[6]); - } else { - throw 'Invalid args for arcTo. Expected 4, 5, or 7, got '+ args.length; - } - - return this; - }; - // Appends arc to SkPath. Arc added is part of ellipse // bounded by oval, from startAngle through sweepAngle. Both startAngle and // sweepAngle are measured in degrees, where zero degrees is aligned with the // positive x-axis, and positive sweeps extends arc clockwise. CanvasKit.SkPath.prototype.arcToOval = function(oval, startAngle, sweepAngle, forceMoveTo) { - this._arcToOval(oval, startAngle, sweepAngle, forceMoveTo); + var oPtr = copyRectToWasm(oval); + this._arcToOval(oPtr, startAngle, sweepAngle, forceMoveTo); return this; }; @@ -832,6 +781,19 @@ CanvasKit.onRuntimeInitialized = function() { return null; }; + // Clients can pass in a Float32Array with length 4 to this and the results + // will be copied into that array. Otherwise, a new TypedArray will be allocated + // and returned. + CanvasKit.SkPath.prototype.getBounds = function(optionalOutputArray) { + this._getBounds(_scratchRectPtr); + var ta = _scratchRect['toTypedArray'](); + if (optionalOutputArray) { + optionalOutputArray.set(ta); + return optionalOutputArray; + } + return ta.slice(); + }; + CanvasKit.SkPath.prototype.lineTo = function(x, y) { this._lineTo(x, y); return this; @@ -996,6 +958,11 @@ CanvasKit.onRuntimeInitialized = function() { this._clipRRect(rPtr, op, antialias); } + CanvasKit.SkCanvas.prototype.clipRect = function(rect, op, antialias) { + var rPtr = copyRectToWasm(rect); + this._clipRect(rPtr, op, antialias); + } + // concat takes a 3x2, a 3x3, or a 4x4 matrix and upscales it (if needed) to 4x4. This is because // under the hood, SkCanvas uses a 4x4 matrix. CanvasKit.SkCanvas.prototype.concat = function(matr) { @@ -1006,6 +973,11 @@ CanvasKit.onRuntimeInitialized = function() { // Deprecated - just use concat CanvasKit.SkCanvas.prototype.concat44 = CanvasKit.SkCanvas.prototype.concat; + CanvasKit.SkCanvas.prototype.drawArc = function(oval, startAngle, sweepAngle, useCenter, paint) { + var oPtr = copyRectToWasm(oval); + this._drawArc(oPtr, startAngle, sweepAngle, useCenter, paint); + } + // atlas is an SkImage, e.g. from CanvasKit.MakeImageFromEncoded // srcRects, dstXforms, and colors should be CanvasKit.SkRectBuilder, CanvasKit.RSXFormBuilder, // and CanvasKit.SkColorBuilder (fastest) @@ -1095,6 +1067,23 @@ CanvasKit.onRuntimeInitialized = function() { this._drawDRRect(oPtr, iPtr, paint); } + CanvasKit.SkCanvas.prototype.drawImageNine = function(img, center, dest, paint) { + var cPtr = copyIRectToWasm(center); + var dPtr = copyRectToWasm(dest); + this._drawImageNine(img, cPtr, dPtr, paint); + } + + CanvasKit.SkCanvas.prototype.drawImageRect = function(img, src, dest, paint, fastSample) { + var sPtr = copyRectToWasm(src, _scratchRectPtr); + var dPtr = copyRectToWasm(dest, _scratchRect2Ptr); + this._drawImageRect(img, sPtr, dPtr, paint, !!fastSample); + } + + CanvasKit.SkCanvas.prototype.drawOval = function(oval, paint) { + var oPtr = copyRectToWasm(oval); + this._drawOval(oPtr, paint); + } + // points is either an array of [x, y] where x and y are numbers or // a typed array from Malloc where the even indices will be treated // as x coordinates and the odd indices will be treated as y coordinates. @@ -1119,6 +1108,11 @@ CanvasKit.onRuntimeInitialized = function() { this._drawRRect(rPtr, paint); } + CanvasKit.SkCanvas.prototype.drawRect = function(rect, paint) { + var rPtr = copyRectToWasm(rect); + this._drawRect(rPtr, paint); + } + CanvasKit.SkCanvas.prototype.drawShadow = function(path, zPlaneParams, lightPos, lightRadius, ambientColor, spotColor, flags) { var ambiPtr = copyColorToWasmNoScratch(ambientColor); var spotPtr = copyColorToWasmNoScratch(spotColor); @@ -1192,6 +1186,13 @@ CanvasKit.onRuntimeInitialized = function() { return pixels; } + CanvasKit.SkCanvas.prototype.saveLayer = function(paint, boundsRect, backdrop, flags) { + // bPtr will be 0 (nullptr) if boundsRect is undefined/null. + var bPtr = copyRectToWasm(boundsRect); + // These or clauses help emscripten, which does not deal with undefined well. + return this._saveLayer(paint || null, bPtr, backdrop || null, flags || 0); + } + // pixels should be a Uint8Array or a plain JS array. CanvasKit.SkCanvas.prototype.writePixels = function(pixels, srcWidth, srcHeight, destX, destY, alphaType, colorType, colorSpace) { @@ -1263,6 +1264,11 @@ CanvasKit.onRuntimeInitialized = function() { this._setColor(cPtr, colorSpace); } + CanvasKit.SkPictureRecorder.prototype.beginRecording = function(bounds) { + var bPtr = copyRectToWasm(bounds); + return this._beginRecording(bPtr); + } + CanvasKit.SkSurface.prototype.captureFrameAsSkPicture = function(drawFrame) { // Set up SkPictureRecorder var spr = new CanvasKit.SkPictureRecorder(); @@ -1276,6 +1282,11 @@ CanvasKit.onRuntimeInitialized = function() { return pic; } + CanvasKit.SkSurface.prototype.makeImageSnapshot = function(optionalBoundsRect) { + var bPtr = copyIRectToWasm(optionalBoundsRect); + return this._makeImageSnapshot(bPtr); + } + CanvasKit.SkSurface.prototype.requestAnimationFrame = function(callback, dirtyRect) { if (!this._cached_canvas) { this._cached_canvas = this.getCanvas(); @@ -1397,6 +1408,19 @@ CanvasKit.onRuntimeInitialized = function() { return rgs; } + // Clients can pass in a Float32Array with length 4 to this and the results + // will be copied into that array. Otherwise, a new TypedArray will be allocated + // and returned. + CanvasKit.SkVertices.prototype.bounds = function(optionalOutputArray) { + this._bounds(_scratchRectPtr); + var ta = _scratchRect['toTypedArray'](); + if (optionalOutputArray) { + optionalOutputArray.set(ta); + return optionalOutputArray; + } + return ta.slice(); + } + // temporary support for deprecated names. CanvasKit.MakeSkDashPathEffect = CanvasKit.SkPathEffect.MakeDash; CanvasKit.MakeLinearGradientShader = CanvasKit.SkShader.MakeLinearGradient; @@ -1437,28 +1461,26 @@ CanvasKit.computeTonalColors = function(tonalColors) { }; CanvasKit.LTRBRect = function(l, t, r, b) { - return { - fLeft: l, - fTop: t, - fRight: r, - fBottom: b, - }; + return Float32Array.of(l, t, r, b); }; CanvasKit.XYWHRect = function(x, y, w, h) { - return { - fLeft: x, - fTop: y, - fRight: x+w, - fBottom: y+h, - }; + return Float32Array.of(x, y, x+w, y+h); +}; + +CanvasKit.LTRBiRect = function(l, t, r, b) { + return Int32Array.of(l, t, r, b); +}; + +CanvasKit.XYWHiRect = function(x, y, w, h) { + return Int32Array.of(x, y, x+w, y+h); }; // RRectXY returns a TypedArray representing an RRect with the given rect and a radiusX and // radiusY for all 4 corners. CanvasKit.RRectXY = function(rect, rx, ry) { return Float32Array.of( - rect['fLeft'], rect['fTop'], rect['fRight'], rect['fBottom'], + rect[0], rect[1], rect[2], rect[3], rx, ry, rx, ry, rx, ry, diff --git a/modules/canvaskit/skottie.js b/modules/canvaskit/skottie.js index a46b231919..2d5f3f60bd 100644 --- a/modules/canvaskit/skottie.js +++ b/modules/canvaskit/skottie.js @@ -63,10 +63,43 @@ CanvasKit.MakeManagedAnimation = function(json, assets, prop_filter_prefix) { CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; CanvasKit._extraInitializations.push(function() { - CanvasKit.ManagedAnimation.prototype.setColor = function(key, color) { - var cPtr = copyColorToWasm(color); - this._setColor(key, cPtr); + CanvasKit.Animation.prototype.render = function(canvas, dstRect) { + var dPtr = copyRectToWasm(dstRect); + this._render(canvas, dPtr); } + if (CanvasKit.ManagedAnimation) { + CanvasKit.ManagedAnimation.prototype.render = function(canvas, dstRect) { + var dPtr = copyRectToWasm(dstRect); + this._render(canvas, dPtr); + } + + CanvasKit.ManagedAnimation.prototype.seek = function(t, optDamageRect) { + this._seek(t, _scratchRectPtr); + var ta = _scratchRect['toTypedArray'](); + if (optDamageRect) { + optDamageRect.set(ta); + return optDamageRect; + } + return ta.slice(); + } + + CanvasKit.ManagedAnimation.prototype.seekFrame = function(frame, optDamageRect) { + this._seekFrame(frame, _scratchRectPtr); + var ta = _scratchRect['toTypedArray'](); + if (optDamageRect) { + optDamageRect.set(ta); + return optDamageRect; + } + return ta.slice(); + } + + CanvasKit.ManagedAnimation.prototype.setColor = function(key, color) { + var cPtr = copyColorToWasm(color); + this._setColor(key, cPtr); + } + } + + }); }(Module)); // When this file is loaded in, the high level object is "Module"; diff --git a/modules/canvaskit/skottie_bindings.cpp b/modules/canvaskit/skottie_bindings.cpp index a2ba98580c..10f628b494 100644 --- a/modules/canvaskit/skottie_bindings.cpp +++ b/modules/canvaskit/skottie_bindings.cpp @@ -112,8 +112,7 @@ public: ~ManagedAnimation() override = default; // skottie::Animation API - void render(SkCanvas* canvas) const { fAnimation->render(canvas, nullptr); } - void render(SkCanvas* canvas, const SkRect& dst) const { fAnimation->render(canvas, &dst); } + void render(SkCanvas* canvas, const SkRect* dst) const { fAnimation->render(canvas, dst); } // Returns a damage rect. SkRect seek(SkScalar t) { sksg::InvalidationController ic; @@ -207,12 +206,10 @@ EMSCRIPTEN_BINDINGS(Skottie) { .function("seekFrame", optional_override([](skottie::Animation& self, double t)->void { self.seekFrame(t); })) - .function("render", optional_override([](skottie::Animation& self, SkCanvas* canvas)->void { - self.render(canvas, nullptr); - }), allow_raw_pointers()) - .function("render", optional_override([](skottie::Animation& self, SkCanvas* canvas, - const SkRect r)->void { - self.render(canvas, &r); + .function("_render", optional_override([](skottie::Animation& self, SkCanvas* canvas, + uintptr_t /* float* */ fPtr)->void { + const SkRect* dst = reinterpret_cast(fPtr); + self.render(canvas, dst); }), allow_raw_pointers()); function("MakeAnimation", optional_override([](std::string json)->sk_sp { @@ -227,11 +224,22 @@ EMSCRIPTEN_BINDINGS(Skottie) { .function("size" , &ManagedAnimation::size) .function("duration" , &ManagedAnimation::duration) .function("fps" , &ManagedAnimation::fps) - .function("seek" , &ManagedAnimation::seek) + .function("_render", optional_override([](ManagedAnimation& self, SkCanvas* canvas, + uintptr_t /* float* */ fPtr)->void { + const SkRect* dst = reinterpret_cast(fPtr); + self.render(canvas, dst); + }), allow_raw_pointers()) + .function("_seek", optional_override([](ManagedAnimation& self, SkScalar t, + uintptr_t /* float* */ fPtr) { + SkRect* damageRect = reinterpret_cast(fPtr); + damageRect[0] = self.seek(t); + })) + .function("_seekFrame", optional_override([](ManagedAnimation& self, double frame, + uintptr_t /* float* */ fPtr) { + SkRect* damageRect = reinterpret_cast(fPtr); + damageRect[0] = self.seekFrame(frame); + })) .function("seekFrame" , &ManagedAnimation::seekFrame) - .function("render" , select_overload(&ManagedAnimation::render), allow_raw_pointers()) - .function("render" , select_overload - (&ManagedAnimation::render), allow_raw_pointers()) .function("_setColor" , optional_override([](ManagedAnimation& self, const std::string& key, uintptr_t /* float* */ cPtr) { float* fourFloats = reinterpret_cast(cPtr); SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] }; diff --git a/modules/canvaskit/tests/canvas.spec.js b/modules/canvaskit/tests/canvas.spec.js index bb89f03d50..f12ba2d035 100644 --- a/modules/canvaskit/tests/canvas.spec.js +++ b/modules/canvaskit/tests/canvas.spec.js @@ -22,7 +22,8 @@ describe('Canvas Behavior', () => { paint.setStyle(CanvasKit.PaintStyle.Stroke); canvas.drawLine(3, 10, 30, 15, paint); - canvas.drawRoundRect(CanvasKit.LTRBRect(5, 35, 45, 80), 15, 10, paint); + const rrect = CanvasKit.RRectXY([5, 35, 45, 80], 15, 10); + canvas.drawRRect(rrect, paint); canvas.drawOval(CanvasKit.LTRBRect(5, 35, 45, 80), paint); @@ -435,7 +436,7 @@ describe('Canvas Behavior', () => { // The rectangle is just a hint, so I've set it to be the area that // we actually draw in before restore is called. It could also be omitted, // see the test below. - canvas.saveLayer(CanvasKit.LTRBRect(10, 10, 220, 180), alpha); + canvas.saveLayer(alpha, CanvasKit.LTRBRect(10, 10, 220, 180)); // Draw the same blue overlapping rectangles as before. Notice in the // final output, we have two different shades of purple instead of the @@ -536,7 +537,7 @@ describe('Canvas Behavior', () => { const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null); - const count = canvas.saveLayer(null, blurIF, 0); + const count = canvas.saveLayer(null, null, blurIF, 0); expect(count).toEqual(1); canvas.scale(1/4, 1/4); canvas.drawCircle(125, 85, 8, redPaint); @@ -593,12 +594,8 @@ describe('Canvas Behavior', () => { canvas.clear(CanvasKit.WHITE); const paint = new CanvasKit.SkPaint(); - canvas.drawImageNine(img, { - fLeft: 40, - fTop: 40, - fRight: 400, - fBottom: 300, - }, CanvasKit.LTRBRect(5, 5, 300, 650), paint); + canvas.drawImageNine(img, CanvasKit.LTRBiRect(40, 40, 400, 300), + CanvasKit.LTRBRect(5, 5, 300, 650), paint); paint.delete(); img.delete(); }, '/assets/mandrill_512.png'); @@ -615,10 +612,7 @@ describe('Canvas Behavior', () => { points, null /*textureCoordinates*/, colors, false /*isVolatile*/); const bounds = vertices.bounds(); - expect(bounds.fLeft).toEqual(0); - expect(bounds.fTop).toEqual(0); - expect(bounds.fRight).toEqual(250); - expect(bounds.fBottom).toEqual(250); + expect(bounds).toEqual(CanvasKit.LTRBRect(0, 0, 250, 250)); canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint); vertices.delete(); @@ -637,10 +631,7 @@ describe('Canvas Behavior', () => { points, null /*textureCoordinates*/, colors, false /*isVolatile*/); const bounds = vertices.bounds(); - expect(bounds.fLeft).toEqual(0); - expect(bounds.fTop).toEqual(0); - expect(bounds.fRight).toEqual(250); - expect(bounds.fBottom).toEqual(250); + expect(bounds).toEqual(CanvasKit.LTRBRect(0, 0, 250, 250)); canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint); vertices.delete(); diff --git a/modules/canvaskit/tests/core.spec.js b/modules/canvaskit/tests/core.spec.js index 066a1ffe90..1b78d7b0b6 100644 --- a/modules/canvaskit/tests/core.spec.js +++ b/modules/canvaskit/tests/core.spec.js @@ -24,7 +24,7 @@ describe('Core canvas behavior', () => { paint.setColor(CanvasKit.Color(0, 0, 0, 1.0)); paint.setStyle(CanvasKit.PaintStyle.Stroke); - rcanvas.drawRoundRect(CanvasKit.LTRBRect(5, 35, 45, 80), 15, 10, paint); + rcanvas.drawRRect(CanvasKit.RRectXY([5, 35, 45, 80], 15, 10), paint); const font = new CanvasKit.SkFont(null, 20); rcanvas.drawText('this picture has a round rect', 5, 100, paint, font); diff --git a/modules/canvaskit/tests/font.spec.js b/modules/canvaskit/tests/font.spec.js index ce5e48b853..9e21584b19 100644 --- a/modules/canvaskit/tests/font.spec.js +++ b/modules/canvaskit/tests/font.spec.js @@ -60,10 +60,10 @@ describe('Font Behavior', () => { canvas.drawText(shapedText, textBoxX, textBoxY, textPaint); const bounds = shapedText.getBounds(); - bounds.fLeft += textBoxX; - bounds.fRight += textBoxX; - bounds.fTop += textBoxY; - bounds.fBottom += textBoxY + bounds[0] += textBoxX; // left + bounds[2] += textBoxX; // right + bounds[1] += textBoxY; // top + bounds[3] += textBoxY // bottom canvas.drawRect(bounds, paint); const SHAPE_TEST_TEXT = 'VAVAVAVAVAFIfi'; @@ -102,9 +102,9 @@ describe('Font Behavior', () => { const arc = new CanvasKit.SkPath(); - arc.arcTo(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true); + arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true); arc.lineTo(210, 140); - arc.arcTo(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true); + arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true); // Only 1 dot should show up in the image, because we run out of path. const str = 'This téxt should follow the curve across contours...'; @@ -134,9 +134,9 @@ describe('Font Behavior', () => { fontPaint.setStyle(CanvasKit.PaintStyle.Fill); const arc = new CanvasKit.SkPath(); - arc.arcTo(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true); + arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true); arc.lineTo(210, 140); - arc.arcTo(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true); + arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true); const str = 'This téxt should follow the curve across contours...'; const textBlob = CanvasKit.SkTextBlob.MakeOnPath(str, arc, font, 60.5); diff --git a/modules/canvaskit/tests/path.spec.js b/modules/canvaskit/tests/path.spec.js index fab831c30b..b3d4765775 100644 --- a/modules/canvaskit/tests/path.spec.js +++ b/modules/canvaskit/tests/path.spec.js @@ -49,13 +49,13 @@ describe('Path Behavior', () => { canvas.drawPath(path, paint); - const rrect = new CanvasKit.SkPath() - .addRoundRect(100, 10, 140, 62, - 10, 4, true); + const rrect = CanvasKit.RRectXY([100, 10, 140, 62], 10, 4); - canvas.drawPath(rrect, paint); - rrect.delete(); + const rrectPath = new CanvasKit.SkPath().addRRect(rrect, true); + canvas.drawPath(rrectPath, paint); + + rrectPath.delete(); path.delete(); paint.delete(); // See PathKit for more tests, since they share implementation @@ -263,7 +263,7 @@ describe('Path Behavior', () => { canvas.clear(CanvasKit.WHITE); const path = new CanvasKit.SkPath(); - + // - x1, y1, x2, y2, radius path.arcToTangent(40, 0, 40, 40, 40); // - oval (as Rect), startAngle, sweepAngle, forceMoveTo @@ -417,10 +417,11 @@ describe('Path Behavior', () => { path.addArc(CanvasKit.LTRBRect(10, 20, 100, 200), 30, 300) .addRect(CanvasKit.LTRBRect(200, 200, 300, 300)) // test single arg, default cw .addRect(CanvasKit.LTRBRect(240, 240, 260, 260), true) // test two arg, true means ccw - .addRect(260, 260, 290, 290, true) // test five arg, true means ccw - .addRoundRect(CanvasKit.LTRBRect(300, 10, 500, 290), - [60, 60, 60, 60, 60, 60, 60, 60], false) // SkRect, radii, ccw - .addRoundRect(CanvasKit.LTRBRect(350, 60, 450, 240), 20, 80, true) // SkRect, rx, ry, ccw + .addRect([260, 260, 290, 290], true) // test five arg, true means ccw + .addRRect([300, 10, 500, 290, // SkRect in LTRB order + 60, 60, 60, 60, 60, 60, 60, 60], // all radii are the same + false) // ccw + .addRRect(CanvasKit.RRectXY([350, 60, 450, 240], 20, 80), true) // SkRect, rx, ry, ccw .addPath(arcpath) .transform(0.54, -0.84, 390.35, 0.84, 0.54, -114.53, diff --git a/modules/canvaskit/tests/skottie.spec.js b/modules/canvaskit/tests/skottie.spec.js index a8f4d425f2..47b6dee6d1 100644 --- a/modules/canvaskit/tests/skottie.spec.js +++ b/modules/canvaskit/tests/skottie.spec.js @@ -14,6 +14,14 @@ describe('Skottie behavior', () => { document.body.removeChild(container); }); + const expectArrayCloseTo = (a, b, precision) => { + precision = precision || 14 // digits of precision in base 10 + expect(a.length).toEqual(b.length); + for (let i=0; i response.arrayBuffer()); const jsonPromise = fetch('/assets/animated_gif.json') @@ -30,14 +38,21 @@ describe('Skottie behavior', () => { 'flightAnim.gif': promises[0], }); expect(animation).toBeTruthy(); - const bounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500}; + const bounds = CanvasKit.LTRBRect(0, 0, 500, 500); canvas.clear(CanvasKit.WHITE); animation.render(canvas, bounds); + // We intentionally make the length of this array 5 and add a sentinel value + // of 999 so we can make sure the bounds are copied into this rect and a new + // one is not allocated. + const damageRect = Float32Array.of(0, 0, 0, 0, 999); + // There was a bug, fixed in https://skia-review.googlesource.com/c/skia/+/241757 // that seeking again and drawing again revealed. - animation.seek(0.5); + animation.seek(0.5, damageRect); + expectArrayCloseTo(damageRect, Float32Array.of(0, 0, 800, 600, 999), 4); + canvas.clear(CanvasKit.WHITE); animation.render(canvas, bounds); animation.delete(); @@ -48,7 +63,7 @@ describe('Skottie behavior', () => { console.warn('Skipping test because not compiled with skottie'); return; } - const bounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500}; + const bounds = CanvasKit.LTRBRect(0, 0, 500, 500); canvas.clear(CanvasKit.WHITE); const animation = CanvasKit.MakeManagedAnimation(promises[0]); diff --git a/tools/perf-canvaskit-puppeteer/skottie-frames.html b/tools/perf-canvaskit-puppeteer/skottie-frames.html index a44f04c0f5..e95fe47c9e 100644 --- a/tools/perf-canvaskit-puppeteer/skottie-frames.html +++ b/tools/perf-canvaskit-puppeteer/skottie-frames.html @@ -70,7 +70,7 @@ would display the animation (e.g. using clock time to determine where to seek, n }; const duration = animation.duration() * 1000; - const bounds = {fLeft: 0, fTop: 0, fRight: WIDTH, fBottom: HEIGHT}; + const bounds = CanvasKit.LTRBRect(0, 0, WIDTH, HEIGHT); const urlSearchParams = new URLSearchParams(window.location.search); let glversion = 2; @@ -89,12 +89,13 @@ would display the animation (e.g. using clock time to determine where to seek, n const clearColor = CanvasKit.WHITE; let idx = 0; const startTime = Date.now(); + const damageRect = Float32Array.of(0, 0, 0, 0); function draw() { const seek = ((Date.now() - startTime) / duration) % 1.0; - const damage = animation.seek(seek); + const damage = animation.seek(seek, damageRect); - if (damage.fRight > damage.fLeft && damage.fBottom > damage.fTop) { + if (damage[2] > damage[0] && damage[3] > damage[1]) { canvas.clear(clearColor); animation.render(canvas, bounds); } diff --git a/tools/skottie-wasm-perf/Makefile b/tools/skottie-wasm-perf/Makefile index 7863cdfbfe..c94b93c471 100644 --- a/tools/skottie-wasm-perf/Makefile +++ b/tools/skottie-wasm-perf/Makefile @@ -1,2 +1,6 @@ -serve: - python -m SimpleHTTPServer 8004 \ No newline at end of file +# This shows an example invocation presuming the user has built canvaskit locally. +lego_loader_with_local: + node skottie-wasm-perf.js --canvaskit_js ../../out/canvaskit_wasm/canvaskit.js \ + --canvaskit_wasm ../../out/canvaskit_wasm/canvaskit.wasm --use_gpu \ + --input ../../modules/canvaskit/perf/assets/lego_loader.json \ + --output lego_loader_perf.json \ No newline at end of file diff --git a/tools/skottie-wasm-perf/skottie-wasm-perf.html b/tools/skottie-wasm-perf/skottie-wasm-perf.html index ac29945eb0..b160926e5a 100644 --- a/tools/skottie-wasm-perf/skottie-wasm-perf.html +++ b/tools/skottie-wasm-perf/skottie-wasm-perf.html @@ -18,99 +18,93 @@ diff --git a/tools/skottie-wasm-perf/skottie-wasm-perf.js b/tools/skottie-wasm-perf/skottie-wasm-perf.js index 4d87af3eb3..ba251bcdb3 100644 --- a/tools/skottie-wasm-perf/skottie-wasm-perf.js +++ b/tools/skottie-wasm-perf/skottie-wasm-perf.js @@ -161,9 +161,9 @@ async function driveBrowser() { waitUntil: 'networkidle0' }); - console.log('Waiting 60s for run to be done'); + console.log('Waiting 90s for run to be done'); await page.waitForFunction(`(window._skottieDone === true) || window._error`, { - timeout: 60000, + timeout: 90000, }); const err = await page.evaluate('window._error');