[canvaskit] Handle passing matrix values into CK ourselves.

This means we take DOMMatrix everywhere now.

This reduced the *_makeShader benchmark by ~25% (4 us -> 3 us)
and cleaned up several callsites.

Trimming this down saves ~3kb in uncompressed code size.

Change-Id: Ie677c7ebb7bc97ed8cd4d4851a039b78b6f8079d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/281018
Reviewed-by: Mike Reed <reed@google.com>
This commit is contained in:
Kevin Lubick 2020-04-02 15:24:15 -04:00
parent 5384f4036f
commit 6bffe39c98
9 changed files with 191 additions and 237 deletions

View File

@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Support for DOMMatrix in addition to the SkMatrix currently supported by some APIs. [WIP]
- Support for DOMMatrix on all APIs that take SkMatrix (i.e. arrays or Float32Arrays of length 9).
### Removed
- Previously deprecated functions MakeSkDashPathEffect, MakeLinearGradientShader,

View File

@ -87,26 +87,15 @@
sk_sp<SkFontMgr> SkFontMgr_New_Custom_Data(const uint8_t** datas, const size_t* sizes, int n);
#endif
// 3x3 Matrices
struct SimpleMatrix {
SkScalar scaleX, skewX, transX;
SkScalar skewY, scaleY, transY;
SkScalar pers0, pers1, pers2;
struct OptionalMatrix : SkMatrix {
OptionalMatrix(uintptr_t mPtr) {
if (mPtr) {
const SkScalar* nineMatrixValues = reinterpret_cast<const SkScalar*>(mPtr);
this->set9(nineMatrixValues);
}
}
};
SkMatrix toSkMatrix(const SimpleMatrix& sm) {
return SkMatrix::MakeAll(sm.scaleX, sm.skewX , sm.transX,
sm.skewY , sm.scaleY, sm.transY,
sm.pers0 , sm.pers1 , sm.pers2);
}
SimpleMatrix toSimpleSkMatrix(const SkMatrix& sm) {
SimpleMatrix m {sm[0], sm[1], sm[2],
sm[3], sm[4], sm[5],
sm[6], sm[7], sm[8]};
return m;
}
// Experimental 4x4 matrices, also represented in JS with arrays.
struct SimpleM44 {
SkScalar m0, m1, m2, m3;
@ -803,29 +792,16 @@ EMSCRIPTEN_BINDINGS(Skia) {
}), allow_raw_pointers());
function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags)->sk_sp<SkShader> {
int count, SkTileMode mode, uint32_t flags,
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
SkPoint points[] = { start, end };
// See comment above for uintptr_t explanation
const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
// TODO(nifong): do not assume color space. Support and test wide gamut color gradients
return SkGradientShader::MakeLinear(points, colors, SkColorSpace::MakeSRGB(), positions, count,
mode, flags, nullptr);
}), allow_raw_pointers());
function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags,
const SimpleMatrix& lm)->sk_sp<SkShader> {
SkPoint points[] = { start, end };
// See comment above for uintptr_t explanation
const SkColor4f* colors = reinterpret_cast<const SkColor4f*> (cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
SkMatrix localMatrix = toSkMatrix(lm);
return SkGradientShader::MakeLinear(points, colors, SkColorSpace::MakeSRGB(), positions, count,
mode, flags, &localMatrix);
mode, flags, &localMatrix);
}), allow_raw_pointers());
#ifdef SK_SERIALIZE_SKP
function("_MakeSkPicture", optional_override([](uintptr_t /* unint8_t* */ dPtr,
@ -837,25 +813,14 @@ EMSCRIPTEN_BINDINGS(Skia) {
return SkPicture::MakeFromData(data.get(), nullptr);
}), allow_raw_pointers());
#endif
function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkColor4f* colors = reinterpret_cast<const SkColor4f*> (cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
return SkGradientShader::MakeRadial(center, radius, colors, SkColorSpace::MakeSRGB(), positions, count,
mode, flags, nullptr);
}), allow_raw_pointers());
function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags,
const SimpleMatrix& lm)->sk_sp<SkShader> {
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkColor4f* colors = reinterpret_cast<const SkColor4f*> (cPtr);
const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
SkMatrix localMatrix = toSkMatrix(lm);
OptionalMatrix localMatrix(mPtr);
return SkGradientShader::MakeRadial(center, radius, colors, SkColorSpace::MakeSRGB(), positions, count,
mode, flags, &localMatrix);
}), allow_raw_pointers());
@ -864,61 +829,25 @@ EMSCRIPTEN_BINDINGS(Skia) {
int count, SkTileMode mode,
SkScalar startAngle, SkScalar endAngle,
uint32_t flags,
const SimpleMatrix& lm)->sk_sp<SkShader> {
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkColor4f* colors = reinterpret_cast<const SkColor4f*> (cPtr);
const SkColor4f* colors = reinterpret_cast<const SkColor4f*>(cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
SkMatrix localMatrix = toSkMatrix(lm);
OptionalMatrix localMatrix(mPtr);
return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count,
mode, startAngle, endAngle, flags,
&localMatrix);
}), allow_raw_pointers());
function("_MakeSweepGradientShader", optional_override([](SkScalar cx, SkScalar cy,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count, uint32_t flags,
const SimpleMatrix& lm)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkColor4f* colors = reinterpret_cast<const SkColor4f*> (cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
SkMatrix localMatrix = toSkMatrix(lm);
return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count,
flags, &localMatrix);
}), allow_raw_pointers());
function("_MakeSweepGradientShader", optional_override([](SkScalar cx, SkScalar cy,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkColor4f* colors = reinterpret_cast<const SkColor4f*> (cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count);
}), allow_raw_pointers());
function("_MakeTwoPointConicalGradientShader", optional_override([](
SkPoint start, SkScalar startRadius,
SkPoint end, SkScalar endRadius,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags)->sk_sp<SkShader> {
SkPoint start, SkScalar startRadius,
SkPoint end, SkScalar endRadius,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags,
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkColor4f* colors = reinterpret_cast<const SkColor4f*> (cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
colors, SkColorSpace::MakeSRGB(), positions, count, mode,
flags, nullptr);
}), allow_raw_pointers());
function("_MakeTwoPointConicalGradientShader", optional_override([](
SkPoint start, SkScalar startRadius,
SkPoint end, SkScalar endRadius,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags,
const SimpleMatrix& lm)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkColor4f* colors = reinterpret_cast<const SkColor4f*> (cPtr);
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
SkMatrix localMatrix = toSkMatrix(lm);
OptionalMatrix localMatrix(mPtr);
return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
colors, SkColorSpace::MakeSRGB(), positions, count, mode,
flags, &localMatrix);
@ -972,8 +901,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
self.clipRRect(toRRect(r), op, doAntiAlias);
}))
.function("clipRect", select_overload<void (const SkRect&, SkClipOp, bool)>(&SkCanvas::clipRect))
.function("concat", optional_override([](SkCanvas& self, const SimpleMatrix& m) {
self.concat(toSkMatrix(m));
.function("_concat", optional_override([](SkCanvas& self, uintptr_t /* SkScalar* */ mPtr) {
// See comment above for uintptr_t explanation
OptionalMatrix localMatrix(mPtr);
self.concat(localMatrix);
}))
.function("drawArc", &SkCanvas::drawArc)
// _drawAtlas takes an SkColor, unlike most private functions handling color.
@ -1067,9 +998,17 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("drawVertices", select_overload<void (const sk_sp<SkVertices>&, SkBlendMode, const SkPaint&)>(&SkCanvas::drawVertices))
.function("flush", &SkCanvas::flush)
.function("getSaveCount", &SkCanvas::getSaveCount)
.function("getTotalMatrix", optional_override([](const SkCanvas& self)->SimpleMatrix {
// We allocate room for the matrix from the JS side and free it there so as to not have
// 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<SkScalar*>(mPtr);
if (!nineMatrixValues) {
return; // matrix cannot be null
}
SkMatrix m = self.getTotalMatrix();
return toSimpleSkMatrix(m);
m.get9(nineMatrixValues);
}))
.function("makeSurface", optional_override([](SkCanvas& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
return self.makeSurface(toSkImageInfo(sii), nullptr);
@ -1274,16 +1213,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("width", &SkImage::width)
.function("_encodeToData", select_overload<sk_sp<SkData>()const>(&SkImage::encodeToData))
.function("_encodeToDataWithFormat", select_overload<sk_sp<SkData>(SkEncodedImageFormat encodedImageFormat, int quality)const>(&SkImage::encodeToData))
// Allow localMatrix to be optional, so we have 2 declarations of these shaders
.function("_makeShader", optional_override([](sk_sp<SkImage> self,
SkTileMode tx, SkTileMode ty)->sk_sp<SkShader> {
return self->makeShader(tx, ty, nullptr);
}), allow_raw_pointers())
.function("_makeShader", optional_override([](sk_sp<SkImage> self,
SkTileMode tx, SkTileMode ty,
const SimpleMatrix& lm)->sk_sp<SkShader> {
SkMatrix localMatrix = toSkMatrix(lm);
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
OptionalMatrix localMatrix(mPtr);
return self->makeShader(tx, ty, &localMatrix);
}), allow_raw_pointers())
.function("_readPixels", optional_override([](sk_sp<SkImage> self,
@ -1309,9 +1242,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
return SkImageFilters::ColorFilter(cf, input);
}))
.class_function("MakeCompose", &SkImageFilters::Compose)
.class_function("MakeMatrixTransform", optional_override([](SimpleMatrix sm, SkFilterQuality fq,
.class_function("_MakeMatrixTransform", optional_override([](uintptr_t /* SkScalar* */ mPtr, SkFilterQuality fq,
sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
return SkImageFilters::MatrixTransform(toSkMatrix(sm), fq, input);
OptionalMatrix matr(mPtr);
return SkImageFilters::MatrixTransform(matr, fq, input);
}));
class_<SkMaskFilter>("SkMaskFilter")
@ -1507,21 +1441,18 @@ EMSCRIPTEN_BINDINGS(Skia) {
}
return effect;
}))
.function("_makeShader", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque)->sk_sp<SkShader> {
.function("_makeShader", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque,
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
void* inputData = reinterpret_cast<void*>(fPtr);
sk_sp<SkData> inputs = SkData::MakeFromMalloc(inputData, fLen);
return self.makeShader(inputs, nullptr, 0, nullptr, isOpaque);
}))
.function("_makeShader", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque, SimpleMatrix sm)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
void* inputData = reinterpret_cast<void*>(fPtr);
sk_sp<SkData> inputs = SkData::MakeFromMalloc(inputData, fLen);
auto m = toSkMatrix(sm);
return self.makeShader(inputs, nullptr, 0, &m, isOpaque);
OptionalMatrix localMatrix(mPtr);
return self.makeShader(inputs, nullptr, 0, &localMatrix, isOpaque);
}))
.function("_makeShaderWithChildren", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque,
uintptr_t /** SkShader*[] */cPtrs, size_t cLen)->sk_sp<SkShader> {
uintptr_t /** SkShader*[] */cPtrs, size_t cLen,
uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
void* inputData = reinterpret_cast<void*>(fPtr);
sk_sp<SkData> inputs = SkData::MakeFromMalloc(inputData, fLen);
@ -1533,25 +1464,8 @@ EMSCRIPTEN_BINDINGS(Skia) {
// so we want to ref the new sk_sp so makeShader doesn't clean it up.
children[i] = sk_ref_sp<SkShader>(childrenPtrs[i]);
}
auto s = self.makeShader(inputs, children, cLen, nullptr, isOpaque);
delete[] children;
return s;
}))
.function("_makeShaderWithChildren", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque,
uintptr_t /** SkShader*[] */cPtrs, size_t cLen, SimpleMatrix sm)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
void* inputData = reinterpret_cast<void*>(fPtr);
sk_sp<SkData> inputs = SkData::MakeFromMalloc(inputData, fLen);
sk_sp<SkShader>* children = new sk_sp<SkShader>[cLen];
SkShader** childrenPtrs = reinterpret_cast<SkShader**>(cPtrs);
for (size_t i = 0; i < cLen; i++) {
// This bare pointer was already part of an sk_sp (owned outside of here),
// so we want to ref the new sk_sp so makeShader doesn't clean it up.
children[i] = sk_ref_sp<SkShader>(childrenPtrs[i]);
}
auto m = toSkMatrix(sm);
auto s = self.makeShader(inputs, children, cLen, &m, isOpaque);
OptionalMatrix localMatrix(mPtr);
auto s = self.makeShader(inputs, children, cLen, &localMatrix, isOpaque);
delete[] children;
return s;
}));
@ -1827,24 +1741,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.field("cap", &StrokeOpts::cap)
.field("precision", &StrokeOpts::precision);
// Allows clients to supply a 1D array of 9 elements and the bindings
// will automatically turn it into a 3x3 2D matrix.
// e.g. path.transform([0,1,2,3,4,5,6,7,8])
// This is likely simpler for the client than exposing SkMatrix
// directly and requiring them to do a lot of .delete().
value_array<SimpleMatrix>("SkMatrix")
.element(&SimpleMatrix::scaleX)
.element(&SimpleMatrix::skewX)
.element(&SimpleMatrix::transX)
.element(&SimpleMatrix::skewY)
.element(&SimpleMatrix::scaleY)
.element(&SimpleMatrix::transY)
.element(&SimpleMatrix::pers0)
.element(&SimpleMatrix::pers1)
.element(&SimpleMatrix::pers2);
value_array<SimpleM44>("SkM44")
.element(&SimpleM44::m0).element(&SimpleM44::m1).element(&SimpleM44::m2).element(&SimpleM44::m3)
.element(&SimpleM44::m4).element(&SimpleM44::m5).element(&SimpleM44::m6).element(&SimpleM44::m7)

View File

@ -158,7 +158,6 @@ var CanvasKit = {
clipPath: function() {},
clipRRect: function() {},
clipRect: function() {},
concat: function() {},
drawAnimatedImage: function() {},
drawArc: function() {},
drawCircle: function() {},
@ -182,7 +181,6 @@ var CanvasKit = {
drawVertices: function() {},
flush: function() {},
getSaveCount: function() {},
getTotalMatrix: function() {},
makeSurface: function() {},
restore: function() {},
restoreToCount: function() {},
@ -194,9 +192,11 @@ var CanvasKit = {
translate: function() {},
// private API
_concat: function() {},
_drawAtlas: function() {},
_drawPoints: function() {},
_drawSimpleText: function() {},
_getTotalMatrix: function() {},
_readPixels: function() {},
_writePixels: function() {},
delete: function() {},
@ -278,6 +278,9 @@ var CanvasKit = {
MakeColorFilter: function() {},
MakeCompose: function() {},
MakeMatrixTransform: function() {},
// private API
_MakeMatrixTransform: function() {},
},
// These are defined in interface.js
@ -834,9 +837,11 @@ CanvasKit.SkSurface.prototype.captureFrameAsSkPicture = function() {};
CanvasKit.SkImage.prototype.encodeToData = function() {};
CanvasKit.SkImage.prototype.makeShader = function() {};
CanvasKit.SkCanvas.prototype.concat = function() {};
CanvasKit.SkCanvas.prototype.drawAtlas = function() {};
CanvasKit.SkCanvas.prototype.drawPoints = function() {};
CanvasKit.SkCanvas.prototype.drawText = function() {};
CanvasKit.SkCanvas.prototype.getTotalMatrix = function() {};
/** @return {Uint8Array} */
CanvasKit.SkCanvas.prototype.readPixels = function() {};
CanvasKit.SkCanvas.prototype.writePixels = function() {};

View File

@ -259,6 +259,43 @@ function copy3dArray(arr, dest, ptr) {
return ptr;
}
var defaultPerspective = Float32Array.of(0, 0, 1);
// Copies the given DOMMatrix/Array/TypedArray to the CanvasKit heap and
// returns a pointer to the memory. This memory is a float* of length 9.
// If the passed in matrix is null/undefined, we return 0 (nullptr). All calls
// on the C++ side should check for nullptr where appropriate. It is generally
// the responsibility of the JS side code to call CanvasKit._free on the
// allocated memory before returning to the user code.
function copy3x3MatrixToWasm(matr) {
if (!matr) {
return nullptr;
}
var mPtr = CanvasKit._malloc(9 * 4); // 9 matrix scalars, each at 4 bytes.
if (matr.length) {
// TODO(kjlubick): Downsample a 16 length (4x4 matrix)
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.
CanvasKit.HEAPF32.set(matr, mPtr / 4);
if (matr.length === 6) {
CanvasKit.HEAPF32.set(defaultPerspective, 6 + mPtr / 4);
}
} else {
// Try as if it's a DOMMatrix. Reminder that DOMMatrix lists their
// column first, then row.
var floats = Float32Array.of(
matr.m11, matr.m21, matr.m41,
matr.m12, matr.m22, matr.m42,
matr.m14, matr.m24, matr.m44);
// have to divide the pointer by 4 to "cast" it from bytes to float.
CanvasKit.HEAPF32.set(floats, mPtr / 4);
}
return mPtr;
}
// Caching the Float32Arrays can save having to reallocate them
// over and over again.
var Float32ArrayCache = {};

View File

@ -806,12 +806,10 @@ CanvasKit.onRuntimeInitialized = function() {
}
CanvasKit.SkImage.prototype.makeShader = function(xTileMode, yTileMode, localMatrix) {
if (localMatrix) {
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix);
return this._makeShader(xTileMode, yTileMode, localMatrix);
} else {
return this._makeShader(xTileMode, yTileMode);
}
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var shader = this._makeShader(xTileMode, yTileMode, localMatrixPtr);
localMatrixPtr && CanvasKit._free(localMatrixPtr);
return shader;
}
CanvasKit.SkImage.prototype.readPixels = function(imageInfo, srcX, srcY) {
@ -852,13 +850,18 @@ CanvasKit.onRuntimeInitialized = function() {
// Free the allocated pixels in the WASM memory
CanvasKit._free(pPtr);
return retVal;
}
CanvasKit.SkCanvas.prototype.concat = function(matr) {
var matrPtr = copy3x3MatrixToWasm(matr);
this._concat(matrPtr);
matrPtr && CanvasKit._free(matrPtr);
}
// atlas is an SkImage, e.g. from CanvasKit.MakeImageFromEncoded
// srcRects and dstXforms should be CanvasKit.SkRectBuilder and CanvasKit.RSXFormBuilder
// or just arrays of floats in groups of 4.
// colors, if provided, should be a CanvasKit.SkColorBuilder or array of Canvaskit.SimpleColor4f
// colors, if provided, should be a CanvasKit.SkColorBuilder or array of CanvasKit.SimpleColor4f
CanvasKit.SkCanvas.prototype.drawAtlas = function(atlas, srcRects, dstXforms, paint,
/*optional*/ blendMode, colors) {
if (!atlas || !paint || !srcRects || !dstXforms) {
@ -887,14 +890,14 @@ CanvasKit.onRuntimeInitialized = function() {
dstXformPtr = copy1dArray(dstXforms, CanvasKit.HEAPF32);
}
var colorPtr = 0; // enscriptem doesn't like undefined for nullptr
var colorPtr = nullptr;
if (colors) {
if (colors.build) {
colorPtr = colors.build();
} else {
if (!isCanvasKitColor(colors[0])) {
SkDebug('DrawAtlas color argument expected to be CanvasKit.SkRectBuilder or array of ' +
'Canvaskit.SimpleColor4f, but got '+colors);
'CanvasKit.SimpleColor4f, but got '+colors);
return;
}
// convert here
@ -937,6 +940,20 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit._free(ptr);
}
CanvasKit.SkCanvas.prototype.getTotalMatrix = function() {
var matrPtr = CanvasKit._malloc(9 * 4); // allocate space for the matrix
// _getTotalMatrix will copy the values into the pointer.
this._getTotalMatrix(matrPtr);
// read them out into an array. TODO(kjlubick): If we change SkMatrix to be
// typedArrays, then we should return a typed array here too.
var rv = new Array(9);
for (var i = 0; i < 9; i++) {
rv[i] = CanvasKit.HEAPF32[matrPtr/4 + i]; // divide by 4 to "cast" to float.
}
CanvasKit._free(matrPtr);
return rv;
}
// returns Uint8Array
CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType,
colorType, dstRowBytes) {
@ -995,8 +1012,7 @@ CanvasKit.onRuntimeInitialized = function() {
// colorMatrix is an SkColorMatrix (e.g. Float32Array of length 20)
CanvasKit.SkColorFilter.MakeMatrix = function(colorMatrix) {
if (!colorMatrix || colorMatrix.length !== 20) {
SkDebug('ignoring invalid color matrix');
return;
throw 'invalid color matrix';
}
var fptr = copy1dArray(colorMatrix, CanvasKit.HEAPF32);
// We know skia memcopies the floats, so we can free our memory after the call returns.
@ -1005,6 +1021,14 @@ CanvasKit.onRuntimeInitialized = function() {
return m;
}
CanvasKit.SkImageFilter.MakeMatrixTransform = function(matr, filterQuality, input) {
var matrPtr = copy3x3MatrixToWasm(matr);
var imgF = CanvasKit.SkImageFilter._MakeMatrixTransform(matrPtr, filterQuality, input);
matrPtr && CanvasKit._free(matrPtr);
return imgF;
}
CanvasKit.SkSurface.prototype.captureFrameAsSkPicture = function(drawFrame) {
// Set up SkPictureRecorder
var spr = new CanvasKit.SkPictureRecorder();
@ -1066,43 +1090,16 @@ CanvasKit.onRuntimeInitialized = function() {
return dpe;
}
function prepare3x3MatrixForGoingToWasm(matr) {
if (!matr) {
return [
1, 0, 0,
0, 1, 0,
0, 0, 1,
];
}
if (Array.isArray(matr)) {
// Add perspective args if not provided.
if (matr.length === 6) {
matr.push(0, 0, 1);
}
return matr;
}
// DOMMatrix, which has their values in column major order.
return [
matr.a, matr.c, matr.e,
matr.b, matr.d, matr.f,
0, 0, 1,
];
}
CanvasKit.SkShader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags) {
var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
if (localMatrix) {
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix);
var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
colors.length, mode, flags, localMatrix);
} else {
var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
colors.length, mode, flags);
}
var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
colors.length, mode, flags, localMatrixPtr);
localMatrixPtr && CanvasKit._free(localMatrixPtr);
CanvasKit._free(colorPtr);
CanvasKit._free(posPtr);
return lgs;
@ -1112,16 +1109,12 @@ CanvasKit.onRuntimeInitialized = function() {
var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
if (localMatrix) {
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix);
var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
colors.length, mode, flags, localMatrix);
} else {
var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
colors.length, mode, flags);
}
var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
colors.length, mode, flags, localMatrixPtr);
localMatrixPtr && CanvasKit._free(localMatrixPtr);
CanvasKit._free(colorPtr);
CanvasKit._free(posPtr);
return rgs;
@ -1133,35 +1126,31 @@ CanvasKit.onRuntimeInitialized = function() {
flags = flags || 0;
startAngle = startAngle || 0;
endAngle = endAngle || 360;
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix);
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var sgs = CanvasKit._MakeSweepGradientShader(cx, cy, colorPtr, posPtr,
colors.length, mode,
startAngle, endAngle, flags,
localMatrix);
colors.length, mode,
startAngle, endAngle, flags,
localMatrixPtr);
localMatrixPtr && CanvasKit._free(localMatrixPtr);
CanvasKit._free(colorPtr);
CanvasKit._free(posPtr);
return sgs;
}
CanvasKit.SkShader.MakeTwoPointConicalGradient = function(start, startRadius, end, endRadius,
colors, pos, mode, localMatrix, flags) {
colors, pos, mode, localMatrix, flags) {
var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
if (localMatrix) {
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix);
var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
start, startRadius, end, endRadius,
colorPtr, posPtr, colors.length, mode, flags, localMatrix);
} else {
var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
start, startRadius, end, endRadius,
colorPtr, posPtr, colors.length, mode, flags);
}
colorPtr, posPtr, colors.length, mode, flags, localMatrixPtr);
localMatrixPtr && CanvasKit._free(localMatrixPtr);
CanvasKit._free(colorPtr);
CanvasKit._free(posPtr);
return rgs;

View File

@ -128,4 +128,4 @@ describe('DOMMatrix', () => {
benchmarkAndReport('dommatrix_makeShader', setup, test, teardown);
});
});
});

View File

@ -1,20 +1,23 @@
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() {
CanvasKit.SkRuntimeEffect.prototype.makeShader = function(floats, isOpaque, matrix) {
CanvasKit.SkRuntimeEffect.prototype.makeShader = function(floats, isOpaque, localMatrix) {
// We don't need to free these floats because they will become owned by the shader.
var fptr = copy1dArray(floats, CanvasKit.HEAPF32);
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
// Our array has 4 bytes per float, so be sure to account for that before
// sending it over the wire.
if (!matrix) {
return this._makeShader(fptr, floats.length * 4, !!isOpaque);
}
return this._makeShader(fptr, floats.length * 4, !!isOpaque, matrix);
var rts = this._makeShader(fptr, floats.length * 4, !!isOpaque, localMatrixPtr);
localMatrixPtr && CanvasKit._free(localMatrixPtr);
return rts;
}
// childrenWithShaders is an array of other shaders (e.g. SkImage.makeShader())
CanvasKit.SkRuntimeEffect.prototype.makeShaderWithChildren = function(floats, isOpaque, childrenShaders, matrix) {
CanvasKit.SkRuntimeEffect.prototype.makeShaderWithChildren = function(floats, isOpaque, childrenShaders, localMatrix) {
// We don't need to free these floats because they will become owned by the shader.
var fptr = copy1dArray(floats, CanvasKit.HEAPF32);
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var barePointers = [];
for (var i = 0; i<childrenShaders.length;i++) {
for (var i = 0; i < childrenShaders.length; i++) {
// childrenShaders are emscriptens smart pointer type. We want to get the bare pointer
// and send that over the wire, so it can be re-wrapped as an sk_sp.
barePointers.push(childrenShaders[i].$$.ptr);
@ -22,9 +25,9 @@ CanvasKit._extraInitializations.push(function() {
var childrenPointers = copy1dArray(barePointers, CanvasKit.HEAPU32);
// Our array has 4 bytes per float, so be sure to account for that before
// sending it over the wire.
if (!matrix) {
return this._makeShaderWithChildren(fptr, floats.length * 4, !!isOpaque, childrenPointers, barePointers.length);
}
return this._makeShaderWithChildren(fptr, floats.length * 4, !!isOpaque, childrenPointers, barePointers.length, matrix);
var rts = this._makeShaderWithChildren(fptr, floats.length * 4, !!isOpaque, childrenPointers,
barePointers.length, localMatrixPtr);
localMatrixPtr && CanvasKit._free(localMatrixPtr);
return rts;
}
});

View File

@ -761,4 +761,28 @@ describe('CanvasKit\'s Canvas Behavior', function() {
reportSurface(surface, 'drawvertices_texture_canvas', done);
});
});
it('can change the matrix on the canvas and read it back', function(done) {
LoadCanvasKit.then(catchException(done, () => {
const canvas = new CanvasKit.SkCanvas();
let matr = canvas.getTotalMatrix();
expect(matr).toEqual(CanvasKit.SkMatrix.identity());
canvas.concat(CanvasKit.SkMatrix.rotated(Math.PI/4));
const d = new DOMMatrix().translate(20, 10);
canvas.concat(d);
matr = canvas.getTotalMatrix();
const expected = CanvasKit.SkMatrix.multiply(
CanvasKit.SkMatrix.rotated(Math.PI/4),
CanvasKit.SkMatrix.translated(20, 10)
);
expect(matr.length).toEqual(expected.length);
for (let i = 0; i < matr.length; i++) {
expect(matr[i]).toBeCloseTo(expected[i], 5);
}
done();
}));
})
});

View File

@ -161,4 +161,4 @@ void main(float2 xy, inout half4 color) {
it('apply a local matrix to the children-based shader', (done) => {
testChildrenShader('rtshader_children_rotated', done, CanvasKit.SkMatrix.rotated(Math.PI/12));
});
});
});