Reland "[canvaskit] Change SkRects to be arrays, not objects."

This is a reland of bdc214a50e

The CPU version of SkottieWasm is timing out for reasons unknown,
but the GPU version is happy. I think we can get rid of the CPU
version of the job since it has been more or less superseded by
the SkottieFrames one (and the latter is more stable).

Original change's description:
> [canvaskit] Change SkRects to be arrays, not objects.
>
> This changes several APIs, so there are lots of breaking
> notes in the Changelog.
>
> This made the "draw 100 colored regions" benchmark about
> 20% faster (1ms -> .8ms).
>
> In theory, rendering should stay the same.
>
> Change-Id: Ib80b15e2d980ad5d568fff4460d2b529766c1b36
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/312491
> Reviewed-by: Nathaniel Nifong <nifong@google.com>

Change-Id: I674aba85ecfb30b72e94cbaf89b2d97bfae3b7a4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/315142
Reviewed-by: Nathaniel Nifong <nifong@google.com>
This commit is contained in:
Kevin Lubick 2020-09-03 10:02:10 -04:00
parent be72801f29
commit f8823b5726
24 changed files with 621 additions and 495 deletions

View File

@ -417,9 +417,9 @@ Run DM on lottie files with tracing turned on and then parse the output.
[DEPS](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#12): [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/python][recipe_engine/recipe_modules/python], [recipe\_engine/step][recipe_engine/recipe_modules/step], [recipe\_engine/time][recipe_engine/recipe_modules/time], [checkout](#recipe_modules-checkout), [env](#recipe_modules-env), [flavor](#recipe_modules-flavor), [infra](#recipe_modules-infra), [run](#recipe_modules-run), [vars](#recipe_modules-vars)
&mdash; **def [RunSteps](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#83)(api):**
&mdash; **def [RunSteps](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#84)(api):**
&mdash; **def [parse\_trace](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#208)(trace_json, lottie_filename, api, renderer):**
&mdash; **def [parse\_trace](/infra/bots/recipes/perf_skottiewasm_lottieweb.py#209)(trace_json, lottie_filename, api, renderer):**
parse_trace parses the specified trace JSON.

View File

@ -35,7 +35,7 @@
" arg_names, **additional_args)",
" File \"RECIPE_REPO[recipe_engine]/recipe_engine/internal/property_invoker.py\", in _invoke_with_properties",
" return callable_obj(*props, **additional_args)",
" File \"RECIPE_REPO[skia]/infra/bots/recipes/perf_skottiewasm_lottieweb.py\", line 128, in RunSteps",
" File \"RECIPE_REPO[skia]/infra/bots/recipes/perf_skottiewasm_lottieweb.py\", line 129, in RunSteps",
" raise Exception('Could not recognize the buildername %s' % buildername)",
"Exception: Could not recognize the buildername Perf-Debian10-none-GCE-CPU-AVX2-x86_64-Release-All-Unrecognized"
]

View File

@ -69,6 +69,7 @@ SKOTTIE_WASM_EXCLUDE = [
'streetby_loading.json',
'streetby_test_loading.json',
# Times out
'an_endless_hike_on_a_tiny_world_.json', # times out on CPU
'beetle.json',
]

View File

@ -7,12 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Breaking
- SkRect are no longer returned from `CanvasKit.LTRBRect`, `CanvasKit.XYWHRect` nor
are accepted as JS objects. Instead, the format is 4 floats in either an array, a
Float32Array or a piece of memory returned by CanvasKit.Malloc. These floats are the
left, top, right, bottom numbers of the rectangle.
- SkIRect (Rectangles with Integer values) are no longer accepted as JS objects.
Instead, the format is 4 ints in either an array, an Int32Array or a piece of memory
returned by CanvasKit.Malloc. These ints are the left, top, right, bottom numbers of
the rectangle.
- SkRRect (Rectangles with rounded corners) are no longer returned from `CanvasKit.RRectXY`
nor accepted as JS objects. Instead, the format is 12 floats in either an array, a
nor are accepted as JS objects. Instead, the format is 12 floats in either an array, a
Float32Array or a piece of memory returned by CanvasKit.Malloc. The first 4 floats
are the left, top, right, bottom numbers of the rectangle and then 4 sets of points
starting in the upper left corner and going clockwise. This change allows for faster
transfer between JS and WASM code.
- `SkPath.addRoundRect` has been replaced with `SkPath.addRRect`. The same functionality
can be had with the `CanvasKit.RRectXY` helper.
- `SkPath.addRect` no longer accepts 4 floats as separate arguments. It only accepts
an SkRect (an array/Float32Array of 4 floats) and an optional boolean for
determining clockwise or counter-clockwise directionality.
- The order of `SkCanvas.saveLayer` arguments is slightly different (more consistent).
It is now `paint, bounds, backdrop, flags`
### Changed
- We now compile CanvasKit with emsdk 2.0.0 when testing and deploying to npm.
@ -20,6 +35,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The signature of `main` used with SkSL passed to `CanvasKit.SkRuntimeEffect.Make` has changed.
There is no longer an `inout half4 color` parameter, effects must return their color instead.
Valid signatures are now `half4 main()` or `half4 main(float2 coord)`.
- `SkPath.getBounds`, `SkShapedText.getBounds`, and `SkVertices.bounds` now
take an optional argument. If a Float32Array with length 4 or greater is
provided, the bounds will be copied into this array instead of allocating
a new one.
### Removed
- `SkCanvas.drawRoundRect` has been removed in favor of `SkCanvas.drawRRect`
The same functionality can be had with the `CanvasKit.RRectXY` helper.
- `SkPath.arcTo` which had been deprecated in favor of `SkPath.arcToOval`,
`SkPath.arcToRotated`, `SkPath.arcToTangent`.
### Added
- `CanvasKit.LTRBiRect` and `CanvasKit.XYWHiRect` as helpers to create SkIRects.
## [0.17.3] - 2020-08-05

View File

@ -219,7 +219,7 @@
path.lineTo(36, 148);
path.moveTo(150, 180);
path.arcTo(150, 100, 50, 200, 20);
path.arcToTangent(150, 100, 50, 200, 20);
path.lineTo(160, 160);
path.moveTo(20, 120);
@ -227,14 +227,14 @@
canvas.drawPath(path, paint);
let rrect = new CanvasKit.SkPath()
.addRoundRect(100, 10, 140, 62,
10, 4, true);
const rrect = CanvasKit.RRectXY([100, 10, 140, 62], 10, 4);
canvas.drawPath(rrect, paint);
const rrectPath = new CanvasKit.SkPath().addRRect(rrect, true);
canvas.drawPath(rrectPath, paint);
rrectPath.delete();
path.delete();
rrect.delete();
paint.delete();
}
// Intentionally just draw frame once
@ -964,10 +964,10 @@
canvas.drawText(shapedText, textBoxX, textBoxY, textPaint);
const bounds = shapedText.getBounds();
bounds.fLeft += textBoxX;
bounds.fRight += textBoxX;
bounds.fTop += textBoxY;
bounds.fBottom += textBoxY
bounds[0] += textBoxX; // left
bounds[2] += textBoxX; // right
bounds[1] += textBoxY; // top
bounds[3] += textBoxY // bottom
canvas.drawRect(bounds, paint);
const SHAPE_TEST_TEXT = 'VAVAVAVAVAFIfi';
@ -1072,9 +1072,9 @@
fontPaint.setAntiAlias(true);
const arc = new CanvasKit.SkPath();
arc.arcTo(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true);
arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true);
arc.lineTo(210, 140);
arc.arcTo(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true);
arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true);
const str = 'This téxt should follow the curve across contours...';
const textBlob = CanvasKit.SkTextBlob.MakeOnPath(str, arc, font);

View File

@ -81,12 +81,12 @@
p.lifetime = 3 + rand(p.seed);
p.vel.y = -50;
}
void update(inout Particle p) {
float w = mix(15, 3, p.age);
p.pos.x = sin(radians(p.age * 320)) * mix(25, 10, p.age) + mix(-w, w, rand(p.seed));
if (rand(p.seed) < 0.5) { p.pos.x = -p.pos.x; }
p.color.g = (mix(75, 220, p.age) + mix(-30, 30, rand(p.seed))) / 255;
}
`
@ -122,10 +122,10 @@
// Examples requiring external resources.
// Set bounds to fix the 4:3 resolution of the legos
Promise.all([ckLoaded, loadLegoJSON]).then(([ck, jsonstr]) => {
SkottieExample(ck, 'sk_legos', jsonstr, {fLeft: -50, fTop: 0, fRight: 350, fBottom: 300});
SkottieExample(ck, 'sk_legos', jsonstr, [-50, 0, 350, 300]);
});
// Re-size to fit
let fullBounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500};
let fullBounds = [0, 0, 500, 500];
Promise.all([ckLoaded, loadDrinksJSON]).then(([ck, jsonstr]) => {
SkottieExample(ck, 'sk_drinks', jsonstr, fullBounds);
});
@ -146,6 +146,11 @@
Promise.all([ckLoaded, loadSkp]).then((results) => {SkpExample(...results)});
Promise.all([ckLoaded, loadBrickTex, loadBrickBump, loadFont]).then((results) => {Camera3D(...results)});
const rectLeft = 0;
const rectTop = 1;
const rectRight = 2;
const rectBottom = 3;
function SkottieExample(CanvasKit, id, jsonStr, bounds, assets) {
if (!CanvasKit || !jsonStr) {
return;
@ -154,7 +159,7 @@
const duration = animation.duration() * 1000;
const size = animation.size();
let c = document.getElementById(id);
bounds = bounds || {fLeft: 0, fTop: 0, fRight: size.w, fBottom: size.h};
bounds = bounds || CanvasKit.LTRBRect(0, 0, size.w, size.h);
// Basic managed animation test.
if (id === 'sk_drinks') {
@ -172,8 +177,8 @@
function drawFrame(canvas) {
let seek = ((Date.now() - firstFrame) / duration) % 1.0;
let damage = animation.seek(seek);
// TODO: SkRect.isEmpty()?
if (damage.fRight > damage.fLeft && damage.fBottom > damage.fTop) {
if (damage[rectRight] > damage[rectLeft] && damage[rectBottom] > damage[rectTop]) {
canvas.clear(CanvasKit.WHITE);
animation.render(canvas, bounds);
}
@ -181,7 +186,6 @@
}
surface.requestAnimationFrame(drawFrame);
//animation.delete();
return surface;
}
@ -523,7 +527,7 @@
float dp = dot(plane_norm, light_dir);
float scale = min(ambient + max(dp, 0), 1);
color = sample(color_map, p) * half4(float4(scale, scale, scale, 1));
color = sample(color_map, p) * half4(float4(scale, scale, scale, 1));
}
`;
@ -830,7 +834,7 @@
// then covering with semitransparent background color.
if (lastImage) {
canvas.drawImage(lastImage, 0, 0, null);
canvas.drawColor(CanvasKit.Color(171, 244, 255, 0.1)); // sky blue, almost transparent
canvas.drawColor(CanvasKit.Color(171, 244, 255, 0.1)); // sky blue, almost transparent
} else {
canvas.clear(CanvasKit.Color(171, 244, 255)); // sky blue, opaque
}
@ -855,8 +859,8 @@
let r = letter['r'];
// rotate about the center of the glyph's rect.
rotationPoint = [
margin + r.fLeft + (r.fRight - r.fLeft) / 2,
marginTop + r.fTop + (r.fBottom - r.fTop) / 2,
margin + r[rectLeft] + (r[rectRight] - r[rectLeft]) / 2,
marginTop + r[rectTop] + (r[rectBottom] - r[rectTop]) / 2,
0
];
distanceFromPointer = CanvasKit.SkVector.dist(pointer, rotationPoint.slice(0, 2));
@ -870,7 +874,7 @@
CanvasKit.SkM44.rotated([0,1,0], distanceFromPointer * -0.035),
CanvasKit.SkM44.translated(CanvasKit.SkVector.mulScalar(rotationPoint, -1)),
));
canvas.drawParagraph(letter['para'], margin + r.fLeft, marginTop + r.fTop);
canvas.drawParagraph(letter['para'], margin + r[rectLeft], marginTop + r[rectTop]);
i++;
canvas.restore();
}
@ -907,9 +911,9 @@
paint.setColor(red, CanvasKit.SkColorSpace.ADOBE_RGB);
canvas.drawPaint(paint);
paint.setColor(red, CanvasKit.SkColorSpace.DISPLAY_P3);
canvas.drawRoundRect(CanvasKit.LTRBRect(50, 50, 250, 250), 30, 30, paint);
canvas.drawRRect(CanvasKit.RRectXY([50, 50, 250, 250], 30, 30), paint);
paint.setColor(red, CanvasKit.SkColorSpace.SRGB);
canvas.drawRoundRect(CanvasKit.LTRBRect(100, 100, 200, 200), 30, 30, paint);
canvas.drawRRect(CanvasKit.RRectXY([100, 100, 200, 200], 30, 30), paint);
surface.flush();
surface.delete();

View File

@ -223,15 +223,6 @@ sk_sp<SkSurface> MakeRenderTarget(sk_sp<GrContext> grContext, SimpleImageInfo si
// isn't assigned to a JS variable and has delete() called on it.
// These Apply methods, combined with the smarter binding code allow for chainable
// commands that don't leak if the return value is ignored (i.e. when used intuitively).
void ApplyAddArc(SkPath& orig, const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
orig.addArc(oval, startAngle, sweepAngle);
}
void ApplyAddOval(SkPath& orig, const SkRect& oval, bool ccw, unsigned start) {
orig.addOval(oval, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW, start);
}
void ApplyAddPath(SkPath& orig, const SkPath& newPath,
SkScalar scaleX, SkScalar skewX, SkScalar transX,
SkScalar skewY, SkScalar scaleY, SkScalar transY,
@ -244,29 +235,11 @@ void ApplyAddPath(SkPath& orig, const SkPath& newPath,
SkPath::kAppend_AddPathMode);
}
void ApplyAddRect(SkPath& path, SkScalar left, SkScalar top,
SkScalar right, SkScalar bottom, bool ccw) {
path.addRect(left, top, right, bottom, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
}
void ApplyAddRoundRect(SkPath& path, SkScalar left, SkScalar top,
SkScalar right, SkScalar bottom, uintptr_t /* SkScalar* */ rPtr,
bool ccw) {
// See comment below for uintptr_t explanation
const SkScalar* radii = reinterpret_cast<const SkScalar*>(rPtr);
path.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom), radii,
ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
}
void ApplyArcToTangent(SkPath& p, SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
SkScalar radius) {
p.arcTo(x1, y1, x2, y2, radius);
}
void ApplyArcToOval(SkPath& p, SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo) {
p.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
}
void ApplyArcToArcSize(SkPath& orig, SkScalar rx, SkScalar ry, SkScalar xAxisRotate,
bool useSmallArc, bool ccw, SkScalar x, SkScalar y) {
auto arcSize = useSmallArc ? SkPath::ArcSize::kSmall_ArcSize : SkPath::ArcSize::kLarge_ArcSize;
@ -687,16 +660,6 @@ void drawShapedText(SkCanvas& canvas, ShapedText st, SkScalar x,
}
#endif //SK_NO_FONTS
int saveLayerRec(SkCanvas& canvas, const SkPaint* paint,
const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) {
return canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, paint, backdrop, flags));
}
int saveLayerRecBounds(SkCanvas& canvas, const SkPaint* paint, const SkImageFilter* backdrop,
SkCanvas::SaveLayerFlags flags, const SkRect& bounds) {
return canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds, paint, backdrop, flags));
}
// This is simpler than dealing with an SkPoint and SkVector
struct PosTan {
SkScalar px, py, tx, ty;
@ -829,7 +792,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
function("_MakeImage", optional_override([](SimpleImageInfo ii,
uintptr_t /* uint8_t* */ pPtr, int plen,
size_t rowBytes)->sk_sp<SkImage> {
// See comment above for uintptr_t explanation
uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
SkImageInfo info = toSkImageInfo(ii);
sk_sp<SkData> pixelData = SkData::MakeFromMalloc(pixels, plen);
@ -847,7 +809,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
uintptr_t /* SkScalar* */ mPtr,
sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
SkPoint points[] = { start, end };
// See comment above for uintptr_t explanation
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
@ -867,7 +828,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
#ifdef SK_SERIALIZE_SKP
function("_MakeSkPicture", optional_override([](uintptr_t /* unint8_t* */ dPtr,
size_t bytes)->sk_sp<SkPicture> {
// See comment above for uintptr_t explanation
uint8_t* d = reinterpret_cast<uint8_t*>(dPtr);
sk_sp<SkData> data = SkData::MakeFromMalloc(d, bytes);
@ -880,7 +840,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
int count, SkTileMode mode, uint32_t flags,
uintptr_t /* SkScalar* */ mPtr,
sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
if (colorType == SkColorType::kRGBA_F32_SkColorType) {
@ -904,7 +863,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
uint32_t flags,
uintptr_t /* SkScalar* */ mPtr,
sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
if (colorType == SkColorType::kRGBA_F32_SkColorType) {
@ -930,7 +888,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
int count, SkTileMode mode, uint32_t flags,
uintptr_t /* SkScalar* */ mPtr,
sk_sp<SkColorSpace> colorSpace)->sk_sp<SkShader> {
// See comment above for uintptr_t explanation
const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
OptionalMatrix localMatrix(mPtr);
@ -997,21 +954,27 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_clipRRect", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, SkClipOp op, bool doAntiAlias) {
self.clipRRect(ptrToSkRRect(fPtr), op, doAntiAlias);
}))
.function("clipRect", select_overload<void (const SkRect&, SkClipOp, bool)>(&SkCanvas::clipRect))
.function("_clipRect", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, SkClipOp op, bool doAntiAlias) {
const SkRect* rect = reinterpret_cast<const SkRect*>(fPtr);
self.clipRect(*rect, op, doAntiAlias);
}))
.function("_concat", optional_override([](SkCanvas& self, uintptr_t /* SkScalar* */ mPtr) {
// See comment above for uintptr_t explanation
//TODO(skbug.com/10108): make the JS side be column major.
const SkScalar* sixteenMatrixValues = reinterpret_cast<const SkScalar*>(mPtr);
SkM44 m = SkM44::RowMajor(sixteenMatrixValues);
self.concat(m);
}))
.function("drawArc", &SkCanvas::drawArc)
.function("_drawArc", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr,
SkScalar startAngle, SkScalar sweepAngle,
bool useCenter, const SkPaint& paint) {
const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
self.drawArc(*oval, startAngle, sweepAngle, useCenter, paint);
}))
// _drawAtlas takes an array of SkColor. There is no SkColor4f override.
.function("_drawAtlas", optional_override([](SkCanvas& self,
const sk_sp<SkImage>& atlas, uintptr_t /* SkRSXform* */ xptr,
uintptr_t /* SkRect* */ rptr, uintptr_t /* SkColor* */ cptr, int count,
SkBlendMode mode, const SkPaint* paint)->void {
// See comment above for uintptr_t explanation
const SkRSXform* dstXforms = reinterpret_cast<const SkRSXform*>(xptr);
const SkRect* srcRects = reinterpret_cast<const SkRect*>(rptr);
const SkColor* colors = nullptr;
@ -1033,7 +996,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("drawColorInt", optional_override([](SkCanvas& self, SkColor color, SkBlendMode mode) {
self.drawColor(color, mode);
}))
.function("_drawDRRect",optional_override([](SkCanvas& self, uintptr_t /* float* */ outerPtr,
.function("_drawDRRect", optional_override([](SkCanvas& self, uintptr_t /* float* */ outerPtr,
uintptr_t /* float* */ innerPtr, const SkPaint& paint) {
self.drawDRRect(ptrToSkRRect(outerPtr), ptrToSkRRect(innerPtr), paint);
}))
@ -1042,43 +1005,54 @@ EMSCRIPTEN_BINDINGS(Skia) {
self.drawDrawable(aImg.get(), x, y);
}), allow_raw_pointers())
.function("drawImage", select_overload<void (const sk_sp<SkImage>&, SkScalar, SkScalar, const SkPaint*)>(&SkCanvas::drawImage), allow_raw_pointers())
.function("drawImageNine", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
SkIRect center, SkRect dst,
const SkPaint* paint)->void {
self.drawImageNine(image, center, dst, paint);
.function("_drawImageNine", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
uintptr_t /* int* */ centerPtr, uintptr_t /* float* */ dstPtr,
const SkPaint* paint)->void {
const SkIRect* center = reinterpret_cast<const SkIRect*>(centerPtr);
const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
self.drawImageNine(image, *center, *dst, paint);
}), allow_raw_pointers())
.function("drawImageRect", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
SkRect src, SkRect dst,
const SkPaint* paint, bool fastSample)->void {
self.drawImageRect(image, src, dst, paint,
fastSample ? SkCanvas::kFast_SrcRectConstraint :
.function("_drawImageRect", optional_override([](SkCanvas& self, const sk_sp<SkImage>& image,
uintptr_t /* float* */ srcPtr, uintptr_t /* float* */ dstPtr,
const SkPaint* paint, bool fastSample)->void {
const SkRect* src = reinterpret_cast<const SkRect*>(srcPtr);
const SkRect* dst = reinterpret_cast<const SkRect*>(dstPtr);
self.drawImageRect(image, *src, *dst, paint,
fastSample ? SkCanvas::kFast_SrcRectConstraint:
SkCanvas::kStrict_SrcRectConstraint);
}), allow_raw_pointers())
.function("drawLine", select_overload<void (SkScalar, SkScalar, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawLine))
.function("drawOval", &SkCanvas::drawOval)
.function("_drawOval", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr,
const SkPaint paint)->void {
const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
self.drawOval(*oval, paint);
}))
.function("drawPaint", &SkCanvas::drawPaint)
#ifdef SK_INCLUDE_PARAGRAPH
.function("drawParagraph", optional_override([](SkCanvas& self, skia::textlayout::Paragraph* p,
SkScalar x, SkScalar y) {
SkScalar x, SkScalar y) {
p->paint(&self, x, y);
}), allow_raw_pointers())
#endif
.function("drawPath", &SkCanvas::drawPath)
// Of note, picture is *not* what is colloquially thought of as a "picture", what we call
// a bitmap. An SkPicture is a series of draw commands.
.function("drawPicture", select_overload<void (const sk_sp<SkPicture>&)>(&SkCanvas::drawPicture))
.function("drawPicture", select_overload<void (const sk_sp<SkPicture>&)>(&SkCanvas::drawPicture))
.function("_drawPoints", optional_override([](SkCanvas& self, SkCanvas::PointMode mode,
uintptr_t /* SkPoint* */ pptr,
int count, SkPaint paint)->void {
// See comment above for uintptr_t explanation
const SkPoint* pts = reinterpret_cast<const SkPoint*>(pptr);
self.drawPoints(mode, count, pts, paint);
}))
.function("_drawRRect",optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr, const SkPaint& paint) {
self.drawRRect(ptrToSkRRect(fPtr), paint);
}))
.function("drawRect", &SkCanvas::drawRect)
.function("drawRoundRect", &SkCanvas::drawRoundRect)
.function("_drawRect", optional_override([](SkCanvas& self, uintptr_t /* float* */ fPtr,
const SkPaint paint)->void {
const SkRect* rect = reinterpret_cast<const SkRect*>(fPtr);
self.drawRect(*rect, paint);
}))
.function("_drawShadow", optional_override([](SkCanvas& self, const SkPath& path,
const SkPoint3& zPlaneParams,
const SkPoint3& lightPos, SkScalar lightRadius,
@ -1095,7 +1069,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_drawSimpleText", optional_override([](SkCanvas& self, uintptr_t /* char* */ sptr,
size_t len, SkScalar x, SkScalar y, const SkFont& font,
const SkPaint& paint) {
// See comment above for uintptr_t explanation
const char* str = reinterpret_cast<const char*>(sptr);
self.drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint);
@ -1109,7 +1082,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
// 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
@ -1147,25 +1119,17 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("restoreToCount", &SkCanvas::restoreToCount)
.function("rotate", select_overload<void (SkScalar, SkScalar, SkScalar)>(&SkCanvas::rotate))
.function("save", &SkCanvas::save)
// 1 param (only the paint)
.function("saveLayer", optional_override([](SkCanvas& self, const SkPaint* p) {
return self.saveLayer(nullptr, p);
.function("_saveLayer", optional_override([](SkCanvas& self, const SkPaint* p, uintptr_t /* float* */ fPtr,
const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags)->int {
SkRect* bounds = reinterpret_cast<SkRect*>(fPtr);
return self.saveLayer(SkCanvas::SaveLayerRec(bounds, p, backdrop, flags));
}), allow_raw_pointers())
// 2 params
.function("saveLayer", select_overload<int (const SkRect&, const SkPaint*)>(&SkCanvas::saveLayer),
allow_raw_pointers())
// 3 params (effectively with SaveLayerRec, but no bounds)
.function("saveLayer", saveLayerRec, allow_raw_pointers())
// 4 params (effectively with SaveLayerRec)
.function("saveLayer", saveLayerRecBounds, allow_raw_pointers())
.function("scale", &SkCanvas::scale)
.function("skew", &SkCanvas::skew)
.function("translate", &SkCanvas::translate)
.function("_writePixels", optional_override([](SkCanvas& self, SimpleImageInfo di,
uintptr_t /* uint8_t* */ pPtr,
size_t srcRowBytes, int dstX, int dstY) {
// See comment above for uintptr_t explanation
uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
SkImageInfo dstInfo = toSkImageInfo(di);
@ -1278,14 +1242,17 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<ShapedText>("ShapedText")
.constructor<ShapedTextOpts>()
.function("getBounds", &ShapedText::getBounds);
.function("_getBounds", optional_override([](ShapedText& self,
uintptr_t /* float* */ fPtr)->void {
SkRect* output = reinterpret_cast<SkRect*>(fPtr);
output[0] = self.getBounds();
}));
class_<SkFontMgr>("SkFontMgr")
.smart_ptr<sk_sp<SkFontMgr>>("sk_sp<SkFontMgr>")
.class_function("_fromData", optional_override([](uintptr_t /* uint8_t** */ dPtr,
uintptr_t /* size_t* */ sPtr,
int numFonts)->sk_sp<SkFontMgr> {
// See comment above for uintptr_t explanation
auto datas = reinterpret_cast<const uint8_t**>(dPtr);
auto sizes = reinterpret_cast<const size_t*>(sPtr);
@ -1315,7 +1282,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_makeTypefaceFromData", optional_override([](SkFontMgr& self,
uintptr_t /* uint8_t* */ fPtr,
int flen)->sk_sp<SkTypeface> {
// See comment above for uintptr_t explanation
uint8_t* font = reinterpret_cast<uint8_t*>(fPtr);
sk_sp<SkData> fontData = SkData::MakeFromMalloc(font, flen);
@ -1338,10 +1304,9 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_readPixels", optional_override([](sk_sp<SkImage> self,
SimpleImageInfo sii, uintptr_t /* uint8_t* */ pPtr,
size_t dstRowBytes, int srcX, int srcY)->bool {
// See comment above for uintptr_t explanation
uint8_t* pixels = reinterpret_cast<uint8_t*>(pPtr);
SkImageInfo ii = toSkImageInfo(sii);
// TODO: Migrate CanvasKit API to require DirectContext arg here.
// TODO(adlai) Migrate CanvasKit API to require DirectContext arg here.
GrDirectContext* dContext = nullptr;
#ifdef SK_GL
dContext = GrAsDirectContext(as_IB(self.get())->context());
@ -1353,12 +1318,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
.smart_ptr<sk_sp<SkImageFilter>>("sk_sp<SkImageFilter>")
.class_function("MakeBlur", optional_override([](SkScalar sigmaX, SkScalar sigmaY,
SkTileMode tileMode, sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
// Emscripten does not like default args nor SkIRect* much
return SkImageFilters::Blur(sigmaX, sigmaY, tileMode, input);
}))
.class_function("MakeColorFilter", optional_override([](sk_sp<SkColorFilter> cf,
sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
// Emscripten does not like default args nor SkIRect* much
sk_sp<SkImageFilter> input)->sk_sp<SkImageFilter> {
return SkImageFilters::ColorFilter(cf, input);
}))
.class_function("MakeCompose", &SkImageFilters::Compose)
@ -1439,7 +1402,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.class_function("MakeCorner", &SkCornerPathEffect::Make)
.class_function("_MakeDash", optional_override([](uintptr_t /* float* */ cptr, int count,
SkScalar phase)->sk_sp<SkPathEffect> {
// See comment above for uintptr_t explanation
const float* intervals = reinterpret_cast<const float*>(cptr);
return SkDashPathEffect::Make(intervals, count, phase);
}), allow_raw_pointers())
@ -1450,23 +1412,44 @@ EMSCRIPTEN_BINDINGS(Skia) {
.constructor<const SkPath&>()
.class_function("_MakeFromCmds", &MakePathFromCmds)
.class_function("_MakeFromVerbsPointsWeights", &MakePathFromVerbsPointsWeights)
.function("_addArc", &ApplyAddArc)
.function("_addArc", optional_override([](SkPath& self,
uintptr_t /* float* */ fPtr,
SkScalar startAngle, SkScalar sweepAngle)->void {
const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
self.addArc(*oval, startAngle, sweepAngle);
}))
.function("_addOval", optional_override([](SkPath& self,
uintptr_t /* float* */ fPtr,
bool ccw, unsigned start)->void {
const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
self.addOval(*oval, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW, start);
}))
// interface.js has 3 overloads of addPath
.function("_addOval", &ApplyAddOval)
.function("_addPath", &ApplyAddPath)
.function("_addPoly", optional_override([](SkPath& self,
uintptr_t /* SkPoint* */ pptr,
uintptr_t /* SkPoint* */ fPtr,
int count, bool close)->void {
// See comment above for uintptr_t explanation
const SkPoint* pts = reinterpret_cast<const SkPoint*>(pptr);
const SkPoint* pts = reinterpret_cast<const SkPoint*>(fPtr);
self.addPoly(pts, count, close);
}))
// interface.js has 4 overloads of addRect
.function("_addRect", &ApplyAddRect)
// interface.js has 4 overloads of addRoundRect
.function("_addRoundRect", &ApplyAddRoundRect)
.function("_addRect", optional_override([](SkPath& self,
uintptr_t /* float* */ fPtr,
bool ccw)->void {
const SkRect* rect = reinterpret_cast<const SkRect*>(fPtr);
self.addRect(*rect, ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
}))
.function("_addRRect", optional_override([](SkPath& self,
uintptr_t /* float* */ fPtr,
bool ccw)->void {
self.addRRect(ptrToSkRRect(fPtr), ccw ? SkPathDirection::kCCW : SkPathDirection::kCW);
}))
.function("_addVerbsPointsWeights", &PathAddVerbsPointsWeights)
.function("_arcToOval", &ApplyArcToOval)
.function("_arcToOval", optional_override([](SkPath& self,
uintptr_t /* float* */ fPtr, SkScalar startAngle,
SkScalar sweepAngle, bool forceMoveTo)->void {
const SkRect* oval = reinterpret_cast<const SkRect*>(fPtr);
self.arcTo(*oval, startAngle, sweepAngle, forceMoveTo);
}))
.function("_arcToRotated", &ApplyArcToArcSize)
.function("_arcToTangent", ApplyArcToTangent)
.function("_close", &ApplyClose)
@ -1507,7 +1490,11 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("setFillType", select_overload<void(SkPathFillType)>(&SkPath::setFillType))
.function("getFillType", &SkPath::getFillType)
.function("getBounds", &SkPath::getBounds)
.function("_getBounds", optional_override([](SkPath& self,
uintptr_t /* float* */ fPtr)->void {
SkRect* output = reinterpret_cast<SkRect*>(fPtr);
output[0] = self.getBounds();
}))
.function("computeTightBounds", &SkPath::computeTightBounds)
.function("equals", &Equals)
.function("copy", &CopyPath)
@ -1543,9 +1530,10 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkPictureRecorder>("SkPictureRecorder")
.constructor<>()
.function("beginRecording", optional_override([](SkPictureRecorder& self,
const SkRect& bounds) -> SkCanvas* {
return self.beginRecording(bounds);
.function("_beginRecording", optional_override([](SkPictureRecorder& self,
uintptr_t /* float* */ fPtr) -> SkCanvas* {
SkRect* bounds = reinterpret_cast<SkRect*>(fPtr);
return self.beginRecording(*bounds, nullptr);
}), allow_raw_pointers())
.function("finishRecordingAsPicture", optional_override([](SkPictureRecorder& self)
-> sk_sp<SkPicture> {
@ -1590,7 +1578,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
}))
.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);
@ -1600,7 +1587,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_makeShaderWithChildren", optional_override([](SkRuntimeEffect& self, uintptr_t fPtr, size_t fLen, bool isOpaque,
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);
@ -1627,8 +1613,13 @@ EMSCRIPTEN_BINDINGS(Skia) {
return {ii.width(), ii.height(), ii.colorType(), ii.alphaType(), ii.refColorSpace()};
}))
.function("height", &SkSurface::height)
.function("makeImageSnapshot", select_overload<sk_sp<SkImage>()>(&SkSurface::makeImageSnapshot))
.function("makeImageSnapshot", select_overload<sk_sp<SkImage>(const SkIRect& bounds)>(&SkSurface::makeImageSnapshot))
.function("_makeImageSnapshot", optional_override([](SkSurface& self, uintptr_t /* int* */ iPtr)->sk_sp<SkImage> {
SkIRect* bounds = reinterpret_cast<SkIRect*>(iPtr);
if (!bounds) {
return self.makeImageSnapshot();
}
return self.makeImageSnapshot(*bounds);
}))
.function("makeSurface", optional_override([](SkSurface& self, SimpleImageInfo sii)->sk_sp<SkSurface> {
return self.makeSurface(toSkImageInfo(sii));
}), allow_raw_pointers())
@ -1654,7 +1645,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
size_t strBtyes,
uintptr_t /* SkRSXform* */ xptr,
const SkFont& font)->sk_sp<SkTextBlob> {
// See comment above for uintptr_t explanation
const char* str = reinterpret_cast<const char*>(sptr);
const SkRSXform* xforms = reinterpret_cast<const SkRSXform*>(xptr);
@ -1662,7 +1652,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
}), allow_raw_pointers())
.class_function("_MakeFromText", optional_override([](uintptr_t /* char* */ sptr,
size_t len, const SkFont& font)->sk_sp<SkTextBlob> {
// See comment above for uintptr_t explanation
const char* str = reinterpret_cast<const char*>(sptr);
return SkTextBlob::MakeFromText(str, len, font, SkTextEncoding::kUTF8);
}), allow_raw_pointers());
@ -1673,7 +1662,11 @@ EMSCRIPTEN_BINDINGS(Skia) {
class_<SkVertices>("SkVertices")
.smart_ptr<sk_sp<SkVertices>>("sk_sp<SkVertices>")
.function("bounds", &SkVertices::bounds)
.function("_bounds", optional_override([](SkVertices& self,
uintptr_t /* float* */ fPtr)->void {
SkRect* output = reinterpret_cast<SkRect*>(fPtr);
output[0] = self.bounds();
}))
.function("uniqueID", &SkVertices::uniqueID);
// Not intended to be called directly by clients
@ -1844,17 +1837,6 @@ EMSCRIPTEN_BINDINGS(Skia) {
.field("text", &ShapedTextOpts::text)
.field("width", &ShapedTextOpts::width);
#endif
value_object<SkRect>("SkRect")
.field("fLeft", &SkRect::fLeft)
.field("fTop", &SkRect::fTop)
.field("fRight", &SkRect::fRight)
.field("fBottom", &SkRect::fBottom);
value_object<SkIRect>("SkIRect")
.field("fLeft", &SkIRect::fLeft)
.field("fTop", &SkIRect::fTop)
.field("fRight", &SkIRect::fRight)
.field("fBottom", &SkIRect::fBottom);
value_object<SimpleImageInfo>("SkImageInfo")
.field("width", &SimpleImageInfo::width)

View File

@ -77,9 +77,9 @@
this._canvas.getContext('2d').putImageData(imageData, 0, 0);
} else {
this._canvas.getContext('2d').putImageData(imageData, 0, 0,
dirtyRect.fLeft, dirtyRect.fTop,
dirtyRect.fRight - dirtyRect.fLeft,
dirtyRect.fBottom - dirtyRect.fTop);
dirtyRect[0], dirtyRect[1],
dirtyRect[2] - dirtyRect[0],
dirtyRect[3] - dirtyRect[1]);
}
}
};

View File

@ -27,11 +27,10 @@ var CanvasKit = {
Color: function() {},
Color4f: function() {},
ColorAsInt: function() {},
/** @return {CanvasKit.SkRect} */
LTRBRect: function() {},
/** @return {CanvasKit.SkRect} */
XYWHRect: function() {},
/** @return {CanvasKit.SkRRect} */
LTRBiRect: function() {},
XYWHiRect: function() {},
RRectXY: function() {},
/** @return {ImageData} */
ImageData: function() {},
@ -101,6 +100,13 @@ var CanvasKit = {
// Objects and properties on CanvasKit
Animation: {
prototype: {
render: function() {},
},
_render: function() {},
},
GrContext: {
// public API (from C++ bindings)
getResourceCacheLimitBytes: function() {},
@ -111,8 +117,14 @@ var CanvasKit = {
ManagedAnimation: {
prototype: {
render: function() {},
seek: function() {},
seekFrame: function() {},
setColor: function() {},
},
_render: function() {},
_seek: function() {},
_seekFrame: function() {},
},
Paragraph: {
@ -168,8 +180,11 @@ var CanvasKit = {
SkRectBuilder: function() {},
ShapedText: {
// public API (from C++ bindings)
getBounds: function() {},
prototype: {
getBounds: function() {},
},
// private API (from C++ bindings)
_getBounds: function() {},
},
SkAnimatedImage: {
@ -185,22 +200,15 @@ var CanvasKit = {
SkCanvas: {
// public API (from C++ bindings)
clipPath: function() {},
clipRect: function() {},
drawAnimatedImage: function() {},
drawArc: function() {},
drawCircle: function() {},
drawColorInt: function() {},
drawImage: function() {},
drawImageNine: function() {},
drawImageRect: function() {},
drawLine: function() {},
drawOval: function() {},
drawPaint: function() {},
drawParagraph: function() {},
drawPath: function() {},
drawPicture: function() {},
drawRect: function() {},
drawRoundRect: function() {},
drawText: function() {},
drawTextBlob: function() {},
drawVertices: function() {},
@ -213,7 +221,6 @@ var CanvasKit = {
restoreToCount: function() {},
rotate: function() {},
save: function() {},
saveLayer: function() {},
scale: function() {},
skew: function() {},
translate: function() {},
@ -221,13 +228,19 @@ var CanvasKit = {
prototype: {
clear: function() {},
clipRRect: function() {},
clipRect: function() {},
concat44: function() {}, // deprecated
concat: function() {},
drawArc: function() {},
drawAtlas: function() {},
drawColor: function() {},
drawColorComponents: function() {},
drawDRRect: function() {},
drawImageNine: function() {},
drawImageRect: function() {},
drawOval: function() {},
drawPoints: function() {},
drawRect: function() {},
drawRRect: function() {},
drawShadow: function() {},
drawText: function() {},
@ -235,17 +248,24 @@ var CanvasKit = {
getLocalToDevice: function() {},
getTotalMatrix: function() {},
readPixels: function() {},
saveLayer: function() {},
writePixels : function() {},
},
// private API
_clear: function() {},
_clipRRect: function() {},
_clipRect: function() {},
_concat: function() {},
_drawArc: function() {},
_drawAtlas: function() {},
_drawColor: function() {},
_drawDRRect: function() {},
_drawImageNine: function() {},
_drawImageRect: function() {},
_drawOval: function() {},
_drawPoints: function() {},
_drawRect: function() {},
_drawRRect: function() {},
_drawShadow: function() {},
_drawSimpleText: function() {},
@ -253,6 +273,7 @@ var CanvasKit = {
_getLocalToDevice: function() {},
_getTotalMatrix: function() {},
_readPixels: function() {},
_saveLayer: function() {},
_writePixels: function() {},
delete: function() {},
},
@ -480,21 +501,53 @@ var CanvasKit = {
toCmds: function() {},
toSVGString: function() {},
prototype: {
addArc: function() {},
addOval: function() {},
addPath: function() {},
addPoly: function() {},
addRect: function() {},
addRRect: function() {},
addVerbsPointsWeights: function() {},
arc: function() {},
arcToOval: function() {},
arcToRotated: function() {},
arcToTangent: function() {},
close: function() {},
conicTo: function() {},
cubicTo: function() {},
dash: function() {},
lineTo: function() {},
moveTo: function() {},
offset: function() {},
op: function() {},
quadTo: function() {},
rArcTo: function() {},
rConicTo: function() {},
rCubicTo: function() {},
rect: function() {},
rLineTo: function() {},
rMoveTo: function() {},
rQuadTo: function() {},
simplify: function() {},
stroke: function() {},
transform: function() {},
trim: function() {},
},
// private API
_MakeFromCmds: function() {},
_MakeFromVerbsPointsWeights: function() {},
_addArc: function() {},
_addOval: function() {},
_addPath: function() {},
_addRect: function() {},
_addPoly: function() {},
_addRoundRect: function() {},
_addRect: function() {},
_addRRect: function() {},
_addVerbsPointsWeights: function() {},
_arc: function() {},
_arcTo: function() {},
_arcToOval: function() {},
_arcToTangent: function() {},
_arcToRotated: function() {},
_arcToTangent: function() {},
_close: function() {},
_conicTo: function() {},
_cubicTo: function() {},
@ -506,10 +559,10 @@ var CanvasKit = {
_rArcTo: function() {},
_rConicTo: function() {},
_rCubicTo: function() {},
_rect: function() {},
_rLineTo: function() {},
_rMoveTo: function() {},
_rQuadTo: function() {},
_rect: function() {},
_simplify: function() {},
_stroke: function() {},
_transform: function() {},
@ -532,27 +585,11 @@ var CanvasKit = {
},
SkPictureRecorder: {
beginRecording: function() {},
finishRecordingAsPicture: function() {},
},
SkRect: {
fLeft: {},
fTop: {},
fRight: {},
fBottom: {},
},
SkRRect: {
rect: {},
rx1: {},
ry1: {},
rx2: {},
ry2: {},
rx3: {},
ry3: {},
rx4: {},
ry4: {},
prototype: {
beginRecording: function() {},
},
_beginRecording: function() {},
},
SkShader: {
@ -574,17 +611,22 @@ var CanvasKit = {
/** @return {CanvasKit.SkCanvas} */
getCanvas: function() {},
imageInfo: function() {},
/** @return {CanvasKit.SkImage} */
makeImageSnapshot: function() {},
makeSurface: function() {},
sampleCnt: function() {},
reportBackendTypeIsGPU: function() {},
grContext: {},
openGLversion: {},
prototype: {
/** @return {CanvasKit.SkImage} */
makeImageSnapshot: function() {},
},
// private API
_flush: function() {},
_getRasterN32PremulSurface: function() {},
_makeImageSnapshot: function() {},
delete: function() {},
},
@ -613,10 +655,14 @@ var CanvasKit = {
SkVertices: {
// public API (from C++ bindings)
bounds: function() {},
mode: function() {},
uniqueID: function() {},
vertexCount: function() {},
prototype: {
bounds: function() {},
},
// private API (from C++ bindings)
_bounds: function() {},
},
_SkVerticesBuilder: {
@ -899,39 +945,6 @@ var CanvasKit = {
// unless they go on the prototype.
CanvasKit.Paragraph.prototype.getRectsForRange = function() {};
CanvasKit.SkPath.prototype.addArc = function() {};
CanvasKit.SkPath.prototype.addOval = function() {};
CanvasKit.SkPath.prototype.addPath = function() {};
CanvasKit.SkPath.prototype.addPoly = function() {};
CanvasKit.SkPath.prototype.addRect = function() {};
CanvasKit.SkPath.prototype.addRoundRect = function() {};
CanvasKit.SkPath.prototype.addVerbsPointsWeights = function() {};
CanvasKit.SkPath.prototype.arc = function() {};
CanvasKit.SkPath.prototype.arcTo = function() {};
CanvasKit.SkPath.prototype.arcToOval = function() {};
CanvasKit.SkPath.prototype.arcToTangent = function() {};
CanvasKit.SkPath.prototype.arcToRotated = function() {};
CanvasKit.SkPath.prototype.close = function() {};
CanvasKit.SkPath.prototype.conicTo = function() {};
CanvasKit.SkPath.prototype.cubicTo = function() {};
CanvasKit.SkPath.prototype.dash = function() {};
CanvasKit.SkPath.prototype.lineTo = function() {};
CanvasKit.SkPath.prototype.moveTo = function() {};
CanvasKit.SkPath.prototype.offset = function() {};
CanvasKit.SkPath.prototype.op = function() {};
CanvasKit.SkPath.prototype.quadTo = function() {};
CanvasKit.SkPath.prototype.rArcTo = function() {};
CanvasKit.SkPath.prototype.rConicTo = function() {};
CanvasKit.SkPath.prototype.rCubicTo = function() {};
CanvasKit.SkPath.prototype.rLineTo = function() {};
CanvasKit.SkPath.prototype.rMoveTo = function() {};
CanvasKit.SkPath.prototype.rQuadTo = function() {};
CanvasKit.SkPath.prototype.rect = function() {};
CanvasKit.SkPath.prototype.simplify = function() {};
CanvasKit.SkPath.prototype.stroke = function() {};
CanvasKit.SkPath.prototype.transform = function() {};
CanvasKit.SkPath.prototype.trim = function() {};
CanvasKit.SkPicture.prototype.saveAsFile = function() {};
CanvasKit.SkSurface.prototype.dispose = function() {};

View File

@ -94,6 +94,19 @@ CanvasKit._extraInitializations.push(function() {
return font;
}
// Clients can pass in a Float32Array with length 4 to this and the results
// will be copied into that array. Otherwise, a new TypedArray will be allocated
// and returned.
CanvasKit.ShapedText.prototype.getBounds = function(optionalOutputArray) {
this._getBounds(_scratchRectPtr);
var ta = _scratchRect['toTypedArray']();
if (optionalOutputArray) {
optionalOutputArray.set(ta);
return optionalOutputArray;
}
return ta.slice();
}
CanvasKit.SkTextBlob.MakeOnPath = function(str, path, font, initialOffset) {
if (!str || !str.length) {
SkDebug('ignoring 0 length string');

View File

@ -43,31 +43,31 @@ CanvasKit.Color4f = function(r, g, b, a) {
// Color constants use property getters to prevent other code from accidentally
// changing them.
Object.defineProperty(CanvasKit, "TRANSPARENT", {
Object.defineProperty(CanvasKit, 'TRANSPARENT', {
get: function() { return CanvasKit.Color4f(0, 0, 0, 0); }
});
Object.defineProperty(CanvasKit, "BLACK", {
Object.defineProperty(CanvasKit, 'BLACK', {
get: function() { return CanvasKit.Color4f(0, 0, 0, 1); }
});
Object.defineProperty(CanvasKit, "WHITE", {
Object.defineProperty(CanvasKit, 'WHITE', {
get: function() { return CanvasKit.Color4f(1, 1, 1, 1); }
});
Object.defineProperty(CanvasKit, "RED", {
Object.defineProperty(CanvasKit, 'RED', {
get: function() { return CanvasKit.Color4f(1, 0, 0, 1); }
});
Object.defineProperty(CanvasKit, "GREEN", {
Object.defineProperty(CanvasKit, 'GREEN', {
get: function() { return CanvasKit.Color4f(0, 1, 0, 1); }
});
Object.defineProperty(CanvasKit, "BLUE", {
Object.defineProperty(CanvasKit, 'BLUE', {
get: function() { return CanvasKit.Color4f(0, 0, 1, 1); }
});
Object.defineProperty(CanvasKit, "YELLOW", {
Object.defineProperty(CanvasKit, 'YELLOW', {
get: function() { return CanvasKit.Color4f(1, 1, 0, 1); }
});
Object.defineProperty(CanvasKit, "CYAN", {
Object.defineProperty(CanvasKit, 'CYAN', {
get: function() { return CanvasKit.Color4f(0, 1, 1, 1); }
});
Object.defineProperty(CanvasKit, "MAGENTA", {
Object.defineProperty(CanvasKit, 'MAGENTA', {
get: function() { return CanvasKit.Color4f(1, 0, 1, 1); }
});
@ -207,9 +207,9 @@ function degreesToRadians(deg) {
// See https://stackoverflow.com/a/31090240
// This contraption keeps closure from minifying away the check
// if btoa is defined *and* prevents runtime "btoa" or "window" is not defined.
// if btoa is defined *and* prevents runtime 'btoa' or 'window' is not defined.
// Defined outside any scopes to make it available in all files.
var isNode = !(new Function("try {return this===window;}catch(e){ return false;}")());
var isNode = !(new Function('try {return this===window;}catch(e){ return false;}')());
function almostEqual(floata, floatb) {
return Math.abs(floata - floatb) < 0.00001;
@ -218,7 +218,7 @@ function almostEqual(floata, floatb) {
var nullptr = 0; // emscripten doesn't like to take null as uintptr_t
// arr can be a normal JS array or a TypedArray
// dest is a string like "HEAPU32" that specifies the type the src array
// dest is a string like 'HEAPU32' that specifies the type the src array
// should be copied into.
// ptr can be optionally provided if the memory was already allocated.
function copy1dArray(arr, dest, ptr) {
@ -290,15 +290,15 @@ function copyFlexibleColorArray(colors) {
colorType: CanvasKit.ColorType.RGBA_F32,
}
if (colors instanceof Float32Array) {
result.colorPtr = copy1dArray(colors, "HEAPF32");
result.colorPtr = copy1dArray(colors, 'HEAPF32');
result.count = colors.length / 4;
} else if (colors instanceof Uint32Array) {
result.colorPtr = copy1dArray(colors, "HEAPU32");
result.colorPtr = copy1dArray(colors, 'HEAPU32');
result.colorType = CanvasKit.ColorType.RGBA_8888;
} else if (colors instanceof Array && colors[0] instanceof Float32Array) {
result.colorPtr = copy2dArray(colors, "HEAPF32");
result.colorPtr = copy2dArray(colors, 'HEAPF32');
} else {
throw('Invalid argument to copyFlexibleColorArray, Not a color array '+typeof(colors));
}
@ -326,9 +326,8 @@ function copy3x3MatrixToWasm(matr) {
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.
var mPtr = copy1dArray(matr, "HEAPF32", _scratch3x3MatrixPtr);
// matr should be an array or typed array.
var mPtr = copy1dArray(matr, 'HEAPF32', _scratch3x3MatrixPtr);
if (matr.length === 6) {
// Overwrite the last 3 floats with the default perspective. The divide
// by 4 casts the pointer into a float pointer.
@ -365,9 +364,8 @@ function copy4x4MatrixToWasm(matr) {
throw 'invalid matrix size';
}
if (matr.length === 16) {
// This should be an array or typed array.
// have to divide the pointer by 4 to "cast" it from bytes to float.
return copy1dArray(matr, "HEAPF32", _scratch4x4MatrixPtr);
// matr should be an array or typed array.
return copy1dArray(matr, 'HEAPF32', _scratch4x4MatrixPtr);
}
// Upscale the row-major 3x3 or 3x2 matrix into a 4x4 row-major matrix
// TODO(skbug.com/10108) This will need to change when we convert our
@ -430,7 +428,7 @@ function copy4x4MatrixFromWasm(matrPtr) {
// typedArrays, then we should return a typed array here too.
var rv = new Array(16);
for (var i = 0; i < 16; i++) {
rv[i] = CanvasKit.HEAPF32[matrPtr/4 + i]; // divide by 4 to "cast" to float.
rv[i] = CanvasKit.HEAPF32[matrPtr/4 + i]; // divide by 4 to cast to float.
}
return rv;
}
@ -439,7 +437,7 @@ var _scratchColorPtr = nullptr;
var _scratchColor; // the result from CanvasKit.Malloc
function copyColorToWasm(color4f, ptr) {
return copy1dArray(color4f, "HEAPF32", ptr || _scratchColorPtr);
return copy1dArray(color4f, 'HEAPF32', ptr || _scratchColorPtr);
}
function copyColorComponentsToWasm(r, g, b, a) {
@ -453,18 +451,35 @@ function copyColorComponentsToWasm(r, g, b, a) {
function copyColorToWasmNoScratch(color4f) {
// TODO(kjlubick): accept 4 floats or int color
return copy1dArray(color4f, "HEAPF32");
return copy1dArray(color4f, 'HEAPF32');
}
// 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.
rv[i] = CanvasKit.HEAPF32[colorPtr/4 + i]; // divide by 4 to cast to float.
}
return rv;
}
// These will be initialized after loading.
var _scratchRect;
var _scratchRectPtr = nullptr;
var _scratchRect2;
var _scratchRect2Ptr = nullptr;
function copyRectToWasm(fourFloats, ptr) {
return copy1dArray(fourFloats, 'HEAPF32', ptr || _scratchRectPtr);
}
var _scratchIRect;
var _scratchIRectPtr = nullptr;
function copyIRectToWasm(fourInts, ptr) {
return copy1dArray(fourInts, 'HEAP32', ptr || _scratchIRectPtr);
}
// These will be initialized after loading.
var _scratchRRect;
var _scratchRRectPtr = nullptr;
@ -474,7 +489,7 @@ var _scratchRRect2Ptr = nullptr;
function copyRRectToWasm(twelveFloats, ptr) {
return copy1dArray(twelveFloats, "HEAPF32", ptr || _scratchRRectPtr);
return copy1dArray(twelveFloats, 'HEAPF32', ptr || _scratchRRectPtr);
}
// Caching the Float32Arrays can save having to reallocate them
@ -517,7 +532,7 @@ function loadCmdsTypedArray(arr) {
}
}
var ptr = copy1dArray(ta, "HEAPF32");
var ptr = copy1dArray(ta, 'HEAPF32');
return [ptr, len];
}
@ -607,7 +622,7 @@ CanvasKit.FourFloatArrayHelper.prototype.build = function() {
if (this._ptr) {
return this._ptr;
}
this._ptr = copy1dArray(this._floats, "HEAPF32");
this._ptr = copy1dArray(this._floats, 'HEAPF32');
return this._ptr;
}
@ -680,7 +695,7 @@ CanvasKit.OneUIntArrayHelper.prototype.build = function() {
if (this._ptr) {
return this._ptr;
}
this._ptr = copy1dArray(this._uints, "HEAPU32");
this._ptr = copy1dArray(this._uints, 'HEAPU32');
return this._ptr;
}

View File

@ -35,7 +35,7 @@ function closePath(skpath) {
}
// Check to see if we are not just a single point
var bounds = skpath.getBounds();
if ((bounds.fBottom - bounds.fTop) || (bounds.fRight - bounds.fLeft)) {
if ((bounds[3] - bounds[1]) || (bounds[2] - bounds[0])) {
skpath.close();
}
}
@ -134,11 +134,12 @@ function quadraticCurveTo(skpath, cpx, cpy, x, y) {
}
function rect(skpath, x, y, width, height) {
if (!allAreFinite([x, y, width, height])) {
var rect = CanvasKit.XYWHRect(x, y, width, height);
if (!allAreFinite(rect)) {
return;
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect
skpath.addRect(x, y, x+width, y+height);
skpath.addRect(rect);
}
function Path2D(path) {

View File

@ -23,6 +23,15 @@ CanvasKit.onRuntimeInitialized = function() {
_scratchRRect2 = CanvasKit.Malloc(Float32Array, 12); // 4 scalars for rrect, 8 for radii.
_scratchRRect2Ptr = _scratchRRect2['byteOffset'];
_scratchRect = CanvasKit.Malloc(Float32Array, 4);
_scratchRectPtr = _scratchRect['byteOffset'];
_scratchRect2 = CanvasKit.Malloc(Float32Array, 4);
_scratchRect2Ptr = _scratchRect2['byteOffset'];
_scratchIRect = CanvasKit.Malloc(Int32Array, 4);
_scratchIRectPtr = _scratchIRect['byteOffset'];
// Create single copies of all three supported color spaces
// These are sk_sp<SkColorSpace>
CanvasKit.SkColorSpace.SRGB = CanvasKit.SkColorSpace._MakeSRGB();
@ -466,8 +475,8 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit.SkM44.setupCamera = function(area, zscale, cam) {
var camera = CanvasKit.SkM44.lookat(cam['eye'], cam['coa'], cam['up']);
var perspective = CanvasKit.SkM44.perspective(cam['near'], cam['far'], cam['angle']);
var center = [(area.fLeft + area.fRight)/2, (area.fTop + area.fBottom)/2, 0];
var viewScale = [(area.fRight - area.fLeft)/2, (area.fBottom - area.fTop)/2, zscale];
var center = [(area[0] + area[2])/2, (area[1] + area[3])/2, 0];
var viewScale = [(area[2] - area[0])/2, (area[3] - area[1])/2, zscale];
var viewport = CanvasKit.SkM44.multiply(
CanvasKit.SkM44.translated(center),
CanvasKit.SkM44.scaled(viewScale));
@ -590,7 +599,8 @@ CanvasKit.onRuntimeInitialized = function() {
CanvasKit.SkPath.prototype.addArc = function(oval, startAngle, sweepAngle) {
// see arc() for the HTMLCanvas version
// note input angles are degrees.
this._addArc(oval, startAngle, sweepAngle);
var oPtr = copyRectToWasm(oval);
this._addArc(oPtr, startAngle, sweepAngle);
return this;
};
@ -598,7 +608,8 @@ CanvasKit.onRuntimeInitialized = function() {
if (startIndex === undefined) {
startIndex = 1;
}
this._addOval(oval, !!isCCW, startIndex);
var oPtr = copyRectToWasm(oval);
this._addOval(oPtr, !!isCCW, startIndex);
return this;
};
@ -664,58 +675,15 @@ CanvasKit.onRuntimeInitialized = function() {
return this;
};
CanvasKit.SkPath.prototype.addRect = function() {
// Takes 1, 2, 4 or 5 args
// - SkRect
// - SkRect, isCCW
// - left, top, right, bottom
// - left, top, right, bottom, isCCW
if (arguments.length === 1 || arguments.length === 2) {
var r = arguments[0];
var ccw = arguments[1] || false;
this._addRect(r.fLeft, r.fTop, r.fRight, r.fBottom, ccw);
} else if (arguments.length === 4 || arguments.length === 5) {
var a = arguments;
this._addRect(a[0], a[1], a[2], a[3], a[4] || false);
} else {
SkDebug('addRect expected to take 1, 2, 4, or 5 args. Got ' + arguments.length);
return null;
}
CanvasKit.SkPath.prototype.addRect = function(rect, isCCW) {
var rPtr = copyRectToWasm(rect);
this._addRect(rPtr, !!isCCW);
return this;
};
CanvasKit.SkPath.prototype.addRoundRect = function() {
// Takes 3, 4, 6 or 7 args
// - SkRect, radii (an array of 8 numbers), ccw
// - SkRect, rx, ry, ccw
// - left, top, right, bottom, radii, ccw
// - left, top, right, bottom, rx, ry, ccw
var args = arguments;
if (args.length === 3 || args.length === 6) {
var radii = args[args.length-2];
} else if (args.length === 4 || args.length === 7){
// duplicate the given (rx, ry) pairs for each corner.
var rx = args[args.length-3];
var ry = args[args.length-2];
var radii = [rx, ry, rx, ry, rx, ry, rx, ry];
} else {
SkDebug('addRoundRect expected to take 3, 4, 6, or 7 args. Got ' + args.length);
return null;
}
if (radii.length !== 8) {
SkDebug('addRoundRect needs 8 radii provided. Got ' + radii.length);
return null;
}
var rptr = copy1dArray(radii, 'HEAPF32');
if (args.length === 3 || args.length === 4) {
var r = args[0];
var ccw = args[args.length - 1];
this._addRoundRect(r.fLeft, r.fTop, r.fRight, r.fBottom, rptr, ccw);
} else if (args.length === 6 || args.length === 7) {
var a = args;
this._addRoundRect(a[0], a[1], a[2], a[3], rptr, ccw);
}
freeArraysThatAreNotMallocedByUsers(rptr, radii);
CanvasKit.SkPath.prototype.addRRect = function(rrect, isCCW) {
var rPtr = copyRRectToWasm(rrect);
this._addRRect(rPtr, !!isCCW);
return this;
};
@ -744,32 +712,13 @@ CanvasKit.onRuntimeInitialized = function() {
return this;
};
// Deprecated, use one of the three variants below depending on how many args you were calling it with.
CanvasKit.SkPath.prototype.arcTo = function() {
// takes 4, 5 or 7 args
// - 5 x1, y1, x2, y2, radius
// - 4 oval (as Rect), startAngle, sweepAngle, forceMoveTo
// - 7 rx, ry, xAxisRotate, useSmallArc, isCCW, x, y
var args = arguments;
if (args.length === 5) {
this._arcToTangent(args[0], args[1], args[2], args[3], args[4]);
} else if (args.length === 4) {
this._arcToOval(args[0], args[1], args[2], args[3]);
} else if (args.length === 7) {
this._arcToRotated(args[0], args[1], args[2], !!args[3], !!args[4], args[5], args[6]);
} else {
throw 'Invalid args for arcTo. Expected 4, 5, or 7, got '+ args.length;
}
return this;
};
// Appends arc to SkPath. Arc added is part of ellipse
// bounded by oval, from startAngle through sweepAngle. Both startAngle and
// sweepAngle are measured in degrees, where zero degrees is aligned with the
// positive x-axis, and positive sweeps extends arc clockwise.
CanvasKit.SkPath.prototype.arcToOval = function(oval, startAngle, sweepAngle, forceMoveTo) {
this._arcToOval(oval, startAngle, sweepAngle, forceMoveTo);
var oPtr = copyRectToWasm(oval);
this._arcToOval(oPtr, startAngle, sweepAngle, forceMoveTo);
return this;
};
@ -832,6 +781,19 @@ CanvasKit.onRuntimeInitialized = function() {
return null;
};
// Clients can pass in a Float32Array with length 4 to this and the results
// will be copied into that array. Otherwise, a new TypedArray will be allocated
// and returned.
CanvasKit.SkPath.prototype.getBounds = function(optionalOutputArray) {
this._getBounds(_scratchRectPtr);
var ta = _scratchRect['toTypedArray']();
if (optionalOutputArray) {
optionalOutputArray.set(ta);
return optionalOutputArray;
}
return ta.slice();
};
CanvasKit.SkPath.prototype.lineTo = function(x, y) {
this._lineTo(x, y);
return this;
@ -996,6 +958,11 @@ CanvasKit.onRuntimeInitialized = function() {
this._clipRRect(rPtr, op, antialias);
}
CanvasKit.SkCanvas.prototype.clipRect = function(rect, op, antialias) {
var rPtr = copyRectToWasm(rect);
this._clipRect(rPtr, op, antialias);
}
// 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) {
@ -1006,6 +973,11 @@ CanvasKit.onRuntimeInitialized = function() {
// Deprecated - just use concat
CanvasKit.SkCanvas.prototype.concat44 = CanvasKit.SkCanvas.prototype.concat;
CanvasKit.SkCanvas.prototype.drawArc = function(oval, startAngle, sweepAngle, useCenter, paint) {
var oPtr = copyRectToWasm(oval);
this._drawArc(oPtr, startAngle, sweepAngle, useCenter, paint);
}
// atlas is an SkImage, e.g. from CanvasKit.MakeImageFromEncoded
// srcRects, dstXforms, and colors should be CanvasKit.SkRectBuilder, CanvasKit.RSXFormBuilder,
// and CanvasKit.SkColorBuilder (fastest)
@ -1095,6 +1067,23 @@ CanvasKit.onRuntimeInitialized = function() {
this._drawDRRect(oPtr, iPtr, paint);
}
CanvasKit.SkCanvas.prototype.drawImageNine = function(img, center, dest, paint) {
var cPtr = copyIRectToWasm(center);
var dPtr = copyRectToWasm(dest);
this._drawImageNine(img, cPtr, dPtr, paint);
}
CanvasKit.SkCanvas.prototype.drawImageRect = function(img, src, dest, paint, fastSample) {
var sPtr = copyRectToWasm(src, _scratchRectPtr);
var dPtr = copyRectToWasm(dest, _scratchRect2Ptr);
this._drawImageRect(img, sPtr, dPtr, paint, !!fastSample);
}
CanvasKit.SkCanvas.prototype.drawOval = function(oval, paint) {
var oPtr = copyRectToWasm(oval);
this._drawOval(oPtr, paint);
}
// 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.
@ -1119,6 +1108,11 @@ CanvasKit.onRuntimeInitialized = function() {
this._drawRRect(rPtr, paint);
}
CanvasKit.SkCanvas.prototype.drawRect = function(rect, paint) {
var rPtr = copyRectToWasm(rect);
this._drawRect(rPtr, paint);
}
CanvasKit.SkCanvas.prototype.drawShadow = function(path, zPlaneParams, lightPos, lightRadius, ambientColor, spotColor, flags) {
var ambiPtr = copyColorToWasmNoScratch(ambientColor);
var spotPtr = copyColorToWasmNoScratch(spotColor);
@ -1192,6 +1186,13 @@ CanvasKit.onRuntimeInitialized = function() {
return pixels;
}
CanvasKit.SkCanvas.prototype.saveLayer = function(paint, boundsRect, backdrop, flags) {
// bPtr will be 0 (nullptr) if boundsRect is undefined/null.
var bPtr = copyRectToWasm(boundsRect);
// These or clauses help emscripten, which does not deal with undefined well.
return this._saveLayer(paint || null, bPtr, backdrop || null, flags || 0);
}
// pixels should be a Uint8Array or a plain JS array.
CanvasKit.SkCanvas.prototype.writePixels = function(pixels, srcWidth, srcHeight,
destX, destY, alphaType, colorType, colorSpace) {
@ -1263,6 +1264,11 @@ CanvasKit.onRuntimeInitialized = function() {
this._setColor(cPtr, colorSpace);
}
CanvasKit.SkPictureRecorder.prototype.beginRecording = function(bounds) {
var bPtr = copyRectToWasm(bounds);
return this._beginRecording(bPtr);
}
CanvasKit.SkSurface.prototype.captureFrameAsSkPicture = function(drawFrame) {
// Set up SkPictureRecorder
var spr = new CanvasKit.SkPictureRecorder();
@ -1276,6 +1282,11 @@ CanvasKit.onRuntimeInitialized = function() {
return pic;
}
CanvasKit.SkSurface.prototype.makeImageSnapshot = function(optionalBoundsRect) {
var bPtr = copyIRectToWasm(optionalBoundsRect);
return this._makeImageSnapshot(bPtr);
}
CanvasKit.SkSurface.prototype.requestAnimationFrame = function(callback, dirtyRect) {
if (!this._cached_canvas) {
this._cached_canvas = this.getCanvas();
@ -1397,6 +1408,19 @@ CanvasKit.onRuntimeInitialized = function() {
return rgs;
}
// Clients can pass in a Float32Array with length 4 to this and the results
// will be copied into that array. Otherwise, a new TypedArray will be allocated
// and returned.
CanvasKit.SkVertices.prototype.bounds = function(optionalOutputArray) {
this._bounds(_scratchRectPtr);
var ta = _scratchRect['toTypedArray']();
if (optionalOutputArray) {
optionalOutputArray.set(ta);
return optionalOutputArray;
}
return ta.slice();
}
// temporary support for deprecated names.
CanvasKit.MakeSkDashPathEffect = CanvasKit.SkPathEffect.MakeDash;
CanvasKit.MakeLinearGradientShader = CanvasKit.SkShader.MakeLinearGradient;
@ -1437,28 +1461,26 @@ CanvasKit.computeTonalColors = function(tonalColors) {
};
CanvasKit.LTRBRect = function(l, t, r, b) {
return {
fLeft: l,
fTop: t,
fRight: r,
fBottom: b,
};
return Float32Array.of(l, t, r, b);
};
CanvasKit.XYWHRect = function(x, y, w, h) {
return {
fLeft: x,
fTop: y,
fRight: x+w,
fBottom: y+h,
};
return Float32Array.of(x, y, x+w, y+h);
};
CanvasKit.LTRBiRect = function(l, t, r, b) {
return Int32Array.of(l, t, r, b);
};
CanvasKit.XYWHiRect = function(x, y, w, h) {
return Int32Array.of(x, y, x+w, y+h);
};
// RRectXY returns a TypedArray representing an RRect with the given rect and a radiusX and
// radiusY for all 4 corners.
CanvasKit.RRectXY = function(rect, rx, ry) {
return Float32Array.of(
rect['fLeft'], rect['fTop'], rect['fRight'], rect['fBottom'],
rect[0], rect[1], rect[2], rect[3],
rx, ry,
rx, ry,
rx, ry,

View File

@ -63,10 +63,43 @@ CanvasKit.MakeManagedAnimation = function(json, assets, prop_filter_prefix) {
CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
CanvasKit._extraInitializations.push(function() {
CanvasKit.ManagedAnimation.prototype.setColor = function(key, color) {
var cPtr = copyColorToWasm(color);
this._setColor(key, cPtr);
CanvasKit.Animation.prototype.render = function(canvas, dstRect) {
var dPtr = copyRectToWasm(dstRect);
this._render(canvas, dPtr);
}
if (CanvasKit.ManagedAnimation) {
CanvasKit.ManagedAnimation.prototype.render = function(canvas, dstRect) {
var dPtr = copyRectToWasm(dstRect);
this._render(canvas, dPtr);
}
CanvasKit.ManagedAnimation.prototype.seek = function(t, optDamageRect) {
this._seek(t, _scratchRectPtr);
var ta = _scratchRect['toTypedArray']();
if (optDamageRect) {
optDamageRect.set(ta);
return optDamageRect;
}
return ta.slice();
}
CanvasKit.ManagedAnimation.prototype.seekFrame = function(frame, optDamageRect) {
this._seekFrame(frame, _scratchRectPtr);
var ta = _scratchRect['toTypedArray']();
if (optDamageRect) {
optDamageRect.set(ta);
return optDamageRect;
}
return ta.slice();
}
CanvasKit.ManagedAnimation.prototype.setColor = function(key, color) {
var cPtr = copyColorToWasm(color);
this._setColor(key, cPtr);
}
}
});
}(Module)); // When this file is loaded in, the high level object is "Module";

View File

@ -112,8 +112,7 @@ public:
~ManagedAnimation() override = default;
// skottie::Animation API
void render(SkCanvas* canvas) const { fAnimation->render(canvas, nullptr); }
void render(SkCanvas* canvas, const SkRect& dst) const { fAnimation->render(canvas, &dst); }
void render(SkCanvas* canvas, const SkRect* dst) const { fAnimation->render(canvas, dst); }
// Returns a damage rect.
SkRect seek(SkScalar t) {
sksg::InvalidationController ic;
@ -207,12 +206,10 @@ EMSCRIPTEN_BINDINGS(Skottie) {
.function("seekFrame", optional_override([](skottie::Animation& self, double t)->void {
self.seekFrame(t);
}))
.function("render", optional_override([](skottie::Animation& self, SkCanvas* canvas)->void {
self.render(canvas, nullptr);
}), allow_raw_pointers())
.function("render", optional_override([](skottie::Animation& self, SkCanvas* canvas,
const SkRect r)->void {
self.render(canvas, &r);
.function("_render", optional_override([](skottie::Animation& self, SkCanvas* canvas,
uintptr_t /* float* */ fPtr)->void {
const SkRect* dst = reinterpret_cast<const SkRect*>(fPtr);
self.render(canvas, dst);
}), allow_raw_pointers());
function("MakeAnimation", optional_override([](std::string json)->sk_sp<skottie::Animation> {
@ -227,11 +224,22 @@ EMSCRIPTEN_BINDINGS(Skottie) {
.function("size" , &ManagedAnimation::size)
.function("duration" , &ManagedAnimation::duration)
.function("fps" , &ManagedAnimation::fps)
.function("seek" , &ManagedAnimation::seek)
.function("_render", optional_override([](ManagedAnimation& self, SkCanvas* canvas,
uintptr_t /* float* */ fPtr)->void {
const SkRect* dst = reinterpret_cast<const SkRect*>(fPtr);
self.render(canvas, dst);
}), allow_raw_pointers())
.function("_seek", optional_override([](ManagedAnimation& self, SkScalar t,
uintptr_t /* float* */ fPtr) {
SkRect* damageRect = reinterpret_cast<SkRect*>(fPtr);
damageRect[0] = self.seek(t);
}))
.function("_seekFrame", optional_override([](ManagedAnimation& self, double frame,
uintptr_t /* float* */ fPtr) {
SkRect* damageRect = reinterpret_cast<SkRect*>(fPtr);
damageRect[0] = self.seekFrame(frame);
}))
.function("seekFrame" , &ManagedAnimation::seekFrame)
.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, uintptr_t /* float* */ cPtr) {
float* fourFloats = reinterpret_cast<float*>(cPtr);
SkColor4f color = { fourFloats[0], fourFloats[1], fourFloats[2], fourFloats[3] };

View File

@ -22,7 +22,8 @@ describe('Canvas Behavior', () => {
paint.setStyle(CanvasKit.PaintStyle.Stroke);
canvas.drawLine(3, 10, 30, 15, paint);
canvas.drawRoundRect(CanvasKit.LTRBRect(5, 35, 45, 80), 15, 10, paint);
const rrect = CanvasKit.RRectXY([5, 35, 45, 80], 15, 10);
canvas.drawRRect(rrect, paint);
canvas.drawOval(CanvasKit.LTRBRect(5, 35, 45, 80), paint);
@ -435,7 +436,7 @@ describe('Canvas Behavior', () => {
// The rectangle is just a hint, so I've set it to be the area that
// we actually draw in before restore is called. It could also be omitted,
// see the test below.
canvas.saveLayer(CanvasKit.LTRBRect(10, 10, 220, 180), alpha);
canvas.saveLayer(alpha, CanvasKit.LTRBRect(10, 10, 220, 180));
// Draw the same blue overlapping rectangles as before. Notice in the
// final output, we have two different shades of purple instead of the
@ -536,7 +537,7 @@ describe('Canvas Behavior', () => {
const blurIF = CanvasKit.SkImageFilter.MakeBlur(8, 0.2, CanvasKit.TileMode.Decal, null);
const count = canvas.saveLayer(null, blurIF, 0);
const count = canvas.saveLayer(null, null, blurIF, 0);
expect(count).toEqual(1);
canvas.scale(1/4, 1/4);
canvas.drawCircle(125, 85, 8, redPaint);
@ -593,12 +594,8 @@ describe('Canvas Behavior', () => {
canvas.clear(CanvasKit.WHITE);
const paint = new CanvasKit.SkPaint();
canvas.drawImageNine(img, {
fLeft: 40,
fTop: 40,
fRight: 400,
fBottom: 300,
}, CanvasKit.LTRBRect(5, 5, 300, 650), paint);
canvas.drawImageNine(img, CanvasKit.LTRBiRect(40, 40, 400, 300),
CanvasKit.LTRBRect(5, 5, 300, 650), paint);
paint.delete();
img.delete();
}, '/assets/mandrill_512.png');
@ -615,10 +612,7 @@ describe('Canvas Behavior', () => {
points, null /*textureCoordinates*/, colors, false /*isVolatile*/);
const bounds = vertices.bounds();
expect(bounds.fLeft).toEqual(0);
expect(bounds.fTop).toEqual(0);
expect(bounds.fRight).toEqual(250);
expect(bounds.fBottom).toEqual(250);
expect(bounds).toEqual(CanvasKit.LTRBRect(0, 0, 250, 250));
canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint);
vertices.delete();
@ -637,10 +631,7 @@ describe('Canvas Behavior', () => {
points, null /*textureCoordinates*/, colors, false /*isVolatile*/);
const bounds = vertices.bounds();
expect(bounds.fLeft).toEqual(0);
expect(bounds.fTop).toEqual(0);
expect(bounds.fRight).toEqual(250);
expect(bounds.fBottom).toEqual(250);
expect(bounds).toEqual(CanvasKit.LTRBRect(0, 0, 250, 250));
canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint);
vertices.delete();

View File

@ -24,7 +24,7 @@ describe('Core canvas behavior', () => {
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
paint.setStyle(CanvasKit.PaintStyle.Stroke);
rcanvas.drawRoundRect(CanvasKit.LTRBRect(5, 35, 45, 80), 15, 10, paint);
rcanvas.drawRRect(CanvasKit.RRectXY([5, 35, 45, 80], 15, 10), paint);
const font = new CanvasKit.SkFont(null, 20);
rcanvas.drawText('this picture has a round rect', 5, 100, paint, font);

View File

@ -60,10 +60,10 @@ describe('Font Behavior', () => {
canvas.drawText(shapedText, textBoxX, textBoxY, textPaint);
const bounds = shapedText.getBounds();
bounds.fLeft += textBoxX;
bounds.fRight += textBoxX;
bounds.fTop += textBoxY;
bounds.fBottom += textBoxY
bounds[0] += textBoxX; // left
bounds[2] += textBoxX; // right
bounds[1] += textBoxY; // top
bounds[3] += textBoxY // bottom
canvas.drawRect(bounds, paint);
const SHAPE_TEST_TEXT = 'VAVAVAVAVAFIfi';
@ -102,9 +102,9 @@ describe('Font Behavior', () => {
const arc = new CanvasKit.SkPath();
arc.arcTo(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true);
arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true);
arc.lineTo(210, 140);
arc.arcTo(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true);
arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true);
// Only 1 dot should show up in the image, because we run out of path.
const str = 'This téxt should follow the curve across contours...';
@ -134,9 +134,9 @@ describe('Font Behavior', () => {
fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
const arc = new CanvasKit.SkPath();
arc.arcTo(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true);
arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true);
arc.lineTo(210, 140);
arc.arcTo(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true);
arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true);
const str = 'This téxt should follow the curve across contours...';
const textBlob = CanvasKit.SkTextBlob.MakeOnPath(str, arc, font, 60.5);

View File

@ -49,13 +49,13 @@ describe('Path Behavior', () => {
canvas.drawPath(path, paint);
const rrect = new CanvasKit.SkPath()
.addRoundRect(100, 10, 140, 62,
10, 4, true);
const rrect = CanvasKit.RRectXY([100, 10, 140, 62], 10, 4);
canvas.drawPath(rrect, paint);
rrect.delete();
const rrectPath = new CanvasKit.SkPath().addRRect(rrect, true);
canvas.drawPath(rrectPath, paint);
rrectPath.delete();
path.delete();
paint.delete();
// See PathKit for more tests, since they share implementation
@ -263,7 +263,7 @@ describe('Path Behavior', () => {
canvas.clear(CanvasKit.WHITE);
const path = new CanvasKit.SkPath();
// - x1, y1, x2, y2, radius
path.arcToTangent(40, 0, 40, 40, 40);
// - oval (as Rect), startAngle, sweepAngle, forceMoveTo
@ -417,10 +417,11 @@ describe('Path Behavior', () => {
path.addArc(CanvasKit.LTRBRect(10, 20, 100, 200), 30, 300)
.addRect(CanvasKit.LTRBRect(200, 200, 300, 300)) // test single arg, default cw
.addRect(CanvasKit.LTRBRect(240, 240, 260, 260), true) // test two arg, true means ccw
.addRect(260, 260, 290, 290, true) // test five arg, true means ccw
.addRoundRect(CanvasKit.LTRBRect(300, 10, 500, 290),
[60, 60, 60, 60, 60, 60, 60, 60], false) // SkRect, radii, ccw
.addRoundRect(CanvasKit.LTRBRect(350, 60, 450, 240), 20, 80, true) // SkRect, rx, ry, ccw
.addRect([260, 260, 290, 290], true) // test five arg, true means ccw
.addRRect([300, 10, 500, 290, // SkRect in LTRB order
60, 60, 60, 60, 60, 60, 60, 60], // all radii are the same
false) // ccw
.addRRect(CanvasKit.RRectXY([350, 60, 450, 240], 20, 80), true) // SkRect, rx, ry, ccw
.addPath(arcpath)
.transform(0.54, -0.84, 390.35,
0.84, 0.54, -114.53,

View File

@ -14,6 +14,14 @@ describe('Skottie behavior', () => {
document.body.removeChild(container);
});
const expectArrayCloseTo = (a, b, precision) => {
precision = precision || 14 // digits of precision in base 10
expect(a.length).toEqual(b.length);
for (let i=0; i<a.length; i++) {
expect(a[i]).toBeCloseTo(b[i], precision);
}
};
const imgPromise = fetch('/assets/flightAnim.gif')
.then((response) => response.arrayBuffer());
const jsonPromise = fetch('/assets/animated_gif.json')
@ -30,14 +38,21 @@ describe('Skottie behavior', () => {
'flightAnim.gif': promises[0],
});
expect(animation).toBeTruthy();
const bounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500};
const bounds = CanvasKit.LTRBRect(0, 0, 500, 500);
canvas.clear(CanvasKit.WHITE);
animation.render(canvas, bounds);
// We intentionally make the length of this array 5 and add a sentinel value
// of 999 so we can make sure the bounds are copied into this rect and a new
// one is not allocated.
const damageRect = Float32Array.of(0, 0, 0, 0, 999);
// There was a bug, fixed in https://skia-review.googlesource.com/c/skia/+/241757
// that seeking again and drawing again revealed.
animation.seek(0.5);
animation.seek(0.5, damageRect);
expectArrayCloseTo(damageRect, Float32Array.of(0, 0, 800, 600, 999), 4);
canvas.clear(CanvasKit.WHITE);
animation.render(canvas, bounds);
animation.delete();
@ -48,7 +63,7 @@ describe('Skottie behavior', () => {
console.warn('Skipping test because not compiled with skottie');
return;
}
const bounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500};
const bounds = CanvasKit.LTRBRect(0, 0, 500, 500);
canvas.clear(CanvasKit.WHITE);
const animation = CanvasKit.MakeManagedAnimation(promises[0]);

View File

@ -70,7 +70,7 @@ would display the animation (e.g. using clock time to determine where to seek, n
};
const duration = animation.duration() * 1000;
const bounds = {fLeft: 0, fTop: 0, fRight: WIDTH, fBottom: HEIGHT};
const bounds = CanvasKit.LTRBRect(0, 0, WIDTH, HEIGHT);
const urlSearchParams = new URLSearchParams(window.location.search);
let glversion = 2;
@ -89,12 +89,13 @@ would display the animation (e.g. using clock time to determine where to seek, n
const clearColor = CanvasKit.WHITE;
let idx = 0;
const startTime = Date.now();
const damageRect = Float32Array.of(0, 0, 0, 0);
function draw() {
const seek = ((Date.now() - startTime) / duration) % 1.0;
const damage = animation.seek(seek);
const damage = animation.seek(seek, damageRect);
if (damage.fRight > damage.fLeft && damage.fBottom > damage.fTop) {
if (damage[2] > damage[0] && damage[3] > damage[1]) {
canvas.clear(clearColor);
animation.render(canvas, bounds);
}

View File

@ -1,2 +1,6 @@
serve:
python -m SimpleHTTPServer 8004
# This shows an example invocation presuming the user has built canvaskit locally.
lego_loader_with_local:
node skottie-wasm-perf.js --canvaskit_js ../../out/canvaskit_wasm/canvaskit.js \
--canvaskit_wasm ../../out/canvaskit_wasm/canvaskit.wasm --use_gpu \
--input ../../modules/canvaskit/perf/assets/lego_loader.json \
--output lego_loader_perf.json

View File

@ -18,99 +18,93 @@
<canvas id=anim width=1000 height=1000 style="height: 1000px; width: 1000px;"></canvas>
</main>
<script type="text/javascript" charset="utf-8">
const WIDTH = 1000;
const HEIGHT = 1000;
const LOTTIE_JSON_PATH = '/res/lottie.json';
const MAX_FRAMES = 25;
const MAX_LOOPS = 25;
const MAX_SAMPLE_MS = 60*1000; // in case something takes a while, stop after 60 seconds.
(function() {
const PATH = '/res/lottie.json';
let lottieJSON = null;
let CK = null;
CanvasKitInit({
locateFile: (file) => '/res/'+file,
}).then((CanvasKit) => {
CK = CanvasKit;
Bench(CK, lottieJSON);
});
fetch(PATH).then((resp) => {
resp.text().then((json) => {
lottieJSON = json;
Bench(CK, lottieJSON);
const loadKit = CanvasKitInit({
locateFile: (file) => '/res/' + file,
});
});
})();
const maxFrames = 25;
const maxLoops = 5;
const loadLottie = fetch(LOTTIE_JSON_PATH).then((resp) => {
return resp.text();
});
function Bench(CK, json) {
if (!CK || !json) {
return;
}
Promise.all([loadKit, loadLottie]).then((values) => {
const [CanvasKit, json] = values;
const animation = CK.MakeManagedAnimation(json, null);
if (!animation) {
window._error = 'Could not process JSON';
return
}
let surface = null;
if (window.location.hash.indexOf('gpu') !== -1) {
surface = CK.MakeWebGLCanvasSurface('anim');
if (!surface) {
window._error = 'Could not make GPU surface';
return;
}
let c = document.getElementById('anim');
// If CanvasKit was unable to instantiate a WebGL context, it will fallback
// to CPU and add a ck-replaced class to the canvas element.
if (c.classList.contains('ck-replaced')) {
window._error = 'fell back to CPU';
return;
}
} else {
surface = CK.MakeSWCanvasSurface('anim');
if (!surface) {
window._error = 'Could not make CPU surface';
return;
}
}
const canvas = surface.getCanvas();
const t_rate = 1.0 / (maxFrames-1);
let seek = 0;
let frame = 0;
let loop = 0;
const drawFrame = () => {
if (frame >= maxFrames) {
// Reached the end of one loop.
loop++;
if (loop == maxLoops) {
// These are global variables to talk with puppeteer.
window._skottieDone = true;
return;
const animation = CanvasKit.MakeManagedAnimation(json, null);
if (!animation) {
window._error = 'Could not process JSON';
return
}
// Reset frame and seek to restart the loop.
frame = 0;
seek = 0;
}
let damage = animation.seek(seek);
if (damage.fRight > damage.fLeft && damage.fBottom > damage.fTop) {
animation.render(canvas, {
fLeft: 0,
fTop: 0,
fRight: 1000,
fBottom: 1000
});
surface.flush();
}
console.log(`Used seek: ${seek}`);
seek += t_rate;
frame++;
window.requestAnimationFrame(drawFrame);
};
window.requestAnimationFrame(drawFrame);
}
let surface = null;
if (window.location.hash.indexOf('gpu') !== -1) {
surface = CanvasKit.MakeWebGLCanvasSurface('anim');
if (!surface) {
window._error = 'Could not make GPU surface';
return;
}
let c = document.getElementById('anim');
// If CanvasKit was unable to instantiate a WebGL context, it will fallback
// to CPU and add a ck-replaced class to the canvas element.
if (c.classList.contains('ck-replaced')) {
window._error = 'fell back to CPU';
return;
}
} else {
surface = CanvasKit.MakeSWCanvasSurface('anim');
if (!surface) {
window._error = 'Could not make CPU surface';
return;
}
}
const canvas = surface.getCanvas();
const t_rate = 1.0 / (MAX_FRAMES-1);
let seek = 0;
let frame = 0;
let loop = 0;
const damageRect = Float32Array.of(0, 0, 0, 0);
const bounds = CanvasKit.LTRBRect(0, 0, WIDTH, HEIGHT);
const start = performance.now();
const drawFrame = () => {
if ((performance.now() - start) > MAX_SAMPLE_MS) {
// This global variable signals we are done.
window._skottieDone = true;
return;
}
if (frame >= MAX_FRAMES) {
// Reached the end of one loop.
loop++;
if (loop == MAX_LOOPS) {
// This global variable signals we are done.
window._skottieDone = true;
return;
}
// Reset frame and seek to restart the loop.
frame = 0;
seek = 0;
}
let damage = animation.seek(seek, damageRect);
if (damage[2] > damage[0] && damage[3] > damage[1]) {
animation.render(canvas, bounds);
surface.flush();
}
console.log(`Used seek: ${seek}`);
seek += t_rate;
frame++;
window.requestAnimationFrame(drawFrame);
};
window.requestAnimationFrame(drawFrame);
});
})();
</script>
</body>
</html>

View File

@ -161,9 +161,9 @@ async function driveBrowser() {
waitUntil: 'networkidle0'
});
console.log('Waiting 60s for run to be done');
console.log('Waiting 90s for run to be done');
await page.waitForFunction(`(window._skottieDone === true) || window._error`, {
timeout: 60000,
timeout: 90000,
});
const err = await page.evaluate('window._error');