[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 <nifong@google.com>
This commit is contained in:
Kevin Lubick 2021-02-02 08:18:11 -05:00
parent b97a9de755
commit ed96264ad7
14 changed files with 220 additions and 96 deletions

View File

@ -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`,

View File

@ -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);

View File

@ -837,7 +837,7 @@ export interface Particles extends EmbindObject<Particles> {
* 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<Path> {
* 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<SkottieAnimation> {
*/
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.
*/

View File

@ -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<SkPoint*>(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<SkColorSpace> colorSpace)->sk_sp<SkShader> {
SkPoint points[] = { start, end };
const SkPoint* points = reinterpret_cast<const SkPoint*>(fourFloatsPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(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<const SkColor4f*>(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<const SkColor*>(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<SkColorSpace> colorSpace)->sk_sp<SkShader> {
const SkPoint* startAndEnd = reinterpret_cast<const SkPoint*>(fourFloatsPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
if (colorType == SkColorType::kRGBA_F32_SkColorType) {
const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(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<const SkColor*>(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<SkPoint>("Point")
.element(&SkPoint::fX)
.element(&SkPoint::fY);
// SkPoint3s can be represented by [x, y, z]
value_array<SkPoint3>("Point3")
.element(&SkPoint3::fX)

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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]);
};
});

View File

@ -128,7 +128,10 @@ EMSCRIPTEN_BINDINGS(Particles) {
su = fromUniform(info->fUniforms[i]);
return su;
}))
.function("setPosition", select_overload<void (SkPoint)>(&SkParticleEffect::setPosition))
.function("_setPosition", optional_override([](SkParticleEffect& self,
SkScalar x, SkScalar y)->void {
self.setPosition({x, y});
}))
.function("setRate", select_overload<void (float)>(&SkParticleEffect::setRate))
.function("start", select_overload<void (double, bool)>(&SkParticleEffect::start))
.function("update", select_overload<void (double)>(&SkParticleEffect::update));

View File

@ -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);
};
}

View File

@ -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<SkSize*>(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>("ManagedAnimation")
.smart_ptr<sk_sp<ManagedAnimation>>("sk_sp<ManagedAnimation>")
.function("version" , &ManagedAnimation::version)
.function("size" , &ManagedAnimation::size)
.function("_size", optional_override([](ManagedAnimation& self,
uintptr_t /* float* */ oPtr)->void {
SkSize* output = reinterpret_cast<SkSize*>(oPtr);
*output = self.size();
}))
.function("duration" , &ManagedAnimation::duration)
.function("fps" , &ManagedAnimation::fps)
.function("_render", optional_override([](ManagedAnimation& self, SkCanvas* canvas,

View File

@ -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);

View File

@ -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<a.length; i++) {
expect(a[i]).toBeCloseTo(b[i], precision);
@ -41,6 +41,9 @@ describe('Skottie behavior', () => {
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);