// 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, CanvasKit.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 colorPtr = copy2dArray(colors, CanvasKit.HEAPF32); var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); flags = flags || 0; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); var lgs = CanvasKit._MakeLinearGradientShader(start, end, colorPtr, posPtr, colors.length, mode, flags, localMatrixPtr, colorSpace); CanvasKit._free(colorPtr); freeArraysThatAreNotMallocedByUsers(posPtr, pos); return lgs; } CanvasKit.SkShader.MakeRadialGradient = function(center, radius, colors, pos, mode, localMatrix, flags, colorSpace) { colorSpace = colorSpace || null var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32); var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); flags = flags || 0; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); var rgs = CanvasKit._MakeRadialGradientShader(center, radius, colorPtr, posPtr, colors.length, mode, flags, localMatrixPtr, colorSpace); CanvasKit._free(colorPtr); freeArraysThatAreNotMallocedByUsers(posPtr, pos); return rgs; } CanvasKit.SkShader.MakeSweepGradient = function(cx, cy, colors, pos, mode, localMatrix, flags, startAngle, endAngle, colorSpace) { colorSpace = colorSpace || null var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32); var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); flags = flags || 0; startAngle = startAngle || 0; endAngle = endAngle || 360; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); var sgs = CanvasKit._MakeSweepGradientShader(cx, cy, colorPtr, posPtr, colors.length, mode, startAngle, endAngle, flags, localMatrixPtr, colorSpace); CanvasKit._free(colorPtr); freeArraysThatAreNotMallocedByUsers(posPtr, pos); return sgs; } CanvasKit.SkShader.MakeTwoPointConicalGradient = function(start, startRadius, end, endRadius, colors, pos, mode, localMatrix, flags, colorSpace) { colorSpace = colorSpace || null var colorPtr = copy2dArray(colors, CanvasKit.HEAPF32); var posPtr = copy1dArray(pos, CanvasKit.HEAPF32); flags = flags || 0; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); var rgs = CanvasKit._MakeTwoPointConicalGradientShader( start, startRadius, end, endRadius, colorPtr, posPtr, colors.length, mode, flags, localMatrixPtr, colorSpace); CanvasKit._free(colorPtr); 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 CanvasKit.computeTonalColors = function(tonalColors) { var cPtrAmbi = copyColorToWasmNoScratch(tonalColors['ambient']); var cPtrSpot = copyColorToWasmNoScratch(tonalColors['spot']); this._computeTonalColors(cPtrAmbi, cPtrSpot); var result = { 'ambient': copyColorFromWasm(cPtrAmbi), 'spot': copyColorFromWasm(cPtrSpot), } freeArraysThatAreNotMallocedByUsers(cPtrAmbi); freeArraysThatAreNotMallocedByUsers(cPtrSpot); 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, }; } CanvasKit.MakePathFromCmds = function(cmds) { var ptrLen = loadCmdsTypedArray(cmds); var path = CanvasKit._MakePathFromCmds(ptrLen[0], ptrLen[1]); CanvasKit._free(ptrLen[0]); return path; } // 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; } // pixels must be a Uint8Array 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, CanvasKit.HEAPU8); // No need to _free pptr, Image takes it with SkData::MakeFromMalloc return CanvasKit._MakeImage(info, pptr, pixels.length, width * bytesPerPixel); } // colors is an array of float color arrays 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, CanvasKit.HEAPF32, builder.positions()); if (builder.texCoords()) { copy2dArray(textureCoordinates, CanvasKit.HEAPF32, builder.texCoords()); } if (builder.colors()) { // Convert from canvaskit 4f colors to 32 bit uint colors which builder supports. copy1dArray(colors.map(toUint32Color), CanvasKit.HEAPU32, builder.colors()); } if (builder.indices()) { copy1dArray(indices, CanvasKit.HEAPU16, builder.indices()); } var idxCount = (indices && indices.length) || 0; // Create the vertices, which owns the memory that the builder had allocated. return builder.detach(); };