[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] ## [Unreleased]
### Added ### 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 ### Removed
- Previously deprecated functions MakeSkDashPathEffect, MakeLinearGradientShader, - 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); sk_sp<SkFontMgr> SkFontMgr_New_Custom_Data(const uint8_t** datas, const size_t* sizes, int n);
#endif #endif
// 3x3 Matrices struct OptionalMatrix : SkMatrix {
struct SimpleMatrix { OptionalMatrix(uintptr_t mPtr) {
SkScalar scaleX, skewX, transX; if (mPtr) {
SkScalar skewY, scaleY, transY; const SkScalar* nineMatrixValues = reinterpret_cast<const SkScalar*>(mPtr);
SkScalar pers0, pers1, pers2; 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. // Experimental 4x4 matrices, also represented in JS with arrays.
struct SimpleM44 { struct SimpleM44 {
SkScalar m0, m1, m2, m3; SkScalar m0, m1, m2, m3;
@ -803,29 +792,16 @@ EMSCRIPTEN_BINDINGS(Skia) {
}), allow_raw_pointers()); }), allow_raw_pointers());
function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end, function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr, 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 }; SkPoint points[] = { start, end };
// See comment above for uintptr_t explanation // 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); 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 // TODO(nifong): do not assume color space. Support and test wide gamut color gradients
return SkGradientShader::MakeLinear(points, colors, SkColorSpace::MakeSRGB(), positions, count, return SkGradientShader::MakeLinear(points, colors, SkColorSpace::MakeSRGB(), positions, count,
mode, flags, nullptr); mode, flags, &localMatrix);
}), 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);
}), allow_raw_pointers()); }), allow_raw_pointers());
#ifdef SK_SERIALIZE_SKP #ifdef SK_SERIALIZE_SKP
function("_MakeSkPicture", optional_override([](uintptr_t /* unint8_t* */ dPtr, function("_MakeSkPicture", optional_override([](uintptr_t /* unint8_t* */ dPtr,
@ -837,25 +813,14 @@ EMSCRIPTEN_BINDINGS(Skia) {
return SkPicture::MakeFromData(data.get(), nullptr); return SkPicture::MakeFromData(data.get(), nullptr);
}), allow_raw_pointers()); }), allow_raw_pointers());
#endif #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, function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr, uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
int count, SkTileMode mode, uint32_t flags, 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 // 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); const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
SkMatrix localMatrix = toSkMatrix(lm);
return SkGradientShader::MakeRadial(center, radius, colors, SkColorSpace::MakeSRGB(), positions, count, return SkGradientShader::MakeRadial(center, radius, colors, SkColorSpace::MakeSRGB(), positions, count,
mode, flags, &localMatrix); mode, flags, &localMatrix);
}), allow_raw_pointers()); }), allow_raw_pointers());
@ -864,61 +829,25 @@ EMSCRIPTEN_BINDINGS(Skia) {
int count, SkTileMode mode, int count, SkTileMode mode,
SkScalar startAngle, SkScalar endAngle, SkScalar startAngle, SkScalar endAngle,
uint32_t flags, uint32_t flags,
const SimpleMatrix& lm)->sk_sp<SkShader> { uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation // 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); const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
SkMatrix localMatrix = toSkMatrix(lm);
return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count, return SkGradientShader::MakeSweep(cx, cy, colors, SkColorSpace::MakeSRGB(), positions, count,
mode, startAngle, endAngle, flags, mode, startAngle, endAngle, flags,
&localMatrix); &localMatrix);
}), allow_raw_pointers()); }), 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([]( function("_MakeTwoPointConicalGradientShader", optional_override([](
SkPoint start, SkScalar startRadius, SkPoint start, SkScalar startRadius,
SkPoint end, SkScalar endRadius, SkPoint end, SkScalar endRadius,
uintptr_t /* SkColor4f* */ cPtr, uintptr_t /* SkScalar* */ pPtr, 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> {
// See comment above for uintptr_t explanation // 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); const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
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);
return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius, return SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
colors, SkColorSpace::MakeSRGB(), positions, count, mode, colors, SkColorSpace::MakeSRGB(), positions, count, mode,
flags, &localMatrix); flags, &localMatrix);
@ -972,8 +901,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
self.clipRRect(toRRect(r), op, doAntiAlias); self.clipRRect(toRRect(r), op, doAntiAlias);
})) }))
.function("clipRect", select_overload<void (const SkRect&, SkClipOp, bool)>(&SkCanvas::clipRect)) .function("clipRect", select_overload<void (const SkRect&, SkClipOp, bool)>(&SkCanvas::clipRect))
.function("concat", optional_override([](SkCanvas& self, const SimpleMatrix& m) { .function("_concat", optional_override([](SkCanvas& self, uintptr_t /* SkScalar* */ mPtr) {
self.concat(toSkMatrix(m)); // See comment above for uintptr_t explanation
OptionalMatrix localMatrix(mPtr);
self.concat(localMatrix);
})) }))
.function("drawArc", &SkCanvas::drawArc) .function("drawArc", &SkCanvas::drawArc)
// _drawAtlas takes an SkColor, unlike most private functions handling color. // _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("drawVertices", select_overload<void (const sk_sp<SkVertices>&, SkBlendMode, const SkPaint&)>(&SkCanvas::drawVertices))
.function("flush", &SkCanvas::flush) .function("flush", &SkCanvas::flush)
.function("getSaveCount", &SkCanvas::getSaveCount) .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(); SkMatrix m = self.getTotalMatrix();
return toSimpleSkMatrix(m); m.get9(nineMatrixValues);
})) }))
.function("makeSurface", optional_override([](SkCanvas& self, SimpleImageInfo sii)->sk_sp<SkSurface> { .function("makeSurface", optional_override([](SkCanvas& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
return self.makeSurface(toSkImageInfo(sii), nullptr); return self.makeSurface(toSkImageInfo(sii), nullptr);
@ -1274,16 +1213,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("width", &SkImage::width) .function("width", &SkImage::width)
.function("_encodeToData", select_overload<sk_sp<SkData>()const>(&SkImage::encodeToData)) .function("_encodeToData", select_overload<sk_sp<SkData>()const>(&SkImage::encodeToData))
.function("_encodeToDataWithFormat", select_overload<sk_sp<SkData>(SkEncodedImageFormat encodedImageFormat, int quality)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, .function("_makeShader", optional_override([](sk_sp<SkImage> self,
SkTileMode tx, SkTileMode ty, SkTileMode tx, SkTileMode ty,
const SimpleMatrix& lm)->sk_sp<SkShader> { uintptr_t /* SkScalar* */ mPtr)->sk_sp<SkShader> {
SkMatrix localMatrix = toSkMatrix(lm); OptionalMatrix localMatrix(mPtr);
return self->makeShader(tx, ty, &localMatrix); return self->makeShader(tx, ty, &localMatrix);
}), allow_raw_pointers()) }), allow_raw_pointers())
.function("_readPixels", optional_override([](sk_sp<SkImage> self, .function("_readPixels", optional_override([](sk_sp<SkImage> self,
@ -1309,9 +1242,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
return SkImageFilters::ColorFilter(cf, input); return SkImageFilters::ColorFilter(cf, input);
})) }))
.class_function("MakeCompose", &SkImageFilters::Compose) .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> { 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") class_<SkMaskFilter>("SkMaskFilter")
@ -1507,21 +1441,18 @@ EMSCRIPTEN_BINDINGS(Skia) {
} }
return effect; 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 // See comment above for uintptr_t explanation
void* inputData = reinterpret_cast<void*>(fPtr); void* inputData = reinterpret_cast<void*>(fPtr);
sk_sp<SkData> inputs = SkData::MakeFromMalloc(inputData, fLen); sk_sp<SkData> inputs = SkData::MakeFromMalloc(inputData, fLen);
return self.makeShader(inputs, nullptr, 0, nullptr, isOpaque);
})) OptionalMatrix localMatrix(mPtr);
.function("_makeShader", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque, SimpleMatrix sm)->sk_sp<SkShader> { return self.makeShader(inputs, nullptr, 0, &localMatrix, isOpaque);
// 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);
})) }))
.function("_makeShaderWithChildren", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool 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 // See comment above for uintptr_t explanation
void* inputData = reinterpret_cast<void*>(fPtr); void* inputData = reinterpret_cast<void*>(fPtr);
sk_sp<SkData> inputs = SkData::MakeFromMalloc(inputData, fLen); 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. // so we want to ref the new sk_sp so makeShader doesn't clean it up.
children[i] = sk_ref_sp<SkShader>(childrenPtrs[i]); children[i] = sk_ref_sp<SkShader>(childrenPtrs[i]);
} }
auto s = self.makeShader(inputs, children, cLen, nullptr, isOpaque); OptionalMatrix localMatrix(mPtr);
delete[] children; auto s = self.makeShader(inputs, children, cLen, &localMatrix, isOpaque);
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);
delete[] children; delete[] children;
return s; return s;
})); }));
@ -1827,24 +1741,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.field("cap", &StrokeOpts::cap) .field("cap", &StrokeOpts::cap)
.field("precision", &StrokeOpts::precision); .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") value_array<SimpleM44>("SkM44")
.element(&SimpleM44::m0).element(&SimpleM44::m1).element(&SimpleM44::m2).element(&SimpleM44::m3) .element(&SimpleM44::m0).element(&SimpleM44::m1).element(&SimpleM44::m2).element(&SimpleM44::m3)
.element(&SimpleM44::m4).element(&SimpleM44::m5).element(&SimpleM44::m6).element(&SimpleM44::m7) .element(&SimpleM44::m4).element(&SimpleM44::m5).element(&SimpleM44::m6).element(&SimpleM44::m7)

View File

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

View File

@ -259,6 +259,43 @@ function copy3dArray(arr, dest, ptr) {
return 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 // Caching the Float32Arrays can save having to reallocate them
// over and over again. // over and over again.
var Float32ArrayCache = {}; var Float32ArrayCache = {};

View File

@ -806,12 +806,10 @@ CanvasKit.onRuntimeInitialized = function() {
} }
CanvasKit.SkImage.prototype.makeShader = function(xTileMode, yTileMode, localMatrix) { CanvasKit.SkImage.prototype.makeShader = function(xTileMode, yTileMode, localMatrix) {
if (localMatrix) { var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix); var shader = this._makeShader(xTileMode, yTileMode, localMatrixPtr);
return this._makeShader(xTileMode, yTileMode, localMatrix); localMatrixPtr && CanvasKit._free(localMatrixPtr);
} else { return shader;
return this._makeShader(xTileMode, yTileMode);
}
} }
CanvasKit.SkImage.prototype.readPixels = function(imageInfo, srcX, srcY) { CanvasKit.SkImage.prototype.readPixels = function(imageInfo, srcX, srcY) {
@ -852,13 +850,18 @@ CanvasKit.onRuntimeInitialized = function() {
// Free the allocated pixels in the WASM memory // Free the allocated pixels in the WASM memory
CanvasKit._free(pPtr); CanvasKit._free(pPtr);
return retVal; 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 // atlas is an SkImage, e.g. from CanvasKit.MakeImageFromEncoded
// srcRects and dstXforms should be CanvasKit.SkRectBuilder and CanvasKit.RSXFormBuilder // srcRects and dstXforms should be CanvasKit.SkRectBuilder and CanvasKit.RSXFormBuilder
// or just arrays of floats in groups of 4. // 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, CanvasKit.SkCanvas.prototype.drawAtlas = function(atlas, srcRects, dstXforms, paint,
/*optional*/ blendMode, colors) { /*optional*/ blendMode, colors) {
if (!atlas || !paint || !srcRects || !dstXforms) { if (!atlas || !paint || !srcRects || !dstXforms) {
@ -887,14 +890,14 @@ CanvasKit.onRuntimeInitialized = function() {
dstXformPtr = copy1dArray(dstXforms, CanvasKit.HEAPF32); dstXformPtr = copy1dArray(dstXforms, CanvasKit.HEAPF32);
} }
var colorPtr = 0; // enscriptem doesn't like undefined for nullptr var colorPtr = nullptr;
if (colors) { if (colors) {
if (colors.build) { if (colors.build) {
colorPtr = colors.build(); colorPtr = colors.build();
} else { } else {
if (!isCanvasKitColor(colors[0])) { if (!isCanvasKitColor(colors[0])) {
SkDebug('DrawAtlas color argument expected to be CanvasKit.SkRectBuilder or array of ' + SkDebug('DrawAtlas color argument expected to be CanvasKit.SkRectBuilder or array of ' +
'Canvaskit.SimpleColor4f, but got '+colors); 'CanvasKit.SimpleColor4f, but got '+colors);
return; return;
} }
// convert here // convert here
@ -937,6 +940,20 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit._free(ptr); 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 // returns Uint8Array
CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType, CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType,
colorType, dstRowBytes) { colorType, dstRowBytes) {
@ -995,8 +1012,7 @@ CanvasKit.onRuntimeInitialized = function() {
// colorMatrix is an SkColorMatrix (e.g. Float32Array of length 20) // colorMatrix is an SkColorMatrix (e.g. Float32Array of length 20)
CanvasKit.SkColorFilter.MakeMatrix = function(colorMatrix) { CanvasKit.SkColorFilter.MakeMatrix = function(colorMatrix) {
if (!colorMatrix || colorMatrix.length !== 20) { if (!colorMatrix || colorMatrix.length !== 20) {
SkDebug('ignoring invalid color matrix'); throw 'invalid color matrix';
return;
} }
var fptr = copy1dArray(colorMatrix, CanvasKit.HEAPF32); var fptr = copy1dArray(colorMatrix, CanvasKit.HEAPF32);
// We know skia memcopies the floats, so we can free our memory after the call returns. // 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; 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) { CanvasKit.SkSurface.prototype.captureFrameAsSkPicture = function(drawFrame) {
// Set up SkPictureRecorder // Set up SkPictureRecorder
var spr = new CanvasKit.SkPictureRecorder(); var spr = new CanvasKit.SkPictureRecorder();
@ -1066,43 +1090,16 @@ CanvasKit.onRuntimeInitialized = function() {
return dpe; 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) { CanvasKit.SkShader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags) {
var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32); var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
flags = flags || 0; flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
if (localMatrix) { var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix); colors.length, mode, flags, localMatrixPtr);
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);
}
localMatrixPtr && CanvasKit._free(localMatrixPtr);
CanvasKit._free(colorPtr); CanvasKit._free(colorPtr);
CanvasKit._free(posPtr); CanvasKit._free(posPtr);
return lgs; return lgs;
@ -1112,16 +1109,12 @@ CanvasKit.onRuntimeInitialized = function() {
var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32); var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
flags = flags || 0; flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
if (localMatrix) { var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix); colors.length, mode, flags, localMatrixPtr);
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);
}
localMatrixPtr && CanvasKit._free(localMatrixPtr);
CanvasKit._free(colorPtr); CanvasKit._free(colorPtr);
CanvasKit._free(posPtr); CanvasKit._free(posPtr);
return rgs; return rgs;
@ -1133,35 +1126,31 @@ CanvasKit.onRuntimeInitialized = function() {
flags = flags || 0; flags = flags || 0;
startAngle = startAngle || 0; startAngle = startAngle || 0;
endAngle = endAngle || 360; endAngle = endAngle || 360;
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix); var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var sgs = CanvasKit._MakeSweepGradientShader(cx, cy, colorPtr, posPtr, var sgs = CanvasKit._MakeSweepGradientShader(cx, cy, colorPtr, posPtr,
colors.length, mode, colors.length, mode,
startAngle, endAngle, flags, startAngle, endAngle, flags,
localMatrix); localMatrixPtr);
localMatrixPtr && CanvasKit._free(localMatrixPtr);
CanvasKit._free(colorPtr); CanvasKit._free(colorPtr);
CanvasKit._free(posPtr); CanvasKit._free(posPtr);
return sgs; return sgs;
} }
CanvasKit.SkShader.MakeTwoPointConicalGradient = function(start, startRadius, end, endRadius, 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 colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
flags = flags || 0; flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
if (localMatrix) { var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
localMatrix = prepare3x3MatrixForGoingToWasm(localMatrix);
var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
start, startRadius, end, endRadius, start, startRadius, end, endRadius,
colorPtr, posPtr, colors.length, mode, flags, localMatrix); colorPtr, posPtr, colors.length, mode, flags, localMatrixPtr);
} else {
var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
start, startRadius, end, endRadius,
colorPtr, posPtr, colors.length, mode, flags);
}
localMatrixPtr && CanvasKit._free(localMatrixPtr);
CanvasKit._free(colorPtr); CanvasKit._free(colorPtr);
CanvasKit._free(posPtr); CanvasKit._free(posPtr);
return rgs; return rgs;

View File

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

View File

@ -1,20 +1,23 @@
CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() { 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 fptr = copy1dArray(floats, CanvasKit.HEAPF32);
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
// Our array has 4 bytes per float, so be sure to account for that before // Our array has 4 bytes per float, so be sure to account for that before
// sending it over the wire. // sending it over the wire.
if (!matrix) { var rts = this._makeShader(fptr, floats.length * 4, !!isOpaque, localMatrixPtr);
return this._makeShader(fptr, floats.length * 4, !!isOpaque); localMatrixPtr && CanvasKit._free(localMatrixPtr);
} return rts;
return this._makeShader(fptr, floats.length * 4, !!isOpaque, matrix);
} }
// childrenWithShaders is an array of other shaders (e.g. SkImage.makeShader()) // 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 fptr = copy1dArray(floats, CanvasKit.HEAPF32);
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var barePointers = []; 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 // 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. // and send that over the wire, so it can be re-wrapped as an sk_sp.
barePointers.push(childrenShaders[i].$$.ptr); barePointers.push(childrenShaders[i].$$.ptr);
@ -22,9 +25,9 @@ CanvasKit._extraInitializations.push(function() {
var childrenPointers = copy1dArray(barePointers, CanvasKit.HEAPU32); var childrenPointers = copy1dArray(barePointers, CanvasKit.HEAPU32);
// Our array has 4 bytes per float, so be sure to account for that before // Our array has 4 bytes per float, so be sure to account for that before
// sending it over the wire. // sending it over the wire.
if (!matrix) { var rts = this._makeShaderWithChildren(fptr, floats.length * 4, !!isOpaque, childrenPointers,
return this._makeShaderWithChildren(fptr, floats.length * 4, !!isOpaque, childrenPointers, barePointers.length); barePointers.length, localMatrixPtr);
} localMatrixPtr && CanvasKit._free(localMatrixPtr);
return this._makeShaderWithChildren(fptr, floats.length * 4, !!isOpaque, childrenPointers, barePointers.length, matrix); return rts;
} }
}); });

View File

@ -761,4 +761,28 @@ describe('CanvasKit\'s Canvas Behavior', function() {
reportSurface(surface, 'drawvertices_texture_canvas', done); 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) => { it('apply a local matrix to the children-based shader', (done) => {
testChildrenShader('rtshader_children_rotated', done, CanvasKit.SkMatrix.rotated(Math.PI/12)); testChildrenShader('rtshader_children_rotated', done, CanvasKit.SkMatrix.rotated(Math.PI/12));
}); });
}); });