// 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']; _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 CanvasKit.ColorSpace.SRGB = CanvasKit.ColorSpace._MakeSRGB(); CanvasKit.ColorSpace.DISPLAY_P3 = CanvasKit.ColorSpace._MakeDisplayP3(); CanvasKit.ColorSpace.ADOBE_RGB = CanvasKit.ColorSpace._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.Matrix = {}; 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 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 expected here. var cPtr = copyColorComponentsToWasm(r, g, b, a); this._setColor(cPtr, colorSpace); }; 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); var lgs = CanvasKit.Shader._MakeLinearGradient(start, end, 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, 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); var rgs = CanvasKit.Shader._MakeTwoPointConicalGradient( 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.Vertices.prototype.bounds = function(optionalOutputArray) { this._bounds(_scratchRectPtr); var ta = _scratchRect['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; }; // 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); }; // 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(); };