Pass 4f colors to private functions with float pointers

Change-Id: I4534a246c37870750298d39edcbc869781dc1008
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/286880
Commit-Queue: Nathaniel Nifong <nifong@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
Nathaniel Nifong 2020-05-04 16:46:17 -04:00 committed by Skia Commit-Bot
parent d16e322f02
commit 1bedbeb081
11 changed files with 313 additions and 91 deletions

View File

@ -22,23 +22,4 @@ using SkPathOrNull = emscripten::val;
using Uint8Array = emscripten::val;
using Float32Array = emscripten::val;
// A struct used for binding the TypedArray colors passed to to canvaskit functions.
// Canvaskit functions returning colors return a Float32Array, which looks the same
// on the javascript side.
struct SimpleColor4f {
// A sensible but noticeable default value to let you know you've called the
// default constructor.
float r = 1.0;
float g = 0.0;
float b = 1.0;
float a = 1.0;
SkColor4f toSkColor4f() const {
return SkColor4f({r, g, b, a});
};
SkColor toSkColor() const {
return toSkColor4f().toSkColor();
};
};
#endif

View File

@ -97,10 +97,9 @@ struct OptionalMatrix : SkMatrix {
}
};
SimpleColor4f toSimpleColor4f(const SkColor4f c) {
SimpleColor4f color {
c.fR, c.fG, c.fB, c.fA,
};
SkColor4f ptrToSkColor4f(uintptr_t /* float* */ cPtr) {
float* fourFloats = reinterpret_cast<float*>(cPtr);
SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] };
return color;
}
@ -623,19 +622,27 @@ SkRRect toRRect(const SimpleRRect& r) {
return rr;
}
struct TonalColors {
SimpleColor4f ambientColor;
SimpleColor4f spotColor;
};
// This function is private, we call it in interface.js
void computeTonalColors(uintptr_t cPtrAmbi /* float * */, uintptr_t cPtrSpot /* float * */) {
// private methods accepting colors take pointers to floats already copied into wasm memory.
float* ambiFloats = reinterpret_cast<float*>(cPtrAmbi);
float* spotFloats = reinterpret_cast<float*>(cPtrSpot);
SkColor4f ambiColor = { ambiFloats[0], ambiFloats[1], ambiFloats[2], ambiFloats[3]};
SkColor4f spotColor = { spotFloats[0], spotFloats[1], spotFloats[2], spotFloats[3]};
TonalColors computeTonalColors(const TonalColors& in) {SkColor resultAmbient, resultSpot;
// This function takes SkColor
SkColor resultAmbi, resultSpot;
SkShadowUtils::ComputeTonalColors(
in.ambientColor.toSkColor(), in.spotColor.toSkColor(),
&resultAmbient, &resultSpot);
TonalColors out;
out.ambientColor = toSimpleColor4f(SkColor4f::FromColor(resultAmbient));
out.spotColor = toSimpleColor4f(SkColor4f::FromColor(resultSpot));
return out;
ambiColor.toSkColor(), spotColor.toSkColor(),
&resultAmbi, &resultSpot);
// Convert back to color4f
const SkColor4f ambi4f = SkColor4f::FromColor(resultAmbi);
const SkColor4f spot4f = SkColor4f::FromColor(resultSpot);
// Re-use the caller's allocated memory to hold the result.
memcpy(ambiFloats, ambi4f.vec(), 4 * sizeof(SkScalar));
memcpy(spotFloats, spot4f.vec(), 4 * sizeof(SkScalar));
}
// These objects have private destructors / delete methods - I don't think
@ -695,7 +702,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
function("setDecodeCacheLimitBytes", &SkResourceCache::SetTotalByteLimit);
function("getDecodeCacheUsedBytes" , &SkResourceCache::GetTotalBytesUsed);
function("computeTonalColors", &computeTonalColors);
function("_computeTonalColors", &computeTonalColors);
function("_decodeAnimatedImage", optional_override([](uintptr_t /* uint8_t* */ iptr,
size_t length)->sk_sp<SkAnimatedImage> {
uint8_t* imgData = reinterpret_cast<uint8_t*>(iptr);
@ -853,8 +860,8 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkCanvas>("SkCanvas")
.constructor<>()
.function("clear", optional_override([](SkCanvas& self, SimpleColor4f c) {
self.clear(c.toSkColor());
.function("_clear", optional_override([](SkCanvas& self, uintptr_t /* float* */ cPtr) {
self.clear(ptrToSkColor4f(cPtr).toSkColor());
}))
.function("clipPath", select_overload<void (const SkPath&, SkClipOp, bool)>(&SkCanvas::clipPath))
.function("clipRRect", optional_override([](SkCanvas& self, const SimpleRRect& r, SkClipOp op, bool doAntiAlias) {
@ -886,11 +893,11 @@ EMSCRIPTEN_BINDINGS(Skia) {
self.drawAtlas(atlas, dstXforms, srcRects, colors, count, mode, nullptr, paint);
}), allow_raw_pointers())
.function("drawCircle", select_overload<void (SkScalar, SkScalar, SkScalar, const SkPaint& paint)>(&SkCanvas::drawCircle))
.function("drawColor", optional_override([](SkCanvas& self, SimpleColor4f c) {
self.drawColor(c.toSkColor());
.function("_drawColor", optional_override([](SkCanvas& self, uintptr_t /* float* */ cPtr) {
self.drawColor(ptrToSkColor4f(cPtr).toSkColor());
}))
.function("drawColor", optional_override([](SkCanvas& self, SimpleColor4f c, SkBlendMode mode) {
self.drawColor(c.toSkColor(), mode);
.function("_drawColor", optional_override([](SkCanvas& self, uintptr_t /* float* */ cPtr, SkBlendMode mode) {
self.drawColor(ptrToSkColor4f(cPtr).toSkColor(), mode);
}))
.function("drawDRRect",optional_override([](SkCanvas& self, const SimpleRRect& o, const SimpleRRect& i, const SkPaint& paint) {
self.drawDRRect(toRRect(o), toRRect(i), paint);
@ -937,13 +944,16 @@ EMSCRIPTEN_BINDINGS(Skia) {
}))
.function("drawRect", &SkCanvas::drawRect)
.function("drawRoundRect", &SkCanvas::drawRoundRect)
.function("drawShadow", optional_override([](SkCanvas& self, const SkPath& path,
.function("_drawShadow", optional_override([](SkCanvas& self, const SkPath& path,
const SkPoint3& zPlaneParams,
const SkPoint3& lightPos, SkScalar lightRadius,
SimpleColor4f ambientColor, SimpleColor4f spotColor,
uintptr_t /* float* */ ambientColorPtr,
uintptr_t /* float* */ spotColorPtr,
uint32_t flags) {
SkShadowUtils::DrawShadow(&self, path, zPlaneParams, lightPos, lightRadius,
ambientColor.toSkColor(), spotColor.toSkColor(), flags);
ptrToSkColor4f(ambientColorPtr).toSkColor(),
ptrToSkColor4f(spotColorPtr).toSkColor(),
flags);
}))
#ifndef SK_NO_FONTS
.function("_drawShapedText", &drawShapedText)
@ -1025,8 +1035,8 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkColorFilter>("SkColorFilter")
.smart_ptr<sk_sp<SkColorFilter>>("sk_sp<SkColorFilter>>")
.class_function("MakeBlend", optional_override([](SimpleColor4f c, SkBlendMode mode)->sk_sp<SkColorFilter> {
return SkColorFilters::Blend(c.toSkColor(), mode);
.class_function("_MakeBlend", optional_override([](uintptr_t /* float* */ cPtr, SkBlendMode mode)->sk_sp<SkColorFilter> {
return SkColorFilters::Blend(ptrToSkColor4f(cPtr).toSkColor(), mode);
}))
.class_function("MakeCompose", &SkColorFilters::Compose)
.class_function("MakeLerp", &SkColorFilters::Lerp)
@ -1216,10 +1226,11 @@ EMSCRIPTEN_BINDINGS(Skia) {
return p;
}))
.function("getBlendMode", &SkPaint::getBlendMode)
.function("getColor", optional_override([](SkPaint& self)->Float32Array {
const SimpleColor4f& c = toSimpleColor4f(self.getColor4f());
const float array[4] = {c.r, c.g, c.b, c.a};
return Float32Array(typed_memory_view(4, array));
// provide an allocated place to put the returned color
.function("_getColor", optional_override([](SkPaint& self, uintptr_t /* float* */ cPtr)->void {
const SkColor4f& c = self.getColor4f();
float* fourFloats = reinterpret_cast<float*>(cPtr);
memcpy(fourFloats, c.vec(), 4 * sizeof(SkScalar));
}))
.function("getFilterQuality", &SkPaint::getFilterQuality)
.function("getStrokeCap", &SkPaint::getStrokeCap)
@ -1229,8 +1240,8 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("setAntiAlias", &SkPaint::setAntiAlias)
.function("setAlphaf", &SkPaint::setAlphaf)
.function("setBlendMode", &SkPaint::setBlendMode)
.function("setColor", optional_override([](SkPaint& self, SimpleColor4f c) {
self.setColor({c.r, c.g, c.b, c.a});
.function("_setColor", optional_override([](SkPaint& self, uintptr_t /* float* */ cPtr) {
self.setColor(ptrToSkColor4f(cPtr));
}))
.function("setColorFilter", &SkPaint::setColorFilter)
.function("setFilterQuality", &SkPaint::setFilterQuality)
@ -1376,9 +1387,9 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkShader>("SkShader")
.smart_ptr<sk_sp<SkShader>>("sk_sp<SkShader>")
.class_function("Blend", select_overload<sk_sp<SkShader>(SkBlendMode, sk_sp<SkShader>, sk_sp<SkShader>)>(&SkShaders::Blend))
.class_function("Color",
optional_override([](SimpleColor4f c)->sk_sp<SkShader> {
return SkShaders::Color(c.toSkColor4f(), SkColorSpace::MakeSRGB());
.class_function("_Color",
optional_override([](uintptr_t /* float* */ cPtr)->sk_sp<SkShader> {
return SkShaders::Color(ptrToSkColor4f(cPtr), SkColorSpace::MakeSRGB());
})
)
.class_function("Lerp", select_overload<sk_sp<SkShader>(float, sk_sp<SkShader>, sk_sp<SkShader>)>(&SkShaders::Lerp));
@ -1651,10 +1662,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.field("fRight", &SkIRect::fRight)
.field("fBottom", &SkIRect::fBottom);
value_object<TonalColors>("TonalColors")
.field("ambient", &TonalColors::ambientColor)
.field("spot", &TonalColors::spotColor);
value_object<SimpleImageInfo>("SkImageInfo")
.field("width", &SimpleImageInfo::width)
.field("height", &SimpleImageInfo::height)
@ -1695,13 +1702,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.field("cap", &StrokeOpts::cap)
.field("precision", &StrokeOpts::precision);
value_array<SimpleColor4f>("SkColor4f")
.element(&SimpleColor4f::r)
.element(&SimpleColor4f::g)
.element(&SimpleColor4f::b)
.element(&SimpleColor4f::a);
constant("MOVE_VERB", MOVE);
constant("LINE_VERB", LINE);
constant("QUAD_VERB", QUAD);

View File

@ -60,7 +60,6 @@ var CanvasKit = {
MakeWebGLCanvasSurface: function() {},
/** @return {TypedArray} */
Malloc: function() {},
/** @return {TonalColors} */
computeTonalColors: function() {},
currentContext: function() {},
getColorComponents: function() {},
@ -74,6 +73,7 @@ var CanvasKit = {
// private API (i.e. things declared in the bindings that we use
// in the pre-js file)
_computeTonalColors: function() {},
_MakeImage: function() {},
_MakeLinearGradientShader: function() {},
_MakePathFromCmds: function() {},
@ -104,6 +104,12 @@ var CanvasKit = {
setResourceCacheLimitBytes: function() {},
},
ManagedAnimation: {
prototype: {
setColor: function() {},
},
},
Paragraph: {
// public API (from C++ bindings)
didExceedMaxLines: function() {},
@ -123,6 +129,21 @@ var CanvasKit = {
_getRectsForRange: function() {},
},
ParagraphBuilder: {
Make: function() {},
addText: function() {},
build: function() {},
pop: function() {},
prototype: {
pushStyle: function() {},
},
// private API
_Make: function() {},
_pushStyle: function() {},
},
SkRuntimeEffect: {
// public API (from C++ bindings)
Make: function() {},
@ -154,14 +175,12 @@ var CanvasKit = {
SkCanvas: {
// public API (from C++ bindings)
clear: function() {},
clipPath: function() {},
clipRRect: function() {},
clipRect: function() {},
drawAnimatedImage: function() {},
drawArc: function() {},
drawCircle: function() {},
drawColor: function() {},
drawDRRect: function() {},
drawImage: function() {},
drawImageNine: function() {},
@ -175,7 +194,6 @@ var CanvasKit = {
drawRRect: function() {},
drawRect: function() {},
drawRoundRect: function() {},
drawShadow: function() {},
drawText: function() {},
drawTextBlob: function() {},
drawVertices: function() {},
@ -191,10 +209,19 @@ var CanvasKit = {
skew: function() {},
translate: function() {},
prototype: {
clear: function() {},
drawColor: function() {},
drawShadow: function() {},
},
// private API
_clear: function() {},
_concat: function() {},
_drawAtlas: function() {},
_drawColor: function() {},
_drawPoints: function() {},
_drawShadow: function() {},
_drawSimpleText: function() {},
_getLocalToCamera: function() {},
_getLocalToDevice: function() {},
@ -214,6 +241,7 @@ var CanvasKit = {
MakeMatrix: function() {},
MakeSRGBToLinearGamma: function() {},
// private API (from C++ bindings)
_MakeBlend: function() {},
_makeMatrix: function() {},
},
@ -329,7 +357,6 @@ var CanvasKit = {
getStrokeWidth: function() {},
setAntiAlias: function() {},
setBlendMode: function() {},
setColor: function() {},
setFilterQuality: function() {},
setImageFilter: function() {},
setMaskFilter: function() {},
@ -341,8 +368,14 @@ var CanvasKit = {
setStrokeWidth: function() {},
setStyle: function() {},
prototype: {
setColor: function() {},
},
// Private API
delete: function() {},
_getColor: function() {},
_setColor: function() {},
},
SkPathEffect: {
@ -472,6 +505,9 @@ var CanvasKit = {
MakeRadialGradient: function() {},
MakeTwoPointConicalGradient: function() {},
MakeSweepGradient: function() {},
// private API (from C++ bindings)
_Color: function() {},
},
SkSurface: {

View File

@ -351,6 +351,16 @@ function copy4x4MatrixFromWasm(matrPtr) {
return rv;
}
// copies the four floats at the given pointer in a js Float32Array
function copyColorFromWasm(colorPtr) {
var rv = new Float32Array(4);
for (var i = 0; i < 4; i++) {
rv[i] = CanvasKit.HEAPF32[colorPtr/4 + i]; // divide by 4 to "cast" to float.
}
CanvasKit._free(colorPtr);
return rv;
}
// Caching the Float32Arrays can save having to reallocate them
// over and over again.
var Float32ArrayCache = {};

View File

@ -852,6 +852,13 @@ CanvasKit.onRuntimeInitialized = function() {
return retVal;
}
// Accepts an array of four numbers in the range of 0-1 representing a 4f color
CanvasKit.SkCanvas.prototype.clear = function (color4f) {
var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
this._clear(cPtr);
CanvasKit._free(cPtr);
}
// concat takes a 3x2, a 3x3, or a 4x4 matrix and upscales it (if needed) to 4x4. This is because
// under the hood, SkCanvas uses a 4x4 matrix.
CanvasKit.SkCanvas.prototype.concat = function(matr) {
@ -866,7 +873,7 @@ CanvasKit.onRuntimeInitialized = function() {
// 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 float colors (arrays of 4 floats)
CanvasKit.SkCanvas.prototype.drawAtlas = function(atlas, srcRects, dstXforms, paint,
/*optional*/ blendMode, colors) {
if (!atlas || !paint || !srcRects || !dstXforms) {
@ -902,7 +909,7 @@ CanvasKit.onRuntimeInitialized = function() {
} else {
if (!isCanvasKitColor(colors[0])) {
SkDebug('DrawAtlas color argument expected to be CanvasKit.SkRectBuilder or array of ' +
'CanvasKit.SimpleColor4f, but got '+colors);
'float arrays, but got '+colors);
return;
}
// convert here
@ -926,6 +933,16 @@ CanvasKit.onRuntimeInitialized = function() {
}
CanvasKit.SkCanvas.prototype.drawColor = function (color4f, mode) {
var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
if (mode !== undefined) {
this._drawColor(cPtr, mode);
} else {
this._drawColor(cPtr);
}
CanvasKit._free(cPtr);
}
// points is either an array of [x, y] where x and y are numbers or
// a typed array from Malloc where the even indices will be treated
// as x coordinates and the odd indices will be treated as y coordinates.
@ -945,6 +962,14 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit._free(ptr);
}
CanvasKit.SkCanvas.prototype.drawShadow = function(path, zPlaneParams, lightPos, lightRadius, ambientColor, spotColor, flags) {
var ambiPtr = copy1dArray(ambientColor, CanvasKit.HEAPF32);
var spotPtr = copy1dArray(spotColor, CanvasKit.HEAPF32);
this._drawShadow(path, zPlaneParams, lightPos, lightRadius, ambiPtr, spotPtr, flags);
CanvasKit._free(ambiPtr);
CanvasKit._free(spotPtr);
}
// getLocalToDevice returns a 4x4 matrix.
CanvasKit.SkCanvas.prototype.getLocalToDevice = function() {
var matrPtr = CanvasKit._malloc(16 * 4); // allocate space for the matrix
@ -1023,6 +1048,13 @@ CanvasKit.onRuntimeInitialized = function() {
return ok;
}
CanvasKit.SkColorFilter.MakeBlend = function(color4f, mode) {
var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
var result = CanvasKit.SkColorFilter._MakeBlend(cPtr, mode);
CanvasKit._free(cPtr);
return result;
}
// colorMatrix is an SkColorMatrix (e.g. Float32Array of length 20)
CanvasKit.SkColorFilter.MakeMatrix = function(colorMatrix) {
if (!colorMatrix || colorMatrix.length !== 20) {
@ -1043,6 +1075,18 @@ CanvasKit.onRuntimeInitialized = function() {
return imgF;
}
CanvasKit.SkPaint.prototype.getColor = function() {
var cPtr = CanvasKit._malloc(16); // 4 floats, 4 bytes each
this._getColor(cPtr);
return copyColorFromWasm(cPtr);
}
CanvasKit.SkPaint.prototype.setColor = function(color4f) {
var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
this._setColor(cPtr);
CanvasKit._free(cPtr);
}
CanvasKit.SkSurface.prototype.captureFrameAsSkPicture = function(drawFrame) {
// Set up SkPictureRecorder
var spr = new CanvasKit.SkPictureRecorder();
@ -1104,6 +1148,13 @@ CanvasKit.onRuntimeInitialized = function() {
return dpe;
}
CanvasKit.SkShader.Color = function(color4f) {
var cPtr = copy1dArray(color4f, CanvasKit.HEAPF32);
var result = CanvasKit.SkShader._Color(cPtr);
CanvasKit._free(cPtr);
return result;
}
CanvasKit.SkShader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags) {
var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32);
var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
@ -1184,6 +1235,23 @@ CanvasKit.onRuntimeInitialized = function() {
}
}; // end CanvasKit.onRuntimeInitialized, that is, anything changing prototypes or dynamic.
// Accepts an object holding two canvaskit colors.
// {
// ambient: {r, g, b, a},
// spot: {r, g, b, a},
// }
// Returns the same format
CanvasKit.computeTonalColors = function(tonalColors) {
var cPtrAmbi = copy1dArray(tonalColors['ambient'], CanvasKit.HEAPF32);
var cPtrSpot = copy1dArray(tonalColors['spot'], CanvasKit.HEAPF32);
this._computeTonalColors(cPtrAmbi, cPtrSpot);
var result = {
'ambient': copyColorFromWasm(cPtrAmbi),
'spot': copyColorFromWasm(cPtrSpot),
}
return result;
}
CanvasKit.LTRBRect = function(l, t, r, b) {
return {
fLeft: l,
@ -1269,7 +1337,7 @@ CanvasKit.MakeImage = function(pixels, width, height, alphaType, colorType) {
return CanvasKit._MakeImage(info, pptr, pixels.length, width * bytesPerPixel);
}
// colors is an array of SimpleColor4f
// colors is an array of float color arrays
CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors,
indices, isVolatile) {
// Default isVolitile to true if not set

View File

@ -15,6 +15,7 @@ module.exports = function(config) {
'../../modules/pathkit/perf/perfReporter.js',
'canvaskit/bin/canvaskit.js',
'tests/canvaskitinit.js',
'tests/util.js',
'perf/*.bench.js'
],

View File

@ -68,6 +68,7 @@
if (!isCanvasKitColor(s['color'])) {
s['color'] = CanvasKit.BLACK;
}
s['foregroundColor'] = s['foregroundColor'] || CanvasKit.TRANSPARENT;
s['backgroundColor'] = s['backgroundColor'] || CanvasKit.TRANSPARENT;
s['decoration'] = s['decoration'] || 0;
@ -108,5 +109,41 @@
}
return copy1dArray(sPtrs, CanvasKit.HEAPU32);
}
function copyColors(textStyle) {
// these two color fields were arrays, but will set to WASM pointers before we pass this
// object over the WASM interface.
textStyle['colorPtr'] = copy1dArray(textStyle['color'], CanvasKit.HEAPF32);
textStyle['foregroundColorPtr'] = nullptr; // nullptr is 0, from helper.js
textStyle['backgroundColorPtr'] = nullptr;
if (isCanvasKitColor(textStyle['foregroundColor']) && textStyle['foregroundColor'][3] > 0) {
textStyle['foregroundColorPtr'] = copy1dArray(textStyle['foregroundColor'], CanvasKit.HEAPF32);
}
if (isCanvasKitColor(textStyle['backgroundColor']) && textStyle['backgroundColor'][3] > 0) {
textStyle['backgroundColorPtr'] = copy1dArray(textStyle['backgroundColor'], CanvasKit.HEAPF32);
}
return textStyle;
}
function freeColors(textStyle) {
CanvasKit._free(textStyle['colorPtr']);
CanvasKit._free(textStyle['foregroundColorPtr']);
CanvasKit._free(textStyle['backgroundColorPtr']);
}
CanvasKit.ParagraphBuilder.Make = function(paragraphStyle, fontManager) {
paragraphStyle['textStyle'] = copyColors(paragraphStyle['textStyle']);
var result = CanvasKit.ParagraphBuilder._Make(paragraphStyle, fontManager);
freeColors(paragraphStyle['textStyle']);
return result;
}
CanvasKit.ParagraphBuilder.prototype.pushStyle = function(textStyle) {
var tmpStyle = copyColors(textStyle);
this._pushStyle(tmpStyle);
freeColors(tmpStyle);
}
});
}(Module)); // When this file is loaded in, the high level object is "Module";

View File

@ -27,6 +27,12 @@ using namespace emscripten;
namespace para = skia::textlayout;
SkColor4f toSkColor4f(uintptr_t /* float* */ cPtr) {
float* fourFloats = reinterpret_cast<float*>(cPtr);
SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] };
return color;
}
struct SimpleFontStyle {
SkFontStyle::Slant slant;
SkFontStyle::Weight weight;
@ -34,9 +40,9 @@ struct SimpleFontStyle {
};
struct SimpleTextStyle {
SimpleColor4f color;
SimpleColor4f foregroundColor;
SimpleColor4f backgroundColor;
uintptr_t /* float* */ colorPtr;
uintptr_t /* float* */ foregroundColorPtr;
uintptr_t /* float* */ backgroundColorPtr;
uint8_t decoration;
SkScalar decorationThickness;
SkScalar fontSize;
@ -50,20 +56,18 @@ para::TextStyle toTextStyle(const SimpleTextStyle& s) {
para::TextStyle ts;
// textstyle.color doesn't support a 4f color, however the foreground and background fields below do.
ts.setColor(s.color.toSkColor());
ts.setColor(toSkColor4f(s.colorPtr).toSkColor());
// Emscripten will not allow a value_object to have an unset field, however
// It is functionally important that these paints be unset when no value was provided.
// paragraph.js defaults these colors to transparent in that case and we use that signal here.
if (s.foregroundColor.a > 0) {
if (s.foregroundColorPtr) {
SkPaint p1;
p1.setColor4f(s.foregroundColor.toSkColor4f());
p1.setColor4f(toSkColor4f(s.foregroundColorPtr));
ts.setForegroundColor(p1);
}
if (s.backgroundColor.a > 0) {
if (s.backgroundColorPtr) {
SkPaint p2;
p2.setColor4f(s.backgroundColor.toSkColor4f());
p2.setColor4f(toSkColor4f(s.backgroundColorPtr));
ts.setBackgroundColor(p2);
}
@ -178,7 +182,7 @@ EMSCRIPTEN_BINDINGS(Paragraph) {
.function("layout", &para::ParagraphImpl::layout);
class_<para::ParagraphBuilderImpl>("ParagraphBuilder")
.class_function("Make", optional_override([](SimpleParagraphStyle style,
.class_function("_Make", optional_override([](SimpleParagraphStyle style,
sk_sp<SkFontMgr> fontMgr)-> para::ParagraphBuilderImpl {
auto fc = sk_make_sp<para::FontCollection>();
fc->setDefaultFontManager(fontMgr);
@ -191,7 +195,7 @@ EMSCRIPTEN_BINDINGS(Paragraph) {
}))
.function("build", &para::ParagraphBuilderImpl::Build, allow_raw_pointers())
.function("pop", &para::ParagraphBuilderImpl::pop)
.function("pushStyle", optional_override([](para::ParagraphBuilderImpl& self,
.function("_pushStyle", optional_override([](para::ParagraphBuilderImpl& self,
SimpleTextStyle textStyle) {
auto ts = toTextStyle(textStyle);
self.pushStyle(ts);
@ -275,9 +279,9 @@ EMSCRIPTEN_BINDINGS(Paragraph) {
.field("textStyle", &SimpleParagraphStyle::textStyle);
value_object<SimpleTextStyle>("TextStyle")
.field("color", &SimpleTextStyle::color)
.field("foregroundColor", &SimpleTextStyle::foregroundColor)
.field("backgroundColor", &SimpleTextStyle::backgroundColor)
.field("colorPtr", &SimpleTextStyle::colorPtr)
.field("foregroundColorPtr", &SimpleTextStyle::foregroundColorPtr)
.field("backgroundColorPtr", &SimpleTextStyle::backgroundColorPtr)
.field("decoration", &SimpleTextStyle::decoration)
.field("decorationThickness", &SimpleTextStyle::decorationThickness)
.field("_fontFamilies", &SimpleTextStyle::fontFamilies)

View File

@ -0,0 +1,70 @@
describe('Basic Canvas ops', () => {
let container = document.createElement('div');
document.body.appendChild(container);
beforeEach(async () => {
container.innerHTML = `
<canvas width=600 height=600 id=test></canvas>`;
await LoadCanvasKit;
});
afterEach(function() {
container.innerHTML = '';
});
it('can draw 100 colored regions', () => {
function setup(ctx) {
ctx.surface = CanvasKit.MakeCanvasSurface('test');
ctx.canvas = ctx.surface.getCanvas();
};
function test(ctx) {
for (let i=0; i<100; i++) {
ctx.canvas.save();
ctx.canvas.clipRect(CanvasKit.LTRBRect(i, i, i+100, i+100),
CanvasKit.ClipOp.Intersect, false);
ctx.canvas.drawColor(CanvasKit.Color4f(1-i/100, 1.0, i/100, 1.0),
CanvasKit.BlendMode.SrcOver);
ctx.canvas.restore();
}
ctx.surface.flush();
};
function teardown(ctx) {
ctx.surface.delete();
};
benchmarkAndReport('canvas_drawColor', setup, test, teardown);
});
it('can draw a shadow with tonal colors', () => {
function setup(ctx) {
ctx.surface = CanvasKit.MakeCanvasSurface('test');
ctx.canvas = ctx.surface.getCanvas();
};
const input = {
ambient: CanvasKit.Color4f(0.2, 0.1, 0.3, 0.5),
spot: CanvasKit.Color4f(0.8, 0.8, 0.9, 0.9),
};
const lightRadius = 30;
const flags = 0;
const lightPos = [250,150,300];
const zPlaneParams = [0,0,1];
const path = starPath(CanvasKit);
function test(ctx) {
const out = CanvasKit.computeTonalColors(input);
ctx.canvas.drawShadow(path, zPlaneParams, lightPos, lightRadius,
out.ambient, out.spot, flags);
ctx.surface.flush();
};
function teardown(ctx) {
ctx.surface.delete();
};
benchmarkAndReport('canvas_drawShadow', setup, test, teardown);
});
});

View File

@ -52,3 +52,16 @@ CanvasKit.MakeManagedAnimation = function(json, assets) {
return anim;
};
(function(CanvasKit){
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() {
CanvasKit.ManagedAnimation.prototype.setColor = function(key, color) {
var cPtr = copy1dArray(color, CanvasKit.HEAPF32);
this._setColor(key, cPtr);
CanvasKit._free(cPtr);
}
});
}(Module)); // When this file is loaded in, the high level object is "Module";

View File

@ -220,8 +220,10 @@ EMSCRIPTEN_BINDINGS(Skottie) {
.function("render" , select_overload<void(SkCanvas*) const>(&ManagedAnimation::render), allow_raw_pointers())
.function("render" , select_overload<void(SkCanvas*, const SkRect&) const>
(&ManagedAnimation::render), allow_raw_pointers())
.function("setColor" , optional_override([](ManagedAnimation& self, const std::string& key, SimpleColor4f c) {
self.setColor(key, c.toSkColor());
.function("_setColor" , optional_override([](ManagedAnimation& self, const std::string& key, uintptr_t /* float* */ cPtr) {
float* fourFloats = reinterpret_cast<float*>(cPtr);
SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] };
self.setColor(key, color.toSkColor());
}))
.function("setOpacity", &ManagedAnimation::setOpacity)
.function("getMarkers", &ManagedAnimation::getMarkers)