// 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']; // Create single copies of all three supported color spaces // These are sk_sp 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 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 expected here. var cPtr = copyColorComponentsToWasm(r, g, b, a); this._setColor(cPtr, colorSpace); } 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.requestAnimationFrame = function(callback, dirtyRect) { if (!this._cached_canvas) { this._cached_canvas = this.getCanvas(); } window.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(); } window.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; } // 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 { fLeft: l, fTop: t, fRight: r, fBottom: b, }; }; CanvasKit.XYWHRect = function(x, y, w, h) { return { fLeft: x, fTop: y, fRight: x+w, fBottom: y+h, }; }; // RRectXY returns an RRect with the given rect and a radiusX and radiusY for // all 4 corners. CanvasKit.RRectXY = function(rect, rx, ry) { return { rect: rect, rx1: rx, ry1: ry, rx2: rx, ry2: ry, rx3: rx, ry3: ry, rx4: rx, ry4: 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(); };