From ed96264ad7877dc21955447f06b54c7bbad9561e Mon Sep 17 00:00:00 2001 From: Kevin Lubick Date: Tue, 2 Feb 2021 08:18:11 -0500 Subject: [PATCH] [canvaskit] Replace Point value_array with Float32Array Using value_array (and value_object), while convenient, adds a measurable overhead. This removes the Point value_object and replaces it as a return value with Float32Array (similar to rects). For inputs of a single point, I just split it into x and y. For inputs with two points, I used a _scratchFourFloats (formerly _scratchRect) bit of memory. Two subtle decisions here: - Why not use scratch memory for a single point? The cost of having one extra param is a small/negligible price to pay for less complex code. - Why not accept Malloc objects? Again, simplicity. Accommodating Malloc would make the code harder to read and require more checks. I don't know if anyone wants to have malloced points; if they do, we can probably accommodate that. Change-Id: I1b1c29f62e01c2f1c8c1218f58e3bad642214322 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/362097 Reviewed-by: Nathaniel Nifong --- modules/canvaskit/CHANGELOG.md | 20 +++-- .../canvaskit/types/canvaskit-wasm-tests.ts | 6 +- modules/canvaskit/canvaskit/types/index.d.ts | 30 ++++--- modules/canvaskit/canvaskit_bindings.cpp | 38 ++++---- modules/canvaskit/externs.js | 18 ++-- modules/canvaskit/font.js | 4 +- modules/canvaskit/interface.js | 88 ++++++++++++------- modules/canvaskit/memory.js | 10 +-- modules/canvaskit/particles.js | 5 +- modules/canvaskit/particles_bindings.cpp | 5 +- modules/canvaskit/skottie.js | 56 +++++++++--- modules/canvaskit/skottie_bindings.cpp | 12 ++- modules/canvaskit/tests/path.spec.js | 19 ++++ modules/canvaskit/tests/skottie.spec.js | 5 +- 14 files changed, 220 insertions(+), 96 deletions(-) diff --git a/modules/canvaskit/CHANGELOG.md b/modules/canvaskit/CHANGELOG.md index c2fe456a49..bda42ad21c 100644 --- a/modules/canvaskit/CHANGELOG.md +++ b/modules/canvaskit/CHANGELOG.md @@ -13,18 +13,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 SkColorMatrix (in case clients have logic to deal with that themselves). ### Breaking -- `MakeImprovedNoise` is removed. -- Particles now use a single code string containing both Effect and Particle code. Uniform APIs are - now shared between Effect and Particle programs, and are no longer prefixed with `Effect` or - `Particle`. For example, instead of `ParticleEffect.getEffectUniform` and - `ParticleEffect.getParticleUniform`, there is now just: `ParticleEffect.getUniform`. + - `MakeImprovedNoise` is removed. + - Particles now use a single code string containing both Effect and Particle code. Uniform APIs are + now shared between Effect and Particle programs, and are no longer prefixed with `Effect` or + `Particle`. For example, instead of `ParticleEffect.getEffectUniform` and + `ParticleEffect.getParticleUniform`, there is now just: `ParticleEffect.getUniform`. + +### Changed + - `Path.getPoint()` and `SkottieAnimation.size()` now return a TypedArray instead of a normal + array. Additionally, they take an optional parameter to allow the result to be copied into + that provided TypedArray instead of a new one being allocated. + - APIs that passed in points should have less overhead (and now can accept a TypedArray). ### Fixed - Improper error returned when a WebGL context could not be used. - 4x4 matrices are "downsampled" properly if necessary to 3x3 matrices by removing the third column and the third row. - -## [0.23.0] - 2021-1-29 + - `SkottieAnimation.size()` was incorrectly returning an object. It now returns a TypedArray of + length 2 (w, h). ### Deprecated - `Canvas.drawImageRect`, `Canvas.drawImage`, `Canvas.drawAtlas`, diff --git a/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts b/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts index c4a8ae1842..fec580f155 100644 --- a/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts +++ b/modules/canvaskit/canvaskit/types/canvaskit-wasm-tests.ts @@ -448,7 +448,8 @@ function pathTests(CK: CanvasKit) { bounds = path.getBounds(); // $ExpectType Float32Array path.getBounds(bounds); const ft = path.getFillType(); - const pt = path.getPoint(7); // $ExpectType Point + const pt = path.getPoint(7); // $ExpectType Float32Array + path.getPoint(8, pt); ok = path.isEmpty(); ok = path.isVolatile(); path.lineTo(10, -20); @@ -663,7 +664,8 @@ function skottieTests(CK: CanvasKit, canvas?: Canvas) { const a = anim.duration(); // $ExpectType number const b = anim.fps(); // $ExpectType number const c = anim.version(); // $ExpectType string - const d = anim.size(); // $ExpectType Point + const d = anim.size(); // $ExpectType Float32Array + anim.size(d); const rect = anim.seek(0.5); anim.seek(0.6, rect); const rect2 = anim.seekFrame(12.3); diff --git a/modules/canvaskit/canvaskit/types/index.d.ts b/modules/canvaskit/canvaskit/types/index.d.ts index 00b4618efd..0f74009602 100644 --- a/modules/canvaskit/canvaskit/types/index.d.ts +++ b/modules/canvaskit/canvaskit/types/index.d.ts @@ -837,7 +837,7 @@ export interface Particles extends EmbindObject { * Sets the base position of the effect. * @param point */ - setPosition(point: Point): void; + setPosition(point: InputPoint): void; /** * Sets the base rate of the effect. @@ -2123,8 +2123,10 @@ export interface Path extends EmbindObject { * Returns the Point at index in Point array. Valid range for index is * 0 to countPoints() - 1. * @param index + * @param outputArray - if provided, the point will be copied into this array instead of + * allocating a new one. */ - getPoint(index: number): Point; + getPoint(index: number, outputArray?: Point): Point; /** * Returns true if there are no verbs in the path. @@ -2504,7 +2506,11 @@ export interface SkottieAnimation extends EmbindObject { */ seekFrame(frame: number, damageRect?: Rect): Rect; - size(): Point; + /** + * Return the size of this animation. + * @param outputSize - If provided, the size will be copied into here as width, height. + */ + size(outputSize?: Point): Point; version(): string; } @@ -3153,7 +3159,7 @@ export interface ShaderFactory { * between them. * @param colorSpace */ - MakeLinearGradient(start: Point, end: Point, colors: InputFlexibleColorArray, + MakeLinearGradient(start: InputPoint, end: InputPoint, colors: InputFlexibleColorArray, pos: number[] | null, mode: TileMode, localMatrix?: InputMatrix, flags?: number, colorSpace?: ColorSpace): Shader; @@ -3170,7 +3176,7 @@ export interface ShaderFactory { * @param flags - 0 to interpolate colors in unpremul, 1 to interpolate colors in premul. * @param colorSpace */ - MakeRadialGradient(center: Point, radius: number, colors: InputFlexibleColorArray, + MakeRadialGradient(center: InputPoint, radius: number, colors: InputFlexibleColorArray, pos: number[] | null, mode: TileMode, localMatrix?: InputMatrix, flags?: number, colorSpace?: ColorSpace): Shader; @@ -3223,10 +3229,10 @@ export interface ShaderFactory { * @param flags * @param colorSpace */ - MakeTwoPointConicalGradient(start: Point, startRadius: number, end: Point, endRadius: number, - colors: InputFlexibleColorArray, pos: number[] | null, - mode: TileMode, localMatrix?: InputMatrix, flags?: number, - colorSpace?: ColorSpace): Shader; + MakeTwoPointConicalGradient(start: InputPoint, startRadius: number, end: InputPoint, + endRadius: number, colors: InputFlexibleColorArray, + pos: number[] | null, mode: TileMode, localMatrix?: InputMatrix, + flags?: number, colorSpace?: ColorSpace): Shader; } /** @@ -3400,7 +3406,7 @@ export type IRect = Int32Array; /** * An Point is represented by 2 floats: (x, y). */ -export type Point = number[]; +export type Point = Float32Array; /** * An Rect is represented by 4 floats. In order, the floats correspond to left, top, * right, bottom. See Rect.h for more @@ -3502,6 +3508,10 @@ export type InputFlattenedRectangleArray = MallocObj | FlattenedRectangleArray | * (e.g. Color). This is convenient for things like gradients when matching up colors to stops. */ export type InputFlexibleColorArray = Float32Array | Uint32Array | Float32Array[]; +/** + * CanvasKit APIs accept a Float32Array or a normal array (of length 2) as a Point. + */ +export type InputPoint = Point | number[]; /** * CanvasKit APIs accept all of these matrix types. Under the hood, we generally use 4x4 matrices. */ diff --git a/modules/canvaskit/canvaskit_bindings.cpp b/modules/canvaskit/canvaskit_bindings.cpp index 681cc64f52..691b417942 100644 --- a/modules/canvaskit/canvaskit_bindings.cpp +++ b/modules/canvaskit/canvaskit_bindings.cpp @@ -1463,7 +1463,11 @@ EMSCRIPTEN_BINDINGS(Skia) { .function("countPoints", &SkPath::countPoints) .function("contains", &SkPath::contains) .function("_cubicTo", &ApplyCubicTo) - .function("getPoint", &SkPath::getPoint) + .function("_getPoint", optional_override([](SkPath& self, int index, + uintptr_t /* float* */ oPtr)->void { + SkPoint* output = reinterpret_cast(oPtr); + *output = self.getPoint(index); + })) .function("isEmpty", &SkPath::isEmpty) .function("isVolatile", &SkPath::isVolatile) .function("_lineTo", &ApplyLineTo) @@ -1586,13 +1590,14 @@ EMSCRIPTEN_BINDINGS(Skia) { // Here and in other gradient functions, cPtr is a pointer to an array of data // representing colors. whether this is an array of SkColor or SkColor4f is indicated // by the colorType argument. Only RGBA_8888 and RGBA_F32 are accepted. - .class_function("_MakeLinearGradient", optional_override([](SkPoint start, SkPoint end, + .class_function("_MakeLinearGradient", optional_override([]( + uintptr_t /* SkPoint* */ fourFloatsPtr, uintptr_t cPtr, SkColorType colorType, uintptr_t /* SkScalar* */ pPtr, int count, SkTileMode mode, uint32_t flags, uintptr_t /* SkScalar* */ mPtr, sk_sp colorSpace)->sk_sp { - SkPoint points[] = { start, end }; + const SkPoint* points = reinterpret_cast(fourFloatsPtr); const SkScalar* positions = reinterpret_cast(pPtr); OptionalMatrix localMatrix(mPtr); @@ -1608,7 +1613,8 @@ EMSCRIPTEN_BINDINGS(Skia) { SkDebugf("%d is not an accepted colorType\n", colorType); return nullptr; }), allow_raw_pointers()) - .class_function("_MakeRadialGradient", optional_override([](SkPoint center, SkScalar radius, + .class_function("_MakeRadialGradient", optional_override([]( + SkScalar cx, SkScalar cy, SkScalar radius, uintptr_t cPtr, SkColorType colorType, uintptr_t /* SkScalar* */ pPtr, int count, SkTileMode mode, uint32_t flags, @@ -1618,12 +1624,12 @@ EMSCRIPTEN_BINDINGS(Skia) { OptionalMatrix localMatrix(mPtr); if (colorType == SkColorType::kRGBA_F32_SkColorType) { const SkColor4f* colors = reinterpret_cast(cPtr); - return SkGradientShader::MakeRadial(center, radius, colors, colorSpace, positions, count, - mode, flags, &localMatrix); + return SkGradientShader::MakeRadial({cx, cy}, radius, colors, colorSpace, + positions, count, mode, flags, &localMatrix); } else if (colorType == SkColorType::kRGBA_8888_SkColorType) { const SkColor* colors = reinterpret_cast(cPtr); - return SkGradientShader::MakeRadial(center, radius, colors, positions, count, - mode, flags, &localMatrix); + return SkGradientShader::MakeRadial({cx, cy}, radius, colors, positions, + count, mode, flags, &localMatrix); } SkDebugf("%d is not an accepted colorType\n", colorType); return nullptr; @@ -1662,24 +1668,27 @@ EMSCRIPTEN_BINDINGS(Skia) { numOctaves, seed, &tileSize); })) .class_function("_MakeTwoPointConicalGradient", optional_override([]( - SkPoint start, SkScalar startRadius, - SkPoint end, SkScalar endRadius, + uintptr_t /* SkPoint* */ fourFloatsPtr, + SkScalar startRadius, SkScalar endRadius, uintptr_t cPtr, SkColorType colorType, uintptr_t /* SkScalar* */ pPtr, int count, SkTileMode mode, uint32_t flags, uintptr_t /* SkScalar* */ mPtr, sk_sp colorSpace)->sk_sp { + const SkPoint* startAndEnd = reinterpret_cast(fourFloatsPtr); const SkScalar* positions = reinterpret_cast(pPtr); OptionalMatrix localMatrix(mPtr); if (colorType == SkColorType::kRGBA_F32_SkColorType) { const SkColor4f* colors = reinterpret_cast(cPtr); - return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius, + return SkGradientShader::MakeTwoPointConical(startAndEnd[0], startRadius, + startAndEnd[1], endRadius, colors, colorSpace, positions, count, mode, flags, &localMatrix); } else if (colorType == SkColorType::kRGBA_8888_SkColorType) { const SkColor* colors = reinterpret_cast(cPtr); - return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius, + return SkGradientShader::MakeTwoPointConical(startAndEnd[0], startRadius, + startAndEnd[1], endRadius, colors, positions, count, mode, flags, &localMatrix); } @@ -1992,11 +2001,6 @@ EMSCRIPTEN_BINDINGS(Skia) { .field("alphaType", &SimpleImageInfo::alphaType) .field("colorSpace", &SimpleImageInfo::colorSpace); - // SkPoints can be represented by [x, y] - value_array("Point") - .element(&SkPoint::fX) - .element(&SkPoint::fY); - // SkPoint3s can be represented by [x, y, z] value_array("Point3") .element(&SkPoint3::fX) diff --git a/modules/canvaskit/externs.js b/modules/canvaskit/externs.js index ef1ef340b3..d3dcc782b6 100644 --- a/modules/canvaskit/externs.js +++ b/modules/canvaskit/externs.js @@ -95,8 +95,10 @@ var CanvasKit = { Animation: { prototype: { render: function() {}, + size: function() {}, }, _render: function() {}, + _size: function() {}, }, GrContext: { @@ -113,10 +115,12 @@ var CanvasKit = { seek: function() {}, seekFrame: function() {}, setColor: function() {}, + size: function() {}, }, _render: function() {}, _seek: function() {}, _seekFrame: function() {}, + _size: function() {}, }, Paragraph: { @@ -440,7 +444,6 @@ var CanvasKit = { /** @return {CanvasKit.Paint} */ copy: function() {}, getBlendMode: function() {}, - getColor: function() {}, getFilterQuality: function() {}, getStrokeCap: function() {}, getStrokeJoin: function() {}, @@ -461,6 +464,7 @@ var CanvasKit = { setStyle: function() {}, prototype: { + getColor: function() {}, setColor: function() {}, setColorComponents: function() {}, setColorInt: function() {}, @@ -488,13 +492,18 @@ var CanvasKit = { getUniformCount: function() {}, getUniformFloatCount: function() {}, getUniformName: function() {}, - setPosition: function() {}, setRate: function() {}, start: function() {}, update: function() {}, + prototype: { + setPosition: function() {}, + uniforms: function() {}, + }, + // private API (from C++ bindings) _uniformPtr: function() {}, + _setPosition: function() {}, }, Path: { @@ -510,7 +519,6 @@ var CanvasKit = { equals: function() {}, getBounds: function() {}, getFillType: function() {}, - getPoint: function() {}, isEmpty: function() {}, isVolatile: function() {}, reset: function() {}, @@ -537,6 +545,7 @@ var CanvasKit = { computeTightBounds: function() {}, cubicTo: function() {}, dash: function() {}, + getPoint: function() {}, lineTo: function() {}, moveTo: function() {}, offset: function() {}, @@ -572,6 +581,7 @@ var CanvasKit = { _computeTightBounds: function() {}, _cubicTo: function() {}, _dash: function() {}, + _getPoint: function() {}, _lineTo: function() {}, _moveTo: function() {}, _op: function() {}, @@ -1029,8 +1039,6 @@ CanvasKit.ColorBuilder.prototype.set = function() {}; CanvasKit.RuntimeEffect.prototype.makeShader = function() {}; CanvasKit.RuntimeEffect.prototype.makeShaderWithChildren = function() {}; -CanvasKit.ParticleEffect.prototype.uniforms = function() {}; - // Define StrokeOpts object var StrokeOpts = {}; StrokeOpts.prototype.width; diff --git a/modules/canvaskit/font.js b/modules/canvaskit/font.js index 6a70f99639..e4e88080e5 100644 --- a/modules/canvaskit/font.js +++ b/modules/canvaskit/font.js @@ -175,8 +175,8 @@ CanvasKit._extraInitializations.push(function() { // 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'](); + this._getBounds(_scratchFourFloatsAPtr); + var ta = _scratchFourFloatsA['toTypedArray'](); if (optionalOutputArray) { optionalOutputArray.set(ta); return optionalOutputArray; diff --git a/modules/canvaskit/interface.js b/modules/canvaskit/interface.js index 4bde9e8230..e6d42a8e2c 100644 --- a/modules/canvaskit/interface.js +++ b/modules/canvaskit/interface.js @@ -23,11 +23,11 @@ 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']; + _scratchFourFloatsA = CanvasKit.Malloc(Float32Array, 4); + _scratchFourFloatsAPtr = _scratchFourFloatsA['byteOffset']; - _scratchRect2 = CanvasKit.Malloc(Float32Array, 4); - _scratchRect2Ptr = _scratchRect2['byteOffset']; + _scratchFourFloatsB = CanvasKit.Malloc(Float32Array, 4); + _scratchFourFloatsBPtr = _scratchFourFloatsB['byteOffset']; _scratchIRect = CanvasKit.Malloc(Int32Array, 4); _scratchIRectPtr = _scratchIRect['byteOffset']; @@ -227,8 +227,8 @@ CanvasKit.onRuntimeInitialized = function() { // will be copied into that array. Otherwise, a new TypedArray will be allocated // and returned. CanvasKit.Path.prototype.computeTightBounds = function(optionalOutputArray) { - this._computeTightBounds(_scratchRectPtr); - var ta = _scratchRect['toTypedArray'](); + this._computeTightBounds(_scratchFourFloatsAPtr); + var ta = _scratchFourFloatsA['toTypedArray'](); if (optionalOutputArray) { optionalOutputArray.set(ta); return optionalOutputArray; @@ -252,8 +252,8 @@ CanvasKit.onRuntimeInitialized = function() { // will be copied into that array. Otherwise, a new TypedArray will be allocated // and returned. CanvasKit.Path.prototype.getBounds = function(optionalOutputArray) { - this._getBounds(_scratchRectPtr); - var ta = _scratchRect['toTypedArray'](); + this._getBounds(_scratchFourFloatsAPtr); + var ta = _scratchFourFloatsA['toTypedArray'](); if (optionalOutputArray) { optionalOutputArray.set(ta); return optionalOutputArray; @@ -574,21 +574,23 @@ CanvasKit.onRuntimeInitialized = function() { }; CanvasKit.Canvas.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); + copyRectToWasm(src, _scratchFourFloatsAPtr); + copyRectToWasm(dest, _scratchFourFloatsBPtr); + this._drawImageRect(img, _scratchFourFloatsAPtr, _scratchFourFloatsBPtr, paint, !!fastSample); }; CanvasKit.Canvas.prototype.drawImageRectCubic = function(img, src, dest, B, C, paint) { - var sPtr = copyRectToWasm(src, _scratchRectPtr); - var dPtr = copyRectToWasm(dest, _scratchRect2Ptr); - this._drawImageRectCubic(img, sPtr, dPtr, B, C, paint || null); + copyRectToWasm(src, _scratchFourFloatsAPtr); + copyRectToWasm(dest, _scratchFourFloatsBPtr); + this._drawImageRectCubic(img, _scratchFourFloatsAPtr, _scratchFourFloatsBPtr, B, C, + paint || null); }; CanvasKit.Canvas.prototype.drawImageRectOptions = function(img, src, dest, filter, mipmap, paint) { - var sPtr = copyRectToWasm(src, _scratchRectPtr); - var dPtr = copyRectToWasm(dest, _scratchRect2Ptr); - this._drawImageRectOptions(img, sPtr, dPtr, filter, mipmap, paint || null); + copyRectToWasm(src, _scratchFourFloatsAPtr); + copyRectToWasm(dest, _scratchFourFloatsBPtr); + this._drawImageRectOptions(img, _scratchFourFloatsAPtr, _scratchFourFloatsBPtr, filter, mipmap, + paint || null); }; CanvasKit.Canvas.prototype.drawOval = function(oval, paint) { @@ -628,11 +630,11 @@ CanvasKit.onRuntimeInitialized = function() { flags, optOutputRect) { var ctmPtr = copy3x3MatrixToWasm(ctm); var ok = this._getShadowLocalBounds(ctmPtr, path, zPlaneParams, lightPos, lightRadius, - flags, _scratchRectPtr); + flags, _scratchFourFloatsAPtr); if (!ok) { return null; } - var ta = _scratchRect['toTypedArray'](); + var ta = _scratchFourFloatsA['toTypedArray'](); if (optOutputRect) { optOutputRect.set(ta); return optOutputRect; @@ -753,6 +755,21 @@ CanvasKit.onRuntimeInitialized = function() { this._setColor(cPtr, colorSpace); }; + CanvasKit.Path.prototype.getPoint = function(idx, optionalOutput) { + // This will copy 2 floats into a space for 4 floats + this._getPoint(idx, _scratchFourFloatsAPtr); + var ta = _scratchFourFloatsA['toTypedArray'](); + if (optionalOutput) { + // We cannot call optionalOutput.set() because it is an error to call .set() with + // a source bigger than the destination. + optionalOutput[0] = ta[0]; + optionalOutput[1] = ta[1]; + return optionalOutput; + } + // Be sure to return a copy of just the first 2 values. + return ta.slice(0, 2); + }; + CanvasKit.PictureRecorder.prototype.beginRecording = function(bounds) { var bPtr = copyRectToWasm(bounds); return this._beginRecording(bPtr); @@ -812,7 +829,7 @@ CanvasKit.onRuntimeInitialized = function() { }; CanvasKit.Shader.MakeColor = function(color4f, colorSpace) { - colorSpace = colorSpace || null + colorSpace = colorSpace || null; var cPtr = copyColorToWasm(color4f); return CanvasKit.Shader._MakeColor(cPtr, colorSpace); }; @@ -829,7 +846,12 @@ CanvasKit.onRuntimeInitialized = function() { flags = flags || 0; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); - var lgs = CanvasKit.Shader._MakeLinearGradient(start, end, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr, + // Copy start and end to _scratchFourFloatsAPtr. + var startEndPts = _scratchFourFloatsA['toTypedArray'](); + startEndPts.set(start); + startEndPts.set(end, 2); + + var lgs = CanvasKit.Shader._MakeLinearGradient(_scratchFourFloatsAPtr, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr, cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace); freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors); @@ -838,14 +860,15 @@ CanvasKit.onRuntimeInitialized = function() { }; CanvasKit.Shader.MakeRadialGradient = function(center, radius, colors, pos, mode, localMatrix, flags, colorSpace) { - colorSpace = colorSpace || null + colorSpace = colorSpace || null; var cPtrInfo = copyFlexibleColorArray(colors); var posPtr = copy1dArray(pos, 'HEAPF32'); flags = flags || 0; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); - var rgs = CanvasKit.Shader._MakeRadialGradient(center, radius, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr, - cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace); + var rgs = CanvasKit.Shader._MakeRadialGradient(center[0], center[1], radius, cPtrInfo.colorPtr, + cPtrInfo.colorType, posPtr, cPtrInfo.count, mode, + flags, localMatrixPtr, colorSpace); freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors); pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos); @@ -853,7 +876,7 @@ CanvasKit.onRuntimeInitialized = function() { }; CanvasKit.Shader.MakeSweepGradient = function(cx, cy, colors, pos, mode, localMatrix, flags, startAngle, endAngle, colorSpace) { - colorSpace = colorSpace || null + colorSpace = colorSpace || null; var cPtrInfo = copyFlexibleColorArray(colors); var posPtr = copy1dArray(pos, 'HEAPF32'); flags = flags || 0; @@ -873,14 +896,19 @@ CanvasKit.onRuntimeInitialized = function() { CanvasKit.Shader.MakeTwoPointConicalGradient = function(start, startRadius, end, endRadius, colors, pos, mode, localMatrix, flags, colorSpace) { - colorSpace = colorSpace || null + colorSpace = colorSpace || null; var cPtrInfo = copyFlexibleColorArray(colors); var posPtr = copy1dArray(pos, 'HEAPF32'); flags = flags || 0; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); - var rgs = CanvasKit.Shader._MakeTwoPointConicalGradient( - start, startRadius, end, endRadius, cPtrInfo.colorPtr, cPtrInfo.colorType, + // Copy start and end to _scratchFourFloatsAPtr. + var startEndPts = _scratchFourFloatsA['toTypedArray'](); + startEndPts.set(start); + startEndPts.set(end, 2); + + var rgs = CanvasKit.Shader._MakeTwoPointConicalGradient(_scratchFourFloatsAPtr, + startRadius, endRadius, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr, cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace); freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors); @@ -892,8 +920,8 @@ CanvasKit.onRuntimeInitialized = function() { // will be copied into that array. Otherwise, a new TypedArray will be allocated // and returned. CanvasKit.Vertices.prototype.bounds = function(optionalOutputArray) { - this._bounds(_scratchRectPtr); - var ta = _scratchRect['toTypedArray'](); + this._bounds(_scratchFourFloatsAPtr); + var ta = _scratchFourFloatsA['toTypedArray'](); if (optionalOutputArray) { optionalOutputArray.set(ta); return optionalOutputArray; diff --git a/modules/canvaskit/memory.js b/modules/canvaskit/memory.js index 0158085ff2..ecabd5c371 100644 --- a/modules/canvaskit/memory.js +++ b/modules/canvaskit/memory.js @@ -84,11 +84,11 @@ var _scratch4x4Matrix; // the result from CanvasKit.Malloc var _scratchColorPtr = nullptr; var _scratchColor; // the result from CanvasKit.Malloc -var _scratchRect; -var _scratchRectPtr = nullptr; +var _scratchFourFloatsA; +var _scratchFourFloatsAPtr = nullptr; -var _scratchRect2; -var _scratchRect2Ptr = nullptr; +var _scratchFourFloatsB; +var _scratchFourFloatsBPtr = nullptr; var _scratchIRect; var _scratchIRectPtr = nullptr; @@ -354,7 +354,7 @@ function copyColorFromWasm(colorPtr) { // copies the given floats into the wasm heap as an SkRect. Unless a non-scratch pointer is // passed into ptr, callers do NOT need to free the returned pointer. function copyRectToWasm(fourFloats, ptr) { - return copy1dArray(fourFloats, 'HEAPF32', ptr || _scratchRectPtr); + return copy1dArray(fourFloats, 'HEAPF32', ptr || _scratchFourFloatsAPtr); } // copies the given ints into the wasm heap as an SkIRect. Unless a non-scratch pointer is diff --git a/modules/canvaskit/particles.js b/modules/canvaskit/particles.js index b7c27b3753..1ee6e07b43 100644 --- a/modules/canvaskit/particles.js +++ b/modules/canvaskit/particles.js @@ -63,6 +63,9 @@ CanvasKit._extraInitializations.push(function() { return new Float32Array(); } return new Float32Array(CanvasKit.HEAPU8.buffer, fptr, numFloats); - } + }; + CanvasKit.ParticleEffect.setPosition = function(pos) { + this._setPosition(pos[0], pos[1]); + }; }); diff --git a/modules/canvaskit/particles_bindings.cpp b/modules/canvaskit/particles_bindings.cpp index 0e2ee06858..e57ccc2433 100644 --- a/modules/canvaskit/particles_bindings.cpp +++ b/modules/canvaskit/particles_bindings.cpp @@ -128,7 +128,10 @@ EMSCRIPTEN_BINDINGS(Particles) { su = fromUniform(info->fUniforms[i]); return su; })) - .function("setPosition", select_overload(&SkParticleEffect::setPosition)) + .function("_setPosition", optional_override([](SkParticleEffect& self, + SkScalar x, SkScalar y)->void { + self.setPosition({x, y}); + })) .function("setRate", select_overload(&SkParticleEffect::setRate)) .function("start", select_overload(&SkParticleEffect::start)) .function("update", select_overload(&SkParticleEffect::update)); diff --git a/modules/canvaskit/skottie.js b/modules/canvaskit/skottie.js index 2d5f3f60bd..f2a8c38f01 100644 --- a/modules/canvaskit/skottie.js +++ b/modules/canvaskit/skottie.js @@ -64,40 +64,70 @@ CanvasKit.MakeManagedAnimation = function(json, assets, prop_filter_prefix) { CanvasKit._extraInitializations.push(function() { CanvasKit.Animation.prototype.render = function(canvas, dstRect) { - var dPtr = copyRectToWasm(dstRect); - this._render(canvas, dPtr); - } + copyRectToWasm(dstRect, _scratchFourFloatsAPtr); + this._render(canvas, _scratchFourFloatsAPtr); + }; + + CanvasKit.Animation.prototype.size = function(optSize) { + // This will copy 2 floats into a space for 4 floats + this._size(_scratchFourFloatsAPtr); + var ta = _scratchFourFloatsA['toTypedArray'](); + if (optSize) { + // We cannot call optSize.set() because it is an error to call .set() with + // a source bigger than the destination. + optSize[0] = ta[0]; + optSize[1] = ta[1]; + return optSize; + } + // Be sure to return a copy of just the first 2 values. + return ta.slice(0, 2); + }; if (CanvasKit.ManagedAnimation) { CanvasKit.ManagedAnimation.prototype.render = function(canvas, dstRect) { - var dPtr = copyRectToWasm(dstRect); - this._render(canvas, dPtr); - } + copyRectToWasm(dstRect, _scratchFourFloatsAPtr); + this._render(canvas, _scratchFourFloatsAPtr); + }; CanvasKit.ManagedAnimation.prototype.seek = function(t, optDamageRect) { - this._seek(t, _scratchRectPtr); - var ta = _scratchRect['toTypedArray'](); + this._seek(t, _scratchFourFloatsAPtr); + var ta = _scratchFourFloatsA['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'](); + this._seekFrame(frame, _scratchFourFloatsAPtr); + var ta = _scratchFourFloatsA['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); - } + }; + + CanvasKit.ManagedAnimation.prototype.size = function(optSize) { + // This will copy 2 floats into a space for 4 floats + this._size(_scratchFourFloatsAPtr); + var ta = _scratchFourFloatsA['toTypedArray'](); + if (optSize) { + // We cannot call optSize.set() because it is an error to call .set() with + // a source bigger than the destination. + optSize[0] = ta[0]; + optSize[1] = ta[1]; + return optSize; + } + // Be sure to return a copy of just the first 2 values. + return ta.slice(0, 2); + }; } diff --git a/modules/canvaskit/skottie_bindings.cpp b/modules/canvaskit/skottie_bindings.cpp index 10f628b494..01b9ade75e 100644 --- a/modules/canvaskit/skottie_bindings.cpp +++ b/modules/canvaskit/skottie_bindings.cpp @@ -197,7 +197,11 @@ EMSCRIPTEN_BINDINGS(Skottie) { .function("version", optional_override([](skottie::Animation& self)->std::string { return std::string(self.version().c_str()); })) - .function("size" , &skottie::Animation::size) + .function("_size", optional_override([](skottie::Animation& self, + uintptr_t /* float* */ oPtr)->void { + SkSize* output = reinterpret_cast(oPtr); + *output = self.size(); + })) .function("duration", &skottie::Animation::duration) .function("fps" , &skottie::Animation::fps) .function("seek", optional_override([](skottie::Animation& self, SkScalar t)->void { @@ -221,7 +225,11 @@ EMSCRIPTEN_BINDINGS(Skottie) { class_("ManagedAnimation") .smart_ptr>("sk_sp") .function("version" , &ManagedAnimation::version) - .function("size" , &ManagedAnimation::size) + .function("_size", optional_override([](ManagedAnimation& self, + uintptr_t /* float* */ oPtr)->void { + SkSize* output = reinterpret_cast(oPtr); + *output = self.size(); + })) .function("duration" , &ManagedAnimation::duration) .function("fps" , &ManagedAnimation::fps) .function("_render", optional_override([](ManagedAnimation& self, SkCanvas* canvas, diff --git a/modules/canvaskit/tests/path.spec.js b/modules/canvaskit/tests/path.spec.js index 8abf0e6091..613926a858 100644 --- a/modules/canvaskit/tests/path.spec.js +++ b/modules/canvaskit/tests/path.spec.js @@ -233,6 +233,25 @@ describe('Path Behavior', () => { CanvasKit.Free(mWeights); }); + it('can retrieve points from a path', () => { + const path = new CanvasKit.Path(); + path.addRect([10, 15, 20, 25]); + + let pt = path.getPoint(0); + expect(pt[0]).toEqual(10); + expect(pt[1]).toEqual(15); + + path.getPoint(2, pt); + expect(pt[0]).toEqual(20); + expect(pt[1]).toEqual(25); + + path.getPoint(1000, pt); // off the end returns (0, 0) as per the docs. + expect(pt[0]).toEqual(0); + expect(pt[1]).toEqual(0); + + path.delete(); + }); + gm('offset_path', (canvas) => { const path = starPath(CanvasKit); diff --git a/modules/canvaskit/tests/skottie.spec.js b/modules/canvaskit/tests/skottie.spec.js index afe7f25b2b..826e351cc1 100644 --- a/modules/canvaskit/tests/skottie.spec.js +++ b/modules/canvaskit/tests/skottie.spec.js @@ -15,7 +15,7 @@ describe('Skottie behavior', () => { }); const expectArrayCloseTo = (a, b, precision) => { - precision = precision || 14 // digits of precision in base 10 + precision = precision || 14; // digits of precision in base 10 expect(a.length).toEqual(b.length); for (let i=0; i { expect(animation).toBeTruthy(); const bounds = CanvasKit.LTRBRect(0, 0, 500, 500); + const size = animation.size(); + expectArrayCloseTo(size, Float32Array.of(800, 600), 4); + canvas.clear(CanvasKit.WHITE); animation.render(canvas, bounds);