1*c8dee2aaSAndroid Build Coastguard Worker// Adds compile-time JS functions to augment the CanvasKit interface. 2*c8dee2aaSAndroid Build Coastguard Worker// Implementations in this file are considerate of GPU builds, i.e. some 3*c8dee2aaSAndroid Build Coastguard Worker// behavior is predicated on whether or not this is being compiled alongside 4*c8dee2aaSAndroid Build Coastguard Worker// webgl.js or webgpu.js. 5*c8dee2aaSAndroid Build Coastguard Worker(function(CanvasKit){ 6*c8dee2aaSAndroid Build Coastguard Worker CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; 7*c8dee2aaSAndroid Build Coastguard Worker CanvasKit._extraInitializations.push(function() { 8*c8dee2aaSAndroid Build Coastguard Worker // Takes in an html id or a canvas element 9*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeSWCanvasSurface = function(idOrElement) { 10*c8dee2aaSAndroid Build Coastguard Worker var canvas = idOrElement; 11*c8dee2aaSAndroid Build Coastguard Worker var isHTMLCanvas = typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement; 12*c8dee2aaSAndroid Build Coastguard Worker var isOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas; 13*c8dee2aaSAndroid Build Coastguard Worker if (!isHTMLCanvas && !isOffscreenCanvas) { 14*c8dee2aaSAndroid Build Coastguard Worker canvas = document.getElementById(idOrElement); 15*c8dee2aaSAndroid Build Coastguard Worker if (!canvas) { 16*c8dee2aaSAndroid Build Coastguard Worker throw 'Canvas with id ' + idOrElement + ' was not found'; 17*c8dee2aaSAndroid Build Coastguard Worker } 18*c8dee2aaSAndroid Build Coastguard Worker } 19*c8dee2aaSAndroid Build Coastguard Worker // Maybe better to use clientWidth/height. See: 20*c8dee2aaSAndroid Build Coastguard Worker // https://webglfundamentals.org/webgl/lessons/webgl-anti-patterns.html 21*c8dee2aaSAndroid Build Coastguard Worker var surface = CanvasKit.MakeSurface(canvas.width, canvas.height); 22*c8dee2aaSAndroid Build Coastguard Worker if (surface) { 23*c8dee2aaSAndroid Build Coastguard Worker surface._canvas = canvas; 24*c8dee2aaSAndroid Build Coastguard Worker } 25*c8dee2aaSAndroid Build Coastguard Worker return surface; 26*c8dee2aaSAndroid Build Coastguard Worker }; 27*c8dee2aaSAndroid Build Coastguard Worker 28*c8dee2aaSAndroid Build Coastguard Worker // Don't over-write the MakeCanvasSurface set by gpu.js if it exists. 29*c8dee2aaSAndroid Build Coastguard Worker if (!CanvasKit.MakeCanvasSurface) { 30*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeCanvasSurface = CanvasKit.MakeSWCanvasSurface; 31*c8dee2aaSAndroid Build Coastguard Worker } 32*c8dee2aaSAndroid Build Coastguard Worker 33*c8dee2aaSAndroid Build Coastguard Worker // Note that color spaces are currently not supported in CPU surfaces. due to the limitation 34*c8dee2aaSAndroid Build Coastguard Worker // canvas.getContext('2d').putImageData imposes a limitation of using an RGBA_8888 color type. 35*c8dee2aaSAndroid Build Coastguard Worker // TODO(nifong): support WGC color spaces while still using an RGBA_8888 color type when 36*c8dee2aaSAndroid Build Coastguard Worker // on a cpu backend. 37*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeSurface = function(width, height) { 38*c8dee2aaSAndroid Build Coastguard Worker var imageInfo = { 39*c8dee2aaSAndroid Build Coastguard Worker 'width': width, 40*c8dee2aaSAndroid Build Coastguard Worker 'height': height, 41*c8dee2aaSAndroid Build Coastguard Worker 'colorType': CanvasKit.ColorType.RGBA_8888, 42*c8dee2aaSAndroid Build Coastguard Worker // Since we are sending these pixels directly into the HTML canvas, 43*c8dee2aaSAndroid Build Coastguard Worker // (and those pixels are un-premultiplied, i.e. straight r,g,b,a) 44*c8dee2aaSAndroid Build Coastguard Worker 'alphaType': CanvasKit.AlphaType.Unpremul, 45*c8dee2aaSAndroid Build Coastguard Worker 'colorSpace': CanvasKit.ColorSpace.SRGB, 46*c8dee2aaSAndroid Build Coastguard Worker }; 47*c8dee2aaSAndroid Build Coastguard Worker var pixelLen = width * height * 4; // it's 8888, so 4 bytes per pixel 48*c8dee2aaSAndroid Build Coastguard Worker // Allocate the buffer of pixels to be drawn into. 49*c8dee2aaSAndroid Build Coastguard Worker var pixelPtr = CanvasKit._malloc(pixelLen); 50*c8dee2aaSAndroid Build Coastguard Worker 51*c8dee2aaSAndroid Build Coastguard Worker // Experiments with using RasterDirect vs Raster showed a 10% slowdown 52*c8dee2aaSAndroid Build Coastguard Worker // over the traditional Surface::MakeRaster approach. This was exacerbated when 53*c8dee2aaSAndroid Build Coastguard Worker // the surface was drawing to Premul and we had to convert to Unpremul each frame 54*c8dee2aaSAndroid Build Coastguard Worker // (up to a 10x further slowdown). 55*c8dee2aaSAndroid Build Coastguard Worker var surface = CanvasKit.Surface._makeRasterDirect(imageInfo, pixelPtr, width*4); 56*c8dee2aaSAndroid Build Coastguard Worker if (surface) { 57*c8dee2aaSAndroid Build Coastguard Worker surface._canvas = null; 58*c8dee2aaSAndroid Build Coastguard Worker surface._width = width; 59*c8dee2aaSAndroid Build Coastguard Worker surface._height = height; 60*c8dee2aaSAndroid Build Coastguard Worker surface._pixelLen = pixelLen; 61*c8dee2aaSAndroid Build Coastguard Worker 62*c8dee2aaSAndroid Build Coastguard Worker surface._pixelPtr = pixelPtr; 63*c8dee2aaSAndroid Build Coastguard Worker // rasterDirectSurface does not initialize the pixels, so we clear them 64*c8dee2aaSAndroid Build Coastguard Worker // to transparent black. 65*c8dee2aaSAndroid Build Coastguard Worker surface.getCanvas().clear(CanvasKit.TRANSPARENT); 66*c8dee2aaSAndroid Build Coastguard Worker } 67*c8dee2aaSAndroid Build Coastguard Worker return surface; 68*c8dee2aaSAndroid Build Coastguard Worker }; 69*c8dee2aaSAndroid Build Coastguard Worker 70*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeRasterDirectSurface = function(imageInfo, mallocObj, bytesPerRow) { 71*c8dee2aaSAndroid Build Coastguard Worker return CanvasKit.Surface._makeRasterDirect(imageInfo, mallocObj['byteOffset'], bytesPerRow); 72*c8dee2aaSAndroid Build Coastguard Worker }; 73*c8dee2aaSAndroid Build Coastguard Worker 74*c8dee2aaSAndroid Build Coastguard Worker // For GPU builds, simply proxies to native code flush. For CPU builds, 75*c8dee2aaSAndroid Build Coastguard Worker // also updates the underlying HTML canvas, optionally with dirtyRect. 76*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Surface.prototype.flush = function(dirtyRect) { 77*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext(this._context); 78*c8dee2aaSAndroid Build Coastguard Worker this._flush(); 79*c8dee2aaSAndroid Build Coastguard Worker // Do we have an HTML canvas to write the pixels to? 80*c8dee2aaSAndroid Build Coastguard Worker // We will not have a canvas if this a GPU build, for example. 81*c8dee2aaSAndroid Build Coastguard Worker if (this._canvas) { 82*c8dee2aaSAndroid Build Coastguard Worker var pixels = new Uint8ClampedArray(CanvasKit.HEAPU8.buffer, this._pixelPtr, this._pixelLen); 83*c8dee2aaSAndroid Build Coastguard Worker var imageData = new ImageData(pixels, this._width, this._height); 84*c8dee2aaSAndroid Build Coastguard Worker 85*c8dee2aaSAndroid Build Coastguard Worker if (!dirtyRect) { 86*c8dee2aaSAndroid Build Coastguard Worker this._canvas.getContext('2d').putImageData(imageData, 0, 0); 87*c8dee2aaSAndroid Build Coastguard Worker } else { 88*c8dee2aaSAndroid Build Coastguard Worker this._canvas.getContext('2d').putImageData(imageData, 0, 0, 89*c8dee2aaSAndroid Build Coastguard Worker dirtyRect[0], dirtyRect[1], 90*c8dee2aaSAndroid Build Coastguard Worker dirtyRect[2] - dirtyRect[0], 91*c8dee2aaSAndroid Build Coastguard Worker dirtyRect[3] - dirtyRect[1]); 92*c8dee2aaSAndroid Build Coastguard Worker } 93*c8dee2aaSAndroid Build Coastguard Worker } 94*c8dee2aaSAndroid Build Coastguard Worker }; 95*c8dee2aaSAndroid Build Coastguard Worker 96*c8dee2aaSAndroid Build Coastguard Worker // Call dispose() instead of delete to clean up the underlying memory. 97*c8dee2aaSAndroid Build Coastguard Worker // TODO(kjlubick) get rid of this and just wrap around delete(). 98*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Surface.prototype.dispose = function() { 99*c8dee2aaSAndroid Build Coastguard Worker if (this._pixelPtr) { 100*c8dee2aaSAndroid Build Coastguard Worker CanvasKit._free(this._pixelPtr); 101*c8dee2aaSAndroid Build Coastguard Worker } 102*c8dee2aaSAndroid Build Coastguard Worker this.delete(); 103*c8dee2aaSAndroid Build Coastguard Worker }; 104*c8dee2aaSAndroid Build Coastguard Worker 105*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext = CanvasKit.setCurrentContext || function() { 106*c8dee2aaSAndroid Build Coastguard Worker // no op if this is a cpu-only build. 107*c8dee2aaSAndroid Build Coastguard Worker }; 108*c8dee2aaSAndroid Build Coastguard Worker 109*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.getCurrentGrDirectContext = CanvasKit.getCurrentGrDirectContext || function() { 110*c8dee2aaSAndroid Build Coastguard Worker // No GrDirectContexts without a GPU backend. 111*c8dee2aaSAndroid Build Coastguard Worker return null; 112*c8dee2aaSAndroid Build Coastguard Worker }; 113*c8dee2aaSAndroid Build Coastguard Worker }); 114*c8dee2aaSAndroid Build Coastguard Worker}(Module)); // When this file is loaded in, the high level object is "Module"; 115