skia2/modules/canvaskit/interface.js

1176 lines
45 KiB
JavaScript
Raw Normal View History

// 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. Path) 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'];
_scratchFourFloatsA = CanvasKit.Malloc(Float32Array, 4);
_scratchFourFloatsAPtr = _scratchFourFloatsA['byteOffset'];
_scratchFourFloatsB = CanvasKit.Malloc(Float32Array, 4);
_scratchFourFloatsBPtr = _scratchFourFloatsB['byteOffset'];
_scratchThreeFloatsA = CanvasKit.Malloc(Float32Array, 3); // 3 floats to represent SkVector3
_scratchThreeFloatsAPtr = _scratchThreeFloatsA['byteOffset'];
_scratchThreeFloatsB = CanvasKit.Malloc(Float32Array, 3); // 3 floats to represent SkVector3
_scratchThreeFloatsBPtr = _scratchThreeFloatsB['byteOffset'];
_scratchIRect = CanvasKit.Malloc(Int32Array, 4);
_scratchIRectPtr = _scratchIRect['byteOffset'];
// Create single copies of all three supported color spaces
// These are sk_sp<ColorSpace>
CanvasKit.ColorSpace.SRGB = CanvasKit.ColorSpace._MakeSRGB();
CanvasKit.ColorSpace.DISPLAY_P3 = CanvasKit.ColorSpace._MakeDisplayP3();
CanvasKit.ColorSpace.ADOBE_RGB = CanvasKit.ColorSpace._MakeAdobeRGB();
CanvasKit.Path.MakeFromCmds = function(cmds) {
var ptrLen = loadCmdsTypedArray(cmds);
var path = CanvasKit.Path._MakeFromCmds(ptrLen[0], ptrLen[1]);
CanvasKit._free(ptrLen[0]);
return path;
};
// The weights array is optional (only used for conics).
CanvasKit.Path.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.Path._MakeFromVerbsPointsWeights(
verbsPtr, verbs.length, pointsPtr, pts.length, weightsPtr, numWeights);
freeArraysThatAreNotMallocedByUsers(verbsPtr, verbs);
freeArraysThatAreNotMallocedByUsers(pointsPtr, pts);
freeArraysThatAreNotMallocedByUsers(weightsPtr, weights);
return path;
};
CanvasKit.Path.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.Path.prototype.addOval = function(oval, isCCW, startIndex) {
if (startIndex === undefined) {
startIndex = 1;
}
var oPtr = copyRectToWasm(oval);
this._addOval(oPtr, !!isCCW, startIndex);
return this;
};
// TODO(kjlubick) clean up this API - split it apart if necessary
CanvasKit.Path.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 {
Debug('addPath expected to take 1, 2, 7, or 10 required args. Got ' + args.length);
return null;
}
return this;
};
// points is a 1d array of length 2n representing n points where the even indices
// will be treated as x coordinates and the odd indices will be treated as y coordinates.
// Like other APIs, this accepts a malloced type array or malloc obj.
CanvasKit.Path.prototype.addPoly = function(points, close) {
var ptr = copy1dArray(points, 'HEAPF32');
this._addPoly(ptr, points.length / 2, close);
freeArraysThatAreNotMallocedByUsers(ptr, points);
return this;
};
CanvasKit.Path.prototype.addRect = function(rect, isCCW) {
var rPtr = copyRectToWasm(rect);
this._addRect(rPtr, !!isCCW);
return this;
};
CanvasKit.Path.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.Path.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.Path.prototype.arc = function(x, y, radius, startAngle, endAngle, ccw) {
// emulates the HTMLCanvas behavior. See addArc() for the Path 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.Path();
temp.addArc(bounds, radiansToDegrees(startAngle), sweep);
this.addPath(temp, true);
temp.delete();
return this;
};
// Appends arc to Path. 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.Path.prototype.arcToOval = function(oval, startAngle, sweepAngle, forceMoveTo) {
var oPtr = copyRectToWasm(oval);
this._arcToOval(oPtr, startAngle, sweepAngle, forceMoveTo);
return this;
};
// Appends arc to Path. 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 point 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 point equals (x, y). arcTo() scales radii
// (rx, ry) to fit last point 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.Path.prototype.arcToRotated = function(rx, ry, xAxisRotate, useSmallArc, isCCW, x, y) {
this._arcToRotated(rx, ry, xAxisRotate, !!useSmallArc, !!isCCW, x, y);
return this;
};
// Appends arc to Path, after appending line if needed. Arc is implemented by conic
// weighted to describe part of circle. Arc is contained by tangent from
// last Path 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.Path.prototype.arcToTangent = function(x1, y1, x2, y2, radius) {
this._arcToTangent(x1, y1, x2, y2, radius);
return this;
};
CanvasKit.Path.prototype.close = function() {
this._close();
return this;
};
CanvasKit.Path.prototype.conicTo = function(x1, y1, x2, y2, w) {
this._conicTo(x1, y1, x2, y2, w);
return this;
};
// 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.Path.prototype.computeTightBounds = function(optionalOutputArray) {
this._computeTightBounds(_scratchFourFloatsAPtr);
var ta = _scratchFourFloatsA['toTypedArray']();
if (optionalOutputArray) {
optionalOutputArray.set(ta);
return optionalOutputArray;
}
return ta.slice();
};
CanvasKit.Path.prototype.cubicTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
this._cubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
return this;
};
CanvasKit.Path.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.Path.prototype.getBounds = function(optionalOutputArray) {
this._getBounds(_scratchFourFloatsAPtr);
var ta = _scratchFourFloatsA['toTypedArray']();
if (optionalOutputArray) {
optionalOutputArray.set(ta);
return optionalOutputArray;
}
return ta.slice();
};
CanvasKit.Path.prototype.lineTo = function(x, y) {
this._lineTo(x, y);
return this;
};
CanvasKit.Path.prototype.moveTo = function(x, y) {
this._moveTo(x, y);
return this;
};
CanvasKit.Path.prototype.offset = function(dx, dy) {
this._transform(1, 0, dx,
0, 1, dy,
0, 0, 1);
return this;
};
CanvasKit.Path.prototype.quadTo = function(cpx, cpy, x, y) {
this._quadTo(cpx, cpy, x, y);
return this;
};
CanvasKit.Path.prototype.rArcTo = function(rx, ry, xAxisRotate, useSmallArc, isCCW, dx, dy) {
this._rArcTo(rx, ry, xAxisRotate, useSmallArc, isCCW, dx, dy);
return this;
};
CanvasKit.Path.prototype.rConicTo = function(dx1, dy1, dx2, dy2, w) {
this._rConicTo(dx1, dy1, dx2, dy2, w);
return this;
};
// These params are all relative
CanvasKit.Path.prototype.rCubicTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
this._rCubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
return this;
};
CanvasKit.Path.prototype.rLineTo = function(dx, dy) {
this._rLineTo(dx, dy);
return this;
};
CanvasKit.Path.prototype.rMoveTo = function(dx, dy) {
this._rMoveTo(dx, dy);
return this;
};
// These params are all relative
CanvasKit.Path.prototype.rQuadTo = function(cpx, cpy, x, y) {
this._rQuadTo(cpx, cpy, x, y);
return this;
};
CanvasKit.Path.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;
};
// TODO(kjlubick) Change this to take a 3x3 or 4x4 matrix (optionally malloc'd)
CanvasKit.Path.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.Path.prototype.trim = function(startT, stopT, isComplement) {
if (this._trim(startT, stopT, !!isComplement)) {
return this;
}
return null;
};
// makeShaderCubic returns a shader for a given image, allowing it to be used on
// a paint as well as other purposes. This shader will be higher quality than
// other shader functions. See CubicResampler in SkSamplingOptions.h for more information
// on the cubicResampler params.
CanvasKit.Image.prototype.makeShaderCubic = function(xTileMode, yTileMode,
cubicResamplerB, cubicResamplerC,
localMatrix) {
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
return this._makeShaderCubic(xTileMode, yTileMode, cubicResamplerB,
cubicResamplerC, localMatrixPtr);
};
// makeShaderCubic returns a shader for a given image, allowing it to be used on
// a paint as well as other purposes. This shader will draw more quickly than
// other shader functions, but at a lower quality.
CanvasKit.Image.prototype.makeShaderOptions = function(xTileMode, yTileMode,
filterMode, mipmapMode,
localMatrix) {
var localMatrixPtr = copy3x3MatrixToWasm(localMatrix);
return this._makeShaderOptions(xTileMode, yTileMode, filterMode, mipmapMode, localMatrixPtr);
};
function readPixels(source, srcX, srcY, imageInfo, destMallocObj, bytesPerRow) {
if (!bytesPerRow) {
bytesPerRow = 4 * imageInfo['width'];
if (imageInfo['colorType'] === CanvasKit.ColorType.RGBA_F16) {
bytesPerRow *= 2;
}
else if (imageInfo['colorType'] === CanvasKit.ColorType.RGBA_F32) {
bytesPerRow *= 4;
}
}
var pBytes = bytesPerRow * imageInfo.height;
var pPtr;
if (destMallocObj) {
pPtr = destMallocObj['byteOffset'];
} else {
pPtr = CanvasKit._malloc(pBytes);
}
if (!source._readPixels(imageInfo, pPtr, bytesPerRow, srcX, srcY)) {
Debug('Could not read pixels with the given inputs');
if (!destMallocObj) {
CanvasKit._free(pPtr);
}
return null;
}
// If the user provided us a buffer to copy into, we don't need to allocate a new TypedArray.
if (destMallocObj) {
return destMallocObj['toTypedArray'](); // Return the typed array wrapper w/o allocating.
}
// 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:
case CanvasKit.ColorType.RGBA_F16: // there is no half-float JS type, so we return raw bytes.
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;
default:
Debug('ColorType not yet supported');
return null;
}
// Free the allocated pixels in the WASM memory
CanvasKit._free(pPtr);
return retVal;
}
CanvasKit.Image.prototype.readPixels = function(srcX, srcY, imageInfo, destMallocObj,
bytesPerRow) {
return readPixels(this, srcX, srcY, imageInfo, destMallocObj, bytesPerRow);
};
// Accepts an array of four numbers in the range of 0-1 representing a 4f color
CanvasKit.Canvas.prototype.clear = function(color4f) {
var cPtr = copyColorToWasm(color4f);
this._clear(cPtr);
};
CanvasKit.Canvas.prototype.clipRRect = function(rrect, op, antialias) {
var rPtr = copyRRectToWasm(rrect);
this._clipRRect(rPtr, op, antialias);
};
CanvasKit.Canvas.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.Canvas.prototype.concat = function(matr) {
var matrPtr = copy4x4MatrixToWasm(matr);
this._concat(matrPtr);
};
CanvasKit.Canvas.prototype.drawArc = function(oval, startAngle, sweepAngle, useCenter, paint) {
var oPtr = copyRectToWasm(oval);
this._drawArc(oPtr, startAngle, sweepAngle, useCenter, paint);
};
// atlas is an Image, e.g. from CanvasKit.MakeImageFromEncoded
// srcRects, dstXforms, and colors should be CanvasKit.RectBuilder, CanvasKit.RSXFormBuilder,
// and CanvasKit.ColorBuilder (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 ColorBuilder, a Uint32Array of int colors,
// a Flat Float32Array of float colors or a 2d Array of Float32Array(4) (deprecated)
// TODO(kjlubick) remove Builders - no longer needed now that Malloc is a thing.
CanvasKit.Canvas.prototype.drawAtlas = function(atlas, srcRects, dstXforms, paint,
/* optional */ blendMode, /* optional */ colors,
/* optional */ sampling) {
if (!atlas || !paint || !srcRects || !dstXforms) {
Debug('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) {
Debug('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');
}
}
// We require one of these:
// 1. sampling is null (we default to linear/none)
// 2. sampling.B and sampling.C --> CubicResampler
// 3. sampling.filter [and sampling.mipmap] --> FilterOptions
//
// Thus if all fields are available, we will choose cubic (since we search for B,C first)
if (sampling && ('B' in sampling) && ('C' in sampling)) {
this._drawAtlasCubic(atlas, dstXformPtr, srcRectPtr, colorPtr, count, blendMode,
sampling['B'], sampling['C'], paint);
} else {
let filter = CanvasKit.FilterMode.Linear;
let mipmap = CanvasKit.MipmapMode.None;
if (sampling) {
filter = sampling['filter']; // 'filter' is a required field
if ('mipmap' in sampling) { // 'mipmap' is optional
mipmap = sampling['mipmap'];
}
}
this._drawAtlasOptions(atlas, dstXformPtr, srcRectPtr, colorPtr, count, blendMode,
filter, mipmap, paint);
}
if (srcRectPtr && !srcRects.build) {
freeArraysThatAreNotMallocedByUsers(srcRectPtr, srcRects);
}
if (dstXformPtr && !dstXforms.build) {
freeArraysThatAreNotMallocedByUsers(dstXformPtr, dstXforms);
}
if (colorPtr && !colors.build) {
freeArraysThatAreNotMallocedByUsers(colorPtr, colors);
}
};
CanvasKit.Canvas.prototype.drawColor = function (color4f, mode) {
var cPtr = copyColorToWasm(color4f);
if (mode !== undefined) {
this._drawColor(cPtr, mode);
} else {
this._drawColor(cPtr);
}
};
CanvasKit.Canvas.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.Canvas.prototype.drawDRRect = function(outer, inner, paint) {
var oPtr = copyRRectToWasm(outer, _scratchRRectPtr);
var iPtr = copyRRectToWasm(inner, _scratchRRect2Ptr);
this._drawDRRect(oPtr, iPtr, paint);
};
CanvasKit.Canvas.prototype.drawGlyphs = function(glyphs, positions, x, y, font, paint) {
if (!(glyphs.length*2 == positions.length)) {
throw 'Need glyphs and positions array to agree on the length';
}
const glyphs_ptr = copy1dArray(glyphs, 'HEAPU16');
const positions_ptr = copy1dArray(positions, 'HEAPF32');
this._drawGlyphs(glyphs.length, glyphs_ptr, positions_ptr, x, y, font, paint);
freeArraysThatAreNotMallocedByUsers(positions_ptr, positions);
freeArraysThatAreNotMallocedByUsers(glyphs_ptr, glyphs);
};
CanvasKit.Canvas.prototype.drawImageNine = function(img, center, dest, filter, paint) {
var cPtr = copyIRectToWasm(center);
var dPtr = copyRectToWasm(dest);
this._drawImageNine(img, cPtr, dPtr, filter, paint || null);
};
CanvasKit.Canvas.prototype.drawImageRect = function(img, src, dest, paint, fastSample) {
copyRectToWasm(src, _scratchFourFloatsAPtr);
copyRectToWasm(dest, _scratchFourFloatsBPtr);
this._drawImageRect(img, _scratchFourFloatsAPtr, _scratchFourFloatsBPtr, paint, !!fastSample);
};
CanvasKit.Canvas.prototype.drawImageRectCubic = function(img, src, dest, B, C, paint) {
copyRectToWasm(src, _scratchFourFloatsAPtr);
copyRectToWasm(dest, _scratchFourFloatsBPtr);
this._drawImageRectCubic(img, _scratchFourFloatsAPtr, _scratchFourFloatsBPtr, B, C,
paint || null);
};
CanvasKit.Canvas.prototype.drawImageRectOptions = function(img, src, dest, filter, mipmap, paint) {
copyRectToWasm(src, _scratchFourFloatsAPtr);
copyRectToWasm(dest, _scratchFourFloatsBPtr);
this._drawImageRectOptions(img, _scratchFourFloatsAPtr, _scratchFourFloatsBPtr, filter, mipmap,
paint || null);
};
CanvasKit.Canvas.prototype.drawOval = function(oval, paint) {
var oPtr = copyRectToWasm(oval);
this._drawOval(oPtr, paint);
};
CanvasKit.Canvas.prototype.drawPatch = function(cubics, colors, texs, mode, paint) {
if (cubics.length < 24) {
throw 'Need 12 cubic points';
}
if (colors && colors.length < 4) {
throw 'Need 4 colors';
}
if (texs && texs.length < 8) {
throw 'Need 4 shader coordinates';
}
const cubics_ptr = copy1dArray(cubics, 'HEAPF32');
const colors_ptr = colors ? copy1dArray(assureIntColors(colors), 'HEAPU32') : nullptr;
const texs_ptr = texs ? copy1dArray(texs, 'HEAPF32') : nullptr;
if (!mode) {
mode = CanvasKit.BlendMode.Modulate;
}
this._drawPatch(cubics_ptr, colors_ptr, texs_ptr, mode, paint);
freeArraysThatAreNotMallocedByUsers(texs_ptr, texs);
freeArraysThatAreNotMallocedByUsers(colors_ptr, colors);
freeArraysThatAreNotMallocedByUsers(cubics_ptr, cubics);
};
// points is a 1d array of length 2n representing n points where the even indices
// will be treated as x coordinates and the odd indices will be treated as y coordinates.
// Like other APIs, this accepts a malloced type array or malloc obj.
CanvasKit.Canvas.prototype.drawPoints = function(mode, points, paint) {
var ptr = copy1dArray(points, 'HEAPF32');
this._drawPoints(mode, ptr, points.length / 2, paint);
freeArraysThatAreNotMallocedByUsers(ptr, points);
};
CanvasKit.Canvas.prototype.drawRRect = function(rrect, paint) {
var rPtr = copyRRectToWasm(rrect);
this._drawRRect(rPtr, paint);
};
CanvasKit.Canvas.prototype.drawRect = function(rect, paint) {
var rPtr = copyRectToWasm(rect);
this._drawRect(rPtr, paint);
};
CanvasKit.Canvas.prototype.drawShadow = function(path, zPlaneParams, lightPos, lightRadius,
ambientColor, spotColor, flags) {
var ambiPtr = copyColorToWasmNoScratch(ambientColor);
var spotPtr = copyColorToWasmNoScratch(spotColor);
// We use the return value from copy1dArray in case the passed in arrays are malloc'd.
var zPlanePtr = copy1dArray(zPlaneParams, 'HEAPF32', _scratchThreeFloatsAPtr);
var lightPosPtr = copy1dArray(lightPos, 'HEAPF32', _scratchThreeFloatsBPtr);
this._drawShadow(path, zPlanePtr, lightPosPtr, lightRadius, ambiPtr, spotPtr, flags);
freeArraysThatAreNotMallocedByUsers(ambiPtr, ambientColor);
freeArraysThatAreNotMallocedByUsers(spotPtr, spotColor);
};
CanvasKit.getShadowLocalBounds = function(ctm, path, zPlaneParams, lightPos, lightRadius,
flags, optOutputRect) {
var ctmPtr = copy3x3MatrixToWasm(ctm);
// We use the return value from copy1dArray in case the passed in arrays are malloc'd.
var zPlanePtr = copy1dArray(zPlaneParams, 'HEAPF32', _scratchThreeFloatsAPtr);
var lightPosPtr = copy1dArray(lightPos, 'HEAPF32', _scratchThreeFloatsBPtr);
var ok = this._getShadowLocalBounds(ctmPtr, path, zPlanePtr, lightPosPtr, lightRadius,
flags, _scratchFourFloatsAPtr);
if (!ok) {
return null;
}
var ta = _scratchFourFloatsA['toTypedArray']();
if (optOutputRect) {
optOutputRect.set(ta);
return optOutputRect;
}
return ta.slice();
};
// getLocalToDevice returns a 4x4 matrix.
CanvasKit.Canvas.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.Canvas.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.Canvas.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 Matrix 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;
};
CanvasKit.Canvas.prototype.readPixels = function(srcX, srcY, imageInfo, destMallocObj,
bytesPerRow) {
return readPixels(this, srcX, srcY, imageInfo, destMallocObj, bytesPerRow);
};
CanvasKit.Canvas.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.Canvas.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.ColorSpace.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.ColorFilter.MakeBlend = function(color4f, mode) {
var cPtr = copyColorToWasm(color4f);
return CanvasKit.ColorFilter._MakeBlend(cPtr, mode);
};
// colorMatrix is an ColorMatrix (e.g. Float32Array of length 20)
CanvasKit.ColorFilter.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.ColorFilter._makeMatrix(fptr);
freeArraysThatAreNotMallocedByUsers(fptr, colorMatrix);
return m;
};
CanvasKit.ContourMeasure.prototype.getPosTan = function(distance, optionalOutput) {
this._getPosTan(distance, _scratchFourFloatsAPtr);
var ta = _scratchFourFloatsA['toTypedArray']();
if (optionalOutput) {
optionalOutput.set(ta);
return optionalOutput;
}
return ta.slice();
};
CanvasKit.ImageFilter.MakeMatrixTransform = function(matr, filterQuality, input) {
var matrPtr = copy3x3MatrixToWasm(matr);
return CanvasKit.ImageFilter._MakeMatrixTransform(matrPtr, filterQuality, input);
};
CanvasKit.Paint.prototype.getColor = function() {
this._getColor(_scratchColorPtr);
return copyColorFromWasm(_scratchColorPtr);
};
CanvasKit.Paint.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<ColorSpace> 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.Paint.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<ColorSpace> expected here.
var cPtr = copyColorComponentsToWasm(r, g, b, a);
this._setColor(cPtr, colorSpace);
};
CanvasKit.Path.prototype.getPoint = function(idx, optionalOutput) {
// This will copy 2 floats into a space for 4 floats
this._getPoint(idx, _scratchFourFloatsAPtr);
var ta = _scratchFourFloatsA['toTypedArray']();
if (optionalOutput) {
// We cannot call optionalOutput.set() because it is an error to call .set() with
// a source bigger than the destination.
optionalOutput[0] = ta[0];
optionalOutput[1] = ta[1];
return optionalOutput;
}
// Be sure to return a copy of just the first 2 values.
return ta.slice(0, 2);
};
CanvasKit.PictureRecorder.prototype.beginRecording = function(bounds) {
var bPtr = copyRectToWasm(bounds);
return this._beginRecording(bPtr);
};
CanvasKit.Surface.prototype.makeImageSnapshot = function(optionalBoundsRect) {
var bPtr = copyIRectToWasm(optionalBoundsRect);
return this._makeImageSnapshot(bPtr);
};
CanvasKit.Surface.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 Surface 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.Surface.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.PathEffect.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.PathEffect._MakeDash(ptr, intervals.length, phase);
freeArraysThatAreNotMallocedByUsers(ptr, intervals);
return dpe;
};
CanvasKit.Shader.MakeColor = function(color4f, colorSpace) {
colorSpace = colorSpace || null;
var cPtr = copyColorToWasm(color4f);
return CanvasKit.Shader._MakeColor(cPtr, colorSpace);
};
// TODO(kjlubick) remove deprecated names.
CanvasKit.Shader.Blend = CanvasKit.Shader.MakeBlend;
CanvasKit.Shader.Color = CanvasKit.Shader.MakeColor;
CanvasKit.Shader.Lerp = CanvasKit.Shader.MakeLerp;
CanvasKit.Shader.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);
// Copy start and end to _scratchFourFloatsAPtr.
var startEndPts = _scratchFourFloatsA['toTypedArray']();
startEndPts.set(start);
startEndPts.set(end, 2);
var lgs = CanvasKit.Shader._MakeLinearGradient(_scratchFourFloatsAPtr, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr,
cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace);
freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
return lgs;
};
CanvasKit.Shader.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.Shader._MakeRadialGradient(center[0], center[1], radius, cPtrInfo.colorPtr,
cPtrInfo.colorType, posPtr, cPtrInfo.count, mode,
flags, localMatrixPtr, colorSpace);
freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors);
pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos);
return rgs;
};
CanvasKit.Shader.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.Shader._MakeSweepGradient(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.Shader.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);
// Copy start and end to _scratchFourFloatsAPtr.
var startEndPts = _scratchFourFloatsA['toTypedArray']();
startEndPts.set(start);
startEndPts.set(end, 2);
var rgs = CanvasKit.Shader._MakeTwoPointConicalGradient(_scratchFourFloatsAPtr,
startRadius, 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.Vertices.prototype.bounds = function(optionalOutputArray) {
this._bounds(_scratchFourFloatsAPtr);
var ta = _scratchFourFloatsA['toTypedArray']();
if (optionalOutputArray) {
optionalOutputArray.set(ta);
return optionalOutputArray;
}
return ta.slice();
};
// 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) {
Debug('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) {
Debug('Could not decode image');
return null;
}
return img;
};
Added CanvasKit.MakeImageFromCanvasImageSource which is useful as an alternative to CanvasKit.MakeImageFromEncoded, when used with Browser APIs for loading/decoding images. - `CanvasKit.MakeImageFromCanvasImageSource` takes either an HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, or OffscreenCanvas and returns an SkImage. This function is an alternative to `CanvasKit.MakeImageFromEncoded` for creating SkImages when loading and decoding images. In the future, codesize of CanvasKit may be able to be reduced by removing image codecs in wasm, if browser APIs for decoding images are used along with `CanvasKit.MakeImageFromCanvasImageSource` instead of `CanvasKit.MakeImageFromEncoded`. - Three usage examples of `CanvasKit.MakeImageFromCanvasImageSource` in core.spec.ts. These examples use browser APIs to decode images including 2d canvas, bitmaprenderer canvas, HTMLImageElement and Blob. - Added support for asynchronous callbacks in perfs and tests. Here are notes on the image decoding approaches we tested and perfed in the process of finding ways to use Browser APIs to decode images: 1. pipeline: ArrayBuffer → ImageData → ctx.putImageData → context.getImageData → Uint8Array → CanvasKit.MakeImage ❌ Problem: ImageData constructor expects decoded bytes already. 2. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded (async function) pipeline: ArrayBuffer → Blob -> HTMLImageElement -> draw on Canvas2d -> context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 3rd place (in my testing locally) 3. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2 (async function) ArrayBuffer → Blob → ImageBitmap → draw on Canvas2d → context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 2nd place (in my testing locally) 4. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded3 (async function) ArrayBuffer → Blob → ImageBitmap → draw on canvas 1 using bitmaprenderer context → draw canvas 1 on canvas 2 using drawImage → context2d.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 1st place (in my testing locally) - quite surprising, this in some ways seems to be a more roundabout way of CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2, but it seems bitmaprenderer context is fairly fast. Bug: skia:10360 Change-Id: I6fe94b8196dfd1ad0d8929f04bb1697da537ca18 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/295390 Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-06-15 18:53:27 +00:00
// 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({
'width': width,
'height': height,
'alphaType': CanvasKit.AlphaType.Unpremul,
'colorType': CanvasKit.ColorType.RGBA_8888,
'colorSpace': CanvasKit.ColorSpace.SRGB
}, imageData.data, 4 * width);
};
Added CanvasKit.MakeImageFromCanvasImageSource which is useful as an alternative to CanvasKit.MakeImageFromEncoded, when used with Browser APIs for loading/decoding images. - `CanvasKit.MakeImageFromCanvasImageSource` takes either an HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, or OffscreenCanvas and returns an SkImage. This function is an alternative to `CanvasKit.MakeImageFromEncoded` for creating SkImages when loading and decoding images. In the future, codesize of CanvasKit may be able to be reduced by removing image codecs in wasm, if browser APIs for decoding images are used along with `CanvasKit.MakeImageFromCanvasImageSource` instead of `CanvasKit.MakeImageFromEncoded`. - Three usage examples of `CanvasKit.MakeImageFromCanvasImageSource` in core.spec.ts. These examples use browser APIs to decode images including 2d canvas, bitmaprenderer canvas, HTMLImageElement and Blob. - Added support for asynchronous callbacks in perfs and tests. Here are notes on the image decoding approaches we tested and perfed in the process of finding ways to use Browser APIs to decode images: 1. pipeline: ArrayBuffer → ImageData → ctx.putImageData → context.getImageData → Uint8Array → CanvasKit.MakeImage ❌ Problem: ImageData constructor expects decoded bytes already. 2. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded (async function) pipeline: ArrayBuffer → Blob -> HTMLImageElement -> draw on Canvas2d -> context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 3rd place (in my testing locally) 3. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2 (async function) ArrayBuffer → Blob → ImageBitmap → draw on Canvas2d → context.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 2nd place (in my testing locally) 4. interface.js - CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded3 (async function) ArrayBuffer → Blob → ImageBitmap → draw on canvas 1 using bitmaprenderer context → draw canvas 1 on canvas 2 using drawImage → context2d.getImageData → Uint8Array → CanvasKit.MakeImage ✅ Works ⏱ Performance: 1st place (in my testing locally) - quite surprising, this in some ways seems to be a more roundabout way of CanvasKit.ExperimentalCanvas2DMakeImageFromEncoded2, but it seems bitmaprenderer context is fairly fast. Bug: skia:10360 Change-Id: I6fe94b8196dfd1ad0d8929f04bb1697da537ca18 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/295390 Reviewed-by: Kevin Lubick <kjlubick@google.com>
2020-06-15 18:53:27 +00:00
// pixels may be an array but Uint8Array or Uint8ClampedArray is recommended,
// with the bytes representing the pixel values.
// (e.g. each set of 4 bytes could represent RGBA values for a single pixel).
CanvasKit.MakeImage = function(info, pixels, bytesPerRow) {
var pptr = CanvasKit._malloc(pixels.length);
CanvasKit.HEAPU8.set(pixels, pptr); // We always want to copy the bytes into the WASM heap.
// No need to _free pptr, Image takes it with SkData::MakeFromMalloc
return CanvasKit._MakeImage(info, pptr, pixels.length, bytesPerRow);
};
// 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.
// ColorBuilder is not accepted.
CanvasKit.MakeVertices = function(mode, positions, textureCoordinates, colors,
indices, isVolatile) {
// Default isVolatile 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._VerticesBuilder(mode, positions.length / 2, idxCount, flags);
copy1dArray(positions, 'HEAPF32', builder.positions());
if (builder.texCoords()) {
copy1dArray(textureCoordinates, 'HEAPF32', builder.texCoords());
}
if (builder.colors()) {
if (colors.build) {
throw('Color builder not accepted by MakeVertices, 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();
};