1*c8dee2aaSAndroid Build Coastguard Worker// Adds compile-time JS functions to augment the CanvasKit interface. 2*c8dee2aaSAndroid Build Coastguard Worker// Specifically, anything that should only be on the WebGL version of canvaskit. 3*c8dee2aaSAndroid Build Coastguard Worker// Functions in this file are supplemented by cpu.js. 4*c8dee2aaSAndroid Build Coastguard Worker(function(CanvasKit){ 5*c8dee2aaSAndroid Build Coastguard Worker CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; 6*c8dee2aaSAndroid Build Coastguard Worker CanvasKit._extraInitializations.push(function() { 7*c8dee2aaSAndroid Build Coastguard Worker function get(obj, attr, defaultValue) { 8*c8dee2aaSAndroid Build Coastguard Worker if (obj && obj.hasOwnProperty(attr)) { 9*c8dee2aaSAndroid Build Coastguard Worker return obj[attr]; 10*c8dee2aaSAndroid Build Coastguard Worker } 11*c8dee2aaSAndroid Build Coastguard Worker return defaultValue; 12*c8dee2aaSAndroid Build Coastguard Worker } 13*c8dee2aaSAndroid Build Coastguard Worker 14*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.GetWebGLContext = function(canvas, attrs) { 15*c8dee2aaSAndroid Build Coastguard Worker if (!canvas) { 16*c8dee2aaSAndroid Build Coastguard Worker throw 'null canvas passed into makeWebGLContext'; 17*c8dee2aaSAndroid Build Coastguard Worker } 18*c8dee2aaSAndroid Build Coastguard Worker var contextAttributes = { 19*c8dee2aaSAndroid Build Coastguard Worker 'alpha': get(attrs, 'alpha', 1), 20*c8dee2aaSAndroid Build Coastguard Worker 'depth': get(attrs, 'depth', 1), 21*c8dee2aaSAndroid Build Coastguard Worker 'stencil': get(attrs, 'stencil', 8), 22*c8dee2aaSAndroid Build Coastguard Worker 'antialias': get(attrs, 'antialias', 0), 23*c8dee2aaSAndroid Build Coastguard Worker 'premultipliedAlpha': get(attrs, 'premultipliedAlpha', 1), 24*c8dee2aaSAndroid Build Coastguard Worker 'preserveDrawingBuffer': get(attrs, 'preserveDrawingBuffer', 0), 25*c8dee2aaSAndroid Build Coastguard Worker 'preferLowPowerToHighPerformance': get(attrs, 'preferLowPowerToHighPerformance', 0), 26*c8dee2aaSAndroid Build Coastguard Worker 'failIfMajorPerformanceCaveat': get(attrs, 'failIfMajorPerformanceCaveat', 0), 27*c8dee2aaSAndroid Build Coastguard Worker 'enableExtensionsByDefault': get(attrs, 'enableExtensionsByDefault', 1), 28*c8dee2aaSAndroid Build Coastguard Worker 'explicitSwapControl': get(attrs, 'explicitSwapControl', 0), 29*c8dee2aaSAndroid Build Coastguard Worker 'renderViaOffscreenBackBuffer': get(attrs, 'renderViaOffscreenBackBuffer', 0), 30*c8dee2aaSAndroid Build Coastguard Worker }; 31*c8dee2aaSAndroid Build Coastguard Worker 32*c8dee2aaSAndroid Build Coastguard Worker if (attrs && attrs['majorVersion']) { 33*c8dee2aaSAndroid Build Coastguard Worker contextAttributes['majorVersion'] = attrs['majorVersion'] 34*c8dee2aaSAndroid Build Coastguard Worker } else { 35*c8dee2aaSAndroid Build Coastguard Worker // Default to WebGL 2 if available and not specified. 36*c8dee2aaSAndroid Build Coastguard Worker contextAttributes['majorVersion'] = (typeof WebGL2RenderingContext !== 'undefined') ? 2 : 1; 37*c8dee2aaSAndroid Build Coastguard Worker } 38*c8dee2aaSAndroid Build Coastguard Worker 39*c8dee2aaSAndroid Build Coastguard Worker // This check is from the emscripten version 40*c8dee2aaSAndroid Build Coastguard Worker if (contextAttributes['explicitSwapControl']) { 41*c8dee2aaSAndroid Build Coastguard Worker throw 'explicitSwapControl is not supported'; 42*c8dee2aaSAndroid Build Coastguard Worker } 43*c8dee2aaSAndroid Build Coastguard Worker // Creates a WebGL context and sets it to be the current context. 44*c8dee2aaSAndroid Build Coastguard Worker // These functions are defined in emscripten's library_webgl.js 45*c8dee2aaSAndroid Build Coastguard Worker var handle = GL.createContext(canvas, contextAttributes); 46*c8dee2aaSAndroid Build Coastguard Worker if (!handle) { 47*c8dee2aaSAndroid Build Coastguard Worker return 0; 48*c8dee2aaSAndroid Build Coastguard Worker } 49*c8dee2aaSAndroid Build Coastguard Worker GL.makeContextCurrent(handle); 50*c8dee2aaSAndroid Build Coastguard Worker // Emscripten does not enable this by default and Skia needs this to handle certain GPU 51*c8dee2aaSAndroid Build Coastguard Worker // corner cases. 52*c8dee2aaSAndroid Build Coastguard Worker GL.currentContext.GLctx.getExtension('WEBGL_debug_renderer_info'); 53*c8dee2aaSAndroid Build Coastguard Worker return handle; 54*c8dee2aaSAndroid Build Coastguard Worker }; 55*c8dee2aaSAndroid Build Coastguard Worker 56*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.deleteContext = function(handle) { 57*c8dee2aaSAndroid Build Coastguard Worker GL.deleteContext(handle); 58*c8dee2aaSAndroid Build Coastguard Worker }; 59*c8dee2aaSAndroid Build Coastguard Worker 60*c8dee2aaSAndroid Build Coastguard Worker CanvasKit._setTextureCleanup({ 61*c8dee2aaSAndroid Build Coastguard Worker 'deleteTexture': function(webglHandle, texHandle) { 62*c8dee2aaSAndroid Build Coastguard Worker var tex = GL.textures[texHandle]; 63*c8dee2aaSAndroid Build Coastguard Worker if (tex) { 64*c8dee2aaSAndroid Build Coastguard Worker GL.getContext(webglHandle).GLctx.deleteTexture(tex); 65*c8dee2aaSAndroid Build Coastguard Worker } 66*c8dee2aaSAndroid Build Coastguard Worker GL.textures[texHandle] = null; 67*c8dee2aaSAndroid Build Coastguard Worker }, 68*c8dee2aaSAndroid Build Coastguard Worker }); 69*c8dee2aaSAndroid Build Coastguard Worker 70*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeWebGLContext = function(ctx) { 71*c8dee2aaSAndroid Build Coastguard Worker // Make sure we are pointing at the right WebGL context. 72*c8dee2aaSAndroid Build Coastguard Worker if (!this.setCurrentContext(ctx)) { 73*c8dee2aaSAndroid Build Coastguard Worker return null; 74*c8dee2aaSAndroid Build Coastguard Worker } 75*c8dee2aaSAndroid Build Coastguard Worker var grCtx = this._MakeGrContext(); 76*c8dee2aaSAndroid Build Coastguard Worker if (!grCtx) { 77*c8dee2aaSAndroid Build Coastguard Worker return null; 78*c8dee2aaSAndroid Build Coastguard Worker } 79*c8dee2aaSAndroid Build Coastguard Worker // This context is an index into the emscripten-provided GL wrapper. 80*c8dee2aaSAndroid Build Coastguard Worker grCtx._context = ctx; 81*c8dee2aaSAndroid Build Coastguard Worker var oldDelete = grCtx.delete.bind(grCtx); 82*c8dee2aaSAndroid Build Coastguard Worker // We need to make sure we are focusing on the correct webgl context 83*c8dee2aaSAndroid Build Coastguard Worker // when Skia cleans up the context. 84*c8dee2aaSAndroid Build Coastguard Worker grCtx['delete'] = function() { 85*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext(this._context); 86*c8dee2aaSAndroid Build Coastguard Worker oldDelete(); 87*c8dee2aaSAndroid Build Coastguard Worker }.bind(grCtx); 88*c8dee2aaSAndroid Build Coastguard Worker // Save this so it is easy to access (e.g. Image.readPixels) 89*c8dee2aaSAndroid Build Coastguard Worker GL.currentContext.grDirectContext = grCtx; 90*c8dee2aaSAndroid Build Coastguard Worker return grCtx; 91*c8dee2aaSAndroid Build Coastguard Worker }; 92*c8dee2aaSAndroid Build Coastguard Worker 93*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeGrContext = CanvasKit.MakeWebGLContext; 94*c8dee2aaSAndroid Build Coastguard Worker 95*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.GrDirectContext.prototype.getResourceCacheLimitBytes = function() { 96*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext(this._context); 97*c8dee2aaSAndroid Build Coastguard Worker this._getResourceCacheLimitBytes(); 98*c8dee2aaSAndroid Build Coastguard Worker }; 99*c8dee2aaSAndroid Build Coastguard Worker 100*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.GrDirectContext.prototype.getResourceCacheUsageBytes = function() { 101*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext(this._context); 102*c8dee2aaSAndroid Build Coastguard Worker this._getResourceCacheUsageBytes(); 103*c8dee2aaSAndroid Build Coastguard Worker }; 104*c8dee2aaSAndroid Build Coastguard Worker 105*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.GrDirectContext.prototype.releaseResourcesAndAbandonContext = function() { 106*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext(this._context); 107*c8dee2aaSAndroid Build Coastguard Worker this._releaseResourcesAndAbandonContext(); 108*c8dee2aaSAndroid Build Coastguard Worker }; 109*c8dee2aaSAndroid Build Coastguard Worker 110*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.GrDirectContext.prototype.setResourceCacheLimitBytes = function(maxResourceBytes) { 111*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext(this._context); 112*c8dee2aaSAndroid Build Coastguard Worker this._setResourceCacheLimitBytes(maxResourceBytes); 113*c8dee2aaSAndroid Build Coastguard Worker }; 114*c8dee2aaSAndroid Build Coastguard Worker 115*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeOnScreenGLSurface = function(grCtx, w, h, colorspace, sc, st) { 116*c8dee2aaSAndroid Build Coastguard Worker if (!this.setCurrentContext(grCtx._context)) { 117*c8dee2aaSAndroid Build Coastguard Worker return null; 118*c8dee2aaSAndroid Build Coastguard Worker } 119*c8dee2aaSAndroid Build Coastguard Worker var surface; 120*c8dee2aaSAndroid Build Coastguard Worker // zero is a valid value for sample count or stencil bits. 121*c8dee2aaSAndroid Build Coastguard Worker if (sc === undefined || st === undefined) { 122*c8dee2aaSAndroid Build Coastguard Worker surface = this._MakeOnScreenGLSurface(grCtx, w, h, colorspace); 123*c8dee2aaSAndroid Build Coastguard Worker } else { 124*c8dee2aaSAndroid Build Coastguard Worker surface = this._MakeOnScreenGLSurface(grCtx, w, h, colorspace, sc, st); 125*c8dee2aaSAndroid Build Coastguard Worker } 126*c8dee2aaSAndroid Build Coastguard Worker if (!surface) { 127*c8dee2aaSAndroid Build Coastguard Worker return null; 128*c8dee2aaSAndroid Build Coastguard Worker } 129*c8dee2aaSAndroid Build Coastguard Worker surface._context = grCtx._context; 130*c8dee2aaSAndroid Build Coastguard Worker return surface; 131*c8dee2aaSAndroid Build Coastguard Worker } 132*c8dee2aaSAndroid Build Coastguard Worker 133*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeRenderTarget = function() { 134*c8dee2aaSAndroid Build Coastguard Worker var grCtx = arguments[0]; 135*c8dee2aaSAndroid Build Coastguard Worker if (!this.setCurrentContext(grCtx._context)) { 136*c8dee2aaSAndroid Build Coastguard Worker return null; 137*c8dee2aaSAndroid Build Coastguard Worker } 138*c8dee2aaSAndroid Build Coastguard Worker var surface; 139*c8dee2aaSAndroid Build Coastguard Worker if (arguments.length === 3) { 140*c8dee2aaSAndroid Build Coastguard Worker surface = this._MakeRenderTargetWH(grCtx, arguments[1], arguments[2]); 141*c8dee2aaSAndroid Build Coastguard Worker if (!surface) { 142*c8dee2aaSAndroid Build Coastguard Worker return null; 143*c8dee2aaSAndroid Build Coastguard Worker } 144*c8dee2aaSAndroid Build Coastguard Worker } else if (arguments.length === 2) { 145*c8dee2aaSAndroid Build Coastguard Worker surface = this._MakeRenderTargetII(grCtx, arguments[1]); 146*c8dee2aaSAndroid Build Coastguard Worker if (!surface) { 147*c8dee2aaSAndroid Build Coastguard Worker return null; 148*c8dee2aaSAndroid Build Coastguard Worker } 149*c8dee2aaSAndroid Build Coastguard Worker } else { 150*c8dee2aaSAndroid Build Coastguard Worker Debug('Expected 2 or 3 params'); 151*c8dee2aaSAndroid Build Coastguard Worker return null; 152*c8dee2aaSAndroid Build Coastguard Worker } 153*c8dee2aaSAndroid Build Coastguard Worker surface._context = grCtx._context; 154*c8dee2aaSAndroid Build Coastguard Worker return surface; 155*c8dee2aaSAndroid Build Coastguard Worker } 156*c8dee2aaSAndroid Build Coastguard Worker 157*c8dee2aaSAndroid Build Coastguard Worker // idOrElement can be of types: 158*c8dee2aaSAndroid Build Coastguard Worker // - String - in which case it is interpreted as an id of a 159*c8dee2aaSAndroid Build Coastguard Worker // canvas element. 160*c8dee2aaSAndroid Build Coastguard Worker // - HTMLCanvasElement - in which the provided canvas element will 161*c8dee2aaSAndroid Build Coastguard Worker // be used directly. 162*c8dee2aaSAndroid Build Coastguard Worker // colorSpace - sk_sp<ColorSpace> - one of the supported color spaces: 163*c8dee2aaSAndroid Build Coastguard Worker // CanvasKit.ColorSpace.SRGB 164*c8dee2aaSAndroid Build Coastguard Worker // CanvasKit.ColorSpace.DISPLAY_P3 165*c8dee2aaSAndroid Build Coastguard Worker // CanvasKit.ColorSpace.ADOBE_RGB 166*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeWebGLCanvasSurface = function(idOrElement, colorSpace, attrs) { 167*c8dee2aaSAndroid Build Coastguard Worker colorSpace = colorSpace || null; 168*c8dee2aaSAndroid Build Coastguard Worker var canvas = idOrElement; 169*c8dee2aaSAndroid Build Coastguard Worker var isHTMLCanvas = typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement; 170*c8dee2aaSAndroid Build Coastguard Worker var isOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas; 171*c8dee2aaSAndroid Build Coastguard Worker if (!isHTMLCanvas && !isOffscreenCanvas) { 172*c8dee2aaSAndroid Build Coastguard Worker canvas = document.getElementById(idOrElement); 173*c8dee2aaSAndroid Build Coastguard Worker if (!canvas) { 174*c8dee2aaSAndroid Build Coastguard Worker throw 'Canvas with id ' + idOrElement + ' was not found'; 175*c8dee2aaSAndroid Build Coastguard Worker } 176*c8dee2aaSAndroid Build Coastguard Worker } 177*c8dee2aaSAndroid Build Coastguard Worker 178*c8dee2aaSAndroid Build Coastguard Worker var ctx = this.GetWebGLContext(canvas, attrs); 179*c8dee2aaSAndroid Build Coastguard Worker if (!ctx || ctx < 0) { 180*c8dee2aaSAndroid Build Coastguard Worker throw 'failed to create webgl context: err ' + ctx; 181*c8dee2aaSAndroid Build Coastguard Worker } 182*c8dee2aaSAndroid Build Coastguard Worker 183*c8dee2aaSAndroid Build Coastguard Worker var grcontext = this.MakeWebGLContext(ctx); 184*c8dee2aaSAndroid Build Coastguard Worker 185*c8dee2aaSAndroid Build Coastguard Worker // Note that canvas.width/height here is used because it gives the size of the buffer we're 186*c8dee2aaSAndroid Build Coastguard Worker // rendering into. This may not be the same size the element is displayed on the page, which 187*c8dee2aaSAndroid Build Coastguard Worker // controlled by css, and available in canvas.clientWidth/height. 188*c8dee2aaSAndroid Build Coastguard Worker var surface = this.MakeOnScreenGLSurface(grcontext, canvas.width, canvas.height, colorSpace); 189*c8dee2aaSAndroid Build Coastguard Worker if (!surface) { 190*c8dee2aaSAndroid Build Coastguard Worker Debug('falling back from GPU implementation to a SW based one'); 191*c8dee2aaSAndroid Build Coastguard Worker // we need to throw away the old canvas (which was locked to 192*c8dee2aaSAndroid Build Coastguard Worker // a webGL context) and create a new one so we can 193*c8dee2aaSAndroid Build Coastguard Worker var newCanvas = canvas.cloneNode(true); 194*c8dee2aaSAndroid Build Coastguard Worker var parent = canvas.parentNode; 195*c8dee2aaSAndroid Build Coastguard Worker parent.replaceChild(newCanvas, canvas); 196*c8dee2aaSAndroid Build Coastguard Worker // add a class so the user can detect that it was replaced. 197*c8dee2aaSAndroid Build Coastguard Worker newCanvas.classList.add('ck-replaced'); 198*c8dee2aaSAndroid Build Coastguard Worker 199*c8dee2aaSAndroid Build Coastguard Worker return CanvasKit.MakeSWCanvasSurface(newCanvas); 200*c8dee2aaSAndroid Build Coastguard Worker } 201*c8dee2aaSAndroid Build Coastguard Worker return surface; 202*c8dee2aaSAndroid Build Coastguard Worker }; 203*c8dee2aaSAndroid Build Coastguard Worker // Default to trying WebGL first. 204*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeCanvasSurface = CanvasKit.MakeWebGLCanvasSurface; 205*c8dee2aaSAndroid Build Coastguard Worker 206*c8dee2aaSAndroid Build Coastguard Worker function pushTexture(tex) { 207*c8dee2aaSAndroid Build Coastguard Worker // GL is an emscripten object that holds onto WebGL state. One item in that state is 208*c8dee2aaSAndroid Build Coastguard Worker // an array of textures, of which the index is the handle/id. We must call getNewId so 209*c8dee2aaSAndroid Build Coastguard Worker // the GL's tracking of textures is up to date and we do not accidentally use the same 210*c8dee2aaSAndroid Build Coastguard Worker // texture in two different places if Skia creates a texture. (e.g. skbug.com/12797) 211*c8dee2aaSAndroid Build Coastguard Worker var texHandle = GL.getNewId(GL.textures); 212*c8dee2aaSAndroid Build Coastguard Worker GL.textures[texHandle] = tex; 213*c8dee2aaSAndroid Build Coastguard Worker return texHandle 214*c8dee2aaSAndroid Build Coastguard Worker } 215*c8dee2aaSAndroid Build Coastguard Worker 216*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Surface.prototype.makeImageFromTexture = function(tex, info) { 217*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext(this._context); 218*c8dee2aaSAndroid Build Coastguard Worker var texHandle = pushTexture(tex); 219*c8dee2aaSAndroid Build Coastguard Worker var img = this._makeImageFromTexture(this._context, texHandle, info); 220*c8dee2aaSAndroid Build Coastguard Worker if (img) { 221*c8dee2aaSAndroid Build Coastguard Worker img._tex = texHandle; 222*c8dee2aaSAndroid Build Coastguard Worker } 223*c8dee2aaSAndroid Build Coastguard Worker return img; 224*c8dee2aaSAndroid Build Coastguard Worker }; 225*c8dee2aaSAndroid Build Coastguard Worker 226*c8dee2aaSAndroid Build Coastguard Worker // We try to find the natural media type (for <img> and <video>), display* for 227*c8dee2aaSAndroid Build Coastguard Worker // https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame and then fall back to 228*c8dee2aaSAndroid Build Coastguard Worker // the height and width (to cover <canvas>, ImageBitmap or ImageData). 229*c8dee2aaSAndroid Build Coastguard Worker function getHeight(src) { 230*c8dee2aaSAndroid Build Coastguard Worker return src['naturalHeight'] || src['videoHeight'] || src['displayHeight'] || src['height']; 231*c8dee2aaSAndroid Build Coastguard Worker } 232*c8dee2aaSAndroid Build Coastguard Worker 233*c8dee2aaSAndroid Build Coastguard Worker function getWidth(src) { 234*c8dee2aaSAndroid Build Coastguard Worker return src['naturalWidth'] || src['videoWidth'] || src['displayWidth'] || src['width']; 235*c8dee2aaSAndroid Build Coastguard Worker } 236*c8dee2aaSAndroid Build Coastguard Worker 237*c8dee2aaSAndroid Build Coastguard Worker function setupTexture(glCtx, newTex, imageInfo, srcIsPremul) { 238*c8dee2aaSAndroid Build Coastguard Worker glCtx.bindTexture(glCtx.TEXTURE_2D, newTex); 239*c8dee2aaSAndroid Build Coastguard Worker // See https://github.com/flutter/flutter/issues/106433#issuecomment-1169102945 240*c8dee2aaSAndroid Build Coastguard Worker // for an example of what can happen if we do not set this. 241*c8dee2aaSAndroid Build Coastguard Worker if (!srcIsPremul && imageInfo['alphaType'] === CanvasKit.AlphaType.Premul) { 242*c8dee2aaSAndroid Build Coastguard Worker glCtx.pixelStorei(glCtx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); 243*c8dee2aaSAndroid Build Coastguard Worker } 244*c8dee2aaSAndroid Build Coastguard Worker return newTex; 245*c8dee2aaSAndroid Build Coastguard Worker } 246*c8dee2aaSAndroid Build Coastguard Worker 247*c8dee2aaSAndroid Build Coastguard Worker function resetTexture(glCtx, imageInfo, srcIsPremul) { 248*c8dee2aaSAndroid Build Coastguard Worker // If we set this earlier, we want to unset it now. 249*c8dee2aaSAndroid Build Coastguard Worker if (!srcIsPremul && imageInfo['alphaType'] === CanvasKit.AlphaType.Premul) { 250*c8dee2aaSAndroid Build Coastguard Worker glCtx.pixelStorei(glCtx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 251*c8dee2aaSAndroid Build Coastguard Worker } 252*c8dee2aaSAndroid Build Coastguard Worker glCtx.bindTexture(glCtx.TEXTURE_2D, null); 253*c8dee2aaSAndroid Build Coastguard Worker } 254*c8dee2aaSAndroid Build Coastguard Worker 255*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Surface.prototype.makeImageFromTextureSource = function(src, info, srcIsPremul) { 256*c8dee2aaSAndroid Build Coastguard Worker if (!info) { 257*c8dee2aaSAndroid Build Coastguard Worker // If the user didn't specify the image info, use some sensible defaults. 258*c8dee2aaSAndroid Build Coastguard Worker info = { 259*c8dee2aaSAndroid Build Coastguard Worker 'height': getHeight(src), 260*c8dee2aaSAndroid Build Coastguard Worker 'width': getWidth(src), 261*c8dee2aaSAndroid Build Coastguard Worker 'colorType': CanvasKit.ColorType.RGBA_8888, 262*c8dee2aaSAndroid Build Coastguard Worker 'alphaType': srcIsPremul ? CanvasKit.AlphaType.Premul: CanvasKit.AlphaType.Unpremul, 263*c8dee2aaSAndroid Build Coastguard Worker }; 264*c8dee2aaSAndroid Build Coastguard Worker } 265*c8dee2aaSAndroid Build Coastguard Worker if (!info['colorSpace']) { 266*c8dee2aaSAndroid Build Coastguard Worker info['colorSpace'] = CanvasKit.ColorSpace.SRGB; 267*c8dee2aaSAndroid Build Coastguard Worker } 268*c8dee2aaSAndroid Build Coastguard Worker if (info['colorType'] !== CanvasKit.ColorType.RGBA_8888) { 269*c8dee2aaSAndroid Build Coastguard Worker Debug('colorType currently has no impact on makeImageFromTextureSource'); 270*c8dee2aaSAndroid Build Coastguard Worker } 271*c8dee2aaSAndroid Build Coastguard Worker 272*c8dee2aaSAndroid Build Coastguard Worker // We want to be pointing at the context associated with this surface. 273*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext(this._context); 274*c8dee2aaSAndroid Build Coastguard Worker var glCtx = GL.currentContext.GLctx; 275*c8dee2aaSAndroid Build Coastguard Worker var newTex = setupTexture(glCtx, glCtx.createTexture(), info, srcIsPremul); 276*c8dee2aaSAndroid Build Coastguard Worker if (GL.currentContext.version === 2) { 277*c8dee2aaSAndroid Build Coastguard Worker glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, info['width'], info['height'], 0, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 278*c8dee2aaSAndroid Build Coastguard Worker } else { 279*c8dee2aaSAndroid Build Coastguard Worker glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 280*c8dee2aaSAndroid Build Coastguard Worker } 281*c8dee2aaSAndroid Build Coastguard Worker resetTexture(glCtx, info); 282*c8dee2aaSAndroid Build Coastguard Worker this._resetContext(); 283*c8dee2aaSAndroid Build Coastguard Worker return this.makeImageFromTexture(newTex, info); 284*c8dee2aaSAndroid Build Coastguard Worker }; 285*c8dee2aaSAndroid Build Coastguard Worker 286*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Surface.prototype.updateTextureFromSource = function(img, src, srcIsPremul) { 287*c8dee2aaSAndroid Build Coastguard Worker if (!img._tex) { 288*c8dee2aaSAndroid Build Coastguard Worker Debug('Image is not backed by a user-provided texture'); 289*c8dee2aaSAndroid Build Coastguard Worker return; 290*c8dee2aaSAndroid Build Coastguard Worker } 291*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext(this._context); 292*c8dee2aaSAndroid Build Coastguard Worker var ii = img.getImageInfo(); 293*c8dee2aaSAndroid Build Coastguard Worker var glCtx = GL.currentContext.GLctx; 294*c8dee2aaSAndroid Build Coastguard Worker // Copy the contents of src over the texture associated with this image. 295*c8dee2aaSAndroid Build Coastguard Worker var tex = setupTexture(glCtx, GL.textures[img._tex], ii, srcIsPremul); 296*c8dee2aaSAndroid Build Coastguard Worker if (GL.currentContext.version === 2) { 297*c8dee2aaSAndroid Build Coastguard Worker glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, getWidth(src), getHeight(src), 0, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 298*c8dee2aaSAndroid Build Coastguard Worker } else { 299*c8dee2aaSAndroid Build Coastguard Worker glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 300*c8dee2aaSAndroid Build Coastguard Worker } 301*c8dee2aaSAndroid Build Coastguard Worker resetTexture(glCtx, ii, srcIsPremul); 302*c8dee2aaSAndroid Build Coastguard Worker // Tell Skia we messed with the currently bound texture. 303*c8dee2aaSAndroid Build Coastguard Worker this._resetContext(); 304*c8dee2aaSAndroid Build Coastguard Worker // Create a new texture entry and put null into the old slot. This keeps our texture alive, 305*c8dee2aaSAndroid Build Coastguard Worker // otherwise it will be deleted when we delete the old Image. 306*c8dee2aaSAndroid Build Coastguard Worker GL.textures[img._tex] = null; 307*c8dee2aaSAndroid Build Coastguard Worker img._tex = pushTexture(tex); 308*c8dee2aaSAndroid Build Coastguard Worker ii['colorSpace'] = img.getColorSpace(); 309*c8dee2aaSAndroid Build Coastguard Worker // Skia may cache parts of the image, and some places assume images are immutable. In order 310*c8dee2aaSAndroid Build Coastguard Worker // to make things work, we create a new SkImage based on the same texture as the old image. 311*c8dee2aaSAndroid Build Coastguard Worker var newImg = this._makeImageFromTexture(this._context, img._tex, ii); 312*c8dee2aaSAndroid Build Coastguard Worker // To make things more ergonomic for the user, we change passed in img object to refer 313*c8dee2aaSAndroid Build Coastguard Worker // to the new image and clean up the old SkImage object. This has the effect of updating 314*c8dee2aaSAndroid Build Coastguard Worker // the Image (from the user's side of things), because they shouldn't be caring about what 315*c8dee2aaSAndroid Build Coastguard Worker // part of WASM memory we are pointing to. 316*c8dee2aaSAndroid Build Coastguard Worker // The $$ part is provided by emscripten's embind, so this could break if they change 317*c8dee2aaSAndroid Build Coastguard Worker // things on us. 318*c8dee2aaSAndroid Build Coastguard Worker // https://github.com/emscripten-core/emscripten/blob/a65d70c809f077542649c60097787e1c7460ced6/src/embind/embind.js 319*c8dee2aaSAndroid Build Coastguard Worker // They do not do anything special to keep closure from minifying things and neither do we. 320*c8dee2aaSAndroid Build Coastguard Worker var oldPtr = img.$$.ptr; 321*c8dee2aaSAndroid Build Coastguard Worker var oldSmartPtr = img.$$.smartPtr; 322*c8dee2aaSAndroid Build Coastguard Worker img.$$.ptr = newImg.$$.ptr; 323*c8dee2aaSAndroid Build Coastguard Worker img.$$.smartPtr = newImg.$$.smartPtr; 324*c8dee2aaSAndroid Build Coastguard Worker // We want to clean up the previous image, so we swap out the pointers and call delete on it 325*c8dee2aaSAndroid Build Coastguard Worker // which should have that effect. 326*c8dee2aaSAndroid Build Coastguard Worker newImg.$$.ptr = oldPtr; 327*c8dee2aaSAndroid Build Coastguard Worker newImg.$$.smartPtr = oldSmartPtr; 328*c8dee2aaSAndroid Build Coastguard Worker newImg.delete(); 329*c8dee2aaSAndroid Build Coastguard Worker // Clean up the colorspace that we used. 330*c8dee2aaSAndroid Build Coastguard Worker ii['colorSpace'].delete(); 331*c8dee2aaSAndroid Build Coastguard Worker } 332*c8dee2aaSAndroid Build Coastguard Worker 333*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.MakeLazyImageFromTextureSource = function(src, info, srcIsPremul) { 334*c8dee2aaSAndroid Build Coastguard Worker if (!info) { 335*c8dee2aaSAndroid Build Coastguard Worker info = { 336*c8dee2aaSAndroid Build Coastguard Worker 'height': getHeight(src), 337*c8dee2aaSAndroid Build Coastguard Worker 'width': getWidth(src), 338*c8dee2aaSAndroid Build Coastguard Worker 'colorType': CanvasKit.ColorType.RGBA_8888, 339*c8dee2aaSAndroid Build Coastguard Worker 'alphaType': srcIsPremul ? CanvasKit.AlphaType.Premul : CanvasKit.AlphaType.Unpremul, 340*c8dee2aaSAndroid Build Coastguard Worker }; 341*c8dee2aaSAndroid Build Coastguard Worker } 342*c8dee2aaSAndroid Build Coastguard Worker if (!info['colorSpace']) { 343*c8dee2aaSAndroid Build Coastguard Worker info['colorSpace'] = CanvasKit.ColorSpace.SRGB; 344*c8dee2aaSAndroid Build Coastguard Worker } 345*c8dee2aaSAndroid Build Coastguard Worker if (info['colorType'] !== CanvasKit.ColorType.RGBA_8888) { 346*c8dee2aaSAndroid Build Coastguard Worker Debug('colorType currently has no impact on MakeLazyImageFromTextureSource'); 347*c8dee2aaSAndroid Build Coastguard Worker } 348*c8dee2aaSAndroid Build Coastguard Worker 349*c8dee2aaSAndroid Build Coastguard Worker var callbackObj = { 350*c8dee2aaSAndroid Build Coastguard Worker 'makeTexture': function() { 351*c8dee2aaSAndroid Build Coastguard Worker // This callback function will make a texture on the current drawing surface (i.e. 352*c8dee2aaSAndroid Build Coastguard Worker // the current WebGL context). It assumes that Skia is just about to draw the texture 353*c8dee2aaSAndroid Build Coastguard Worker // to the desired surface, and thus the currentContext is the correct one. 354*c8dee2aaSAndroid Build Coastguard Worker // This is a lot easier than needing to pass the surface handle from the C++ side here. 355*c8dee2aaSAndroid Build Coastguard Worker var ctx = GL.currentContext; 356*c8dee2aaSAndroid Build Coastguard Worker var glCtx = ctx.GLctx; 357*c8dee2aaSAndroid Build Coastguard Worker var newTex = setupTexture(glCtx, glCtx.createTexture(), info, srcIsPremul); 358*c8dee2aaSAndroid Build Coastguard Worker if (ctx.version === 2) { 359*c8dee2aaSAndroid Build Coastguard Worker glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, info['width'], info['height'], 0, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 360*c8dee2aaSAndroid Build Coastguard Worker } else { 361*c8dee2aaSAndroid Build Coastguard Worker glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 362*c8dee2aaSAndroid Build Coastguard Worker } 363*c8dee2aaSAndroid Build Coastguard Worker resetTexture(glCtx, info, srcIsPremul); 364*c8dee2aaSAndroid Build Coastguard Worker return pushTexture(newTex); 365*c8dee2aaSAndroid Build Coastguard Worker }, 366*c8dee2aaSAndroid Build Coastguard Worker 'freeSrc': function() { 367*c8dee2aaSAndroid Build Coastguard Worker // This callback will be executed whenever the returned image is deleted. This gives 368*c8dee2aaSAndroid Build Coastguard Worker // us a chance to free up the src (which we now own). Generally, there's nothing 369*c8dee2aaSAndroid Build Coastguard Worker // we need to do (we can let JS garbage collection do its thing). The one exception 370*c8dee2aaSAndroid Build Coastguard Worker // is for https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame, which we should 371*c8dee2aaSAndroid Build Coastguard Worker // close when we are done. 372*c8dee2aaSAndroid Build Coastguard Worker }, 373*c8dee2aaSAndroid Build Coastguard Worker } 374*c8dee2aaSAndroid Build Coastguard Worker if (src.constructor.name === 'VideoFrame') { 375*c8dee2aaSAndroid Build Coastguard Worker callbackObj['freeSrc'] = function() { 376*c8dee2aaSAndroid Build Coastguard Worker src.close(); 377*c8dee2aaSAndroid Build Coastguard Worker } 378*c8dee2aaSAndroid Build Coastguard Worker } 379*c8dee2aaSAndroid Build Coastguard Worker return CanvasKit.Image._makeFromGenerator(info, callbackObj); 380*c8dee2aaSAndroid Build Coastguard Worker } 381*c8dee2aaSAndroid Build Coastguard Worker 382*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.setCurrentContext = function(ctx) { 383*c8dee2aaSAndroid Build Coastguard Worker if (!ctx) { 384*c8dee2aaSAndroid Build Coastguard Worker return false; 385*c8dee2aaSAndroid Build Coastguard Worker } 386*c8dee2aaSAndroid Build Coastguard Worker return GL.makeContextCurrent(ctx); 387*c8dee2aaSAndroid Build Coastguard Worker }; 388*c8dee2aaSAndroid Build Coastguard Worker 389*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.getCurrentGrDirectContext = function() { 390*c8dee2aaSAndroid Build Coastguard Worker if (GL.currentContext && GL.currentContext.grDirectContext && 391*c8dee2aaSAndroid Build Coastguard Worker !GL.currentContext.grDirectContext['isDeleted']()) { 392*c8dee2aaSAndroid Build Coastguard Worker return GL.currentContext.grDirectContext; 393*c8dee2aaSAndroid Build Coastguard Worker } 394*c8dee2aaSAndroid Build Coastguard Worker return null; 395*c8dee2aaSAndroid Build Coastguard Worker }; 396*c8dee2aaSAndroid Build Coastguard Worker 397*c8dee2aaSAndroid Build Coastguard Worker }); 398*c8dee2aaSAndroid Build Coastguard Worker}(Module)); // When this file is loaded in, the high level object is "Module"; 399