xref: /aosp_15_r20/external/skia/modules/canvaskit/webgl.js (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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