| // Adds compile-time JS functions to augment the CanvasKit interface. |
| // Specifically, anything that should only be on the GPU version of canvaskit. |
| // Functions in this file are supplemented by cpu.js. |
| (function(CanvasKit){ |
| CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; |
| CanvasKit._extraInitializations.push(function() { |
| function get(obj, attr, defaultValue) { |
| if (obj && obj.hasOwnProperty(attr)) { |
| return obj[attr]; |
| } |
| return defaultValue; |
| } |
| |
| CanvasKit.GetWebGLContext = function(canvas, attrs) { |
| if (!canvas) { |
| throw 'null canvas passed into makeWebGLContext'; |
| } |
| var contextAttributes = { |
| 'alpha': get(attrs, 'alpha', 1), |
| 'depth': get(attrs, 'depth', 1), |
| 'stencil': get(attrs, 'stencil', 8), |
| 'antialias': get(attrs, 'antialias', 0), |
| 'premultipliedAlpha': get(attrs, 'premultipliedAlpha', 1), |
| 'preserveDrawingBuffer': get(attrs, 'preserveDrawingBuffer', 0), |
| 'preferLowPowerToHighPerformance': get(attrs, 'preferLowPowerToHighPerformance', 0), |
| 'failIfMajorPerformanceCaveat': get(attrs, 'failIfMajorPerformanceCaveat', 0), |
| 'enableExtensionsByDefault': get(attrs, 'enableExtensionsByDefault', 1), |
| 'explicitSwapControl': get(attrs, 'explicitSwapControl', 0), |
| 'renderViaOffscreenBackBuffer': get(attrs, 'renderViaOffscreenBackBuffer', 0), |
| }; |
| |
| if (attrs && attrs['majorVersion']) { |
| contextAttributes['majorVersion'] = attrs['majorVersion'] |
| } else { |
| // Default to WebGL 2 if available and not specified. |
| contextAttributes['majorVersion'] = (typeof WebGL2RenderingContext !== 'undefined') ? 2 : 1; |
| } |
| |
| // This check is from the emscripten version |
| if (contextAttributes['explicitSwapControl']) { |
| throw 'explicitSwapControl is not supported'; |
| } |
| // Creates a WebGL context and sets it to be the current context. |
| // These functions are defined in emscripten's library_webgl.js |
| var handle = GL.createContext(canvas, contextAttributes); |
| if (!handle) { |
| return 0; |
| } |
| GL.makeContextCurrent(handle); |
| return handle; |
| }; |
| |
| CanvasKit.deleteContext = function(handle) { |
| GL.deleteContext(handle); |
| }; |
| |
| CanvasKit._setTextureCleanup({ |
| 'deleteTexture': function(webglHandle, texHandle) { |
| var tex = GL.textures[texHandle]; |
| if (tex) { |
| GL.getContext(webglHandle).GLctx.deleteTexture(tex); |
| } |
| GL.textures[texHandle] = null; |
| }, |
| }); |
| |
| CanvasKit.MakeGrContext = function(ctx) { |
| // Make sure we are pointing at the right WebGL context. |
| if (!this.setCurrentContext(ctx)) { |
| return null; |
| } |
| var grCtx = this._MakeGrContext(); |
| if (!grCtx) { |
| return null; |
| } |
| // This context is an index into the emscripten-provided GL wrapper. |
| grCtx._context = ctx; |
| return grCtx; |
| } |
| |
| CanvasKit.MakeOnScreenGLSurface = function(grCtx, w, h, colorspace) { |
| if (!this.setCurrentContext(grCtx._context)) { |
| return null; |
| } |
| var surface = this._MakeOnScreenGLSurface(grCtx, w, h, colorspace); |
| if (!surface) { |
| return null; |
| } |
| surface._context = grCtx._context; |
| return surface; |
| } |
| |
| CanvasKit.MakeRenderTarget = function() { |
| var grCtx = arguments[0]; |
| if (!this.setCurrentContext(grCtx._context)) { |
| return null; |
| } |
| var surface; |
| if (arguments.length === 3) { |
| surface = this._MakeRenderTargetWH(grCtx, arguments[1], arguments[2]); |
| if (!surface) { |
| return null; |
| } |
| } else if (arguments.length === 2) { |
| surface = this._MakeRenderTargetII(grCtx, arguments[1]); |
| if (!surface) { |
| return null; |
| } |
| } else { |
| Debug('Expected 2 or 3 params'); |
| return null; |
| } |
| surface._context = grCtx._context; |
| return surface; |
| } |
| |
| // idOrElement can be of types: |
| // - String - in which case it is interpreted as an id of a |
| // canvas element. |
| // - HTMLCanvasElement - in which the provided canvas element will |
| // be used directly. |
| // colorSpace - sk_sp<ColorSpace> - one of the supported color spaces: |
| // CanvasKit.ColorSpace.SRGB |
| // CanvasKit.ColorSpace.DISPLAY_P3 |
| // CanvasKit.ColorSpace.ADOBE_RGB |
| CanvasKit.MakeWebGLCanvasSurface = function(idOrElement, colorSpace, attrs) { |
| colorSpace = colorSpace || null; |
| var canvas = idOrElement; |
| var isHTMLCanvas = typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement; |
| var isOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas; |
| if (!isHTMLCanvas && !isOffscreenCanvas) { |
| canvas = document.getElementById(idOrElement); |
| if (!canvas) { |
| throw 'Canvas with id ' + idOrElement + ' was not found'; |
| } |
| } |
| |
| var ctx = this.GetWebGLContext(canvas, attrs); |
| if (!ctx || ctx < 0) { |
| throw 'failed to create webgl context: err ' + ctx; |
| } |
| |
| var grcontext = this.MakeGrContext(ctx); |
| |
| // Note that canvas.width/height here is used because it gives the size of the buffer we're |
| // rendering into. This may not be the same size the element is displayed on the page, which |
| // constrolled by css, and available in canvas.clientWidth/height. |
| var surface = this.MakeOnScreenGLSurface(grcontext, canvas.width, canvas.height, colorSpace); |
| if (!surface) { |
| Debug('falling back from GPU implementation to a SW based one'); |
| // we need to throw away the old canvas (which was locked to |
| // a webGL context) and create a new one so we can |
| var newCanvas = canvas.cloneNode(true); |
| var parent = canvas.parentNode; |
| parent.replaceChild(newCanvas, canvas); |
| // add a class so the user can detect that it was replaced. |
| newCanvas.classList.add('ck-replaced'); |
| |
| return CanvasKit.MakeSWCanvasSurface(newCanvas); |
| } |
| return surface; |
| }; |
| // Default to trying WebGL first. |
| CanvasKit.MakeCanvasSurface = CanvasKit.MakeWebGLCanvasSurface; |
| |
| function pushTexture(tex) { |
| // GL is an emscripten object that holds onto WebGL state. One item in that state is |
| // an array of textures, of which the index is the handle/id. We must call getNewId so |
| // the GL's tracking of textures is up to date and we do not accidentally use the same |
| // texture in two different places if Skia creates a texture. (e.g. skbug.com/12797) |
| var texHandle = GL.getNewId(GL.textures); |
| GL.textures[texHandle] = tex; |
| return texHandle |
| } |
| |
| CanvasKit.Surface.prototype.makeImageFromTexture = function(tex, info) { |
| CanvasKit.setCurrentContext(this._context); |
| var texHandle = pushTexture(tex); |
| var img = this._makeImageFromTexture(this._context, texHandle, info); |
| if (img) { |
| img._tex = texHandle; |
| } |
| return img; |
| }; |
| |
| // We try to find the natural media type (for <img> and <video>), display* for |
| // https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame and then fall back to |
| // the height and width (to cover <canvas>, ImageBitmap or ImageData). |
| function getHeight(src) { |
| return src['naturalHeight'] || src['videoHeight'] || src['displayHeight'] || src['height']; |
| } |
| |
| function getWidth(src) { |
| return src['naturalWidth'] || src['videoWidth'] || src['displayWidth'] || src['width']; |
| } |
| |
| CanvasKit.Surface.prototype.makeImageFromTextureSource = function(src, info) { |
| if (!info) { |
| info = { |
| 'height': getHeight(src), |
| 'width': getWidth(src), |
| 'colorType': CanvasKit.ColorType.RGBA_8888, |
| 'alphaType': CanvasKit.AlphaType.Unpremul, |
| }; |
| } |
| if (!info['colorSpace']) { |
| info['colorSpace'] = CanvasKit.ColorSpace.SRGB; |
| } |
| if (info['colorType'] !== CanvasKit.ColorType.RGBA_8888) { |
| Debug('colorType currently has no impact on makeImageFromTextureSource'); |
| } |
| |
| // We want to be pointing at the context associated with this surface. |
| CanvasKit.setCurrentContext(this._context); |
| var glCtx = GL.currentContext.GLctx; |
| var newTex = glCtx.createTexture(); |
| glCtx.bindTexture(glCtx.TEXTURE_2D, newTex); |
| if (GL.currentContext.version === 2) { |
| glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, info['width'], info['height'], 0, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); |
| } else { |
| glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); |
| } |
| glCtx.bindTexture(glCtx.TEXTURE_2D, null); |
| return this.makeImageFromTexture(newTex, info); |
| }; |
| |
| CanvasKit.Surface.prototype.updateTextureFromSource = function(img, src) { |
| if (!img._tex) { |
| Debug('Image is not backed by a user-provided texture'); |
| return; |
| } |
| CanvasKit.setCurrentContext(this._context); |
| var glCtx = GL.currentContext.GLctx; |
| // Copy the contents of src over the texture associated with this image. |
| var tex = GL.textures[img._tex]; |
| glCtx.bindTexture(glCtx.TEXTURE_2D, tex); |
| if (GL.currentContext.version === 2) { |
| glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, getWidth(src), getHeight(src), 0, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); |
| } else { |
| glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); |
| } |
| glCtx.bindTexture(glCtx.TEXTURE_2D, null); |
| // Tell Skia we messed with the currently bound texture. |
| this._resetContext(); |
| // Create a new texture entry and put null into the old slot. This keeps our texture alive, |
| // otherwise it will be deleted when we delete the old Image. |
| GL.textures[img._tex] = null; |
| img._tex = pushTexture(tex); |
| var ii = img.getImageInfo(); |
| ii['colorSpace'] = img.getColorSpace(); |
| // Skia may cache parts of the image, and some places assume images are immutable. In order |
| // to make things work, we create a new SkImage based on the same texture as the old image. |
| var newImg = this._makeImageFromTexture(this._context, img._tex, ii); |
| // To make things more ergonomic for the user, we change passed in img object to refer |
| // to the new image and clean up the old SkImage object. This has the effect of updating |
| // the Image (from the user's side of things), because they shouldn't be caring about what |
| // part of WASM memory we are pointing to. |
| // The $$ part is provided by emscripten's embind, so this could break if they change |
| // things on us. |
| // https://github.com/emscripten-core/emscripten/blob/a65d70c809f077542649c60097787e1c7460ced6/src/embind/embind.js |
| // They do not do anything special to keep closure from minifying things and neither do we. |
| var oldPtr = img.$$.ptr; |
| var oldSmartPtr = img.$$.smartPtr; |
| img.$$.ptr = newImg.$$.ptr; |
| img.$$.smartPtr = newImg.$$.smartPtr; |
| // We want to clean up the previous image, so we swap out the pointers and call delete on it |
| // which should have that effect. |
| newImg.$$.ptr = oldPtr; |
| newImg.$$.smartPtr = oldSmartPtr; |
| newImg.delete(); |
| // Clean up the colorspace that we used. |
| ii['colorSpace'].delete(); |
| } |
| |
| CanvasKit.MakeLazyImageFromTextureSource = function(src, info) { |
| if (!info) { |
| info = { |
| 'height': getHeight(src), |
| 'width': getWidth(src), |
| 'colorType': CanvasKit.ColorType.RGBA_8888, |
| 'alphaType': CanvasKit.AlphaType.Unpremul, |
| }; |
| } |
| if (!info['colorSpace']) { |
| info['colorSpace'] = CanvasKit.ColorSpace.SRGB; |
| } |
| if (info['colorType'] !== CanvasKit.ColorType.RGBA_8888) { |
| Debug('colorType currently has no impact on MakeLazyImageFromTextureSource'); |
| } |
| |
| var callbackObj = { |
| 'makeTexture': function() { |
| // This callback function will make a texture on the current drawing surface (i.e. |
| // the current WebGL context). It assumes that Skia is just about to draw the texture |
| // to the desired surface, and thus the currentContext is the correct one. |
| // This is a lot easier than needing to pass the surface handle from the C++ side here. |
| var ctx = GL.currentContext; |
| var glCtx = ctx.GLctx; |
| var newTex = glCtx.createTexture(); |
| glCtx.bindTexture(glCtx.TEXTURE_2D, newTex); |
| if (ctx.version === 2) { |
| glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, info['width'], info['height'], 0, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); |
| } else { |
| glCtx.texImage2D(glCtx.TEXTURE_2D, 0, glCtx.RGBA, glCtx.RGBA, glCtx.UNSIGNED_BYTE, src); |
| } |
| glCtx.bindTexture(glCtx.TEXTURE_2D, null); |
| return pushTexture(newTex); |
| }, |
| 'freeSrc': function() { |
| // This callback will be executed whenever the returned image is deleted. This gives |
| // us a chance to free up the src (which we now own). Generally, there's nothing |
| // we need to do (we can let JS garbage collection do its thing). The one exception |
| // is for https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame, which we should |
| // close when we are done. |
| }, |
| } |
| if (src.constructor.name === 'VideoFrame') { |
| callbackObj['freeSrc'] = function() { |
| src.close(); |
| } |
| } |
| return CanvasKit.Image._makeFromGenerator(info, callbackObj); |
| } |
| |
| CanvasKit.setCurrentContext = function(ctx) { |
| if (!ctx) { |
| return false; |
| } |
| return GL.makeContextCurrent(ctx); |
| }; |
| }); |
| }(Module)); // When this file is loaded in, the high level object is "Module"; |