1// Adds compile-time JS functions to augment the CanvasKit interface. 2// Specifically, anything that should only be on the GPU version of canvaskit. 3// Functions in this file are supplemented by cpu.js. 4(function(CanvasKit){ 5 CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; 6 CanvasKit._extraInitializations.push(function() { 7 function get(obj, attr, defaultValue) { 8 if (obj && obj.hasOwnProperty(attr)) { 9 return obj[attr]; 10 } 11 return defaultValue; 12 } 13 14 CanvasKit.GetWebGLContext = function(canvas, attrs) { 15 if (!canvas) { 16 throw 'null canvas passed into makeWebGLContext'; 17 } 18 var contextAttributes = { 19 'alpha': get(attrs, 'alpha', 1), 20 'depth': get(attrs, 'depth', 1), 21 'stencil': get(attrs, 'stencil', 8), 22 'antialias': get(attrs, 'antialias', 0), 23 'premultipliedAlpha': get(attrs, 'premultipliedAlpha', 1), 24 'preserveDrawingBuffer': get(attrs, 'preserveDrawingBuffer', 0), 25 'preferLowPowerToHighPerformance': get(attrs, 'preferLowPowerToHighPerformance', 0), 26 'failIfMajorPerformanceCaveat': get(attrs, 'failIfMajorPerformanceCaveat', 0), 27 'enableExtensionsByDefault': get(attrs, 'enableExtensionsByDefault', 1), 28 'explicitSwapControl': get(attrs, 'explicitSwapControl', 0), 29 'renderViaOffscreenBackBuffer': get(attrs, 'renderViaOffscreenBackBuffer', 0), 30 }; 31 32 if (attrs && attrs['majorVersion']) { 33 contextAttributes['majorVersion'] = attrs['majorVersion'] 34 } else { 35 // Default to WebGL 2 if available and not specified. 36 contextAttributes['majorVersion'] = (typeof WebGL2RenderingContext !== 'undefined') ? 2 : 1; 37 } 38 39 // This check is from the emscripten version 40 if (contextAttributes['explicitSwapControl']) { 41 throw 'explicitSwapControl is not supported'; 42 } 43 // Creates a WebGL context and sets it to be the current context. 44 // These functions are defined in emscripten's library_webgl.js 45 var handle = GL.createContext(canvas, contextAttributes); 46 if (!handle) { 47 return 0; 48 } 49 GL.makeContextCurrent(handle); 50 return handle; 51 }; 52 53 CanvasKit.deleteContext = function(handle) { 54 GL.deleteContext(handle); 55 }; 56 57 CanvasKit._setTextureCleanup({ 58 'deleteTexture': function(webglHandle, texHandle) { 59 var tex = GL.textures[texHandle]; 60 if (tex) { 61 GL.getContext(webglHandle).GLctx.deleteTexture(tex); 62 } 63 GL.textures[texHandle] = null; 64 }, 65 }); 66 67 CanvasKit.MakeGrContext = function(ctx) { 68 // Make sure we are pointing at the right WebGL context. 69 if (!this.setCurrentContext(ctx)) { 70 return null; 71 } 72 var grCtx = this._MakeGrContext(); 73 if (!grCtx) { 74 return null; 75 } 76 // This context is an index into the emscripten-provided GL wrapper. 77 grCtx._context = ctx; 78 return grCtx; 79 } 80 81 CanvasKit.MakeOnScreenGLSurface = function(grCtx, w, h, colorspace) { 82 var surface = this._MakeOnScreenGLSurface(grCtx, w, h, colorspace); 83 if (!surface) { 84 return null; 85 } 86 surface._context = grCtx._context; 87 return surface; 88 } 89 90 CanvasKit.MakeRenderTarget = function(grCtx, w, h) { 91 var surface = this._MakeRenderTargetWH(grCtx, w, h); 92 if (!surface) { 93 return null; 94 } 95 surface._context = grCtx._context; 96 return surface; 97 } 98 99 CanvasKit.MakeRenderTarget = function(grCtx, imageInfo) { 100 var surface = this._MakeRenderTargetII(grCtx, imageInfo); 101 if (!surface) { 102 return null; 103 } 104 surface._context = grCtx._context; 105 return surface; 106 } 107 108 // idOrElement can be of types: 109 // - String - in which case it is interpreted as an id of a 110 // canvas element. 111 // - HTMLCanvasElement - in which the provided canvas element will 112 // be used directly. 113 // colorSpace - sk_sp<ColorSpace> - one of the supported color spaces: 114 // CanvasKit.ColorSpace.SRGB 115 // CanvasKit.ColorSpace.DISPLAY_P3 116 // CanvasKit.ColorSpace.ADOBE_RGB 117 CanvasKit.MakeWebGLCanvasSurface = function(idOrElement, colorSpace, attrs) { 118 colorSpace = colorSpace || null; 119 var canvas = idOrElement; 120 var isHTMLCanvas = typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement; 121 var isOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas; 122 if (!isHTMLCanvas && !isOffscreenCanvas) { 123 canvas = document.getElementById(idOrElement); 124 if (!canvas) { 125 throw 'Canvas with id ' + idOrElement + ' was not found'; 126 } 127 } 128 129 var ctx = this.GetWebGLContext(canvas, attrs); 130 if (!ctx || ctx < 0) { 131 throw 'failed to create webgl context: err ' + ctx; 132 } 133 134 var grcontext = this.MakeGrContext(ctx); 135 136 // Note that canvas.width/height here is used because it gives the size of the buffer we're 137 // rendering into. This may not be the same size the element is displayed on the page, which 138 // constrolled by css, and available in canvas.clientWidth/height. 139 var surface = this.MakeOnScreenGLSurface(grcontext, canvas.width, canvas.height, colorSpace); 140 if (!surface) { 141 Debug('falling back from GPU implementation to a SW based one'); 142 // we need to throw away the old canvas (which was locked to 143 // a webGL context) and create a new one so we can 144 var newCanvas = canvas.cloneNode(true); 145 var parent = canvas.parentNode; 146 parent.replaceChild(newCanvas, canvas); 147 // add a class so the user can detect that it was replaced. 148 newCanvas.classList.add('ck-replaced'); 149 150 return CanvasKit.MakeSWCanvasSurface(newCanvas); 151 } 152 return surface; 153 }; 154 // Default to trying WebGL first. 155 CanvasKit.MakeCanvasSurface = CanvasKit.MakeWebGLCanvasSurface; 156 157 function pushTexture(tex) { 158 // GL is an emscripten object that holds onto WebGL state. One item in that state is 159 // an array of textures, of which the index is the handle/id. 160 var texHandle = GL.textures.length; 161 if (!texHandle) { 162 // If our texture handle is 0, Skia interprets that as an invalid texture id. 163 // As a special case, we push a null texture there so the first texture has id 1. 164 GL.textures.push(null); 165 texHandle = 1; 166 } 167 GL.textures.push(tex); 168 return texHandle 169 } 170 171 CanvasKit.Surface.prototype.makeImageFromTexture = function(tex, info) { 172 CanvasKit.setCurrentContext(this._context); 173 var texHandle = pushTexture(tex); 174 return this._makeImageFromTexture(this._context, texHandle, info); 175 }; 176 177 // We try to find the natural media type (for <img> and <video>), display* for 178 // https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame and then fall back to 179 // the height and width (to cover <canvas>, ImageBitmap or ImageData). 180 function getHeight(src) { 181 return src['naturalHeight'] || src['videoHeight'] || src['displayHeight'] || src['height']; 182 } 183 184 function getWidth(src) { 185 return src['naturalWidth'] || src['videoWidth'] || src['displayWidth'] || src['width']; 186 } 187 188 CanvasKit.Surface.prototype.makeImageFromTextureSource = function(src, info) { 189 if (!info) { 190 info = { 191 'height': getHeight(src), 192 'width': getWidth(src), 193 'colorType': CanvasKit.ColorType.RGBA_8888, 194 'alphaType': CanvasKit.AlphaType.Unpremul, 195 }; 196 } 197 if (!info['colorSpace']) { 198 info['colorSpace'] = CanvasKit.ColorSpace.SRGB; 199 } 200 if (info['colorType'] !== CanvasKit.ColorType.RGBA_8888) { 201 Debug('colorType currently has no impact on makeImageFromTextureSource'); 202 } 203 204 // We want to be pointing at the context associated with this surface. 205 CanvasKit.setCurrentContext(this._context); 206 var glCtx = GL.currentContext.GLctx; 207 var newTex = glCtx.createTexture(); 208 glCtx.bindTexture(glCtx.TEXTURE_2D, newTex); 209 if (GL.currentContext.version === 2) { 210 glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, info['width'], info['height'], 0, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 211 } else { 212 glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 213 } 214 glCtx.bindTexture(glCtx.TEXTURE_2D, null); 215 return this.makeImageFromTexture(newTex, info); 216 }; 217 218 CanvasKit.MakeLazyImageFromTextureSource = function(src, info) { 219 if (!info) { 220 info = { 221 'height': getHeight(src), 222 'width': getWidth(src), 223 'colorType': CanvasKit.ColorType.RGBA_8888, 224 'alphaType': CanvasKit.AlphaType.Unpremul, 225 }; 226 } 227 if (!info['colorSpace']) { 228 info['colorSpace'] = CanvasKit.ColorSpace.SRGB; 229 } 230 if (info['colorType'] !== CanvasKit.ColorType.RGBA_8888) { 231 Debug('colorType currently has no impact on MakeLazyImageFromTextureSource'); 232 } 233 234 var callbackObj = { 235 'makeTexture': function() { 236 // This callback function will make a texture on the current drawing surface (i.e. 237 // the current WebGL context). It assumes that Skia is just about to draw the texture 238 // to the desired surface, and thus the currentContext is the correct one. 239 // This is a lot easier than needing to pass the surface handle from the C++ side here. 240 var ctx = GL.currentContext; 241 var glCtx = ctx.GLctx; 242 var newTex = glCtx.createTexture(); 243 glCtx.bindTexture(glCtx.TEXTURE_2D, newTex); 244 if (ctx.version === 2) { 245 glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, info['width'], info['height'], 0, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 246 } else { 247 glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); 248 } 249 glCtx.bindTexture(glCtx.TEXTURE_2D, null); 250 return pushTexture(newTex); 251 }, 252 'freeSrc': function() { 253 // This callback will be executed whenever the returned image is deleted. This gives 254 // us a chance to free up the src (which we now own). Generally, there's nothing 255 // we need to do (we can let JS garbage collection do its thing). The one exception 256 // is for https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame, which we should 257 // close when we are done. 258 }, 259 } 260 if (src.constructor.name === 'VideoFrame') { 261 callbackObj['freeSrc'] = function() { 262 src.close(); 263 } 264 } 265 return CanvasKit.Image._makeFromGenerator(info, callbackObj); 266 } 267 268 CanvasKit.setCurrentContext = function(ctx) { 269 if (!ctx) { 270 return false; 271 } 272 return GL.makeContextCurrent(ctx); 273 }; 274 }); 275}(Module)); // When this file is loaded in, the high level object is "Module"; 276