blob: febdffceb55134af3dfb3b88f998d65108db61b3 [file] [log] [blame]
Kevin Lubick217056c2018-09-20 17:39:31 -04001// Adds JS functions to augment the CanvasKit interface.
2// For example, if there is a wrapper around the C++ call or logic to allow
3// chaining, it should go here.
Kevin Lubickb5ae3b52018-11-03 07:51:19 -04004(function(CanvasKit) {
Kevin Lubick217056c2018-09-20 17:39:31 -04005 // CanvasKit.onRuntimeInitialized is called after the WASM library has loaded.
6 // Anything that modifies an exposed class (e.g. SkPath) should be set
7 // after onRuntimeInitialized, otherwise, it can happen outside of that scope.
8 CanvasKit.onRuntimeInitialized = function() {
9 // All calls to 'this' need to go in externs.js so closure doesn't minify them away.
Kevin Lubick1a05fce2018-11-20 12:51:16 -050010
11
12 // Add some helpers for matrices. This is ported from SkMatrix.cpp
13 // to save complexity and overhead of going back and forth between
14 // C++ and JS layers.
15 CanvasKit.SkMatrix = {};
16 function sdot(a, b, c, d, e, f) {
17 e = e || 0;
18 f = f || 0;
19 return a * b + c * d + e * f;
20 }
21
22 // Return a matrix representing a rotation by n degrees.
23 // px, py optionally say which point the rotation should be around
24 // with the default being (0, 0);
25 CanvasKit.SkMatrix.rotated = function(degrees, px, py) {
26 px = px || 0;
27 py = py || 0;
28 var rad = degreesToRadians(degrees);
29 var sinV = Math.sin(rad);
30 var cosV = Math.cos(rad);
31 return [
32 cosV, -sinV, sdot( sinV, py, 1 - cosV, px),
33 sinV, cosV, sdot(-sinV, px, 1 - cosV, py),
34 0, 0, 1,
35 ];
36 };
37 // TODO(kjlubick): translated, scaled
38
39
40 CanvasKit.SkPath.prototype.addArc = function(oval, startAngle, sweepAngle) {
41 // see arc() for the HTMLCanvas version
42 // note input angles are degrees.
43 this._addArc(oval, startAngle, sweepAngle);
44 return this;
45 };
46
Kevin Lubick217056c2018-09-20 17:39:31 -040047 CanvasKit.SkPath.prototype.addPath = function() {
Kevin Lubick1a05fce2018-11-20 12:51:16 -050048 // Takes 1, 2, 7, or 10 required args, where the first arg is always the path.
49 // The last arg is optional and chooses between add or extend mode.
Kevin Lubick217056c2018-09-20 17:39:31 -040050 // The options for the remaining args are:
Kevin Lubick1a05fce2018-11-20 12:51:16 -050051 // - an array of 6 or 9 parameters (perspective is optional)
52 // - the 9 parameters of a full matrix or
Kevin Lubickb5ae3b52018-11-03 07:51:19 -040053 // the 6 non-perspective params of a matrix.
Kevin Lubick1a05fce2018-11-20 12:51:16 -050054 var args = Array.prototype.slice.call(arguments);
55 var path = args[0];
56 var extend = false;
57 if (typeof args[args.length-1] === "boolean") {
58 extend = args.pop();
59 }
60 if (args.length === 1) {
61 // Add path, unchanged. Use identity matrix
62 this._addPath(path, 1, 0, 0,
63 0, 1, 0,
64 0, 0, 1,
65 extend);
66 } else if (args.length === 2) {
Kevin Lubick217056c2018-09-20 17:39:31 -040067 // User provided the 9 params of a full matrix as an array.
Kevin Lubick1a05fce2018-11-20 12:51:16 -050068 var a = args[1];
69 this._addPath(path, a[0], a[1], a[2],
70 a[3], a[4], a[5],
71 a[6] || 0, a[7] || 0, a[8] || 1,
72 extend);
73 } else if (args.length === 7 || args.length === 10) {
Kevin Lubick217056c2018-09-20 17:39:31 -040074 // User provided the 9 params of a (full) matrix directly.
Kevin Lubickb5ae3b52018-11-03 07:51:19 -040075 // (or just the 6 non perspective ones)
Kevin Lubick217056c2018-09-20 17:39:31 -040076 // These are in the same order as what Skia expects.
Kevin Lubick1a05fce2018-11-20 12:51:16 -050077 var a = args;
78 this._addPath(path, a[1], a[2], a[3],
79 a[4], a[5], a[6],
80 a[7] || 0, a[8] || 0, a[9] || 1,
81 extend);
Kevin Lubick217056c2018-09-20 17:39:31 -040082 } else {
Kevin Lubick6fccc9d2018-11-20 15:55:10 -050083 SkDebug('addPath expected to take 1, 2, 7, or 10 required args. Got ' + args.length);
Kevin Lubick1a05fce2018-11-20 12:51:16 -050084 return null;
85 }
86 return this;
87 };
88
89 CanvasKit.SkPath.prototype.addRect = function() {
90 // Takes 1, 2, 4 or 5 args
91 // - SkRect
92 // - SkRect, isCCW
93 // - left, top, right, bottom
94 // - left, top, right, bottom, isCCW
95 if (arguments.length === 1 || arguments.length === 2) {
96 var r = arguments[0];
97 var ccw = arguments[1] || false;
98 this._addRect(r.fLeft, r.fTop, r.fRight, r.fBottom, ccw);
99 } else if (arguments.length === 4 || arguments.length === 5) {
100 var a = arguments;
101 this._addRect(a[0], a[1], a[2], a[3], a[4] || false);
102 } else {
Kevin Lubick6fccc9d2018-11-20 15:55:10 -0500103 SkDebug('addRect expected to take 1, 2, 4, or 5 args. Got ' + arguments.length);
Kevin Lubick217056c2018-09-20 17:39:31 -0400104 return null;
105 }
106 return this;
107 };
108
Alexander Khovansky3e119332018-11-15 02:01:19 +0300109 CanvasKit.SkPath.prototype.arc = function(x, y, radius, startAngle, endAngle, ccw) {
Kevin Lubick1a05fce2018-11-20 12:51:16 -0500110 // emulates the HTMLCanvas behavior. See addArc() for the SkPath version.
111 // Note input angles are radians.
112 var bounds = CanvasKit.LTRBRect(x-radius, y-radius, x+radius, y+radius);
113 var sweep = radiansToDegrees(endAngle - startAngle) - (360 * !!ccw);
114 var temp = new CanvasKit.SkPath();
115 temp.addArc(bounds, radiansToDegrees(startAngle), sweep);
116 this.addPath(temp, true);
117 temp.delete();
Alexander Khovansky3e119332018-11-15 02:01:19 +0300118 return this;
119 };
120
Kevin Lubick217056c2018-09-20 17:39:31 -0400121 CanvasKit.SkPath.prototype.arcTo = function(x1, y1, x2, y2, radius) {
122 this._arcTo(x1, y1, x2, y2, radius);
123 return this;
124 };
125
126 CanvasKit.SkPath.prototype.close = function() {
127 this._close();
128 return this;
129 };
130
131 CanvasKit.SkPath.prototype.conicTo = function(x1, y1, x2, y2, w) {
132 this._conicTo(x1, y1, x2, y2, w);
133 return this;
134 };
135
136 CanvasKit.SkPath.prototype.cubicTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
137 this._cubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
138 return this;
139 };
140
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400141 CanvasKit.SkPath.prototype.dash = function(on, off, phase) {
142 if (this._dash(on, off, phase)) {
143 return this;
144 }
145 return null;
146 };
147
Kevin Lubick217056c2018-09-20 17:39:31 -0400148 CanvasKit.SkPath.prototype.lineTo = function(x, y) {
149 this._lineTo(x, y);
150 return this;
151 };
152
153 CanvasKit.SkPath.prototype.moveTo = function(x, y) {
154 this._moveTo(x, y);
155 return this;
156 };
157
158 CanvasKit.SkPath.prototype.op = function(otherPath, op) {
159 if (this._op(otherPath, op)) {
160 return this;
161 }
162 return null;
163 };
164
165 CanvasKit.SkPath.prototype.quadTo = function(cpx, cpy, x, y) {
166 this._quadTo(cpx, cpy, x, y);
167 return this;
168 };
169
170 CanvasKit.SkPath.prototype.simplify = function() {
171 if (this._simplify()) {
172 return this;
173 }
174 return null;
175 };
176
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400177 CanvasKit.SkPath.prototype.stroke = function(opts) {
178 // Fill out any missing values with the default values.
179 /**
180 * See externs.js for this definition
181 * @type {StrokeOpts}
182 */
183 opts = opts || {};
184 opts.width = opts.width || 1;
185 opts.miter_limit = opts.miter_limit || 4;
186 opts.cap = opts.cap || CanvasKit.StrokeCap.BUTT;
187 opts.join = opts.join || CanvasKit.StrokeJoin.MITER;
188 if (this._stroke(opts)) {
189 return this;
190 }
191 return null;
192 };
193
Kevin Lubick217056c2018-09-20 17:39:31 -0400194 CanvasKit.SkPath.prototype.transform = function() {
195 // Takes 1 or 9 args
196 if (arguments.length === 1) {
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400197 // argument 1 should be a 6 or 9 element array.
Kevin Lubick217056c2018-09-20 17:39:31 -0400198 var a = arguments[0];
199 this._transform(a[0], a[1], a[2],
200 a[3], a[4], a[5],
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400201 a[6] || 0, a[7] || 0, a[8] || 1);
202 } else if (arguments.length === 6 || arguments.length === 9) {
203 // these arguments are the 6 or 9 members of the matrix
Kevin Lubick217056c2018-09-20 17:39:31 -0400204 var a = arguments;
205 this._transform(a[0], a[1], a[2],
206 a[3], a[4], a[5],
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400207 a[6] || 0, a[7] || 0, a[8] || 1);
Kevin Lubick217056c2018-09-20 17:39:31 -0400208 } else {
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400209 throw 'transform expected to take 1 or 9 arguments. Got ' + arguments.length;
Kevin Lubick217056c2018-09-20 17:39:31 -0400210 }
211 return this;
212 };
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400213 // isComplement is optional, defaults to false
214 CanvasKit.SkPath.prototype.trim = function(startT, stopT, isComplement) {
215 if (this._trim(startT, stopT, !!isComplement)) {
216 return this;
217 }
218 return null;
219 };
220
221 // bones should be a 3d array.
222 // Each bone is a 3x2 transformation matrix in column major order:
223 // | scaleX skewX transX |
224 // | skewY scaleY transY |
225 // and bones is an array of those matrices.
226 // Returns a copy of this (SkVertices) with the bones applied.
227 CanvasKit.SkVertices.prototype.applyBones = function(bones) {
228 var bPtr = copy3dArray(bones, CanvasKit.HEAPF32);
229 var vert = this._applyBones(bPtr, bones.length);
230 CanvasKit._free(bPtr);
231 return vert;
232 }
Kevin Lubick53965c92018-10-11 08:51:55 -0400233
Alexander Khovansky3e119332018-11-15 02:01:19 +0300234 CanvasKit.SkImage.prototype.encodeToData = function() {
235 if (arguments.length === 0) {
236 return this._encodeToData();
237 }
238
239 if (arguments.length === 2) {
240 var a = arguments;
241 return this._encodeToDataWithFormat(a[0], a[1]);
242 }
243
244 throw 'encodeToData expected to take 0 or 2 arguments. Got ' + arguments.length;
245 }
246
Kevin Lubick5b90b842018-10-17 07:57:18 -0400247 // Run through the JS files that are added at compile time.
248 if (CanvasKit._extraInitializations) {
249 CanvasKit._extraInitializations.forEach(function(init) {
250 init();
251 });
Kevin Lubick217056c2018-09-20 17:39:31 -0400252 }
Kevin Lubick3d99b1e2018-10-16 10:15:01 -0400253 } // end CanvasKit.onRuntimeInitialized, that is, anything changing prototypes or dynamic.
Kevin Lubick53965c92018-10-11 08:51:55 -0400254
Kevin Lubick217056c2018-09-20 17:39:31 -0400255 CanvasKit.LTRBRect = function(l, t, r, b) {
256 return {
257 fLeft: l,
258 fTop: t,
259 fRight: r,
260 fBottom: b,
261 };
262 }
263
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400264 var nullptr = 0; // emscripten doesn't like to take null as uintptr_t
265
266 // arr can be a normal JS array or a TypedArray
267 // dest is something like CanvasKit.HEAPF32
268 function copy1dArray(arr, dest) {
269 if (!arr || !arr.length) {
270 return nullptr;
271 }
272 var ptr = CanvasKit._malloc(arr.length * dest.BYTES_PER_ELEMENT);
273 // In c++ terms, the WASM heap is a uint8_t*, a long buffer/array of single
274 // byte elements. When we run _malloc, we always get an offset/pointer into
275 // that block of memory.
276 // CanvasKit exposes some different views to make it easier to work with
277 // different types. HEAPF32 for example, exposes it as a float*
278 // However, to make the ptr line up, we have to do some pointer arithmetic.
279 // Concretely, we need to convert ptr to go from an index into a 1-byte-wide
280 // buffer to an index into a 4-byte-wide buffer (in the case of HEAPF32)
281 // and thus we divide ptr by 4.
282 dest.set(arr, ptr / dest.BYTES_PER_ELEMENT);
283 return ptr;
284 }
285
286 // arr should be a non-jagged 2d JS array (TypeyArrays can't be nested
287 // inside themselves.)
288 // dest is something like CanvasKit.HEAPF32
289 function copy2dArray(arr, dest) {
290 if (!arr || !arr.length) {
291 return nullptr;
292 }
293 var ptr = CanvasKit._malloc(arr.length * arr[0].length * dest.BYTES_PER_ELEMENT);
294 var idx = 0;
295 var adjustedPtr = ptr / dest.BYTES_PER_ELEMENT;
296 for (var r = 0; r < arr.length; r++) {
297 for (var c = 0; c < arr[0].length; c++) {
298 dest[adjustedPtr + idx] = arr[r][c];
299 idx++;
300 }
301 }
302 return ptr;
303 }
304
305 // arr should be a non-jagged 3d JS array (TypeyArrays can't be nested
306 // inside themselves.)
307 // dest is something like CanvasKit.HEAPF32
308 function copy3dArray(arr, dest) {
309 if (!arr || !arr.length || !arr[0].length) {
310 return nullptr;
311 }
312 var ptr = CanvasKit._malloc(arr.length * arr[0].length * arr[0][0].length * dest.BYTES_PER_ELEMENT);
313 var idx = 0;
314 var adjustedPtr = ptr / dest.BYTES_PER_ELEMENT;
315 for (var x = 0; x < arr.length; x++) {
316 for (var y = 0; y < arr[0].length; y++) {
317 for (var z = 0; z < arr[0][0].length; z++) {
318 dest[adjustedPtr + idx] = arr[x][y][z];
319 idx++;
320 }
321 }
322 }
323 return ptr;
324 }
325
Kevin Lubick217056c2018-09-20 17:39:31 -0400326 CanvasKit.MakeSkDashPathEffect = function(intervals, phase) {
327 if (!phase) {
328 phase = 0;
329 }
330 if (!intervals.length || intervals.length % 2 === 1) {
331 throw 'Intervals array must have even length';
332 }
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400333 var ptr = copy1dArray(intervals, CanvasKit.HEAPF32);
334 var dpe = CanvasKit._MakeSkDashPathEffect(ptr, intervals.length, phase);
335 CanvasKit._free(ptr);
336 return dpe;
337 }
338
339 CanvasKit.MakeImageShader = function(imgData, xTileMode, yTileMode) {
340 var iptr = CanvasKit._malloc(imgData.byteLength);
341 CanvasKit.HEAPU8.set(new Uint8Array(imgData), iptr);
342 // No need to _free iptr, ImageShader takes it with SkData::MakeFromMalloc
343
344 return CanvasKit._MakeImageShader(iptr, imgData.byteLength, xTileMode, yTileMode);
345 }
346
347 CanvasKit.MakeLinearGradientShader = function(start, end, colors, pos, mode, localMatrix, flags) {
348 var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
349 var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
350 flags = flags || 0;
351
352 if (localMatrix) {
353 // Add perspective args if not provided.
354 if (localMatrix.length === 6) {
355 localMatrix.push(0, 0, 1);
356 }
357 var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
358 colors.length, mode, flags, localMatrix);
359 } else {
360 var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr,
361 colors.length, mode, flags);
Kevin Lubick217056c2018-09-20 17:39:31 -0400362 }
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400363
364 CanvasKit._free(colorPtr);
365 CanvasKit._free(posPtr);
366 return lgs;
367 }
368
369 CanvasKit.MakeRadialGradientShader = function(center, radius, colors, pos, mode, localMatrix, flags) {
370 // TODO: matrix and flags
371 var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
372 var posPtr = copy1dArray(pos, CanvasKit.HEAPF32);
373 flags = flags || 0;
374
375 if (localMatrix) {
376 // Add perspective args if not provided.
377 if (localMatrix.length === 6) {
378 localMatrix.push(0, 0, 1);
379 }
380 var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
381 colors.length, mode, flags, localMatrix);
382 } else {
383 var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr,
384 colors.length, mode, flags);
385 }
386
387 CanvasKit._free(colorPtr);
388 CanvasKit._free(posPtr);
389 return rgs;
390 }
391
392 CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors,
393 boneIndices, boneWeights, indices) {
394 var positionPtr = copy2dArray(positions, CanvasKit.HEAPF32);
395 var texPtr = copy2dArray(textureCoordinates, CanvasKit.HEAPF32);
396 // Since we write the colors to memory as signed integers (JSColor), we can
397 // read them out on the other side as unsigned ints (SkColor) just fine
398 // - it's effectively casting.
399 var colorPtr = copy1dArray(colors, CanvasKit.HEAP32);
400
401 var boneIdxPtr = copy2dArray(boneIndices, CanvasKit.HEAP32);
402 var boneWtPtr = copy2dArray(boneWeights, CanvasKit.HEAPF32);
403 var idxPtr = copy1dArray(indices, CanvasKit.HEAPU16);
404
405 var idxCount = (indices && indices.length) || 0;
406 // _MakeVertices will copy all the values in, so we are free to release
407 // the memory after.
408 var vertices = CanvasKit._MakeSkVertices(mode, positions.length, positionPtr,
409 texPtr, colorPtr, boneIdxPtr, boneWtPtr,
410 idxCount, idxPtr);
411 positionPtr && CanvasKit._free(positionPtr);
412 texPtr && CanvasKit._free(texPtr);
413 colorPtr && CanvasKit._free(colorPtr);
414 idxPtr && CanvasKit._free(idxPtr);
415 boneIdxPtr && CanvasKit._free(boneIdxPtr);
416 boneWtPtr && CanvasKit._free(boneWtPtr);
417 return vertices;
Kevin Lubick217056c2018-09-20 17:39:31 -0400418 }
419
Kevin Lubick134be1d2018-10-30 15:05:04 -0400420 CanvasKit.MakeNimaActor = function(nimaFile, nimaTexture) {
421 var nptr = CanvasKit._malloc(nimaFile.byteLength);
422 CanvasKit.HEAPU8.set(new Uint8Array(nimaFile), nptr);
423 var tptr = CanvasKit._malloc(nimaTexture.byteLength);
424 CanvasKit.HEAPU8.set(new Uint8Array(nimaTexture), tptr);
Kevin Lubickb5ae3b52018-11-03 07:51:19 -0400425 // No need to _free these ptrs, NimaActor takes them with SkData::MakeFromMalloc
Kevin Lubick134be1d2018-10-30 15:05:04 -0400426
427 return CanvasKit._MakeNimaActor(nptr, nimaFile.byteLength, tptr, nimaTexture.byteLength);
428 }
429
Kevin Lubick217056c2018-09-20 17:39:31 -0400430}(Module)); // When this file is loaded in, the high level object is "Module";
Kevin Lubick1a05fce2018-11-20 12:51:16 -0500431
432// Intentionally added outside the scope to allow usage in canvas2d.js and other
433// pre-js files. These names are unlikely to cause emscripten collisions.
434function radiansToDegrees(rad) {
435 return (rad / Math.PI) * 180;
436}
437
438function degreesToRadians(deg) {
439 return (deg / 180) * Math.PI;
440}
441