skia2/modules/canvaskit/interface.js
Kevin Lubick f8823b5726 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>
2020-09-03 14:31:38 +00:00

1612 lines
58 KiB
JavaScript

// 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.
// 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.
CanvasKit.onRuntimeInitialized = function() {
// All calls to 'this' need to go in externs.js so closure doesn't minify them away.
_scratchColor = CanvasKit.Malloc(Float32Array, 4); // 4 color scalars.
_scratchColorPtr = _scratchColor['byteOffset'];
_scratch4x4Matrix = CanvasKit.Malloc(Float32Array, 16); // 16 matrix scalars.
_scratch4x4MatrixPtr = _scratch4x4Matrix['byteOffset'];
_scratch3x3Matrix = CanvasKit.Malloc(Float32Array, 9); // 9 matrix scalars.
_scratch3x3MatrixPtr = _scratch3x3Matrix['byteOffset'];
_scratchRRect = CanvasKit.Malloc(Float32Array, 12); // 4 scalars for rrect, 8 for radii.
_scratchRRectPtr = _scratchRRect['byteOffset'];
_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();
CanvasKit.SkColorSpace.DISPLAY_P3 = CanvasKit.SkColorSpace._MakeDisplayP3();
CanvasKit.SkColorSpace.ADOBE_RGB = CanvasKit.SkColorSpace._MakeAdobeRGB();
// Add some helpers for matrices. This is ported from SkMatrix.cpp
// to save complexity and overhead of going back and forth between
// C++ and JS layers.
// I would have liked to use something like DOMMatrix, except it
// isn't widely supported (would need polyfills) and it doesn't
// have a mapPoints() function (which could maybe be tacked on here).
// If DOMMatrix catches on, it would be worth re-considering this usage.
CanvasKit.SkMatrix = {};
function sdot() { // to be called with an even number of scalar args
var acc = 0;
for (var i=0; i < arguments.length-1; i+=2) {
acc += arguments[i] * arguments[i+1];
}
return acc;
}
// Private general matrix functions used in both 3x3s and 4x4s.
// Return a square identity matrix of size n.
var identityN = function(n) {
var size = n*n;
var m = new Array(size);
while(size--) {
m[size] = size%(n+1) == 0 ? 1.0 : 0.0;
}
return m;
}
// Stride, a function for compactly representing several ways of copying an array into another.
// Write vector `v` into matrix `m`. `m` is a matrix encoded as an array in row-major
// order. Its width is passed as `width`. `v` is an array with length < (m.length/width).
// An element of `v` is copied into `m` starting at `offset` and moving `colStride` cols right
// each row.
//
// For example, a width of 4, offset of 3, and stride of -1 would put the vector here.
// _ _ 0 _
// _ 1 _ _
// 2 _ _ _
// _ _ _ 3
//
var stride = function(v, m, width, offset, colStride) {
for (var i=0; i<v.length; i++) {
m[i * width + // column
(i * colStride + offset + width) % width // row
] = v[i];
}
return m;
}
CanvasKit.SkMatrix.identity = function() {
return identityN(3);
};
// Return the inverse (if it exists) of this matrix.
// Otherwise, return null.
CanvasKit.SkMatrix.invert = function(m) {
// Find the determinant by the sarrus rule. https://en.wikipedia.org/wiki/Rule_of_Sarrus
var det = m[0]*m[4]*m[8] + m[1]*m[5]*m[6] + m[2]*m[3]*m[7]
- m[2]*m[4]*m[6] - m[1]*m[3]*m[8] - m[0]*m[5]*m[7];
if (!det) {
SkDebug('Warning, uninvertible matrix');
return null;
}
// Return the inverse by the formula adj(m)/det.
// adj (adjugate) of a 3x3 is the transpose of it's cofactor matrix.
// a cofactor matrix is a matrix where each term is +-det(N) where matrix N is the 2x2 formed
// by removing the row and column we're currently setting from the source.
// the sign alternates in a checkerboard pattern with a `+` at the top left.
// that's all been combined here into one expression.
return [
(m[4]*m[8] - m[5]*m[7])/det, (m[2]*m[7] - m[1]*m[8])/det, (m[1]*m[5] - m[2]*m[4])/det,
(m[5]*m[6] - m[3]*m[8])/det, (m[0]*m[8] - m[2]*m[6])/det, (m[2]*m[3] - m[0]*m[5])/det,
(m[3]*m[7] - m[4]*m[6])/det, (m[1]*m[6] - m[0]*m[7])/det, (m[0]*m[4] - m[1]*m[3])/det,
];
};
// Maps the given points according to the passed in matrix.
// Results are done in place.
// See SkMatrix.h::mapPoints for the docs on the math.
CanvasKit.SkMatrix.mapPoints = function(matrix, ptArr) {
if (skIsDebug && (ptArr.length % 2)) {
throw 'mapPoints requires an even length arr';
}
for (var i = 0; i < ptArr.length; i+=2) {
var x = ptArr[i], y = ptArr[i+1];
// Gx+Hy+I
var denom = matrix[6]*x + matrix[7]*y + matrix[8];
// Ax+By+C
var xTrans = matrix[0]*x + matrix[1]*y + matrix[2];
// Dx+Ey+F
var yTrans = matrix[3]*x + matrix[4]*y + matrix[5];
ptArr[i] = xTrans/denom;
ptArr[i+1] = yTrans/denom;
}
return ptArr;
};
function isnumber(val) { return val !== NaN; };
// generalized iterative algorithm for multiplying two matrices.
function multiply(m1, m2, size) {
if (skIsDebug && (!m1.every(isnumber) || !m2.every(isnumber))) {
throw 'Some members of matrices are NaN m1='+m1+', m2='+m2+'';
}
if (skIsDebug && (m1.length !== m2.length)) {
throw 'Undefined for matrices of different sizes. m1.length='+m1.length+', m2.length='+m2.length;
}
if (skIsDebug && (size*size !== m1.length)) {
throw 'Undefined for non-square matrices. array size was '+size;
}
var result = Array(m1.length);
for (var r = 0; r < size; r++) {
for (var c = 0; c < size; c++) {
// accumulate a sum of m1[r,k]*m2[k, c]
var acc = 0;
for (var k = 0; k < size; k++) {
acc += m1[size * r + k] * m2[size * k + c];
}
result[r * size + c] = acc;
}
}
return result;
};
// Accept an integer indicating the size of the matrices being multiplied (3 for 3x3), and any
// number of matrices following it.
function multiplyMany(size, listOfMatrices) {
if (skIsDebug && (listOfMatrices.length < 2)) {
throw 'multiplication expected two or more matrices';
}
var result = multiply(listOfMatrices[0], listOfMatrices[1], size);
var next = 2;
while (next < listOfMatrices.length) {
result = multiply(result, listOfMatrices[next], size);
next++;
}
return result;
};
// Accept any number 3x3 of matrices as arguments, multiply them together.
// Matrix multiplication is associative but not commutative. the order of the arguments
// matters, but it does not matter that this implementation multiplies them left to right.
CanvasKit.SkMatrix.multiply = function() {
return multiplyMany(3, arguments);
};
// Return a matrix representing a rotation by n radians.
// px, py optionally say which point the rotation should be around
// with the default being (0, 0);
CanvasKit.SkMatrix.rotated = function(radians, px, py) {
px = px || 0;
py = py || 0;
var sinV = Math.sin(radians);
var cosV = Math.cos(radians);
return [
cosV, -sinV, sdot( sinV, py, 1 - cosV, px),
sinV, cosV, sdot(-sinV, px, 1 - cosV, py),
0, 0, 1,
];
};
CanvasKit.SkMatrix.scaled = function(sx, sy, px, py) {
px = px || 0;
py = py || 0;
var m = stride([sx, sy], identityN(3), 3, 0, 1);
return stride([px-sx*px, py-sy*py], m, 3, 2, 0);
};
CanvasKit.SkMatrix.skewed = function(kx, ky, px, py) {
px = px || 0;
py = py || 0;
var m = stride([kx, ky], identityN(3), 3, 1, -1);
return stride([-kx*px, -ky*py], m, 3, 2, 0);
};
CanvasKit.SkMatrix.translated = function(dx, dy) {
return stride(arguments, identityN(3), 3, 2, 0);
};
// Functions for manipulating vectors.
// Loosely based off of SkV3 in SkM44.h but skia also has SkVec2 and Skv4. This combines them and
// works on vectors of any length.
CanvasKit.SkVector = {};
CanvasKit.SkVector.dot = function(a, b) {
if (skIsDebug && (a.length !== b.length)) {
throw 'Cannot perform dot product on arrays of different length ('+a.length+' vs '+b.length+')';
}
return a.map(function(v, i) { return v*b[i] }).reduce(function(acc, cur) { return acc + cur; });
}
CanvasKit.SkVector.lengthSquared = function(v) {
return CanvasKit.SkVector.dot(v, v);
}
CanvasKit.SkVector.length = function(v) {
return Math.sqrt(CanvasKit.SkVector.lengthSquared(v));
}
CanvasKit.SkVector.mulScalar = function(v, s) {
return v.map(function(i) { return i*s });
}
CanvasKit.SkVector.add = function(a, b) {
return a.map(function(v, i) { return v+b[i] });
}
CanvasKit.SkVector.sub = function(a, b) {
return a.map(function(v, i) { return v-b[i]; });
}
CanvasKit.SkVector.dist = function(a, b) {
return CanvasKit.SkVector.length(CanvasKit.SkVector.sub(a, b));
}
CanvasKit.SkVector.normalize = function(v) {
return CanvasKit.SkVector.mulScalar(v, 1/CanvasKit.SkVector.length(v));
}
CanvasKit.SkVector.cross = function(a, b) {
if (skIsDebug && (a.length !== 3 || a.length !== 3)) {
throw 'Cross product is only defined for 3-dimensional vectors (a.length='+a.length+', b.length='+b.length+')';
}
return [
a[1]*b[2] - a[2]*b[1],
a[2]*b[0] - a[0]*b[2],
a[0]*b[1] - a[1]*b[0],
];
}
// Functions for creating and manipulating (row-major) 4x4 matrices. Accepted in place of
// SkM44 in canvas methods, for the same reasons as the 3x3 matrices above.
// ported from C++ code in SkM44.cpp
CanvasKit.SkM44 = {};
// Create a 4x4 identity matrix
CanvasKit.SkM44.identity = function() {
return identityN(4);
}
// Anything named vec below is an array of length 3 representing a vector/point in 3D space.
// Create a 4x4 matrix representing a translate by the provided 3-vec
CanvasKit.SkM44.translated = function(vec) {
return stride(vec, identityN(4), 4, 3, 0);
}
// Create a 4x4 matrix representing a scaling by the provided 3-vec
CanvasKit.SkM44.scaled = function(vec) {
return stride(vec, identityN(4), 4, 0, 1);
}
// Create a 4x4 matrix representing a rotation about the provided axis 3-vec.
// axis does not need to be normalized.
CanvasKit.SkM44.rotated = function(axisVec, radians) {
return CanvasKit.SkM44.rotatedUnitSinCos(
CanvasKit.SkVector.normalize(axisVec), Math.sin(radians), Math.cos(radians));
}
// Create a 4x4 matrix representing a rotation about the provided normalized axis 3-vec.
// Rotation is provided redundantly as both sin and cos values.
// This rotate can be used when you already have the cosAngle and sinAngle values
// so you don't have to atan(cos/sin) to call roatated() which expects an angle in radians.
// this does no checking! Behavior for invalid sin or cos values or non-normalized axis vectors
// is incorrect. Prefer rotated().
CanvasKit.SkM44.rotatedUnitSinCos = function(axisVec, sinAngle, cosAngle) {
var x = axisVec[0];
var y = axisVec[1];
var z = axisVec[2];
var c = cosAngle;
var s = sinAngle;
var t = 1 - c;
return [
t*x*x + c, t*x*y - s*z, t*x*z + s*y, 0,
t*x*y + s*z, t*y*y + c, t*y*z - s*x, 0,
t*x*z - s*y, t*y*z + s*x, t*z*z + c, 0,
0, 0, 0, 1
];
}
// Create a 4x4 matrix representing a camera at eyeVec, pointed at centerVec.
CanvasKit.SkM44.lookat = function(eyeVec, centerVec, upVec) {
var f = CanvasKit.SkVector.normalize(CanvasKit.SkVector.sub(centerVec, eyeVec));
var u = CanvasKit.SkVector.normalize(upVec);
var s = CanvasKit.SkVector.normalize(CanvasKit.SkVector.cross(f, u));
var m = CanvasKit.SkM44.identity();
// set each column's top three numbers
stride(s, m, 4, 0, 0);
stride(CanvasKit.SkVector.cross(s, f), m, 4, 1, 0);
stride(CanvasKit.SkVector.mulScalar(f, -1), m, 4, 2, 0);
stride(eyeVec, m, 4, 3, 0);
var m2 = CanvasKit.SkM44.invert(m);
if (m2 === null) {
return CanvasKit.SkM44.identity();
}
return m2;
}
// Create a 4x4 matrix representing a perspective. All arguments are scalars.
// angle is in radians.
CanvasKit.SkM44.perspective = function(near, far, angle) {
if (skIsDebug && (far <= near)) {
throw 'far must be greater than near when constructing SkM44 using perspective.';
}
var dInv = 1 / (far - near);
var halfAngle = angle / 2;
var cot = Math.cos(halfAngle) / Math.sin(halfAngle);
return [
cot, 0, 0, 0,
0, cot, 0, 0,
0, 0, (far+near)*dInv, 2*far*near*dInv,
0, 0, -1, 1,
];
}
// Returns the number at the given row and column in matrix m.
CanvasKit.SkM44.rc = function(m, r, c) {
return m[r*4+c];
}
// Accepts any number of 4x4 matrix arguments, multiplies them left to right.
CanvasKit.SkM44.multiply = function() {
return multiplyMany(4, arguments);
}
// Invert the 4x4 matrix if it is invertible and return it. if not, return null.
// taken from SkM44.cpp (altered to use row-major order)
// m is not altered.
CanvasKit.SkM44.invert = function(m) {
if (skIsDebug && !m.every(isnumber)) {
throw 'some members of matrix are NaN m='+m;
}
var a00 = m[0];
var a01 = m[4];
var a02 = m[8];
var a03 = m[12];
var a10 = m[1];
var a11 = m[5];
var a12 = m[9];
var a13 = m[13];
var a20 = m[2];
var a21 = m[6];
var a22 = m[10];
var a23 = m[14];
var a30 = m[3];
var a31 = m[7];
var a32 = m[11];
var a33 = m[15];
var b00 = a00 * a11 - a01 * a10;
var b01 = a00 * a12 - a02 * a10;
var b02 = a00 * a13 - a03 * a10;
var b03 = a01 * a12 - a02 * a11;
var b04 = a01 * a13 - a03 * a11;
var b05 = a02 * a13 - a03 * a12;
var b06 = a20 * a31 - a21 * a30;
var b07 = a20 * a32 - a22 * a30;
var b08 = a20 * a33 - a23 * a30;
var b09 = a21 * a32 - a22 * a31;
var b10 = a21 * a33 - a23 * a31;
var b11 = a22 * a33 - a23 * a32;
// calculate determinate
var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
var invdet = 1.0 / det;
// bail out if the matrix is not invertible
if (det === 0 || invdet === Infinity) {
SkDebug('Warning, uninvertible matrix');
return null;
}
b00 *= invdet;
b01 *= invdet;
b02 *= invdet;
b03 *= invdet;
b04 *= invdet;
b05 *= invdet;
b06 *= invdet;
b07 *= invdet;
b08 *= invdet;
b09 *= invdet;
b10 *= invdet;
b11 *= invdet;
// store result in row major order
var tmp = [
a11 * b11 - a12 * b10 + a13 * b09,
a12 * b08 - a10 * b11 - a13 * b07,
a10 * b10 - a11 * b08 + a13 * b06,
a11 * b07 - a10 * b09 - a12 * b06,
a02 * b10 - a01 * b11 - a03 * b09,
a00 * b11 - a02 * b08 + a03 * b07,
a01 * b08 - a00 * b10 - a03 * b06,
a00 * b09 - a01 * b07 + a02 * b06,
a31 * b05 - a32 * b04 + a33 * b03,
a32 * b02 - a30 * b05 - a33 * b01,
a30 * b04 - a31 * b02 + a33 * b00,
a31 * b01 - a30 * b03 - a32 * b00,
a22 * b04 - a21 * b05 - a23 * b03,
a20 * b05 - a22 * b02 + a23 * b01,
a21 * b02 - a20 * b04 - a23 * b00,
a20 * b03 - a21 * b01 + a22 * b00,
];
if (!tmp.every(function(val) { return val !== NaN && val !== Infinity && val !== -Infinity; })) {
SkDebug('inverted matrix contains infinities or NaN '+tmp);
return null;
}
return tmp;
}
CanvasKit.SkM44.transpose = function(m) {
return [
m[0], m[4], m[8], m[12],
m[1], m[5], m[9], m[13],
m[2], m[6], m[10], m[14],
m[3], m[7], m[11], m[15],
];
}
// Return the inverse of an SkM44. throw an error if it's not invertible
CanvasKit.SkM44.mustInvert = function(m) {
var m2 = CanvasKit.SkM44.invert(m);
if (m2 === null) {
throw 'Matrix not invertible';
}
return m2;
}
// returns a matrix that sets up a 3D perspective view from a given camera.
//
// area - a rect describing the viewport. (0, 0, canvas_width, canvas_height) suggested
// zscale - a scalar describing the scale of the z axis. min(width, height)/2 suggested
// cam - an object with the following attributes
// const cam = {
// 'eye' : [0, 0, 1 / Math.tan(Math.PI / 24) - 1], // a 3D point locating the camera
// 'coa' : [0, 0, 0], // center of attention - the 3D point the camera is looking at.
// 'up' : [0, 1, 0], // a unit vector pointing in the camera's up direction, because eye and coa alone leave roll unspecified.
// 'near' : 0.02, // near clipping plane
// 'far' : 4, // far clipping plane
// 'angle': Math.PI / 12, // field of view in radians
// };
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[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));
return CanvasKit.SkM44.multiply(
viewport, perspective, camera, CanvasKit.SkM44.mustInvert(viewport));
}
// An SkColorMatrix is a 4x4 color matrix that transforms the 4 color channels
// with a 1x4 matrix that post-translates those 4 channels.
// For example, the following is the layout with the scale (S) and post-transform
// (PT) items indicated.
// RS, 0, 0, 0 | RPT
// 0, GS, 0, 0 | GPT
// 0, 0, BS, 0 | BPT
// 0, 0, 0, AS | APT
//
// Much of this was hand-transcribed from SkColorMatrix.cpp, because it's easier to
// deal with a Float32Array of length 20 than to try to expose the SkColorMatrix object.
var rScale = 0;
var gScale = 6;
var bScale = 12;
var aScale = 18;
var rPostTrans = 4;
var gPostTrans = 9;
var bPostTrans = 14;
var aPostTrans = 19;
CanvasKit.SkColorMatrix = {};
CanvasKit.SkColorMatrix.identity = function() {
var m = new Float32Array(20);
m[rScale] = 1;
m[gScale] = 1;
m[bScale] = 1;
m[aScale] = 1;
return m;
}
CanvasKit.SkColorMatrix.scaled = function(rs, gs, bs, as) {
var m = new Float32Array(20);
m[rScale] = rs;
m[gScale] = gs;
m[bScale] = bs;
m[aScale] = as;
return m;
}
var rotateIndices = [
[6, 7, 11, 12],
[0, 10, 2, 12],
[0, 1, 5, 6],
];
// axis should be 0, 1, 2 for r, g, b
CanvasKit.SkColorMatrix.rotated = function(axis, sine, cosine) {
var m = CanvasKit.SkColorMatrix.identity();
var indices = rotateIndices[axis];
m[indices[0]] = cosine;
m[indices[1]] = sine;
m[indices[2]] = -sine;
m[indices[3]] = cosine;
return m;
}
// m is a SkColorMatrix (i.e. a Float32Array), and this sets the 4 "special"
// params that will translate the colors after they are multiplied by the 4x4 matrix.
CanvasKit.SkColorMatrix.postTranslate = function(m, dr, dg, db, da) {
m[rPostTrans] += dr;
m[gPostTrans] += dg;
m[bPostTrans] += db;
m[aPostTrans] += da;
return m;
}
// concat returns a new SkColorMatrix that is the result of multiplying outer*inner;
CanvasKit.SkColorMatrix.concat = function(outer, inner) {
var m = new Float32Array(20);
var index = 0;
for (var j = 0; j < 20; j += 5) {
for (var i = 0; i < 4; i++) {
m[index++] = outer[j + 0] * inner[i + 0] +
outer[j + 1] * inner[i + 5] +
outer[j + 2] * inner[i + 10] +
outer[j + 3] * inner[i + 15];
}
m[index++] = outer[j + 0] * inner[4] +
outer[j + 1] * inner[9] +
outer[j + 2] * inner[14] +
outer[j + 3] * inner[19] +
outer[j + 4];
}
return m;
};
CanvasKit.SkPath.MakeFromCmds = function(cmds) {
var ptrLen = loadCmdsTypedArray(cmds);
var path = CanvasKit.SkPath._MakeFromCmds(ptrLen[0], ptrLen[1]);
CanvasKit._free(ptrLen[0]);
return path;
};
// Deprecated
CanvasKit.MakePathFromCmds = CanvasKit.SkPath.MakeFromCmds;
// The weights array is optional (only used for conics).
CanvasKit.SkPath.MakeFromVerbsPointsWeights = function(verbs, pts, weights) {
var verbsPtr = copy1dArray(verbs, 'HEAPU8');
var pointsPtr = copy1dArray(pts, 'HEAPF32');
var weightsPtr = copy1dArray(weights, 'HEAPF32');
var numWeights = (weights && weights.length) || 0;
var path = CanvasKit.SkPath._MakeFromVerbsPointsWeights(
verbsPtr, verbs.length, pointsPtr, pts.length, weightsPtr, numWeights);
freeArraysThatAreNotMallocedByUsers(verbsPtr, verbs);
freeArraysThatAreNotMallocedByUsers(pointsPtr, pts);
freeArraysThatAreNotMallocedByUsers(weightsPtr, weights);
return path;
};
CanvasKit.SkPath.prototype.addArc = function(oval, startAngle, sweepAngle) {
// see arc() for the HTMLCanvas version
// note input angles are degrees.
var oPtr = copyRectToWasm(oval);
this._addArc(oPtr, startAngle, sweepAngle);
return this;
};
CanvasKit.SkPath.prototype.addOval = function(oval, isCCW, startIndex) {
if (startIndex === undefined) {
startIndex = 1;
}
var oPtr = copyRectToWasm(oval);
this._addOval(oPtr, !!isCCW, startIndex);
return this;
};
CanvasKit.SkPath.prototype.addPath = function() {
// Takes 1, 2, 7, or 10 required args, where the first arg is always the path.
// The last arg is optional and chooses between add or extend mode.
// The options for the remaining args are:
// - an array of 6 or 9 parameters (perspective is optional)
// - the 9 parameters of a full matrix or
// the 6 non-perspective params of a matrix.
var args = Array.prototype.slice.call(arguments);
var path = args[0];
var extend = false;
if (typeof args[args.length-1] === 'boolean') {
extend = args.pop();
}
if (args.length === 1) {
// Add path, unchanged. Use identity matrix
this._addPath(path, 1, 0, 0,
0, 1, 0,
0, 0, 1,
extend);
} else if (args.length === 2) {
// User provided the 9 params of a full matrix as an array.
var a = args[1];
this._addPath(path, a[0], a[1], a[2],
a[3], a[4], a[5],
a[6] || 0, a[7] || 0, a[8] || 1,
extend);
} else if (args.length === 7 || args.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 = args;
this._addPath(path, a[1], a[2], a[3],
a[4], a[5], a[6],
a[7] || 0, a[8] || 0, a[9] || 1,
extend);
} else {
SkDebug('addPath expected to take 1, 2, 7, or 10 required args. Got ' + args.length);
return null;
}
return this;
};
// 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.
CanvasKit.SkPath.prototype.addPoly = function(points, close) {
var ptr;
var n;
// This was created with CanvasKit.Malloc, so assume the user has
// already been filled with data.
if (points['_ck']) {
ptr = points.byteOffset;
n = points.length/2;
} else {
ptr = copy2dArray(points, 'HEAPF32');
n = points.length;
}
this._addPoly(ptr, n, close);
freeArraysThatAreNotMallocedByUsers(ptr, points);
return this;
};
CanvasKit.SkPath.prototype.addRect = function(rect, isCCW) {
var rPtr = copyRectToWasm(rect);
this._addRect(rPtr, !!isCCW);
return this;
};
CanvasKit.SkPath.prototype.addRRect = function(rrect, isCCW) {
var rPtr = copyRRectToWasm(rrect);
this._addRRect(rPtr, !!isCCW);
return this;
};
// The weights array is optional (only used for conics).
CanvasKit.SkPath.prototype.addVerbsPointsWeights = function(verbs, points, weights) {
var verbsPtr = copy1dArray(verbs, 'HEAPU8');
var pointsPtr = copy1dArray(points, 'HEAPF32');
var weightsPtr = copy1dArray(weights, 'HEAPF32');
var numWeights = (weights && weights.length) || 0;
this._addVerbsPointsWeights(verbsPtr, verbs.length, pointsPtr, points.length,
weightsPtr, numWeights);
freeArraysThatAreNotMallocedByUsers(verbsPtr, verbs);
freeArraysThatAreNotMallocedByUsers(pointsPtr, points);
freeArraysThatAreNotMallocedByUsers(weightsPtr, weights);
};
CanvasKit.SkPath.prototype.arc = function(x, y, radius, startAngle, endAngle, ccw) {
// emulates the HTMLCanvas behavior. See addArc() for the SkPath version.
// Note input angles are radians.
var bounds = CanvasKit.LTRBRect(x-radius, y-radius, x+radius, y+radius);
var sweep = radiansToDegrees(endAngle - startAngle) - (360 * !!ccw);
var temp = new CanvasKit.SkPath();
temp.addArc(bounds, radiansToDegrees(startAngle), sweep);
this.addPath(temp, true);
temp.delete();
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) {
var oPtr = copyRectToWasm(oval);
this._arcToOval(oPtr, startAngle, sweepAngle, forceMoveTo);
return this;
};
// Appends arc to SkPath. Arc is implemented by one or more conics weighted to
// describe part of oval with radii (rx, ry) rotated by xAxisRotate degrees. Arc
// curves from last SkPath SkPoint to (x, y), choosing one of four possible routes:
// clockwise or counterclockwise, and smaller or larger.
// Arc sweep is always less than 360 degrees. arcTo() appends line to (x, y) if
// either radii are zero, or if last SkPath SkPoint equals (x, y). arcTo() scales radii
// (rx, ry) to fit last SkPath SkPoint and (x, y) if both are greater than zero but
// too small.
// arcToRotated() appends up to four conic curves.
// arcToRotated() implements the functionality of SVG arc, although SVG sweep-flag value
// is opposite the integer value of sweep; SVG sweep-flag uses 1 for clockwise,
// while kCW_Direction cast to int is zero.
CanvasKit.SkPath.prototype.arcToRotated = function(rx, ry, xAxisRotate, useSmallArc, isCCW, x, y) {
this._arcToRotated(rx, ry, xAxisRotate, !!useSmallArc, !!isCCW, x, y);
return this;
};
// Appends arc to SkPath, after appending line if needed. Arc is implemented by conic
// weighted to describe part of circle. Arc is contained by tangent from
// last SkPath point to (x1, y1), and tangent from (x1, y1) to (x2, y2). Arc
// is part of circle sized to radius, positioned so it touches both tangent lines.
// If last Path Point does not start Arc, arcTo appends connecting Line to Path.
// The length of Vector from (x1, y1) to (x2, y2) does not affect Arc.
// Arc sweep is always less than 180 degrees. If radius is zero, or if
// tangents are nearly parallel, arcTo appends Line from last Path Point to (x1, y1).
// arcToTangent appends at most one Line and one conic.
// arcToTangent implements the functionality of PostScript arct and HTML Canvas arcTo.
CanvasKit.SkPath.prototype.arcToTangent = function(x1, y1, x2, y2, radius) {
this._arcToTangent(x1, y1, x2, y2, radius);
return this;
};
CanvasKit.SkPath.prototype.close = function() {
this._close();
return this;
};
CanvasKit.SkPath.prototype.conicTo = function(x1, y1, x2, y2, w) {
this._conicTo(x1, y1, x2, y2, w);
return this;
};
CanvasKit.SkPath.prototype.cubicTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
this._cubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
return this;
};
CanvasKit.SkPath.prototype.dash = function(on, off, phase) {
if (this._dash(on, off, phase)) {
return this;
}
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;
};
CanvasKit.SkPath.prototype.moveTo = function(x, y) {
this._moveTo(x, y);
return this;
};
CanvasKit.SkPath.prototype.offset = function(dx, dy) {
this._transform(1, 0, dx,
0, 1, dy,
0, 0, 1);
return this;
};
CanvasKit.SkPath.prototype.quadTo = function(cpx, cpy, x, y) {
this._quadTo(cpx, cpy, x, y);
return this;
};
CanvasKit.SkPath.prototype.rArcTo = function(rx, ry, xAxisRotate, useSmallArc, isCCW, dx, dy) {
this._rArcTo(rx, ry, xAxisRotate, useSmallArc, isCCW, dx, dy);
return this;
};
CanvasKit.SkPath.prototype.rConicTo = function(dx1, dy1, dx2, dy2, w) {
this._rConicTo(dx1, dy1, dx2, dy2, w);
return this;
};
// These params are all relative
CanvasKit.SkPath.prototype.rCubicTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
this._rCubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
return this;
};
CanvasKit.SkPath.prototype.rLineTo = function(dx, dy) {
this._rLineTo(dx, dy);
return this;
};
CanvasKit.SkPath.prototype.rMoveTo = function(dx, dy) {
this._rMoveTo(dx, dy);
return this;
};
// These params are all relative
CanvasKit.SkPath.prototype.rQuadTo = function(cpx, cpy, x, y) {
this._rQuadTo(cpx, cpy, x, y);
return this;
};
CanvasKit.SkPath.prototype.stroke = function(opts) {
// Fill out any missing values with the default values.
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;
opts['precision'] = opts['precision'] || 1;
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 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] || 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] || 0, a[7] || 0, a[8] || 1);
} else {
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;
};
CanvasKit.SkImage.prototype.encodeToData = function() {
if (!arguments.length) {
return this._encodeToData();
}
if (arguments.length === 2) {
var a = arguments;
return this._encodeToDataWithFormat(a[0], a[1]);
}
throw 'encodeToData expected to take 0 or 2 arguments. Got ' + arguments.length;
}
CanvasKit.SkImage.prototype.makeShader = function(xTileMode, yTileMode, localMatrix) {
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
return this._makeShader(xTileMode, yTileMode, localMatrixPtr);
}
CanvasKit.SkImage.prototype.readPixels = function(imageInfo, srcX, srcY) {
var rowBytes;
// Important to use ['string'] notation here, otherwise the closure compiler will
// minify away the colorType.
switch (imageInfo['colorType']) {
case CanvasKit.ColorType.RGBA_8888:
rowBytes = imageInfo.width * 4; // 1 byte per channel == 4 bytes per pixel in 8888
break;
case CanvasKit.ColorType.RGBA_F32:
rowBytes = imageInfo.width * 16; // 4 bytes per channel == 16 bytes per pixel in F32
break;
default:
SkDebug('Colortype not yet supported');
return;
}
var pBytes = rowBytes * imageInfo.height;
var pPtr = CanvasKit._malloc(pBytes);
if (!this._readPixels(imageInfo, pPtr, rowBytes, srcX, srcY)) {
SkDebug('Could not read pixels with the given inputs');
return null;
}
// Put those pixels into a typed array of the right format and then
// make a copy with slice() that we can return.
var retVal = null;
switch (imageInfo['colorType']) {
case CanvasKit.ColorType.RGBA_8888:
retVal = new Uint8Array(CanvasKit.HEAPU8.buffer, pPtr, pBytes).slice();
break;
case CanvasKit.ColorType.RGBA_F32:
retVal = new Float32Array(CanvasKit.HEAPU8.buffer, pPtr, pBytes).slice();
break;
}
// Free the allocated pixels in the WASM memory
CanvasKit._free(pPtr);
return retVal;
}
// Accepts an array of four numbers in the range of 0-1 representing a 4f color
CanvasKit.SkCanvas.prototype.clear = function(color4f) {
var cPtr = copyColorToWasm(color4f);
this._clear(cPtr);
}
CanvasKit.SkCanvas.prototype.clipRRect = function(rrect, op, antialias) {
var rPtr = copyRRectToWasm(rrect);
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) {
var matrPtr = copy4x4MatrixToWasm(matr);
this._concat(matrPtr);
}
// 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)
// Or they can be an array of floats of length 4*number of destinations.
// colors are optional and used to tint the drawn images using the optional blend mode
// Colors may be an SkColorBuilder, a Uint32Array of int colors,
// a Flat Float32Array of float colors or a 2d Array of Float32Array(4) (deprecated)
CanvasKit.SkCanvas.prototype.drawAtlas = function(atlas, srcRects, dstXforms, paint,
/*optional*/ blendMode, colors) {
if (!atlas || !paint || !srcRects || !dstXforms) {
SkDebug('Doing nothing since missing a required input');
return;
}
// builder arguments report the length as the number of rects, but when passed as arrays
// their.length attribute is 4x higher because it's the number of total components of all rects.
// colors is always going to report the same length, at least until floats colors are supported
// by this function.
if (srcRects.length !== dstXforms.length) {
SkDebug('Doing nothing since input arrays length mismatches');
return;
}
if (!blendMode) {
blendMode = CanvasKit.BlendMode.SrcOver;
}
var srcRectPtr;
if (srcRects.build) {
srcRectPtr = srcRects.build();
} else {
srcRectPtr = copy1dArray(srcRects, 'HEAPF32');
}
var count = 1;
var dstXformPtr;
if (dstXforms.build) {
dstXformPtr = dstXforms.build();
count = dstXforms.length;
} else {
dstXformPtr = copy1dArray(dstXforms, 'HEAPF32');
count = dstXforms.length / 4;
}
var colorPtr = nullptr;
if (colors) {
if (colors.build) {
colorPtr = colors.build();
} else {
colorPtr = copy1dArray(assureIntColors(colors), 'HEAPU32');
}
}
this._drawAtlas(atlas, dstXformPtr, srcRectPtr, colorPtr, count, blendMode, paint);
if (srcRectPtr && !srcRects.build) {
freeArraysThatAreNotMallocedByUsers(srcRectPtr, srcRects);
}
if (dstXformPtr && !dstXforms.build) {
freeArraysThatAreNotMallocedByUsers(dstXformPtr, dstXforms);
}
if (colorPtr && !colors.build) {
freeArraysThatAreNotMallocedByUsers(colorPtr, colors);
}
}
CanvasKit.SkCanvas.prototype.drawColor = function (color4f, mode) {
var cPtr = copyColorToWasm(color4f);
if (mode !== undefined) {
this._drawColor(cPtr, mode);
} else {
this._drawColor(cPtr);
}
}
CanvasKit.SkCanvas.prototype.drawColorComponents = function (r, g, b, a, mode) {
var cPtr = copyColorComponentsToWasm(r, g, b, a);
if (mode !== undefined) {
this._drawColor(cPtr, mode);
} else {
this._drawColor(cPtr);
}
}
CanvasKit.SkCanvas.prototype.drawDRRect = function(outer, inner, paint) {
var oPtr = copyRRectToWasm(outer, _scratchRRectPtr);
var iPtr = copyRRectToWasm(inner, _scratchRRect2Ptr);
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.
CanvasKit.SkCanvas.prototype.drawPoints = function(mode, points, paint) {
var ptr;
var n;
// This was created with CanvasKit.Malloc, so assume the user has
// already been filled with data.
if (points['_ck']) {
ptr = points.byteOffset;
n = points.length/2;
} else {
ptr = copy2dArray(points, 'HEAPF32');
n = points.length;
}
this._drawPoints(mode, ptr, n, paint);
freeArraysThatAreNotMallocedByUsers(ptr, points);
}
CanvasKit.SkCanvas.prototype.drawRRect = function(rrect, paint) {
var rPtr = copyRRectToWasm(rrect);
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);
this._drawShadow(path, zPlaneParams, lightPos, lightRadius, ambiPtr, spotPtr, flags);
freeArraysThatAreNotMallocedByUsers(ambiPtr, ambientColor);
freeArraysThatAreNotMallocedByUsers(spotPtr, spotColor);
}
// getLocalToDevice returns a 4x4 matrix.
CanvasKit.SkCanvas.prototype.getLocalToDevice = function() {
// _getLocalToDevice will copy the values into the pointer.
this._getLocalToDevice(_scratch4x4MatrixPtr);
return copy4x4MatrixFromWasm(_scratch4x4MatrixPtr);
}
// findMarkedCTM returns a 4x4 matrix, or null if a matrix was not found at
// the provided marker.
CanvasKit.SkCanvas.prototype.findMarkedCTM = function(marker) {
// _getLocalToDevice will copy the values into the pointer.
var found = this._findMarkedCTM(marker, _scratch4x4MatrixPtr);
if (!found) {
return null;
}
return copy4x4MatrixFromWasm(_scratch4x4MatrixPtr);
}
// getTotalMatrix returns the current matrix as a 3x3 matrix.
CanvasKit.SkCanvas.prototype.getTotalMatrix = function() {
// _getTotalMatrix will copy the values into the pointer.
this._getTotalMatrix(_scratch3x3MatrixPtr);
// read them out into an array. TODO(kjlubick): If we change SkMatrix to be
// typedArrays, then we should return a typed array here too.
var rv = new Array(9);
for (var i = 0; i < 9; i++) {
rv[i] = CanvasKit.HEAPF32[_scratch3x3MatrixPtr/4 + i]; // divide by 4 to "cast" to float.
}
return rv;
}
// returns Uint8Array
CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType,
colorType, colorSpace, dstRowBytes) {
// supply defaults (which are compatible with HTMLCanvas's getImageData)
alphaType = alphaType || CanvasKit.AlphaType.Unpremul;
colorType = colorType || CanvasKit.ColorType.RGBA_8888;
colorSpace = colorSpace || CanvasKit.SkColorSpace.SRGB;
var pixBytes = 4;
if (colorType === CanvasKit.ColorType.RGBA_F16) {
pixBytes = 8;
}
dstRowBytes = dstRowBytes || (pixBytes * w);
var len = h * dstRowBytes
var pptr = CanvasKit._malloc(len);
var ok = this._readPixels({
'width': w,
'height': h,
'colorType': colorType,
'alphaType': alphaType,
'colorSpace': colorSpace,
}, pptr, dstRowBytes, x, y);
if (!ok) {
CanvasKit._free(pptr);
return null;
}
// The first typed array is just a view into memory. Because we will
// be free-ing that, we call slice to make a persistent copy.
var pixels = new Uint8Array(CanvasKit.HEAPU8.buffer, pptr, len).slice();
CanvasKit._free(pptr);
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) {
if (pixels.byteLength % (srcWidth * srcHeight)) {
throw 'pixels length must be a multiple of the srcWidth * srcHeight';
}
var bytesPerPixel = pixels.byteLength / (srcWidth * srcHeight);
// supply defaults (which are compatible with HTMLCanvas's putImageData)
alphaType = alphaType || CanvasKit.AlphaType.Unpremul;
colorType = colorType || CanvasKit.ColorType.RGBA_8888;
colorSpace = colorSpace || CanvasKit.SkColorSpace.SRGB;
var srcRowBytes = bytesPerPixel * srcWidth;
var pptr = copy1dArray(pixels, 'HEAPU8');
var ok = this._writePixels({
'width': srcWidth,
'height': srcHeight,
'colorType': colorType,
'alphaType': alphaType,
'colorSpace': colorSpace,
}, pptr, srcRowBytes, destX, destY);
freeArraysThatAreNotMallocedByUsers(pptr, pixels);
return ok;
}
CanvasKit.SkColorFilter.MakeBlend = function(color4f, mode) {
var cPtr = copyColorToWasm(color4f);
var result = CanvasKit.SkColorFilter._MakeBlend(cPtr, mode);
return result;
}
// colorMatrix is an SkColorMatrix (e.g. Float32Array of length 20)
CanvasKit.SkColorFilter.MakeMatrix = function(colorMatrix) {
if (!colorMatrix || colorMatrix.length !== 20) {
throw 'invalid color matrix';
}
var fptr = copy1dArray(colorMatrix, 'HEAPF32');
// We know skia memcopies the floats, so we can free our memory after the call returns.
var m = CanvasKit.SkColorFilter._makeMatrix(fptr);
freeArraysThatAreNotMallocedByUsers(fptr, colorMatrix);
return m;
}
CanvasKit.SkImageFilter.MakeMatrixTransform = function(matr, filterQuality, input) {
var matrPtr = copy3x3MatrixToWasm(matr);
return CanvasKit.SkImageFilter._MakeMatrixTransform(matrPtr, filterQuality, input);
}
CanvasKit.SkPaint.prototype.getColor = function() {
this._getColor(_scratchColorPtr);
return copyColorFromWasm(_scratchColorPtr);
}
CanvasKit.SkPaint.prototype.setColor = function(color4f, colorSpace) {
colorSpace = colorSpace || null; // null will be replaced with sRGB in the C++ method.
// emscripten wouldn't bind undefined to the sk_sp<SkColorSpace> expected here.
var cPtr = copyColorToWasm(color4f);
this._setColor(cPtr, colorSpace);
}
// The color components here are expected to be floating point values (nominally between
// 0.0 and 1.0, but with wider color gamuts, the values could exceed this range). To convert
// between standard 8 bit colors and floats, just divide by 255 before passing them in.
CanvasKit.SkPaint.prototype.setColorComponents = function(r, g, b, a, colorSpace) {
colorSpace = colorSpace || null; // null will be replaced with sRGB in the C++ method.
// emscripten wouldn't bind undefined to the sk_sp<SkColorSpace> expected here.
var cPtr = copyColorComponentsToWasm(r, g, b, a);
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();
var canvas = spr.beginRecording(
CanvasKit.LTRBRect(0, 0, this.width(), this.height()));
drawFrame(canvas);
var pic = spr.finishRecordingAsPicture();
spr.delete();
// TODO: do we need to clean up the memory for canvas?
// If we delete it here, saveAsFile doesn't work correctly.
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();
}
requestAnimationFrame(function() {
if (this._context !== undefined) {
CanvasKit.setCurrentContext(this._context);
}
callback(this._cached_canvas);
// We do not dispose() of the SkSurface here, as the client will typically
// call requestAnimationFrame again from within the supplied callback.
// For drawing a single frame, prefer drawOnce().
this.flush(dirtyRect);
}.bind(this));
}
// drawOnce will dispose of the surface after drawing the frame using the provided
// callback.
CanvasKit.SkSurface.prototype.drawOnce = function(callback, dirtyRect) {
if (!this._cached_canvas) {
this._cached_canvas = this.getCanvas();
}
requestAnimationFrame(function() {
if (this._context !== undefined) {
CanvasKit.setCurrentContext(this._context);
}
callback(this._cached_canvas);
this.flush(dirtyRect);
this.dispose();
}.bind(this));
}
CanvasKit.SkPathEffect.MakeDash = function(intervals, phase) {
if (!phase) {
phase = 0;
}
if (!intervals.length || intervals.length % 2 === 1) {
throw 'Intervals array must have even length';
}
var ptr = copy1dArray(intervals, 'HEAPF32');
var dpe = CanvasKit.SkPathEffect._MakeDash(ptr, intervals.length, phase);
freeArraysThatAreNotMallocedByUsers(ptr, intervals);
return dpe;
}
CanvasKit.SkShader.Color = function(color4f, colorSpace) {
colorSpace = colorSpace || null
var cPtr = copyColorToWasm(color4f);
var result = CanvasKit.SkShader._Color(cPtr, colorSpace);
return result;
}
CanvasKit.SkShader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags, colorSpace) {
colorSpace = colorSpace || null
var cPtrInfo = copyFlexibleColorArray(colors);
var posPtr = copy1dArray(pos, 'HEAPF32');
flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var lgs = CanvasKit._MakeLinearGradientShader(start, end, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr,
cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace);
freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
return lgs;
}
CanvasKit.SkShader.MakeRadialGradient = function(center, radius, colors, pos, mode, localMatrix, flags, colorSpace) {
colorSpace = colorSpace || null
var cPtrInfo = copyFlexibleColorArray(colors);
var posPtr = copy1dArray(pos, 'HEAPF32');
flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var rgs = CanvasKit._MakeRadialGradientShader(center, radius, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr,
cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace);
freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
return rgs;
}
CanvasKit.SkShader.MakeSweepGradient = function(cx, cy, colors, pos, mode, localMatrix, flags, startAngle, endAngle, colorSpace) {
colorSpace = colorSpace || null
var cPtrInfo = copyFlexibleColorArray(colors);
var posPtr = copy1dArray(pos, 'HEAPF32');
flags = flags || 0;
startAngle = startAngle || 0;
endAngle = endAngle || 360;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var sgs = CanvasKit._MakeSweepGradientShader(cx, cy, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr,
cPtrInfo.count, mode,
startAngle, endAngle, flags,
localMatrixPtr, colorSpace);
freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
return sgs;
}
CanvasKit.SkShader.MakeTwoPointConicalGradient = function(start, startRadius, end, endRadius,
colors, pos, mode, localMatrix, flags, colorSpace) {
colorSpace = colorSpace || null
var cPtrInfo = copyFlexibleColorArray(colors);
var posPtr = copy1dArray(pos, 'HEAPF32');
flags = flags || 0;
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
var rgs = CanvasKit._MakeTwoPointConicalGradientShader(
start, startRadius, end, endRadius, cPtrInfo.colorPtr, cPtrInfo.colorType,
posPtr, cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace);
freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
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;
CanvasKit.MakeRadialGradientShader = CanvasKit.SkShader.MakeRadialGradient;
CanvasKit.MakeTwoPointConicalGradientShader = CanvasKit.SkShader.MakeTwoPointConicalGradient;
// Run through the JS files that are added at compile time.
if (CanvasKit._extraInitializations) {
CanvasKit._extraInitializations.forEach(function(init) {
init();
});
}
}; // end CanvasKit.onRuntimeInitialized, that is, anything changing prototypes or dynamic.
// Accepts an object holding two canvaskit colors.
// {
// ambient: {r, g, b, a},
// spot: {r, g, b, a},
// }
// Returns the same format. Note, if malloced colors are passed in, the memory
// housing the passed in colors passed in will be overwritten with the computed
// tonal colors.
CanvasKit.computeTonalColors = function(tonalColors) {
// copy the colors into WASM
var cPtrAmbi = copyColorToWasmNoScratch(tonalColors['ambient']);
var cPtrSpot = copyColorToWasmNoScratch(tonalColors['spot']);
// The output of this function will be the same pointers we passed in.
this._computeTonalColors(cPtrAmbi, cPtrSpot);
// Read the results out.
var result = {
'ambient': copyColorFromWasm(cPtrAmbi),
'spot': copyColorFromWasm(cPtrSpot),
};
// If the user passed us malloced colors in here, we don't want to clean them up.
freeArraysThatAreNotMallocedByUsers(cPtrAmbi, tonalColors['ambient']);
freeArraysThatAreNotMallocedByUsers(cPtrSpot, tonalColors['spot']);
return result;
};
CanvasKit.LTRBRect = function(l, t, r, b) {
return Float32Array.of(l, t, r, b);
};
CanvasKit.XYWHRect = function(x, y, w, 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[0], rect[1], rect[2], rect[3],
rx, ry,
rx, ry,
rx, ry,
rx, ry,
);
};
// data is a TypedArray or ArrayBuffer e.g. from fetch().then(resp.arrayBuffer())
CanvasKit.MakeAnimatedImageFromEncoded = function(data) {
data = new Uint8Array(data);
var iptr = CanvasKit._malloc(data.byteLength);
CanvasKit.HEAPU8.set(data, iptr);
var img = CanvasKit._decodeAnimatedImage(iptr, data.byteLength);
if (!img) {
SkDebug('Could not decode animated image');
return null;
}
return img;
}
// data is a TypedArray or ArrayBuffer e.g. from fetch().then(resp.arrayBuffer())
CanvasKit.MakeImageFromEncoded = function(data) {
data = new Uint8Array(data);
var iptr = CanvasKit._malloc(data.byteLength);
CanvasKit.HEAPU8.set(data, iptr);
var img = CanvasKit._decodeImage(iptr, data.byteLength);
if (!img) {
SkDebug('Could not decode image');
return null;
}
return img;
}
// A variable to hold a canvasElement which can be reused once created the first time.
var memoizedCanvas2dElement = null;
// Alternative to CanvasKit.MakeImageFromEncoded. Allows for CanvasKit users to take advantage of
// browser APIs to decode images instead of using codecs included in the CanvasKit wasm binary.
// Expects that the canvasImageSource has already loaded/decoded.
// CanvasImageSource reference: https://developer.mozilla.org/en-US/docs/Web/API/CanvasImageSource
CanvasKit.MakeImageFromCanvasImageSource = function(canvasImageSource) {
var width = canvasImageSource.width;
var height = canvasImageSource.height;
if (!memoizedCanvas2dElement) {
memoizedCanvas2dElement = document.createElement('canvas');
}
memoizedCanvas2dElement.width = width;
memoizedCanvas2dElement.height = height;
var ctx2d = memoizedCanvas2dElement.getContext('2d');
ctx2d.drawImage(canvasImageSource, 0, 0);
var imageData = ctx2d.getImageData(0, 0, width, height);
return CanvasKit.MakeImage(
imageData.data,
width,
height,
CanvasKit.AlphaType.Unpremul,
CanvasKit.ColorType.RGBA_8888,
CanvasKit.SkColorSpace.SRGB
);
}
// pixels may be any Typed Array, but Uint8Array or Uint8ClampedArray is recommended,
// with bytes representing the pixel values.
// (e.g. each set of 4 bytes could represent RGBA values for a single pixel).
CanvasKit.MakeImage = function(pixels, width, height, alphaType, colorType, colorSpace) {
var bytesPerPixel = pixels.length / (width * height);
var info = {
'width': width,
'height': height,
'alphaType': alphaType,
'colorType': colorType,
'colorSpace': colorSpace,
};
var pptr = copy1dArray(pixels, 'HEAPU8');
// No need to _free pptr, Image takes it with SkData::MakeFromMalloc
return CanvasKit._MakeImage(info, pptr, pixels.length, width * bytesPerPixel);
}
// Colors may be a Uint32Array of int colors, a Flat Float32Array of float colors
// or a 2d Array of Float32Array(4) (deprecated)
// the underlying skia function accepts only int colors so it is recommended
// to pass an array of int colors to avoid an extra conversion.
// SkColorBuilder is not accepted.
CanvasKit.MakeSkVertices = function(mode, positions, textureCoordinates, colors,
indices, isVolatile) {
// Default isVolitile to true if not set
isVolatile = isVolatile === undefined ? true : isVolatile;
var idxCount = (indices && indices.length) || 0;
var flags = 0;
// These flags are from SkVertices.h and should be kept in sync with those.
if (textureCoordinates && textureCoordinates.length) {
flags |= (1 << 0);
}
if (colors && colors.length) {
flags |= (1 << 1);
}
if (!isVolatile) {
flags |= (1 << 2);
}
var builder = new CanvasKit._SkVerticesBuilder(mode, positions.length, idxCount, flags);
copy2dArray(positions, 'HEAPF32', builder.positions());
if (builder.texCoords()) {
copy2dArray(textureCoordinates, 'HEAPF32', builder.texCoords());
}
if (builder.colors()) {
if (colors.build) {
throw('Color builder not accepted by MakeSkVertices, use array of ints');
} else {
copy1dArray(assureIntColors(colors), 'HEAPU32', builder.colors());
}
}
if (builder.indices()) {
copy1dArray(indices, 'HEAPU16', builder.indices());
}
// Create the vertices, which owns the memory that the builder had allocated.
return builder.detach();
};