aboutsummaryrefslogtreecommitdiff
path: root/experimental
diff options
context:
space:
mode:
authorKevin Lubick <kjlubick@google.com>2018-11-03 07:51:19 -0400
committerSkia Commit-Bot <skia-commit-bot@chromium.org>2018-11-03 12:18:04 +0000
commitb5ae3b5afc29d78c9add8e16cb77d2de6cce2358 (patch)
treea222781a944b5b55f70e44306fb2ad7d1576e7d0 /experimental
parent0e6fc6fdae02bbc52aff49f5c3fb5ce0dcfe459a (diff)
downloadskqp-b5ae3b5afc29d78c9add8e16cb77d2de6cce2358.tar.gz
[canvaskit] Add drawVertices API
This also does some clean up to how we name enums - the caps felt a bit obnoxious. CAPS are reserved now for constants (like colors). Small bug fix with leaking memory on discrete path effects This also adds a few more things from PathKit Bug: skia: Change-Id: Iad7e21ac36d35a36a8b255dc82b1dcc886344db1 Reviewed-on: https://skia-review.googlesource.com/c/166804 Commit-Queue: Kevin Lubick <kjlubick@google.com> Reviewed-by: Mike Reed <reed@google.com>
Diffstat (limited to 'experimental')
-rw-r--r--experimental/canvaskit/canvaskit/example.html212
-rw-r--r--experimental/canvaskit/canvaskit_bindings.cpp347
-rwxr-xr-xexperimental/canvaskit/compile.sh5
-rw-r--r--experimental/canvaskit/externs.js43
-rw-r--r--experimental/canvaskit/htmlcanvas/canvas2d.js4
-rw-r--r--experimental/canvaskit/interface.js225
6 files changed, 768 insertions, 68 deletions
diff --git a/experimental/canvaskit/canvaskit/example.html b/experimental/canvaskit/canvaskit/example.html
index 8df01c7be5..6173ff9a73 100644
--- a/experimental/canvaskit/canvaskit/example.html
+++ b/experimental/canvaskit/canvaskit/example.html
@@ -17,6 +17,9 @@
</style>
<h2> CanvasKit draws Paths to the browser</h2>
+<canvas id=vertex1 width=300 height=300></canvas>
+<canvas id=vertex2 width=300 height=300></canvas>
+<canvas id=gradient1 width=300 height=300></canvas>
<canvas id=patheffect width=300 height=300></canvas>
<canvas id=paths width=200 height=200></canvas>
<canvas id=ink width=300 height=300></canvas>
@@ -50,24 +53,40 @@
var nimaFile = null;
var nimaTexture = null;
+
+ var bonesImage = null;
CanvasKitInit({
locateFile: (file) => '/node_modules/canvaskit/bin/'+file,
}).then((CK) => {
- CK.initFonts();
- CanvasKit = CK;
- DrawingExample(CanvasKit);
- PathExample(CanvasKit);
- InkExample(CanvasKit);
- // Set bounds to fix the 4:3 resolution of the legos
- addScreenshotListener(SkottieExample(CanvasKit, 'sk_legos', legoJSON,
- {fLeft: -50, fTop: 0, fRight: 350, fBottom: 300}));
- // Re-size to fit
- SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds);
- SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds);
- SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds);
- NimaExample(CanvasKit, nimaFile, nimaTexture);
+ // when debugging, it can be handy to not run directly in the then, because if there
+ // is a failure (for example, miscalling an API), the WASM loader tries to re-load
+ // the web assembly in the (much slower) ArrayBuffer version. This will also fail
+ // and thus there is a lot of extra log spew.
+ // Thus, the setTimeout to run on the next microtask avoids this second loading
+ // and the log spew.
+ setTimeout(() => {
+ CK.initFonts();
+ CanvasKit = CK;
+ DrawingExample(CanvasKit);
+ PathExample(CanvasKit);
+ InkExample(CanvasKit);
+ // Set bounds to fix the 4:3 resolution of the legos
+ addScreenshotListener(SkottieExample(CanvasKit, 'sk_legos', legoJSON,
+ {fLeft: -50, fTop: 0, fRight: 350, fBottom: 300}));
+ // Re-size to fit
+ SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds);
+ SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds);
+ SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds);
+
+ NimaExample(CanvasKit, nimaFile, nimaTexture);
+
+ CanvasAPI1(CanvasKit);
+
+ VertexAPI1(CanvasKit);
+ VertexAPI2(CanvasKit, bonesImage);
- CanvasAPI1(CanvasKit);
+ GradiantAPI1(CanvasKit);
+ }, 0);
});
fetch('https://storage.googleapis.com/skia-cdn/misc/lego_loader.json').then((resp) => {
@@ -121,6 +140,17 @@
});
});
+ fetch('https://storage.googleapis.com/skia-cdn/misc/bones.jpg').then((resp) => {
+ resp.blob().then((blob) => {
+ let reader = new FileReader();
+ reader.addEventListener("loadend", function() {
+ bonesImage = reader.result;
+ VertexAPI2(CanvasKit, bonesImage);
+ });
+ reader.readAsArrayBuffer(blob);
+ });
+ });
+
function addScreenshotListener(surface) {
if (!surface) {
return;
@@ -156,7 +186,7 @@
const paint = new CanvasKit.SkPaint();
const textPaint = new CanvasKit.SkPaint();
- textPaint.setColor(CanvasKit.Color(40, 0, 0, 1.0));
+ textPaint.setColor(CanvasKit.RED);
textPaint.setTextSize(30);
textPaint.setAntiAlias(true);
@@ -172,12 +202,12 @@
i++;
paint.setPathEffect(dpe);
- paint.setStyle(CanvasKit.PaintStyle.STROKE);
+ paint.setStyle(CanvasKit.PaintStyle.Stroke);
paint.setStrokeWidth(5.0 + -3 * Math.cos(i/30));
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(66, 129, 164, 1.0));
- canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
+ canvas.clear(CanvasKit.TRANSPARENT);
canvas.drawPath(path, paint);
canvas.drawText('Try Clicking!', 10, 280, textPaint);
@@ -221,7 +251,7 @@
paint.setStrokeWidth(1.0);
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
- paint.setStyle(CanvasKit.PaintStyle.STROKE);
+ paint.setStyle(CanvasKit.PaintStyle.Stroke);
const path = new CanvasKit.SkPath();
path.moveTo(20, 5);
@@ -277,7 +307,7 @@
let paint = new CanvasKit.SkPaint();
paint.setAntiAlias(true);
paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
- paint.setStyle(CanvasKit.PaintStyle.STROKE);
+ paint.setStyle(CanvasKit.PaintStyle.Stroke);
paint.setStrokeWidth(4.0);
paint.setPathEffect(CanvasKit.MakeSkCornerPathEffect(50));
@@ -381,7 +411,7 @@
let seek = ((Date.now() - firstFrame) / duration) % 1.0;
CanvasKit.setCurrentContext(context);
animation.seek(seek);
- canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
+ canvas.clear(CanvasKit.TRANSPARENT);
animation.render(canvas, bounds);
surface.flush();
window.requestAnimationFrame(drawFrame);
@@ -443,7 +473,7 @@
function drawFrame() {
let seek = ((Date.now() - firstFrame) / 1000.0);
CanvasKit.setCurrentContext(context);
- canvas.clear(CanvasKit.Color(255, 255, 255, 0.0));
+ canvas.clear(CanvasKit.TRANSPARENT);
animation.seek(seek);
animation.render(canvas);
surface.flush();
@@ -452,4 +482,144 @@
window.requestAnimationFrame(drawFrame);
}
+ function VertexAPI1(CanvasKit) {
+ const surface = CanvasKit.MakeCanvasSurface('vertex1');
+ if (!surface) {
+ console.error('Could not make surface');
+ return;
+ }
+ const context = CanvasKit.currentContext();
+ const canvas = surface.getCanvas();
+ let paint = new CanvasKit.SkPaint();
+
+ // See https://fiddle.skia.org/c/f48b22eaad1bb7adcc3faaa321754af6
+ // for original c++ version.
+ let points = [[ 0, 0 ], [ 250, 0 ], [ 100, 100 ], [ 0, 250 ]];
+ let colors = [CanvasKit.RED, CanvasKit.BLUE,
+ CanvasKit.YELLOW, CanvasKit.CYAN];
+ let vertices = CanvasKit.MakeSkVertices(CanvasKit.VertexMode.TriangleFan,
+ points, null, colors);
+
+ canvas.drawVertices(vertices, CanvasKit.BlendMode.Src, paint);
+ surface.flush();
+
+ vertices.delete();
+
+ // See https://fiddle.skia.org/c/e8bdae9bea3227758989028424fcac3d
+ // for original c++ version.
+ points = [[ 300, 300 ], [ 50, 300 ], [ 200, 200 ], [ 300, 50 ]];
+ let texs = [[ 0, 0 ], [ 0, 250 ], [ 250, 250 ], [ 250, 0 ]];
+ vertices = CanvasKit.MakeSkVertices(CanvasKit.VertexMode.TriangleFan,
+ points, texs, colors);
+
+ let shader = CanvasKit.MakeLinearGradientShader([0, 0], [250, 0],
+ colors, null, CanvasKit.TileMode.Clamp);
+ paint.setShader(shader);
+
+ canvas.drawVertices(vertices, CanvasKit.BlendMode.Darken, paint);
+ surface.flush();
+
+ shader.delete();
+ paint.delete();
+ surface.delete();
+
+ }
+
+ function VertexAPI2(CanvasKit, bonesImage) {
+ if (!CanvasKit || !bonesImage) {
+ return;
+ }
+ const surface = CanvasKit.MakeCanvasSurface('vertex2');
+ if (!surface) {
+ console.error('Could not make surface');
+ return;
+ }
+ const context = CanvasKit.currentContext();
+ const canvas = surface.getCanvas();
+ let paint = new CanvasKit.SkPaint();
+
+ let shader = CanvasKit.MakeImageShader(bonesImage,
+ CanvasKit.TileMode.Clamp, CanvasKit.TileMode.Clamp);
+
+ // comment this out to see just the triangles move.
+ paint.setShader(shader);
+
+ // points is the destination location on the canvas We want the output
+ // to be a 280x280 box (to start).
+ let points = [[ 0, 0 ], [ 280, 0 ], [ 280, 280 ], [ 0, 280 ]];
+ // texs is the coordinates of the source in the texture
+ // (provided by the image shader). The image is 334x226 px big.
+ let texs = [[ 0, 0 ], [ 334, 0 ], [ 334, 226 ], [ 0, 226 ]];
+ let boneidxs = [[1,0,0,0], [2,0,0,0], [3,0,0,0], [2,3,0,0]];
+ let bonewts = [[1,0,0,0], [1,0,0,0], [1,0,0,0], [.5,.5,0,0]];
+ let vertices = CanvasKit.MakeSkVertices(CanvasKit.VertexMode.TriangleFan,
+ points, texs, null, boneidxs, bonewts);
+
+ function drawFrame() {
+ let now = Date.now();
+ let bones = [
+ [[1,0, // world bone (move 10px down and to the right to center)
+ 0,1,
+ 10,10]],
+ [[1,0, // identity bone (bone for vertices that are static)
+ 0,1,
+ 0,0]],
+ [[1,0, // ossilate in x bone
+ 0,1,
+ 10*Math.sin(now/500),0]],
+ [[1,0, // ossilate in y bone
+ 0,1,
+ 0,30*Math.cos(now/500)]],
+ ];
+ let tVerts = vertices.applyBones(bones);
+ CanvasKit.setCurrentContext(context);
+ //canvas.clear(CanvasKit.TRANSPARENT);
+ canvas.drawVertices(tVerts, CanvasKit.BlendMode.Src, paint);
+ surface.flush();
+
+ tVerts.delete();
+ window.requestAnimationFrame(drawFrame);
+ }
+ window.requestAnimationFrame(drawFrame);
+ //tVerts.delete();
+ //vertices.delete();
+
+ //shader && shader.delete();
+ //paint.delete();
+ //surface.delete();
+
+ }
+ /**
+ SkColor colors[2] = {SK_ColorBLUE, SK_ColorYELLOW};
+ SkPaint paint;
+ paint.setShader(SkGradientShader::MakeRadial(
+ SkPoint::Make(128.0f, 128.0f), 180.0f,
+ colors, nullptr, 2, SkShader::kClamp_TileMode, 0, nullptr));
+ canvas->drawPaint(paint);*/
+ function GradiantAPI1(CanvasKit) {
+ const surface = CanvasKit.MakeCanvasSurface('gradient1');
+ if (!surface) {
+ console.error('Could not make surface');
+ return;
+ }
+ const context = CanvasKit.currentContext();
+ const canvas = surface.getCanvas();
+ let paint = new CanvasKit.SkPaint();
+
+ // See https://fiddle.skia.org/c/f48b22eaad1bb7adcc3faaa321754af6
+ // for original c++ version.
+ let points = [[ 0, 0 ], [ 250, 0 ], [ 100, 100 ], [ 0, 250 ]];
+ let colors = [CanvasKit.BLUE, CanvasKit.YELLOW, CanvasKit.RED];
+ let pos = [0, .7, 1.0];
+ let transform = [2, 0, 0,
+ 0, 2, 0,
+ 0, 0, 1]
+ let shader = CanvasKit.MakeRadialGradientShader([150,150], 130, colors,
+ pos, CanvasKit.TileMode.Mirror, transform);
+
+ paint.setShader(shader);
+ paint.setTextSize(100);
+ canvas.drawText("Radial", 10, 200, paint);
+ surface.flush();
+ }
</script>
diff --git a/experimental/canvaskit/canvaskit_bindings.cpp b/experimental/canvaskit/canvaskit_bindings.cpp
index 129ee50c8d..9f3db2ba33 100644
--- a/experimental/canvaskit/canvaskit_bindings.cpp
+++ b/experimental/canvaskit/canvaskit_bindings.cpp
@@ -12,19 +12,33 @@
#include "GrGLTypes.h"
#endif
+#include "SkBlendMode.h"
#include "SkCanvas.h"
-#include "SkDashPathEffect.h"
+#include "SkColor.h"
#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkData.h"
#include "SkDiscretePathEffect.h"
#include "SkFontMgr.h"
#include "SkFontMgrPriv.h"
+#include "SkGradientShader.h"
+#include "SkImageShader.h"
#include "SkPaint.h"
+#include "SkParsePath.h"
#include "SkPath.h"
#include "SkPathEffect.h"
+#include "SkPathOps.h"
#include "SkScalar.h"
+#include "SkShader.h"
+#include "SkString.h"
+#include "SkStrokeRec.h"
#include "SkSurface.h"
#include "SkSurfaceProps.h"
#include "SkTestFontMgr.h"
+#include "SkTrimPathEffect.h"
+#include "SkVertices.h"
+
#if SK_INCLUDE_SKOTTIE
#include "Skottie.h"
#endif
@@ -47,6 +61,14 @@ using namespace emscripten;
// Self-documenting types
using JSArray = emscripten::val;
using JSColor = int32_t;
+using JSString = emscripten::val;
+using SkPathOrNull = emscripten::val;
+using Uint8Array = emscripten::val;
+
+// Aliases for less typing
+using BoneIndices = SkVertices::BoneIndices;
+using BoneWeights = SkVertices::BoneWeights;
+using Bone = SkVertices::Bone;
void EMSCRIPTEN_KEEPALIVE initFonts() {
gSkFontMgr_DefaultFactory = &sk_tool_utils::MakePortableFontMgr;
@@ -106,11 +128,17 @@ sk_sp<SkSurface> getWebGLSurface(std::string id, int width, int height) {
}
#endif
-#if SK_INCLUDE_SKOTTIE
-sk_sp<skottie::Animation> MakeAnimation(std::string json) {
- return skottie::Animation::Make(json.c_str(), json.length());
+struct SimpleMatrix {
+ SkScalar scaleX, skewX, transX;
+ SkScalar skewY, scaleY, transY;
+ SkScalar pers0, pers1, pers2;
+};
+
+SkMatrix toSkMatrix(const SimpleMatrix& sm) {
+ return SkMatrix::MakeAll(sm.scaleX, sm.skewX , sm.transX,
+ sm.skewY , sm.scaleY, sm.transY,
+ sm.pers0 , sm.pers1 , sm.pers2);
}
-#endif
//========================================================================================
// Path things
@@ -173,6 +201,28 @@ void ApplyTransform(SkPath& orig,
orig.transform(m);
}
+bool EMSCRIPTEN_KEEPALIVE ApplySimplify(SkPath& path) {
+ return Simplify(path, &path);
+}
+
+bool EMSCRIPTEN_KEEPALIVE ApplyPathOp(SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
+ return Op(pathOne, pathTwo, op, &pathOne);
+}
+
+JSString EMSCRIPTEN_KEEPALIVE ToSVGString(const SkPath& path) {
+ SkString s;
+ SkParsePath::ToSVGString(path, &s);
+ return emscripten::val(s.c_str());
+}
+
+SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
+ SkPath out;
+ if (Op(pathOne, pathTwo, op, &out)) {
+ return emscripten::val(out);
+ }
+ return emscripten::val::null();
+}
+
SkPath EMSCRIPTEN_KEEPALIVE CopyPath(const SkPath& a) {
SkPath copy(a);
return copy;
@@ -182,12 +232,68 @@ bool EMSCRIPTEN_KEEPALIVE Equals(const SkPath& a, const SkPath& b) {
return a == b;
}
+//========================================================================================
+// Path Effects
+//========================================================================================
+
+bool ApplyDash(SkPath& path, SkScalar on, SkScalar off, SkScalar phase) {
+ SkScalar intervals[] = { on, off };
+ auto pe = SkDashPathEffect::Make(intervals, 2, phase);
+ if (!pe) {
+ SkDebugf("Invalid args to dash()\n");
+ return false;
+ }
+ SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
+ if (pe->filterPath(&path, path, &rec, nullptr)) {
+ return true;
+ }
+ SkDebugf("Could not make dashed path\n");
+ return false;
+}
+
+bool ApplyTrim(SkPath& path, SkScalar startT, SkScalar stopT, bool isComplement) {
+ auto mode = isComplement ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal;
+ auto pe = SkTrimPathEffect::Make(startT, stopT, mode);
+ if (!pe) {
+ SkDebugf("Invalid args to trim(): startT and stopT must be in [0,1]\n");
+ return false;
+ }
+ SkStrokeRec rec(SkStrokeRec::InitStyle::kHairline_InitStyle);
+ if (pe->filterPath(&path, path, &rec, nullptr)) {
+ return true;
+ }
+ SkDebugf("Could not trim path\n");
+ return false;
+}
+
+struct StrokeOpts {
+ // Default values are set in chaining.js which allows clients
+ // to set any number of them. Otherwise, the binding code complains if
+ // any are omitted.
+ SkScalar width;
+ SkScalar miter_limit;
+ SkPaint::Join join;
+ SkPaint::Cap cap;
+};
+
+bool ApplyStroke(SkPath& path, StrokeOpts opts) {
+ SkPaint p;
+ p.setStyle(SkPaint::kStroke_Style);
+ p.setStrokeCap(opts.cap);
+ p.setStrokeJoin(opts.join);
+ p.setStrokeWidth(opts.width);
+ p.setStrokeMiter(opts.miter_limit);
+
+ return p.getFillPath(path, &path);
+}
+
// to map from raw memory to a uint8array
-val getSkDataBytes(const SkData *data) {
- return val(typed_memory_view(data->size(), data->bytes()));
+Uint8Array getSkDataBytes(const SkData *data) {
+ return Uint8Array(typed_memory_view(data->size(), data->bytes()));
}
-// Hack to avoid embind creating a binding for SkData destructor
+// These objects have private destructors / delete mthods - I don't think
+// we need to do anything other than tell emscripten to do nothing.
namespace emscripten {
namespace internal {
template<typename ClassType>
@@ -196,6 +302,10 @@ namespace emscripten {
template<>
void raw_destructor<SkData>(SkData *ptr) {
}
+
+ template<>
+ void raw_destructor<SkVertices>(SkVertices *ptr) {
+ }
}
}
@@ -222,21 +332,100 @@ EMSCRIPTEN_BINDINGS(Skia) {
return SkSurface::MakeRasterN32Premul(width, height, nullptr);
}), allow_raw_pointers());
#endif
+ function("getSkDataBytes", &getSkDataBytes, allow_raw_pointers());
function("MakeSkCornerPathEffect", &SkCornerPathEffect::Make, allow_raw_pointers());
function("MakeSkDiscretePathEffect", &SkDiscretePathEffect::Make, allow_raw_pointers());
- // Won't be called directly, there's a JS helper to deal with typed arrays.
+ function("MakePathFromOp", &MakePathFromOp);
+
+ // These won't be called directly, there's a JS helper to deal with typed arrays.
function("_MakeSkDashPathEffect", 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());
- function("getSkDataBytes", &getSkDataBytes, allow_raw_pointers());
+ function("_MakeImageShader", optional_override([](uintptr_t /* uint8_t* */ iPtr, int ilen,
+ SkShader::TileMode tx, SkShader::TileMode ty)->sk_sp<SkShader> {
+ // See comment above for uintptr_t explanation
+ const uint8_t* imgBytes = reinterpret_cast<const uint8_t*>(iPtr);
+
+ auto imgData = SkData::MakeFromMalloc(imgBytes, ilen);
+ auto img = SkImage::MakeFromEncoded(imgData);
+ if (!img) {
+ SkDebugf("Could not decode image\n");
+ return nullptr;
+ }
+
+ return SkImageShader::Make(img, tx, ty, nullptr);
+ }), allow_raw_pointers());
+ function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
+ uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
+ int count, SkShader::TileMode mode, uint32_t flags)->sk_sp<SkShader> {
+ SkPoint points[] = { start, end };
+ // See comment above for uintptr_t explanation
+ const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
+ const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
+
+ return SkGradientShader::MakeLinear(points, colors, positions, count,
+ mode, flags, nullptr);
+ }), allow_raw_pointers());
+ function("_MakeLinearGradientShader", optional_override([](SkPoint start, SkPoint end,
+ uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
+ int count, SkShader::TileMode mode, uint32_t flags,
+ const SimpleMatrix& lm)->sk_sp<SkShader> {
+ SkPoint points[] = { start, end };
+ // See comment above for uintptr_t explanation
+ const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
+ const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
+
+ SkMatrix localMatrix = toSkMatrix(lm);
+
+ return SkGradientShader::MakeLinear(points, colors, positions, count,
+ mode, flags, &localMatrix);
+ }), allow_raw_pointers());
+ function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
+ uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
+ int count, SkShader::TileMode mode, uint32_t flags)->sk_sp<SkShader> {
+ // See comment above for uintptr_t explanation
+ const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
+ const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
+
+ return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
+ mode, flags, nullptr);
+ }), allow_raw_pointers());
+ function("_MakeRadialGradientShader", optional_override([](SkPoint center, SkScalar radius,
+ uintptr_t /* SkColor* */ cPtr, uintptr_t /* SkScalar* */ pPtr,
+ int count, SkShader::TileMode mode, uint32_t flags,
+ const SimpleMatrix& lm)->sk_sp<SkShader> {
+ // See comment above for uintptr_t explanation
+ const SkColor* colors = reinterpret_cast<const SkColor*> (cPtr);
+ const SkScalar* positions = reinterpret_cast<const SkScalar*>(pPtr);
+
+ SkMatrix localMatrix = toSkMatrix(lm);
+ return SkGradientShader::MakeRadial(center, radius, colors, positions, count,
+ mode, flags, &localMatrix);
+ }), allow_raw_pointers());
+ function("_MakeSkVertices", optional_override([](SkVertices::VertexMode mode, int vertexCount,
+ uintptr_t /* SkPoint* */ pPtr, uintptr_t /* SkPoint* */ tPtr,
+ uintptr_t /* SkColor* */ cPtr,
+ uintptr_t /* BoneIndices* */ biPtr, uintptr_t /* BoneWeights* */ bwPtr,
+ int indexCount, uintptr_t /* uint16_t * */ iPtr)->sk_sp<SkVertices> {
+ // See comment above for uintptr_t explanation
+ const SkPoint* positions = reinterpret_cast<const SkPoint*>(pPtr);
+ const SkPoint* texs = reinterpret_cast<const SkPoint*>(tPtr);
+ const SkColor* colors = reinterpret_cast<const SkColor*>(cPtr);
+ const BoneIndices* boneIndices = reinterpret_cast<const BoneIndices*>(biPtr);
+ const BoneWeights* boneWeights = reinterpret_cast<const BoneWeights*>(bwPtr);
+ const uint16_t* indices = reinterpret_cast<const uint16_t*>(iPtr);
+
+ return SkVertices::MakeCopy(mode, vertexCount, positions, texs, colors,
+ boneIndices, boneWeights, indexCount, indices);
+ }), allow_raw_pointers());
class_<SkCanvas>("SkCanvas")
.constructor<>()
.function("clear", optional_override([](SkCanvas& self, JSColor color)->void {
// JS side gives us a signed int instead of an unsigned int for color
- // Add a lambda to change it out.
+ // Add a optional_override to change it out.
self.clear(SkColor(color));
}))
.function("drawPaint", &SkCanvas::drawPaint)
@@ -248,8 +437,9 @@ EMSCRIPTEN_BINDINGS(Skia) {
// Otherwise, go with std::wstring and set UTF-32 encoding.
self.drawText(text.c_str(), text.length(), x, y, p);
}))
+ .function("drawVertices", select_overload<void (const sk_sp<SkVertices>&, SkBlendMode, const SkPaint&)>(&SkCanvas::drawVertices))
.function("flush", &SkCanvas::flush)
- .function("rotate", select_overload<void (SkScalar degrees, SkScalar px, SkScalar py)>(&SkCanvas::rotate))
+ .function("rotate", select_overload<void (SkScalar, SkScalar, SkScalar)>(&SkCanvas::rotate))
.function("save", &SkCanvas::save)
.function("scale", &SkCanvas::scale)
.function("setMatrix", &SkCanvas::setMatrix)
@@ -279,7 +469,7 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("setAntiAlias", &SkPaint::setAntiAlias)
.function("setColor", optional_override([](SkPaint& self, JSColor color)->void {
// JS side gives us a signed int instead of an unsigned int for color
- // Add a lambda to change it out.
+ // Add a optional_override to change it out.
self.setColor(SkColor(color));
}))
.function("setPathEffect", &SkPaint::setPathEffect)
@@ -305,6 +495,18 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_quadTo", &ApplyQuadTo)
.function("_transform", select_overload<void(SkPath& orig, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar, SkScalar)>(&ApplyTransform))
+ // PathEffects
+ .function("_dash", &ApplyDash)
+ .function("_trim", &ApplyTrim)
+ .function("_stroke", &ApplyStroke)
+
+ // PathOps
+ .function("_simplify", &ApplySimplify)
+ .function("_op", &ApplyPathOp)
+
+ // Exporting
+ .function("toSVGString", &ToSVGString)
+
.function("setFillType", &SkPath::setFillType)
.function("getFillType", &SkPath::getFillType)
.function("getBounds", &SkPath::getBounds)
@@ -312,6 +514,9 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("equals", &Equals)
.function("copy", &CopyPath);
+ class_<SkShader>("SkShader")
+ .smart_ptr<sk_sp<SkShader>>("sk_sp<SkShader>");
+
class_<SkSurface>("SkSurface")
.smart_ptr<sk_sp<SkSurface>>("sk_sp<SkSurface>")
.function("width", &SkSurface::width)
@@ -319,23 +524,90 @@ EMSCRIPTEN_BINDINGS(Skia) {
.function("_flush", &SkSurface::flush)
.function("makeImageSnapshot", &SkSurface::makeImageSnapshot)
.function("_readPixels", optional_override([](SkSurface& self, int width, int height, uintptr_t /* uint8_t* */ cptr)->bool {
- auto* dst = reinterpret_cast<uint8_t*>(cptr);
+ uint8_t* dst = reinterpret_cast<uint8_t*>(cptr);
auto dstInfo = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
return self.readPixels(dstInfo, dst, width*4, 0, 0);
}))
.function("getCanvas", &SkSurface::getCanvas, allow_raw_pointers());
+ class_<SkVertices>("SkVertices")
+ .smart_ptr<sk_sp<SkVertices>>("sk_sp<SkVertices>")
+ .function("_applyBones", optional_override([](SkVertices& self, uintptr_t /* Bone* */ bptr, int boneCount)->sk_sp<SkVertices> {
+ // See comment above for uintptr_t explanation
+ const Bone* bones = reinterpret_cast<const Bone*>(bptr);
+ return self.applyBones(bones, boneCount);
+ }))
+ .function("bounds", &SkVertices::bounds)
+ .function("mode", &SkVertices::mode)
+ .function("uniqueID", &SkVertices::uniqueID)
+ .function("dumpPositions", optional_override([](SkVertices& self)->void {
+ auto pos = self.positions();
+ for(int i = 0; i< self.vertexCount(); i++) {
+ SkDebugf("position[%d] = (%f, %f)\n", i, pos[i].x(), pos[i].y());
+ }
+ }))
+ .function("vertexCount", &SkVertices::vertexCount);
+
+
+ enum_<SkBlendMode>("BlendMode")
+ .value("Clear", SkBlendMode::kClear)
+ .value("Src", SkBlendMode::kSrc)
+ .value("Dst", SkBlendMode::kDst)
+ .value("SrcOver", SkBlendMode::kSrcOver)
+ .value("DstOver", SkBlendMode::kDstOver)
+ .value("SrcIn", SkBlendMode::kSrcIn)
+ .value("DstIn", SkBlendMode::kDstIn)
+ .value("SrcOut", SkBlendMode::kSrcOut)
+ .value("DstOut", SkBlendMode::kDstOut)
+ .value("SrcATop", SkBlendMode::kSrcATop)
+ .value("DstATop", SkBlendMode::kDstATop)
+ .value("Xor", SkBlendMode::kXor)
+ .value("Plus", SkBlendMode::kPlus)
+ .value("Modulate", SkBlendMode::kModulate)
+ .value("Screen", SkBlendMode::kScreen)
+ .value("Overlay", SkBlendMode::kOverlay)
+ .value("Darken", SkBlendMode::kDarken)
+ .value("Lighten", SkBlendMode::kLighten)
+ .value("ColorDodge", SkBlendMode::kColorDodge)
+ .value("ColorBurn", SkBlendMode::kColorBurn)
+ .value("HardLight", SkBlendMode::kHardLight)
+ .value("SoftLight", SkBlendMode::kSoftLight)
+ .value("Difference", SkBlendMode::kDifference)
+ .value("Exclusion", SkBlendMode::kExclusion)
+ .value("Multiply", SkBlendMode::kMultiply)
+ .value("Hue", SkBlendMode::kHue)
+ .value("Saturation", SkBlendMode::kSaturation)
+ .value("Color", SkBlendMode::kColor)
+ .value("Luminosity", SkBlendMode::kLuminosity);
enum_<SkPaint::Style>("PaintStyle")
- .value("FILL", SkPaint::Style::kFill_Style)
- .value("STROKE", SkPaint::Style::kStroke_Style)
- .value("STROKE_AND_FILL", SkPaint::Style::kStrokeAndFill_Style);
+ .value("Fill", SkPaint::Style::kFill_Style)
+ .value("Stroke", SkPaint::Style::kStroke_Style)
+ .value("StrokeAndFill", SkPaint::Style::kStrokeAndFill_Style);
enum_<SkPath::FillType>("FillType")
- .value("WINDING", SkPath::FillType::kWinding_FillType)
- .value("EVENODD", SkPath::FillType::kEvenOdd_FillType)
- .value("INVERSE_WINDING", SkPath::FillType::kInverseWinding_FillType)
- .value("INVERSE_EVENODD", SkPath::FillType::kInverseEvenOdd_FillType);
+ .value("Winding", SkPath::FillType::kWinding_FillType)
+ .value("EvenOdd", SkPath::FillType::kEvenOdd_FillType)
+ .value("InverseWinding", SkPath::FillType::kInverseWinding_FillType)
+ .value("InverseEvenOdd", SkPath::FillType::kInverseEvenOdd_FillType);
+
+ enum_<SkPathOp>("PathOp")
+ .value("Difference", SkPathOp::kDifference_SkPathOp)
+ .value("Intersect", SkPathOp::kIntersect_SkPathOp)
+ .value("Union", SkPathOp::kUnion_SkPathOp)
+ .value("XOR", SkPathOp::kXOR_SkPathOp)
+ .value("ReverseDifference", SkPathOp::kReverseDifference_SkPathOp);
+
+ enum_<SkShader::TileMode>("TileMode")
+ .value("Clamp", SkShader::TileMode::kClamp_TileMode)
+ .value("Repeat", SkShader::TileMode::kRepeat_TileMode)
+ .value("Mirror", SkShader::TileMode::kMirror_TileMode);
+
+ enum_<SkVertices::VertexMode>("VertexMode")
+ .value("Triangles", SkVertices::VertexMode::kTriangles_VertexMode)
+ .value("TrianglesStrip", SkVertices::VertexMode::kTriangleStrip_VertexMode)
+ .value("TriangleFan", SkVertices::VertexMode::kTriangleFan_VertexMode);
+
// A value object is much simpler than a class - it is returned as a JS
// object and does not require delete().
@@ -360,6 +632,31 @@ EMSCRIPTEN_BINDINGS(Skia) {
.field("w", &SkISize::fWidth)
.field("h", &SkISize::fHeight);
+ // Allows clients to supply a 1D array of 9 elements and the bindings
+ // will automatically turn it into a 3x3 2D matrix.
+ // e.g. path.transform([0,1,2,3,4,5,6,7,8])
+ // This is likely simpler for the client than exposing SkMatrix
+ // directly and requiring them to do a lot of .delete().
+ value_array<SimpleMatrix>("SkMatrix")
+ .element(&SimpleMatrix::scaleX)
+ .element(&SimpleMatrix::skewX)
+ .element(&SimpleMatrix::transX)
+
+ .element(&SimpleMatrix::skewY)
+ .element(&SimpleMatrix::scaleY)
+ .element(&SimpleMatrix::transY)
+
+ .element(&SimpleMatrix::pers0)
+ .element(&SimpleMatrix::pers1)
+ .element(&SimpleMatrix::pers2);
+
+ constant("TRANSPARENT", (JSColor) SK_ColorTRANSPARENT);
+ constant("RED", (JSColor) SK_ColorRED);
+ constant("BLUE", (JSColor) SK_ColorBLUE);
+ constant("YELLOW", (JSColor) SK_ColorYELLOW);
+ constant("CYAN", (JSColor) SK_ColorCYAN);
+ // TODO(?)
+
#if SK_INCLUDE_SKOTTIE
// Animation things (may eventually go in own library)
class_<skottie::Animation>("Animation")
@@ -377,7 +674,9 @@ EMSCRIPTEN_BINDINGS(Skia) {
self.render(canvas, &r);
}), allow_raw_pointers());
- function("MakeAnimation", &MakeAnimation);
+ function("MakeAnimation", optional_override([](std::string json)->sk_sp<skottie::Animation> {
+ return skottie::Animation::Make(json.c_str(), json.length());
+ }));
constant("skottie", true);
#endif
@@ -405,8 +704,8 @@ EMSCRIPTEN_BINDINGS(Skia) {
const uint8_t* nimaBytes = reinterpret_cast<const uint8_t*>(nptr);
const uint8_t* textureBytes = reinterpret_cast<const uint8_t*>(tptr);
- auto nima = SkData::MakeWithoutCopy(nimaBytes, nlen);
- auto texture = SkData::MakeWithoutCopy(textureBytes, tlen);
+ auto nima = SkData::MakeFromMalloc(nimaBytes, nlen);
+ auto texture = SkData::MakeFromMalloc(textureBytes, tlen);
return new NimaActor(nima, texture);
}), allow_raw_pointers());
constant("nima", true);
diff --git a/experimental/canvaskit/compile.sh b/experimental/canvaskit/compile.sh
index 026ca326fa..b7a8653de1 100755
--- a/experimental/canvaskit/compile.sh
+++ b/experimental/canvaskit/compile.sh
@@ -117,7 +117,8 @@ echo "Compiling bitcode"
skia_use_freetype=true \
skia_use_icu=false \
skia_use_libheif=false \
- skia_use_libjpeg_turbo=false \
+ skia_use_system_libjpeg_turbo = false \
+ skia_use_libjpeg_turbo=true \
skia_use_libpng=true \
skia_use_libwebp=false \
skia_use_lua=false \
@@ -161,7 +162,9 @@ ${EMCXX} \
-Imodules/skottie/include \
-Imodules/sksg/include \
-Isrc/core/ \
+ -Isrc/gpu/ \
-Isrc/sfnt/ \
+ -Isrc/shaders/ \
-Isrc/utils/ \
-Itools \
-Itools/fonts \
diff --git a/experimental/canvaskit/externs.js b/experimental/canvaskit/externs.js
index c11b4caac9..4ced26d656 100644
--- a/experimental/canvaskit/externs.js
+++ b/experimental/canvaskit/externs.js
@@ -29,8 +29,12 @@ var CanvasKit = {
LTRBRect: function() {},
MakeCanvas: function() {},
MakeCanvasSurface: function() {},
+ MakeImageShader: function() {},
+ MakeLinearGradientShader: function() {},
+ MakeRadialGradientShader: function() {},
MakeNimaActor: function() {},
MakeSkDashPathEffect: function() {},
+ MakeSkVertices: function() {},
MakeSurface: function() {},
currentContext: function() {},
getSkDataBytes: function() {},
@@ -39,8 +43,12 @@ var CanvasKit = {
// private API (i.e. things declared in the bindings that we use
// in the pre-js file)
+ _MakeImageShader: function() {},
+ _MakeLinearGradientShader: function() {},
_MakeNimaActor: function() {},
+ _MakeRadialGradientShader: function() {},
_MakeSkDashPathEffect: function() {},
+ _MakeSkVertices: function() {},
_getRasterN32PremulSurface: function() {},
_getWebGLSurface: function() {},
@@ -89,13 +97,16 @@ var CanvasKit = {
_close: function() {},
_conicTo: function() {},
_cubicTo: function() {},
+ _dash: function() {},
_lineTo: function() {},
_moveTo: function() {},
_op: function() {},
_quadTo: function() {},
_rect: function() {},
_simplify: function() {},
+ _stroke: function() {},
_transform: function() {},
+ _trim: function() {},
delete: function() {},
},
@@ -137,6 +148,16 @@ var CanvasKit = {
delete: function() {},
},
+ SkVertices: {
+ // public API (from C++ bindings)
+ /** @return {CanvasKit.SkVertices} */
+ applyBones: function() {},
+
+ // private API
+ /** @return {CanvasKit.SkVertices} */
+ _applyBones: function() {},
+ },
+
// Constants and Enums
gpu: {},
skottie: {},
@@ -162,11 +183,19 @@ var CanvasKit = {
/**
* @type {Float32Array}
*/
- HEAPF32: {}, // only needed for TypedArray mallocs
+ HEAPF32: {},
/**
* @type {Uint8Array}
*/
HEAPU8: {},
+ /**
+ * @type {Uint16Array}
+ */
+ HEAPU16: {},
+ /**
+ * @type {Int32Array}
+ */
+ HEAP32: {},
_malloc: function() {},
_free: function() {},
@@ -181,16 +210,28 @@ CanvasKit.SkPath.prototype.arcTo = 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.op = function() {};
CanvasKit.SkPath.prototype.quadTo = 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.SkSurface.prototype.flush = function() {};
CanvasKit.SkSurface.prototype.dispose = function() {};
+CanvasKit.SkVertices.prototype.applyBones = function() {};
+
+// Define StrokeOpts object
+var StrokeOpts = {};
+StrokeOpts.prototype.width;
+StrokeOpts.prototype.miter_limit;
+StrokeOpts.prototype.cap;
+StrokeOpts.prototype.join;
+
// Not sure why this is needed - might be a bug in emsdk that this isn't properly declared.
function loadWebAssemblyModule() {}
diff --git a/experimental/canvaskit/htmlcanvas/canvas2d.js b/experimental/canvaskit/htmlcanvas/canvas2d.js
index 4289d87796..0d13fb0dbd 100644
--- a/experimental/canvaskit/htmlcanvas/canvas2d.js
+++ b/experimental/canvaskit/htmlcanvas/canvas2d.js
@@ -141,14 +141,14 @@
this.stroke = function() {
if (this._currentPath) {
- this._paint.setStyle(CanvasKit.PaintStyle.STROKE);
+ this._paint.setStyle(CanvasKit.PaintStyle.Stroke);
this._canvas.drawPath(this._currentPath, this._paint);
}
}
this.strokeText = function(text, x, y, maxWidth) {
// TODO do something with maxWidth, probably involving measure
- this._paint.setStyle(CanvasKit.PaintStyle.STROKE);
+ this._paint.setStyle(CanvasKit.PaintStyle.Stroke);
this._canvas.drawText(text, x, y, this._paint);
}
diff --git a/experimental/canvaskit/interface.js b/experimental/canvaskit/interface.js
index d74f380e4d..be9cab1a83 100644
--- a/experimental/canvaskit/interface.js
+++ b/experimental/canvaskit/interface.js
@@ -1,7 +1,7 @@
// Adds JS functions to augment the CanvasKit interface.
// For example, if there is a wrapper around the C++ call or logic to allow
// chaining, it should go here.
-(function(CanvasKit){
+(function(CanvasKit) {
// CanvasKit.onRuntimeInitialized is called after the WASM library has loaded.
// Anything that modifies an exposed class (e.g. SkPath) should be set
// after onRuntimeInitialized, otherwise, it can happen outside of that scope.
@@ -11,7 +11,9 @@
// Takes 1, 2, or 10 args, where the first arg is always the path.
// The options for the remaining args are:
// - an array of 9 parameters
- // - the 9 parameters of a full Matrix
+ // - the 9 parameters of a full matrix
+ // an array of 6 parameters (omitting perspective)
+ // the 6 non-perspective params of a matrix.
if (arguments.length === 1) {
// Add path, unchanged. Use identify matrix
this._addPath(arguments[0], 1, 0, 0,
@@ -22,16 +24,17 @@
var sm = arguments[1];
this._addPath(arguments[0], a[1], a[2], a[3],
a[4], a[5], a[6],
- a[7], a[8], a[9]);
- } else if (arguments.length === 10) {
+ a[7] || 0, a[8] || 0, a[9] || 1);
+ } else if (arguments.length === 7 || arguments.length === 10) {
// User provided the 9 params of a (full) matrix directly.
+ // (or just the 6 non perspective ones)
// These are in the same order as what Skia expects.
var a = arguments;
this._addPath(arguments[0], a[1], a[2], a[3],
a[4], a[5], a[6],
- a[7], a[8], a[9]);
+ a[7] || 0, a[8] || 0, a[9] || 1);
} else {
- console.err('addPath expected to take 1, 2, or 10 args. Got ' + arguments.length);
+ console.err('addPath expected to take 1, 2, 7, or 10 args. Got ' + arguments.length);
return null;
}
return this;
@@ -57,6 +60,13 @@
return this;
};
+ CanvasKit.SkPath.prototype.dash = function(on, off, phase) {
+ if (this._dash(on, off, phase)) {
+ return this;
+ }
+ return null;
+ };
+
CanvasKit.SkPath.prototype.lineTo = function(x, y) {
this._lineTo(x, y);
return this;
@@ -86,26 +96,62 @@
return null;
};
+ CanvasKit.SkPath.prototype.stroke = function(opts) {
+ // Fill out any missing values with the default values.
+ /**
+ * See externs.js for this definition
+ * @type {StrokeOpts}
+ */
+ opts = opts || {};
+ opts.width = opts.width || 1;
+ opts.miter_limit = opts.miter_limit || 4;
+ opts.cap = opts.cap || CanvasKit.StrokeCap.BUTT;
+ opts.join = opts.join || CanvasKit.StrokeJoin.MITER;
+ if (this._stroke(opts)) {
+ return this;
+ }
+ return null;
+ };
+
CanvasKit.SkPath.prototype.transform = function() {
// Takes 1 or 9 args
if (arguments.length === 1) {
- // argument 1 should be a 9 element array.
+ // argument 1 should be a 6 or 9 element array.
var a = arguments[0];
this._transform(a[0], a[1], a[2],
a[3], a[4], a[5],
- a[6], a[7], a[8]);
- } else if (arguments.length === 9) {
- // these arguments are the 9 members of the matrix
+ a[6] || 0, a[7] || 0, a[8] || 1);
+ } else if (arguments.length === 6 || arguments.length === 9) {
+ // these arguments are the 6 or 9 members of the matrix
var a = arguments;
this._transform(a[0], a[1], a[2],
a[3], a[4], a[5],
- a[6], a[7], a[8]);
+ a[6] || 0, a[7] || 0, a[8] || 1);
} else {
- console.err('transform expected to take 1 or 9 arguments. Got ' + arguments.length);
- return null;
+ throw 'transform expected to take 1 or 9 arguments. Got ' + arguments.length;
}
return this;
};
+ // isComplement is optional, defaults to false
+ CanvasKit.SkPath.prototype.trim = function(startT, stopT, isComplement) {
+ if (this._trim(startT, stopT, !!isComplement)) {
+ return this;
+ }
+ return null;
+ };
+
+ // bones should be a 3d array.
+ // Each bone is a 3x2 transformation matrix in column major order:
+ // | scaleX skewX transX |
+ // | skewY scaleY transY |
+ // and bones is an array of those matrices.
+ // Returns a copy of this (SkVertices) with the bones applied.
+ CanvasKit.SkVertices.prototype.applyBones = function(bones) {
+ var bPtr = copy3dArray(bones, CanvasKit.HEAPF32);
+ var vert = this._applyBones(bPtr, bones.length);
+ CanvasKit._free(bPtr);
+ return vert;
+ }
// Run through the JS files that are added at compile time.
if (CanvasKit._extraInitializations) {
@@ -125,6 +171,68 @@
};
}
+ 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 something like CanvasKit.HEAPF32
+ function copy1dArray(arr, dest) {
+ if (!arr || !arr.length) {
+ return nullptr;
+ }
+ var ptr = CanvasKit._malloc(arr.length * dest.BYTES_PER_ELEMENT);
+ // In c++ terms, the WASM heap is a uint8_t*, a long buffer/array of single
+ // byte elements. When we run _malloc, we always get an offset/pointer into
+ // that block of memory.
+ // CanvasKit exposes some different views to make it easier to work with
+ // different types. HEAPF32 for example, exposes it as a float*
+ // However, to make the ptr line up, we have to do some pointer arithmetic.
+ // Concretely, we need to convert ptr to go from an index into a 1-byte-wide
+ // buffer to an index into a 4-byte-wide buffer (in the case of HEAPF32)
+ // and thus we divide ptr by 4.
+ dest.set(arr, ptr / dest.BYTES_PER_ELEMENT);
+ return ptr;
+ }
+
+ // arr should be a non-jagged 2d JS array (TypeyArrays can't be nested
+ // inside themselves.)
+ // dest is something like CanvasKit.HEAPF32
+ function copy2dArray(arr, dest) {
+ if (!arr || !arr.length) {
+ return nullptr;
+ }
+ var ptr = CanvasKit._malloc(arr.length * arr[0].length * dest.BYTES_PER_ELEMENT);
+ var idx = 0;
+ var adjustedPtr = ptr / dest.BYTES_PER_ELEMENT;
+ for (var r = 0; r < arr.length; r++) {
+ for (var c = 0; c < arr[0].length; c++) {
+ dest[adjustedPtr + idx] = arr[r][c];
+ idx++;
+ }
+ }
+ return ptr;
+ }
+
+ // arr should be a non-jagged 3d JS array (TypeyArrays can't be nested
+ // inside themselves.)
+ // dest is something like CanvasKit.HEAPF32
+ function copy3dArray(arr, dest) {
+ if (!arr || !arr.length || !arr[0].length) {
+ return nullptr;
+ }
+ var ptr = CanvasKit._malloc(arr.length * arr[0].length * arr[0][0].length * dest.BYTES_PER_ELEMENT);
+ var idx = 0;
+ var adjustedPtr = ptr / dest.BYTES_PER_ELEMENT;
+ for (var x = 0; x < arr.length; x++) {
+ for (var y = 0; y < arr[0].length; y++) {
+ for (var z = 0; z < arr[0][0].length; z++) {
+ dest[adjustedPtr + idx] = arr[x][y][z];
+ idx++;
+ }
+ }
+ }
+ return ptr;
+ }
+
CanvasKit.MakeSkDashPathEffect = function(intervals, phase) {
if (!phase) {
phase = 0;
@@ -132,13 +240,91 @@
if (!intervals.length || intervals.length % 2 === 1) {
throw 'Intervals array must have even length';
}
- if (!(intervals instanceof Float32Array)) {
- intervals = Float32Array.from(intervals);
+ var ptr = copy1dArray(intervals, CanvasKit.HEAPF32);
+ var dpe = CanvasKit._MakeSkDashPathEffect(ptr, intervals.length, phase);
+ CanvasKit._free(ptr);
+ return dpe;
+ }
+
+ CanvasKit.MakeImageShader = function(imgData, xTileMode, yTileMode) {
+ var iptr = CanvasKit._malloc(imgData.byteLength);
+ CanvasKit.HEAPU8.set(new Uint8Array(imgData), iptr);
+ // No need to _free iptr, ImageShader takes it with SkData::MakeFromMalloc
+
+ return CanvasKit._MakeImageShader(iptr, imgData.byteLength, xTileMode, yTileMode);
+ }
+
+ CanvasKit.MakeLinearGradientShader = function(start, end, colors, pos, mode, localMatrix, flags) {
+ var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
+ var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
+ flags = flags || 0;
+
+ if (localMatrix) {
+ // Add perspective args if not provided.
+ if (localMatrix.length === 6) {
+ localMatrix.push(0, 0, 1);
+ }
+ var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
+ colors.length, mode, flags, localMatrix);
+ } else {
+ var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
+ colors.length, mode, flags);
+ }
+
+ CanvasKit._free(colorPtr);
+ CanvasKit._free(posPtr);
+ return lgs;
+ }
+
+ CanvasKit.MakeRadialGradientShader = function(center, radius, colors, pos, mode, localMatrix, flags) {
+ // TODO: matrix and flags
+ var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
+ var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
+ flags = flags || 0;
+
+ if (localMatrix) {
+ // Add perspective args if not provided.
+ if (localMatrix.length === 6) {
+ localMatrix.push(0, 0, 1);
+ }
+ var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
+ colors.length, mode, flags, localMatrix);
+ } else {
+ var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
+ colors.length, mode, flags);
}
- var BYTES_PER_ELEMENT = 4; // Float32Array always has 4 bytes per element
- var ptr = CanvasKit._malloc(intervals.length * BYTES_PER_ELEMENT);
- CanvasKit.HEAPF32.set(intervals, ptr / BYTES_PER_ELEMENT);
- return CanvasKit._MakeSkDashPathEffect(ptr, intervals.length, phase);
+
+ CanvasKit._free(colorPtr);
+ CanvasKit._free(posPtr);
+ return rgs;
+ }
+
+ CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors,
+ boneIndices, boneWeights, indices) {
+ var positionPtr = copy2dArray(positions, CanvasKit.HEAPF32);
+ var texPtr = copy2dArray(textureCoordinates, CanvasKit.HEAPF32);
+ // Since we write the colors to memory as signed integers (JSColor), we can
+ // read them out on the other side as unsigned ints (SkColor) just fine
+ // - it's effectively casting.
+ var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
+
+ var boneIdxPtr = copy2dArray(boneIndices, CanvasKit.HEAP32);
+ var boneWtPtr = copy2dArray(boneWeights, CanvasKit.HEAPF32);
+ var idxPtr = copy1dArray(indices, CanvasKit.HEAPU16);
+
+ var idxCount = (indices && indices.length) || 0;
+ // _MakeVertices will copy all the values in, so we are free to release
+ // the memory after.
+ var vertices = CanvasKit._MakeSkVertices(mode, positions.length, positionPtr,
+ texPtr, colorPtr, boneIdxPtr, boneWtPtr,
+ idxCount, idxPtr);
+ positionPtr && CanvasKit._free(positionPtr);
+ texPtr && CanvasKit._free(texPtr);
+ colorPtr && CanvasKit._free(colorPtr);
+ idxPtr && CanvasKit._free(idxPtr);
+ boneIdxPtr && CanvasKit._free(boneIdxPtr);
+ boneWtPtr && CanvasKit._free(boneWtPtr);
+ return vertices;
}
CanvasKit.MakeNimaActor = function(nimaFile, nimaTexture) {
@@ -146,6 +332,7 @@
CanvasKit.HEAPU8.set(new Uint8Array(nimaFile), nptr);
var tptr = CanvasKit._malloc(nimaTexture.byteLength);
CanvasKit.HEAPU8.set(new Uint8Array(nimaTexture), tptr);
+ // No need to _free these ptrs, NimaActor takes them with SkData::MakeFromMalloc
return CanvasKit._MakeNimaActor(nptr, nimaFile.byteLength, tptr, nimaTexture.byteLength);
}