canvaskit.js 生成的
var CanvasKitInit = (() => { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; return ( function(CanvasKitInit) { CanvasKitInit = CanvasKitInit || {}; "use strict"; // The Module object: Our interface to the outside world. We import // and export values on it. There are various ways Module can be used: // 1. Not defined. We create it here // 2. A function parameter, function(Module) { ..generated code.. } // 3. pre-run appended it, var Module = {}; ..generated code.. // 4. External script tag defines var Module. // We need to check if Module already exists (e.g. case 3 above). // Substitution will be replaced with actual code on later stage of the build, // this way Closure Compiler will not mangle it (e.g. case 4. above). // Note that if you want to run closure, and also to use Module // after the generated code, you will need to define var Module = {}; // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. var Module = typeof CanvasKitInit != 'undefined' ? CanvasKitInit : {}; // See https://caniuse.com/mdn-javascript_builtins_object_assign // See https://caniuse.com/mdn-javascript_builtins_bigint64array // Set up the promise that indicates the Module is initialized var readyPromiseResolve, readyPromiseReject; Module['ready'] = new Promise(function(resolve, reject) { readyPromiseResolve = resolve; readyPromiseReject = reject; }); if (!Object.getOwnPropertyDescriptor(Module['ready'], '_malloc')) { Object.defineProperty(Module['ready'], '_malloc', { configurable: true, get: function() { abort('You are getting _malloc on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); Object.defineProperty(Module['ready'], '_malloc', { configurable: true, set: function() { abort('You are setting _malloc on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); } if (!Object.getOwnPropertyDescriptor(Module['ready'], '_free')) { Object.defineProperty(Module['ready'], '_free', { configurable: true, get: function() { abort('You are getting _free on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); Object.defineProperty(Module['ready'], '_free', { configurable: true, set: function() { abort('You are setting _free on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); } if (!Object.getOwnPropertyDescriptor(Module['ready'], '_fflush')) { Object.defineProperty(Module['ready'], '_fflush', { configurable: true, get: function() { abort('You are getting _fflush on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); Object.defineProperty(Module['ready'], '_fflush', { configurable: true, set: function() { abort('You are setting _fflush on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); } if (!Object.getOwnPropertyDescriptor(Module['ready'], '___getTypeName')) { Object.defineProperty(Module['ready'], '___getTypeName', { configurable: true, get: function() { abort('You are getting ___getTypeName on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); Object.defineProperty(Module['ready'], '___getTypeName', { configurable: true, set: function() { abort('You are setting ___getTypeName on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); } if (!Object.getOwnPropertyDescriptor(Module['ready'], '___embind_register_native_and_builtin_types')) { Object.defineProperty(Module['ready'], '___embind_register_native_and_builtin_types', { configurable: true, get: function() { abort('You are getting ___embind_register_native_and_builtin_types on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); Object.defineProperty(Module['ready'], '___embind_register_native_and_builtin_types', { configurable: true, set: function() { abort('You are setting ___embind_register_native_and_builtin_types on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); } if (!Object.getOwnPropertyDescriptor(Module['ready'], 'onRuntimeInitialized')) { Object.defineProperty(Module['ready'], 'onRuntimeInitialized', { configurable: true, get: function() { abort('You are getting onRuntimeInitialized on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); Object.defineProperty(Module['ready'], 'onRuntimeInitialized', { configurable: true, set: function() { abort('You are setting onRuntimeInitialized on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js') } }); } // --pre-jses are emitted after the Module integration code, so that they can // refer to Module (if they choose; they can also define Module) function Debug(msg) { console.warn(msg); } /** @const */ var IsDebug = true; // Adds compile-time JS functions to augment the CanvasKit interface. // Implementations in this file are considerate of GPU builds, i.e. some // behavior is predicated on whether or not this is being compiled alongside // webgl.js or webgpu.js. (function(CanvasKit){ CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; CanvasKit._extraInitializations.push(function() { // Takes in an html id or a canvas element CanvasKit.MakeSWCanvasSurface = function(idOrElement) { var canvas = idOrElement; if (canvas.tagName !== 'CANVAS') { // TODO(nifong): unit test canvas = document.getElementById(idOrElement); if (!canvas) { throw 'Canvas with id ' + idOrElement + ' was not found'; } } // Maybe better to use clientWidth/height. See: // https://webglfundamentals.org/webgl/lessons/webgl-anti-patterns.html var surface = CanvasKit.MakeSurface(canvas.width, canvas.height); if (surface) { surface._canvas = canvas; } return surface; }; // Don't over-write the MakeCanvasSurface set by gpu.js if it exists. if (!CanvasKit.MakeCanvasSurface) { CanvasKit.MakeCanvasSurface = CanvasKit.MakeSWCanvasSurface; } // Note that color spaces are currently not supported in CPU surfaces. due to the limitation // canvas.getContext('2d').putImageData imposes a limitation of using an RGBA_8888 color type. // TODO(nifong): support WGC color spaces while still using an RGBA_8888 color type when // on a cpu backend. CanvasKit.MakeSurface = function(width, height) { var imageInfo = { 'width': width, 'height': height, 'colorType': CanvasKit.ColorType.RGBA_8888, // Since we are sending these pixels directly into the HTML canvas, // (and those pixels are un-premultiplied, i.e. straight r,g,b,a) 'alphaType': CanvasKit.AlphaType.Unpremul, 'colorSpace': CanvasKit.ColorSpace.SRGB, }; var pixelLen = width * height * 4; // it's 8888, so 4 bytes per pixel // Allocate the buffer of pixels to be drawn into. var pixelPtr = CanvasKit._malloc(pixelLen); // Experiments with using RasterDirect vs Raster showed a 10% slowdown // over the traditional Surface::MakeRaster approach. This was exacerbated when // the surface was drawing to Premul and we had to convert to Unpremul each frame // (up to a 10x further slowdown). var surface = CanvasKit.Surface._makeRasterDirect(imageInfo, pixelPtr, width*4); if (surface) { surface._canvas = null; surface._width = width; surface._height = height; surface._pixelLen = pixelLen; surface._pixelPtr = pixelPtr; // rasterDirectSurface does not initialize the pixels, so we clear them // to transparent black. surface.getCanvas().clear(CanvasKit.TRANSPARENT); } return surface; }; CanvasKit.MakeRasterDirectSurface = function(imageInfo, mallocObj, bytesPerRow) { return CanvasKit.Surface._makeRasterDirect(imageInfo, mallocObj['byteOffset'], bytesPerRow); }; // For GPU builds, simply proxies to native code flush. For CPU builds, // also updates the underlying HTML canvas, optionally with dirtyRect. CanvasKit.Surface.prototype.flush = function(dirtyRect) { CanvasKit.setCurrentContext(this._context); this._flush(); // Do we have an HTML canvas to write the pixels to? // We will not have a canvas if this a GPU build, for example. if (this._canvas) { var pixels = new Uint8ClampedArray(CanvasKit.HEAPU8.buffer, this._pixelPtr, this._pixelLen); var imageData = new ImageData(pixels, this._width, this._height); if (!dirtyRect) { this._canvas.getContext('2d').putImageData(imageData, 0, 0); } else { this._canvas.getContext('2d').putImageData(imageData, 0, 0, dirtyRect[0], dirtyRect[1], dirtyRect[2] - dirtyRect[0], dirtyRect[3] - dirtyRect[1]); } } }; // Call dispose() instead of delete to clean up the underlying memory. // TODO(kjlubick) get rid of this and just wrap around delete(). CanvasKit.Surface.prototype.dispose = function() { if (this._pixelPtr) { CanvasKit._free(this._pixelPtr); } this.delete(); }; CanvasKit.setCurrentContext = CanvasKit.setCurrentContext || function() { // no op if this is a cpu-only build. }; CanvasKit.getCurrentGrDirectContext = CanvasKit.getCurrentGrDirectContext || function() { // No GrDirectContexts without a GPU backend. return null; }; }); }(Module)); // When this file is loaded in, the high level object is "Module"; // Adds compile-time JS functions to augment the CanvasKit interface. // Specifically, anything that should only be on the WebGL 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); // Emscripten does not enable this by default and Skia needs this to handle certain GPU // corner cases. GL.currentContext.GLctx.getExtension('WEBGL_debug_renderer_info'); 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.MakeWebGLContext = 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; var oldDelete = grCtx.delete.bind(grCtx); // We need to make sure we are focusing on the correct webgl context // when Skia cleans up the context. grCtx['delete'] = function() { CanvasKit.setCurrentContext(this._context); oldDelete(); }.bind(grCtx); // Save this so it is easy to access (e.g. Image.readPixels) GL.currentContext.grDirectContext = grCtx; return grCtx; }; CanvasKit.MakeGrContext = CanvasKit.MakeWebGLContext; CanvasKit.GrDirectContext.prototype.getResourceCacheLimitBytes = function() { CanvasKit.setCurrentContext(this._context); this._getResourceCacheLimitBytes(); }; CanvasKit.GrDirectContext.prototype.getResourceCacheUsageBytes = function() { CanvasKit.setCurrentContext(this._context); this._getResourceCacheUsageBytes(); }; CanvasKit.GrDirectContext.prototype.releaseResourcesAndAbandonContext = function() { CanvasKit.setCurrentContext(this._context); this._releaseResourcesAndAbandonContext(); }; CanvasKit.GrDirectContext.prototype.setResourceCacheLimitBytes = function(maxResourceBytes) { CanvasKit.setCurrentContext(this._context); this._setResourceCacheLimitBytes(maxResourceBytes); }; CanvasKit.MakeOnScreenGLSurface = function(grCtx, w, h, colorspace, sc, st) { if (!this.setCurrentContext(grCtx._context)) { return null; } var surface; // zero is a valid value for sample count or stencil bits. if (sc === undefined || st === undefined) { surface = this._MakeOnScreenGLSurface(grCtx, w, h, colorspace); } else { surface = this._MakeOnScreenGLSurface(grCtx, w, h, colorspace, sc, st); } 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.MakeWebGLContext(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 // controlled 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']; } function setupTexture(glCtx, newTex, imageInfo, srcIsPremul) { glCtx.bindTexture(glCtx.TEXTURE_2D, newTex); // See https://github.com/flutter/flutter/issues/106433#issuecomment-1169102945 // for an example of what can happen if we do not set this. if (!srcIsPremul && imageInfo['alphaType'] === CanvasKit.AlphaType.Premul) { glCtx.pixelStorei(glCtx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); } return newTex; } function resetTexture(glCtx, imageInfo, srcIsPremul) { // If we set this earlier, we want to unset it now. if (!srcIsPremul && imageInfo['alphaType'] === CanvasKit.AlphaType.Premul) { glCtx.pixelStorei(glCtx.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); } glCtx.bindTexture(glCtx.TEXTURE_2D, null); } CanvasKit.Surface.prototype.makeImageFromTextureSource = function(src, info, srcIsPremul) { if (!info) { // If the user didn't specify the image info, use some sensible defaults. info = { 'height': getHeight(src), 'width': getWidth(src), 'colorType': CanvasKit.ColorType.RGBA_8888, 'alphaType': srcIsPremul ? CanvasKit.AlphaType.Premul: 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 = setupTexture(glCtx, glCtx.createTexture(), info, srcIsPremul); 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); } resetTexture(glCtx, info); this._resetContext(); return this.makeImageFromTexture(newTex, info); }; CanvasKit.Surface.prototype.updateTextureFromSource = function(img, src, srcIsPremul) { if (!img._tex) { Debug('Image is not backed by a user-provided texture'); return; } CanvasKit.setCurrentContext(this._context); var ii = img.getImageInfo(); var glCtx = GL.currentContext.GLctx; // Copy the contents of src over the texture associated with this image. var tex = setupTexture(glCtx, GL.textures[img._tex], ii, srcIsPremul); 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); } resetTexture(glCtx, ii, srcIsPremul); // 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); 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, srcIsPremul) { if (!info) { info = { 'height': getHeight(src), 'width': getWidth(src), 'colorType': CanvasKit.ColorType.RGBA_8888, 'alphaType': srcIsPremul ? CanvasKit.AlphaType.Premul : 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 = setupTexture(glCtx, glCtx.createTexture(), info, srcIsPremul); 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); } resetTexture(glCtx, info, srcIsPremul); 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); }; CanvasKit.getCurrentGrDirectContext = function() { if (GL.currentContext && GL.currentContext.grDirectContext && !GL.currentContext.grDirectContext['isDeleted']()) { return GL.currentContext.grDirectContext; } return null; }; }); }(Module)); // When this file is loaded in, the high level object is "Module"; // Adds compile-time JS functions to augment the CanvasKit interface. (function(CanvasKit) { // This intentionally dangles because we want all the // JS code to be in the same scope, but JS doesn't support // namespaces like C++ does. Thus, we simply include this // preamble.js file, all the source .js files and then postamble.js // to bundle everything in the same scope. /* * This file houses functions that deal with color. */ // Constructs a Color with the same API as CSS's rgba(), that is // r,g,b are 0-255, and a is 0.0 to 1.0. // if a is omitted, it will be assumed to be 1.0 // Internally, Colors are a TypedArray of four unpremultiplied 32-bit floats: a, r, g, b // In order to construct one with more precision or in a wider gamut, use // CanvasKit.Color4f CanvasKit.Color = function(r, g, b, a) { if (a === undefined) { a = 1; } return CanvasKit.Color4f(clamp(r)/255, clamp(g)/255, clamp(b)/255, a); }; // Constructs a Color as a 32 bit unsigned integer, with 8 bits assigned to each channel. // Channels are expected to be between 0 and 255 and will be clamped as such. CanvasKit.ColorAsInt = function(r, g, b, a) { // default to opaque if (a === undefined) { a = 255; } // This is consistent with how Skia represents colors in C++, as an unsigned int. // This is also consistent with how Flutter represents colors: // https://github.com/flutter/engine/blob/243bb59c7179a7e701ce478080d6ce990710ae73/lib/web_ui/lib/src/ui/painting.dart#L50 return (((clamp(a) << 24) | (clamp(r) << 16) | (clamp(g) << 8) | (clamp(b) << 0) & 0xFFFFFFF) // This truncates the unsigned to 32 bits and signals to JS engines they can // represent the number with an int instead of a double. >>> 0); // This makes the value an unsigned int. }; // Construct a 4-float color. // Opaque if opacity is omitted. CanvasKit.Color4f = function(r, g, b, a) { if (a === undefined) { a = 1; } return Float32Array.of(r, g, b, a); }; // Color constants use property getters to prevent other code from accidentally // changing them. Object.defineProperty(CanvasKit, 'TRANSPARENT', { get: function() { return CanvasKit.Color4f(0, 0, 0, 0); } }); Object.defineProperty(CanvasKit, 'BLACK', { get: function() { return CanvasKit.Color4f(0, 0, 0, 1); } }); Object.defineProperty(CanvasKit, 'WHITE', { get: function() { return CanvasKit.Color4f(1, 1, 1, 1); } }); Object.defineProperty(CanvasKit, 'RED', { get: function() { return CanvasKit.Color4f(1, 0, 0, 1); } }); Object.defineProperty(CanvasKit, 'GREEN', { get: function() { return CanvasKit.Color4f(0, 1, 0, 1); } }); Object.defineProperty(CanvasKit, 'BLUE', { get: function() { return CanvasKit.Color4f(0, 0, 1, 1); } }); Object.defineProperty(CanvasKit, 'YELLOW', { get: function() { return CanvasKit.Color4f(1, 1, 0, 1); } }); Object.defineProperty(CanvasKit, 'CYAN', { get: function() { return CanvasKit.Color4f(0, 1, 1, 1); } }); Object.defineProperty(CanvasKit, 'MAGENTA', { get: function() { return CanvasKit.Color4f(1, 0, 1, 1); } }); // returns a css style [r, g, b, a] from a CanvasKit.Color // where r, g, b are returned as ints in the range [0, 255] // where a is scaled between 0 and 1.0 CanvasKit.getColorComponents = function(color) { return [ Math.floor(color[0]*255), Math.floor(color[1]*255), Math.floor(color[2]*255), color[3] ]; }; // parseColorString takes in a CSS color value and returns a CanvasKit.Color // (which is an array of 4 floats in RGBA order). An optional colorMap // may be provided which maps custom strings to values. // In the CanvasKit canvas2d shim layer, we provide this map for processing // canvas2d calls, but not here for code size reasons. CanvasKit.parseColorString = function(colorStr, colorMap) { colorStr = colorStr.toLowerCase(); // See https://drafts.csswg.org/css-color/#typedef-hex-color if (colorStr.startsWith('#')) { var r, g, b, a = 255; switch (colorStr.length) { case 9: // 8 hex chars #RRGGBBAA a = parseInt(colorStr.slice(7, 9), 16); case 7: // 6 hex chars #RRGGBB r = parseInt(colorStr.slice(1, 3), 16); g = parseInt(colorStr.slice(3, 5), 16); b = parseInt(colorStr.slice(5, 7), 16); break; case 5: // 4 hex chars #RGBA // multiplying by 17 is the same effect as // appending another character of the same value // e.g. e => ee == 14 => 238 a = parseInt(colorStr.slice(4, 5), 16) * 17; case 4: // 6 hex chars #RGB r = parseInt(colorStr.slice(1, 2), 16) * 17; g = parseInt(colorStr.slice(2, 3), 16) * 17; b = parseInt(colorStr.slice(3, 4), 16) * 17; break; } return CanvasKit.Color(r, g, b, a/255); } else if (colorStr.startsWith('rgba')) { // Trim off rgba( and the closing ) colorStr = colorStr.slice(5, -1); var nums = colorStr.split(','); return CanvasKit.Color(+nums[0], +nums[1], +nums[2], valueOrPercent(nums[3])); } else if (colorStr.startsWith('rgb')) { // Trim off rgba( and the closing ) colorStr = colorStr.slice(4, -1); var nums = colorStr.split(','); // rgb can take 3 or 4 arguments return CanvasKit.Color(+nums[0], +nums[1], +nums[2], valueOrPercent(nums[3])); } else if (colorStr.startsWith('gray(')) { // TODO(kjlubick) } else if (colorStr.startsWith('hsl')) { // TODO(kjlubick) } else if (colorMap) { // Try for named color var nc = colorMap[colorStr]; if (nc !== undefined) { return nc; } } Debug('unrecognized color ' + colorStr); return CanvasKit.BLACK; }; function isCanvasKitColor(ob) { if (!ob) { return false; } return (ob.constructor === Float32Array && ob.length === 4); } // Warning information is lost by this conversion function toUint32Color(c) { return ((clamp(c[3]*255) << 24) | (clamp(c[0]*255) << 16) | (clamp(c[1]*255) << 8) | (clamp(c[2]*255) << 0)) >>> 0; } // Accepts various colors representations and converts them to an array of int colors. // Does not handle builders. function assureIntColors(arr) { if (wasMalloced(arr)) { return arr; // Assume if the memory was malloced that the user has done it correctly. } else if (arr instanceof Float32Array) { var count = Math.floor(arr.length / 4); var result = new Uint32Array(count); for (var i = 0; i < count; i ++) { result[i] = toUint32Color(arr.slice(i*4, (i+1)*4)); } return result; } else if (arr instanceof Uint32Array) { return arr; } else if (arr instanceof Array && arr[0] instanceof Float32Array) { return arr.map(toUint32Color); } } function uIntColorToCanvasKitColor(c) { return CanvasKit.Color( (c >> 16) & 0xFF, (c >> 8) & 0xFF, (c >> 0) & 0xFF, ((c >> 24) & 0xFF) / 255 ); } function valueOrPercent(aStr) { if (aStr === undefined) { return 1; // default to opaque. } var a = parseFloat(aStr); if (aStr && aStr.indexOf('%') !== -1) { return a / 100; } return a; } function clamp(c) { return Math.round(Math.max(0, Math.min(c || 0, 255))); } // TODO(kjlubick) delete this, as it is now trivial with 4f colors CanvasKit.multiplyByAlpha = function(color, alpha) { // make a copy of the color so the function remains pure. var result = color.slice(); result[3] = Math.max(0, Math.min(result[3] * alpha, 1)); return result; }; /* * This file houses utilities for copying blocks of memory to and from * the WASM heap. */ /** * Malloc returns a TypedArray backed by the C++ memory of the * given length. It should only be used by advanced users who * can manage memory and initialize values properly. When used * correctly, it can save copying of data between JS and C++. * When used incorrectly, it can lead to memory leaks. * Any memory allocated by CanvasKit.Malloc needs to be released with CanvasKit.Free. * * const mObj = CanvasKit.Malloc(Float32Array, 20); * Get a TypedArray view around the malloc'd memory (this does not copy anything). * const ta = mObj.toTypedArray(); * // store data into ta * const cf = CanvasKit.ColorFilter.MakeMatrix(ta); // mObj could also be used. * * // eventually... * CanvasKit.Free(mObj); * * @param {TypedArray} typedArray - constructor for the typedArray. * @param {number} len - number of *elements* to store. */ CanvasKit.Malloc = function(typedArray, len) { var byteLen = len * typedArray.BYTES_PER_ELEMENT; var ptr = CanvasKit._malloc(byteLen); return { '_ck': true, 'length': len, 'byteOffset': ptr, typedArray: null, 'subarray': function(start, end) { var sa = this['toTypedArray']().subarray(start, end); sa['_ck'] = true; return sa; }, 'toTypedArray': function() { // Check if the previously allocated array is still usable. // If it's falsy, then we haven't created an array yet. // If it's empty, then WASM resized memory and emptied the array. if (this.typedArray && this.typedArray.length) { return this.typedArray; } this.typedArray = new typedArray(CanvasKit.HEAPU8.buffer, ptr, len); // add a marker that this was allocated in C++ land this.typedArray['_ck'] = true; return this.typedArray; }, }; }; /** * Free frees the memory returned by Malloc. * Any memory allocated by CanvasKit.Malloc needs to be released with CanvasKit.Free. */ CanvasKit.Free = function(mallocObj) { CanvasKit._free(mallocObj['byteOffset']); mallocObj['byteOffset'] = nullptr; // Set these to null to make sure the TypedArrays can be garbage collected. mallocObj['toTypedArray'] = null; mallocObj.typedArray = null; }; // This helper will free the given pointer unless the provided array is one // that was returned by CanvasKit.Malloc. function freeArraysThatAreNotMallocedByUsers(ptr, arr) { if (!wasMalloced(arr)) { CanvasKit._free(ptr); } } // wasMalloced returns true if the object was created by a call to Malloc. This is determined // by looking at a property that was added to our Malloc obj and typed arrays. function wasMalloced(obj) { return obj && obj['_ck']; } // We define some "scratch" variables which will house both the pointer to // memory we allocate at startup as well as a Malloc object, which we can // use to get a TypedArray view of that memory. var _scratch3x3MatrixPtr = nullptr; var _scratch3x3Matrix; // the result from CanvasKit.Malloc var _scratch4x4MatrixPtr = nullptr; var _scratch4x4Matrix; var _scratchColorPtr = nullptr; var _scratchColor; var _scratchFourFloatsA; var _scratchFourFloatsAPtr = nullptr; var _scratchFourFloatsB; var _scratchFourFloatsBPtr = nullptr; var _scratchThreeFloatsA; var _scratchThreeFloatsAPtr = nullptr; var _scratchThreeFloatsB; var _scratchThreeFloatsBPtr = nullptr; var _scratchIRect; var _scratchIRectPtr = nullptr; var _scratchRRect; var _scratchRRectPtr = nullptr; var _scratchRRect2; var _scratchRRect2Ptr = nullptr; // arr can be a normal JS array or a TypedArray // dest is a string like 'HEAPU32' that specifies the type the src array // should be copied into. // ptr can be optionally provided if the memory was already allocated. // Callers should eventually free the data unless the C++ object owns the memory, // or the provided pointer is a scratch pointer or a user-malloced value. // see also freeArraysThatAreNotMallocedByUsers(). function copy1dArray(arr, dest, ptr) { if (!arr || !arr.length) { return nullptr; } // This was created with CanvasKit.Malloc, so it's already been copied. if (wasMalloced(arr)) { return arr.byteOffset; } var bytesPerElement = CanvasKit[dest].BYTES_PER_ELEMENT; if (!ptr) { ptr = CanvasKit._malloc(arr.length * bytesPerElement); } // In c++ terms, the WASM heap is a uint8_t*, a long buffer/array of single // byte elements. When we run _malloc, we always get an offset/pointer into // that block of memory. // CanvasKit exposes some different views to make it easier to work with // different types. HEAPF32 for example, exposes it as a float* // However, to make the ptr line up, we have to do some pointer arithmetic. // Concretely, we need to convert ptr to go from an index into a 1-byte-wide // buffer to an index into a 4-byte-wide buffer (in the case of HEAPF32) // and thus we divide ptr by 4. // It is important to make sure we are grabbing the freshest view of the // memory possible because if we call _malloc and the heap needs to grow, // the TypedArrayView will no longer be valid. CanvasKit[dest].set(arr, ptr / bytesPerElement); return ptr; } // Copies an array of colors to wasm, returning an object with the pointer // and info necessary to use the copied colors. // Accepts either a flat Float32Array, flat Uint32Array or Array of Float32Arrays. // If color is an object that was allocated with CanvasKit.Malloc, its pointer is // returned and no extra copy is performed. // TODO(nifong): have this accept color builders. function copyFlexibleColorArray(colors) { var result = { colorPtr: nullptr, count: colors.length, colorType: CanvasKit.ColorType.RGBA_F32, }; if (colors instanceof Float32Array) { result.colorPtr = copy1dArray(colors, 'HEAPF32'); result.count = colors.length / 4; } else if (colors instanceof Uint32Array) { result.colorPtr = copy1dArray(colors, 'HEAPU32'); result.colorType = CanvasKit.ColorType.RGBA_8888; } else if (colors instanceof Array) { result.colorPtr = copyColorArray(colors); } else { throw('Invalid argument to copyFlexibleColorArray, Not a color array '+typeof(colors)); } return result; } function copyColorArray(arr) { if (!arr || !arr.length) { return nullptr; } // 4 floats per color, 4 bytes per float. var ptr = CanvasKit._malloc(arr.length * 4 * 4); var idx = 0; var adjustedPtr = ptr / 4; // cast the byte pointer into a float pointer. for (var r = 0; r < arr.length; r++) { for (var c = 0; c < 4; c++) { CanvasKit.HEAPF32[adjustedPtr + idx] = arr[r][c]; idx++; } } return ptr; } var defaultPerspective = Float32Array.of(0, 0, 1); // Copies the given DOMMatrix/Array/TypedArray to the CanvasKit heap and // returns a pointer to the memory. This memory is a float* of length 9. // If the passed in matrix is null/undefined, we return 0 (nullptr). The // returned pointer should NOT be freed, as it is either null or a scratch // pointer. function copy3x3MatrixToWasm(matr) { if (!matr) { return nullptr; } var wasm3x3Matrix = _scratch3x3Matrix['toTypedArray'](); if (matr.length) { if (matr.length === 6 || matr.length === 9) { // matr should be an array or typed array. copy1dArray(matr, 'HEAPF32', _scratch3x3MatrixPtr); if (matr.length === 6) { // Overwrite the last 3 floats with the default perspective. The divide // by 4 casts the pointer into a float pointer. CanvasKit.HEAPF32.set(defaultPerspective, 6 + _scratch3x3MatrixPtr / 4); } return _scratch3x3MatrixPtr; } else if (matr.length === 16) { // Downsample the 4x4 matrix into a 3x3 wasm3x3Matrix[0] = matr[0]; wasm3x3Matrix[1] = matr[1]; wasm3x3Matrix[2] = matr[3]; wasm3x3Matrix[3] = matr[4]; wasm3x3Matrix[4] = matr[5]; wasm3x3Matrix[5] = matr[7]; wasm3x3Matrix[6] = matr[12]; wasm3x3Matrix[7] = matr[13]; wasm3x3Matrix[8] = matr[15]; return _scratch3x3MatrixPtr; } throw 'invalid matrix size'; } else if (matr['m11'] === undefined) { throw 'invalid matrix argument'; } // Reminder that DOMMatrix is column-major. wasm3x3Matrix[0] = matr['m11']; wasm3x3Matrix[1] = matr['m21']; wasm3x3Matrix[2] = matr['m41']; wasm3x3Matrix[3] = matr['m12']; wasm3x3Matrix[4] = matr['m22']; wasm3x3Matrix[5] = matr['m42']; wasm3x3Matrix[6] = matr['m14']; wasm3x3Matrix[7] = matr['m24']; wasm3x3Matrix[8] = matr['m44']; return _scratch3x3MatrixPtr; } // Copies the given DOMMatrix/Array/TypedArray to the CanvasKit heap and // returns a pointer to the memory. This memory is a float* of length 16. // If the passed in matrix is null/undefined, we return 0 (nullptr). The // returned pointer should NOT be freed, as it is either null or a scratch // pointer. function copy4x4MatrixToWasm(matr) { if (!matr) { return nullptr; } var wasm4x4Matrix = _scratch4x4Matrix['toTypedArray'](); if (matr.length) { if (matr.length !== 16 && matr.length !== 6 && matr.length !== 9) { throw 'invalid matrix size'; } if (matr.length === 16) { // matr should be an array or typed array. return copy1dArray(matr, 'HEAPF32', _scratch4x4MatrixPtr); } // Upscale the row-major 3x3 or 3x2 matrix into a 4x4 row-major matrix // TODO(skbug.com/10108) This will need to change when we convert our // JS 4x4 to be column-major. // When upscaling, we need to overwrite the 3rd column and the 3rd row with // 0s. It's easiest to just do that with a fill command. wasm4x4Matrix.fill(0); wasm4x4Matrix[0] = matr[0]; wasm4x4Matrix[1] = matr[1]; // skip col 2 wasm4x4Matrix[3] = matr[2]; wasm4x4Matrix[4] = matr[3]; wasm4x4Matrix[5] = matr[4]; // skip col 2 wasm4x4Matrix[7] = matr[5]; // row2 == identity wasm4x4Matrix[10] = 1; wasm4x4Matrix[12] = matr[6]; wasm4x4Matrix[13] = matr[7]; // skip col 2 wasm4x4Matrix[15] = matr[8]; if (matr.length === 6) { // fix perspective for the 3x2 case (from above, they will be undefined). wasm4x4Matrix[12]=0; wasm4x4Matrix[13]=0; wasm4x4Matrix[15]=1; } return _scratch4x4MatrixPtr; } else if (matr['m11'] === undefined) { throw 'invalid matrix argument'; } // Reminder that DOMMatrix is column-major. wasm4x4Matrix[0] = matr['m11']; wasm4x4Matrix[1] = matr['m21']; wasm4x4Matrix[2] = matr['m31']; wasm4x4Matrix[3] = matr['m41']; wasm4x4Matrix[4] = matr['m12']; wasm4x4Matrix[5] = matr['m22']; wasm4x4Matrix[6] = matr['m32']; wasm4x4Matrix[7] = matr['m42']; wasm4x4Matrix[8] = matr['m13']; wasm4x4Matrix[9] = matr['m23']; wasm4x4Matrix[10] = matr['m33']; wasm4x4Matrix[11] = matr['m43']; wasm4x4Matrix[12] = matr['m14']; wasm4x4Matrix[13] = matr['m24']; wasm4x4Matrix[14] = matr['m34']; wasm4x4Matrix[15] = matr['m44']; return _scratch4x4MatrixPtr; } // copies a 4x4 matrix at the given pointer into a JS array. function copy4x4MatrixFromWasm(matrPtr) { // read them out into an array. TODO(kjlubick): If we change Matrix to be // typedArrays, then we should return a typed array here too. var rv = new Array(16); for (var i = 0; i < 16; i++) { rv[i] = CanvasKit.HEAPF32[matrPtr/4 + i]; // divide by 4 to cast to float. } return rv; } // copies the given floats into the wasm heap as an SkColor4f. Unless a non-scratch pointer is // passed into ptr, callers do NOT need to free the returned pointer. function copyColorToWasm(color4f, ptr) { return copy1dArray(color4f, 'HEAPF32', ptr || _scratchColorPtr); } // copies the given color into the wasm heap. Callers do not need to free the returned pointer. function copyColorComponentsToWasm(r, g, b, a) { var colors = _scratchColor['toTypedArray'](); colors[0] = r; colors[1] = g; colors[2] = b; colors[3] = a; return _scratchColorPtr; } // copies the given color into the wasm heap. Callers must free the returned pointer. function copyColorToWasmNoScratch(color4f) { // TODO(kjlubick): accept 4 floats or int color return copy1dArray(color4f, 'HEAPF32'); } // copies the four floats at the given pointer in a js Float32Array function copyColorFromWasm(colorPtr) { var rv = new Float32Array(4); for (var i = 0; i < 4; i++) { rv[i] = CanvasKit.HEAPF32[colorPtr/4 + i]; // divide by 4 to cast to float. } return rv; } // copies the given floats into the wasm heap as an SkRect. Unless a non-scratch pointer is // passed into ptr, callers do NOT need to free the returned pointer. function copyRectToWasm(fourFloats, ptr) { return copy1dArray(fourFloats, 'HEAPF32', ptr || _scratchFourFloatsAPtr); } // copies the given ints into the wasm heap as an SkIRect. Unless a non-scratch pointer is // passed into ptr, callers do NOT need to free the returned pointer. function copyIRectToWasm(fourInts, ptr) { return copy1dArray(fourInts, 'HEAP32', ptr || _scratchIRectPtr); } // copies the four ints at the given pointer into a JS Int32Array function copyIRectFromWasm(rectMalloc, outputArray) { var ta = rectMalloc['toTypedArray'](); if (outputArray) { outputArray.set(ta); return outputArray; } return ta.slice(); } // copies the given floats into the wasm heap as an SkRRect. Unless a non-scratch pointer is // passed into ptr, callers do NOT need to free the returned pointer. function copyRRectToWasm(twelveFloats, ptr) { return copy1dArray(twelveFloats, 'HEAPF32', ptr || _scratchRRectPtr); } /** * This file houses miscellaneous helper functions and constants. */ var nullptr = 0; // emscripten doesn't like to take null as uintptr_t function radiansToDegrees(rad) { return (rad / Math.PI) * 180; } function degreesToRadians(deg) { return (deg / 180) * Math.PI; } function almostEqual(floata, floatb) { return Math.abs(floata - floatb) < 0.00001; } // Adds JS functions to augment the CanvasKit interface. // For example, if there is a wrapper around the C++ call or logic to allow // chaining, it should go here. // CanvasKit.onRuntimeInitialized is called after the WASM library has loaded. // Anything that modifies an exposed class (e.g. Path) should be set // after onRuntimeInitialized, otherwise, it can happen outside of that scope. CanvasKit.onRuntimeInitialized = function() { // All calls to 'this' need to go in externs.js so closure doesn't minify them away. _scratchColor = CanvasKit.Malloc(Float32Array, 4); // 4 color scalars. _scratchColorPtr = _scratchColor['byteOffset']; _scratch4x4Matrix = CanvasKit.Malloc(Float32Array, 16); // 16 matrix scalars. _scratch4x4MatrixPtr = _scratch4x4Matrix['byteOffset']; _scratch3x3Matrix = CanvasKit.Malloc(Float32Array, 9); // 9 matrix scalars. _scratch3x3MatrixPtr = _scratch3x3Matrix['byteOffset']; _scratchRRect = CanvasKit.Malloc(Float32Array, 12); // 4 scalars for rrect, 8 for radii. _scratchRRectPtr = _scratchRRect['byteOffset']; _scratchRRect2 = CanvasKit.Malloc(Float32Array, 12); // 4 scalars for rrect, 8 for radii. _scratchRRect2Ptr = _scratchRRect2['byteOffset']; _scratchFourFloatsA = CanvasKit.Malloc(Float32Array, 4); _scratchFourFloatsAPtr = _scratchFourFloatsA['byteOffset']; _scratchFourFloatsB = CanvasKit.Malloc(Float32Array, 4); _scratchFourFloatsBPtr = _scratchFourFloatsB['byteOffset']; _scratchThreeFloatsA = CanvasKit.Malloc(Float32Array, 3); // 3 floats to represent SkVector3 _scratchThreeFloatsAPtr = _scratchThreeFloatsA['byteOffset']; _scratchThreeFloatsB = CanvasKit.Malloc(Float32Array, 3); // 3 floats to represent SkVector3 _scratchThreeFloatsBPtr = _scratchThreeFloatsB['byteOffset']; _scratchIRect = CanvasKit.Malloc(Int32Array, 4); _scratchIRectPtr = _scratchIRect['byteOffset']; // Create single copies of all three supported color spaces // These are sk_sp<ColorSpace> CanvasKit.ColorSpace.SRGB = CanvasKit.ColorSpace._MakeSRGB(); CanvasKit.ColorSpace.DISPLAY_P3 = CanvasKit.ColorSpace._MakeDisplayP3(); CanvasKit.ColorSpace.ADOBE_RGB = CanvasKit.ColorSpace._MakeAdobeRGB(); // Use quotes to tell closure compiler not to minify the names CanvasKit['GlyphRunFlags'] = { 'IsWhiteSpace': CanvasKit['_GlyphRunFlags_isWhiteSpace'], }; CanvasKit.Path.MakeFromCmds = function(cmds) { var cmdPtr = copy1dArray(cmds, 'HEAPF32'); var path = CanvasKit.Path._MakeFromCmds(cmdPtr, cmds.length); freeArraysThatAreNotMallocedByUsers(cmdPtr, cmds); return path; }; // The weights array is optional (only used for conics). CanvasKit.Path.MakeFromVerbsPointsWeights = function(verbs, pts, weights) { var verbsPtr = copy1dArray(verbs, 'HEAPU8'); var pointsPtr = copy1dArray(pts, 'HEAPF32'); var weightsPtr = copy1dArray(weights, 'HEAPF32'); var numWeights = (weights && weights.length) || 0; var path = CanvasKit.Path._MakeFromVerbsPointsWeights( verbsPtr, verbs.length, pointsPtr, pts.length, weightsPtr, numWeights); freeArraysThatAreNotMallocedByUsers(verbsPtr, verbs); freeArraysThatAreNotMallocedByUsers(pointsPtr, pts); freeArraysThatAreNotMallocedByUsers(weightsPtr, weights); return path; }; CanvasKit.Path.prototype.addArc = function(oval, startAngle, sweepAngle) { // see arc() for the HTMLCanvas version // note input angles are degrees. var oPtr = copyRectToWasm(oval); this._addArc(oPtr, startAngle, sweepAngle); return this; }; CanvasKit.Path.prototype.addCircle = function(x, y, r, isCCW) { this._addCircle(x, y, r, !!isCCW); return this; }; CanvasKit.Path.prototype.addOval = function(oval, isCCW, startIndex) { if (startIndex === undefined) { startIndex = 1; } var oPtr = copyRectToWasm(oval); this._addOval(oPtr, !!isCCW, startIndex); return this; }; // TODO(kjlubick) clean up this API - split it apart if necessary CanvasKit.Path.prototype.addPath = function() { // Takes 1, 2, 7, or 10 required args, where the first arg is always the path. // The last arg is optional and chooses between add or extend mode. // The options for the remaining args are: // - an array of 6 or 9 parameters (perspective is optional) // - the 9 parameters of a full matrix or // the 6 non-perspective params of a matrix. var args = Array.prototype.slice.call(arguments); var path = args[0]; var extend = false; if (typeof args[args.length-1] === 'boolean') { extend = args.pop(); } if (args.length === 1) { // Add path, unchanged. Use identity matrix this._addPath(path, 1, 0, 0, 0, 1, 0, 0, 0, 1, extend); } else if (args.length === 2) { // User provided the 9 params of a full matrix as an array. var a = args[1]; this._addPath(path, a[0], a[1], a[2], a[3], a[4], a[5], a[6] || 0, a[7] || 0, a[8] || 1, extend); } else if (args.length === 7 || args.length === 10) { // User provided the 9 params of a (full) matrix directly. // (or just the 6 non perspective ones) // These are in the same order as what Skia expects. var a = args; this._addPath(path, a[1], a[2], a[3], a[4], a[5], a[6], a[7] || 0, a[8] || 0, a[9] || 1, extend); } else { Debug('addPath expected to take 1, 2, 7, or 10 required args. Got ' + args.length); return null; } return this; }; // points is a 1d array of length 2n representing n points where the even indices // will be treated as x coordinates and the odd indices will be treated as y coordinates. // Like other APIs, this accepts a malloced type array or malloc obj. CanvasKit.Path.prototype.addPoly = function(points, close) { var ptr = copy1dArray(points, 'HEAPF32'); this._addPoly(ptr, points.length / 2, close); freeArraysThatAreNotMallocedByUsers(ptr, points); return this; }; CanvasKit.Path.prototype.addRect = function(rect, isCCW) { var rPtr = copyRectToWasm(rect); this._addRect(rPtr, !!isCCW); return this; }; CanvasKit.Path.prototype.addRRect = function(rrect, isCCW) { var rPtr = copyRRectToWasm(rrect); this._addRRect(rPtr, !!isCCW); return this; }; // The weights array is optional (only used for conics). CanvasKit.Path.prototype.addVerbsPointsWeights = function(verbs, points, weights) { var verbsPtr = copy1dArray(verbs, 'HEAPU8'); var pointsPtr = copy1dArray(points, 'HEAPF32'); var weightsPtr = copy1dArray(weights, 'HEAPF32'); var numWeights = (weights && weights.length) || 0; this._addVerbsPointsWeights(verbsPtr, verbs.length, pointsPtr, points.length, weightsPtr, numWeights); freeArraysThatAreNotMallocedByUsers(verbsPtr, verbs); freeArraysThatAreNotMallocedByUsers(pointsPtr, points); freeArraysThatAreNotMallocedByUsers(weightsPtr, weights); }; CanvasKit.Path.prototype.arc = function(x, y, radius, startAngle, endAngle, ccw) { // emulates the HTMLCanvas behavior. See addArc() for the Path version. // Note input angles are radians. var bounds = CanvasKit.LTRBRect(x-radius, y-radius, x+radius, y+radius); var sweep = radiansToDegrees(endAngle - startAngle) - (360 * !!ccw); var temp = new CanvasKit.Path(); temp.addArc(bounds, radiansToDegrees(startAngle), sweep); this.addPath(temp, true); temp.delete(); return this; }; // Appends arc to Path. Arc added is part of ellipse // bounded by oval, from startAngle through sweepAngle. Both startAngle and // sweepAngle are measured in degrees, where zero degrees is aligned with the // positive x-axis, and positive sweeps extends arc clockwise. CanvasKit.Path.prototype.arcToOval = function(oval, startAngle, sweepAngle, forceMoveTo) { var oPtr = copyRectToWasm(oval); this._arcToOval(oPtr, startAngle, sweepAngle, forceMoveTo); return this; }; // Appends arc to Path. Arc is implemented by one or more conics weighted to // describe part of oval with radii (rx, ry) rotated by xAxisRotate degrees. Arc // curves from last point to (x, y), choosing one of four possible routes: // clockwise or counterclockwise, and smaller or larger. // Arc sweep is always less than 360 degrees. arcTo() appends line to (x, y) if // either radii are zero, or if last point equals (x, y). arcTo() scales radii // (rx, ry) to fit last point and (x, y) if both are greater than zero but // too small. // arcToRotated() appends up to four conic curves. // arcToRotated() implements the functionality of SVG arc, although SVG sweep-flag value // is opposite the integer value of sweep; SVG sweep-flag uses 1 for clockwise, // while kCW_Direction cast to int is zero. CanvasKit.Path.prototype.arcToRotated = function(rx, ry, xAxisRotate, useSmallArc, isCCW, x, y) { this._arcToRotated(rx, ry, xAxisRotate, !!useSmallArc, !!isCCW, x, y); return this; }; // Appends arc to Path, after appending line if needed. Arc is implemented by conic // weighted to describe part of circle. Arc is contained by tangent from // last Path point to (x1, y1), and tangent from (x1, y1) to (x2, y2). Arc // is part of circle sized to radius, positioned so it touches both tangent lines. // If last Path Point does not start Arc, arcTo appends connecting Line to Path. // The length of Vector from (x1, y1) to (x2, y2) does not affect Arc. // Arc sweep is always less than 180 degrees. If radius is zero, or if // tangents are nearly parallel, arcTo appends Line from last Path Point to (x1, y1). // arcToTangent appends at most one Line and one conic. // arcToTangent implements the functionality of PostScript arct and HTML Canvas arcTo. CanvasKit.Path.prototype.arcToTangent = function(x1, y1, x2, y2, radius) { this._arcToTangent(x1, y1, x2, y2, radius); return this; }; CanvasKit.Path.prototype.close = function() { this._close(); return this; }; CanvasKit.Path.prototype.conicTo = function(x1, y1, x2, y2, w) { this._conicTo(x1, y1, x2, y2, w); return this; }; // Clients can pass in a Float32Array with length 4 to this and the results // will be copied into that array. Otherwise, a new TypedArray will be allocated // and returned. CanvasKit.Path.prototype.computeTightBounds = function(optionalOutputArray) { this._computeTightBounds(_scratchFourFloatsAPtr); var ta = _scratchFourFloatsA['toTypedArray'](); if (optionalOutputArray) { optionalOutputArray.set(ta); return optionalOutputArray; } return ta.slice(); }; CanvasKit.Path.prototype.cubicTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { this._cubicTo(cp1x, cp1y, cp2x, cp2y, x, y); return this; }; CanvasKit.Path.prototype.dash = function(on, off, phase) { if (this._dash(on, off, phase)) { return this; } return null; }; // Clients can pass in a Float32Array with length 4 to this and the results // will be copied into that array. Otherwise, a new TypedArray will be allocated // and returned. CanvasKit.Path.prototype.getBounds = function(optionalOutputArray) { this._getBounds(_scratchFourFloatsAPtr); var ta = _scratchFourFloatsA['toTypedArray'](); if (optionalOutputArray) { optionalOutputArray.set(ta); return optionalOutputArray; } return ta.slice(); }; CanvasKit.Path.prototype.lineTo = function(x, y) { this._lineTo(x, y); return this; }; CanvasKit.Path.prototype.moveTo = function(x, y) { this._moveTo(x, y); return this; }; CanvasKit.Path.prototype.offset = function(dx, dy) { this._transform(1, 0, dx, 0, 1, dy, 0, 0, 1); return this; }; CanvasKit.Path.prototype.quadTo = function(cpx, cpy, x, y) { this._quadTo(cpx, cpy, x, y); return this; }; CanvasKit.Path.prototype.rArcTo = function(rx, ry, xAxisRotate, useSmallArc, isCCW, dx, dy) { this._rArcTo(rx, ry, xAxisRotate, useSmallArc, isCCW, dx, dy); return this; }; CanvasKit.Path.prototype.rConicTo = function(dx1, dy1, dx2, dy2, w) { this._rConicTo(dx1, dy1, dx2, dy2, w); return this; }; // These params are all relative CanvasKit.Path.prototype.rCubicTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { this._rCubicTo(cp1x, cp1y, cp2x, cp2y, x, y); return this; }; CanvasKit.Path.prototype.rLineTo = function(dx, dy) { this._rLineTo(dx, dy); return this; }; CanvasKit.Path.prototype.rMoveTo = function(dx, dy) { this._rMoveTo(dx, dy); return this; }; // These params are all relative CanvasKit.Path.prototype.rQuadTo = function(cpx, cpy, x, y) { this._rQuadTo(cpx, cpy, x, y); return this; }; CanvasKit.Path.prototype.stroke = function(opts) { // Fill out any missing values with the default values. opts = opts || {}; opts['width'] = opts['width'] || 1; opts['miter_limit'] = opts['miter_limit'] || 4; opts['cap'] = opts['cap'] || CanvasKit.StrokeCap.Butt; opts['join'] = opts['join'] || CanvasKit.StrokeJoin.Miter; opts['precision'] = opts['precision'] || 1; if (this._stroke(opts)) { return this; } return null; }; // TODO(kjlubick) Change this to take a 3x3 or 4x4 matrix (optionally malloc'd) CanvasKit.Path.prototype.transform = function() { // Takes 1 or 9 args if (arguments.length === 1) { // argument 1 should be a 6 or 9 element array. var a = arguments[0]; this._transform(a[0], a[1], a[2], a[3], a[4], a[5], a[6] || 0, a[7] || 0, a[8] || 1); } else if (arguments.length === 6 || arguments.length === 9) { // these arguments are the 6 or 9 members of the matrix var a = arguments; this._transform(a[0], a[1], a[2], a[3], a[4], a[5], a[6] || 0, a[7] || 0, a[8] || 1); } else { throw 'transform expected to take 1 or 9 arguments. Got ' + arguments.length; } return this; }; // isComplement is optional, defaults to false CanvasKit.Path.prototype.trim = function(startT, stopT, isComplement) { if (this._trim(startT, stopT, !!isComplement)) { return this; } return null; }; // makeShaderCubic returns a shader for a given image, allowing it to be used on // a paint as well as other purposes. This shader will be higher quality than // other shader functions. See CubicResampler in SkSamplingOptions.h for more information // on the cubicResampler params. CanvasKit.Image.prototype.makeShaderCubic = function(xTileMode, yTileMode, cubicResamplerB, cubicResamplerC, localMatrix) { var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); return this._makeShaderCubic(xTileMode, yTileMode, cubicResamplerB, cubicResamplerC, localMatrixPtr); }; // makeShaderCubic returns a shader for a given image, allowing it to be used on // a paint as well as other purposes. This shader will draw more quickly than // other shader functions, but at a lower quality. CanvasKit.Image.prototype.makeShaderOptions = function(xTileMode, yTileMode, filterMode, mipmapMode, localMatrix) { var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); return this._makeShaderOptions(xTileMode, yTileMode, filterMode, mipmapMode, localMatrixPtr); }; function readPixels(source, srcX, srcY, imageInfo, destMallocObj, bytesPerRow, grCtx) { if (!bytesPerRow) { bytesPerRow = 4 * imageInfo['width']; if (imageInfo['colorType'] === CanvasKit.ColorType.RGBA_F16) { bytesPerRow *= 2; } else if (imageInfo['colorType'] === CanvasKit.ColorType.RGBA_F32) { bytesPerRow *= 4; } } var pBytes = bytesPerRow * imageInfo.height; var pPtr; if (destMallocObj) { pPtr = destMallocObj['byteOffset']; } else { pPtr = CanvasKit._malloc(pBytes); } var rv; if (grCtx) { rv = source._readPixels(imageInfo, pPtr, bytesPerRow, srcX, srcY, grCtx); } else { rv = source._readPixels(imageInfo, pPtr, bytesPerRow, srcX, srcY); } if (!rv) { Debug('Could not read pixels with the given inputs'); if (!destMallocObj) { CanvasKit._free(pPtr); } return null; } // If the user provided us a buffer to copy into, we don't need to allocate a new TypedArray. if (destMallocObj) { return destMallocObj['toTypedArray'](); // Return the typed array wrapper w/o allocating. } // Put those pixels into a typed array of the right format and then // make a copy with slice() that we can return. var retVal = null; switch (imageInfo['colorType']) { case CanvasKit.ColorType.RGBA_8888: case CanvasKit.ColorType.RGBA_F16: // there is no half-float JS type, so we return raw bytes. retVal = new Uint8Array(CanvasKit.HEAPU8.buffer, pPtr, pBytes).slice(); break; case CanvasKit.ColorType.RGBA_F32: retVal = new Float32Array(CanvasKit.HEAPU8.buffer, pPtr, pBytes).slice(); break; default: Debug('ColorType not yet supported'); return null; } // Free the allocated pixels in the WASM memory CanvasKit._free(pPtr); return retVal; } CanvasKit.Image.prototype.readPixels = function(srcX, srcY, imageInfo, destMallocObj, bytesPerRow) { var grCtx = CanvasKit.getCurrentGrDirectContext(); return readPixels(this, srcX, srcY, imageInfo, destMallocObj, bytesPerRow, grCtx); }; // Accepts an array of four numbers in the range of 0-1 representing a 4f color CanvasKit.Canvas.prototype.clear = function(color4f) { CanvasKit.setCurrentContext(this._context); var cPtr = copyColorToWasm(color4f); this._clear(cPtr); }; CanvasKit.Canvas.prototype.clipRRect = function(rrect, op, antialias) { CanvasKit.setCurrentContext(this._context); var rPtr = copyRRectToWasm(rrect); this._clipRRect(rPtr, op, antialias); }; CanvasKit.Canvas.prototype.clipRect = function(rect, op, antialias) { CanvasKit.setCurrentContext(this._context); var rPtr = copyRectToWasm(rect); this._clipRect(rPtr, op, antialias); }; // concat takes a 3x2, a 3x3, or a 4x4 matrix and upscales it (if needed) to 4x4. This is because // under the hood, SkCanvas uses a 4x4 matrix. CanvasKit.Canvas.prototype.concat = function(matr) { CanvasKit.setCurrentContext(this._context); var matrPtr = copy4x4MatrixToWasm(matr); this._concat(matrPtr); }; CanvasKit.Canvas.prototype.drawArc = function(oval, startAngle, sweepAngle, useCenter, paint) { CanvasKit.setCurrentContext(this._context); var oPtr = copyRectToWasm(oval); this._drawArc(oPtr, startAngle, sweepAngle, useCenter, paint); }; // atlas is an Image, e.g. from CanvasKit.MakeImageFromEncoded // srcRects, dstXformsshould be arrays of floats of length 4*number of destinations. // The colors param is optional and is used to tint the drawn images using the optional blend // mode. Colors can be a Uint32Array of int colors or a flat Float32Array of float colors. CanvasKit.Canvas.prototype.drawAtlas = function(atlas, srcRects, dstXforms, paint, /* optional */ blendMode, /* optional */ colors, /* optional */ sampling) { if (!atlas || !paint || !srcRects || !dstXforms) { Debug('Doing nothing since missing a required input'); return; } // builder arguments report the length as the number of rects, but when passed as arrays // their.length attribute is 4x higher because it's the number of total components of all rects. // colors is always going to report the same length, at least until floats colors are supported // by this function. if (srcRects.length !== dstXforms.length) { Debug('Doing nothing since input arrays length mismatches'); return; } CanvasKit.setCurrentContext(this._context); if (!blendMode) { blendMode = CanvasKit.BlendMode.SrcOver; } var srcRectPtr = copy1dArray(srcRects, 'HEAPF32'); var dstXformPtr = copy1dArray(dstXforms, 'HEAPF32'); var count = dstXforms.length / 4; var colorPtr = copy1dArray(assureIntColors(colors), 'HEAPU32'); // We require one of these: // 1. sampling is null (we default to linear/none) // 2. sampling.B and sampling.C --> CubicResampler // 3. sampling.filter [and sampling.mipmap] --> FilterOptions // // Thus if all fields are available, we will choose cubic (since we search for B,C first) if (sampling && ('B' in sampling) && ('C' in sampling)) { this._drawAtlasCubic(atlas, dstXformPtr, srcRectPtr, colorPtr, count, blendMode, sampling['B'], sampling['C'], paint); } else { let filter = CanvasKit.FilterMode.Linear; let mipmap = CanvasKit.MipmapMode.None; if (sampling) { filter = sampling['filter']; // 'filter' is a required field if ('mipmap' in sampling) { // 'mipmap' is optional mipmap = sampling['mipmap']; } } this._drawAtlasOptions(atlas, dstXformPtr, srcRectPtr, colorPtr, count, blendMode, filter, mipmap, paint); } freeArraysThatAreNotMallocedByUsers(srcRectPtr, srcRects); freeArraysThatAreNotMallocedByUsers(dstXformPtr, dstXforms); freeArraysThatAreNotMallocedByUsers(colorPtr, colors); }; CanvasKit.Canvas.prototype.drawCircle = function(cx, cy, r, paint) { CanvasKit.setCurrentContext(this._context); this._drawCircle(cx, cy, r, paint); } CanvasKit.Canvas.prototype.drawColor = function(color4f, mode) { CanvasKit.setCurrentContext(this._context); var cPtr = copyColorToWasm(color4f); if (mode !== undefined) { this._drawColor(cPtr, mode); } else { this._drawColor(cPtr); } }; CanvasKit.Canvas.prototype.drawColorInt = function(color, mode) { CanvasKit.setCurrentContext(this._context); this._drawColorInt(color, mode || CanvasKit.BlendMode.SrcOver); } CanvasKit.Canvas.prototype.drawColorComponents = function(r, g, b, a, mode) { CanvasKit.setCurrentContext(this._context); var cPtr = copyColorComponentsToWasm(r, g, b, a); if (mode !== undefined) { this._drawColor(cPtr, mode); } else { this._drawColor(cPtr); } }; CanvasKit.Canvas.prototype.drawDRRect = function(outer, inner, paint) { CanvasKit.setCurrentContext(this._context); var oPtr = copyRRectToWasm(outer, _scratchRRectPtr); var iPtr = copyRRectToWasm(inner, _scratchRRect2Ptr); this._drawDRRect(oPtr, iPtr, paint); }; CanvasKit.Canvas.prototype.drawImage = function(img, x, y, paint) { CanvasKit.setCurrentContext(this._context); this._drawImage(img, x, y, paint || null); }; CanvasKit.Canvas.prototype.drawImageCubic = function(img, x, y, b, c, paint) { CanvasKit.setCurrentContext(this._context); this._drawImageCubic(img, x, y, b, c, paint || null); }; CanvasKit.Canvas.prototype.drawImageOptions = function(img, x, y, filter, mipmap, paint) { CanvasKit.setCurrentContext(this._context); this._drawImageOptions(img, x, y, filter, mipmap, paint || null); }; CanvasKit.Canvas.prototype.drawImageNine = function(img, center, dest, filter, paint) { CanvasKit.setCurrentContext(this._context); var cPtr = copyIRectToWasm(center); var dPtr = copyRectToWasm(dest); this._drawImageNine(img, cPtr, dPtr, filter, paint || null); }; CanvasKit.Canvas.prototype.drawImageRect = function(img, src, dest, paint, fastSample) { CanvasKit.setCurrentContext(this._context); copyRectToWasm(src, _scratchFourFloatsAPtr); copyRectToWasm(dest, _scratchFourFloatsBPtr); this._drawImageRect(img, _scratchFourFloatsAPtr, _scratchFourFloatsBPtr, paint, !!fastSample); }; CanvasKit.Canvas.prototype.drawImageRectCubic = function(img, src, dest, B, C, paint) { CanvasKit.setCurrentContext(this._context); copyRectToWasm(src, _scratchFourFloatsAPtr); copyRectToWasm(dest, _scratchFourFloatsBPtr); this._drawImageRectCubic(img, _scratchFourFloatsAPtr, _scratchFourFloatsBPtr, B, C, paint || null); }; CanvasKit.Canvas.prototype.drawImageRectOptions = function(img, src, dest, filter, mipmap, paint) { CanvasKit.setCurrentContext(this._context); copyRectToWasm(src, _scratchFourFloatsAPtr); copyRectToWasm(dest, _scratchFourFloatsBPtr); this._drawImageRectOptions(img, _scratchFourFloatsAPtr, _scratchFourFloatsBPtr, filter, mipmap, paint || null); }; CanvasKit.Canvas.prototype.drawLine = function(x1, y1, x2, y2, paint) { CanvasKit.setCurrentContext(this._context); this._drawLine(x1, y1, x2, y2, paint); } CanvasKit.Canvas.prototype.drawOval = function(oval, paint) { CanvasKit.setCurrentContext(this._context); var oPtr = copyRectToWasm(oval); this._drawOval(oPtr, paint); }; CanvasKit.Canvas.prototype.drawPaint = function(paint) { CanvasKit.setCurrentContext(this._context); this._drawPaint(paint); } CanvasKit.Canvas.prototype.drawParagraph = function(p, x, y) { CanvasKit.setCurrentContext(this._context); this._drawParagraph(p, x, y); } CanvasKit.Canvas.prototype.drawPatch = function(cubics, colors, texs, mode, paint) { if (cubics.length < 24) { throw 'Need 12 cubic points'; } if (colors && colors.length < 4) { throw 'Need 4 colors'; } if (texs && texs.length < 8) { throw 'Need 4 shader coordinates'; } CanvasKit.setCurrentContext(this._context); const cubics_ptr = copy1dArray(cubics, 'HEAPF32'); const colors_ptr = colors ? copy1dArray(assureIntColors(colors), 'HEAPU32') : nullptr; const texs_ptr = texs ? copy1dArray(texs, 'HEAPF32') : nullptr; if (!mode) { mode = CanvasKit.BlendMode.Modulate; } this._drawPatch(cubics_ptr, colors_ptr, texs_ptr, mode, paint); freeArraysThatAreNotMallocedByUsers(texs_ptr, texs); freeArraysThatAreNotMallocedByUsers(colors_ptr, colors); freeArraysThatAreNotMallocedByUsers(cubics_ptr, cubics); }; CanvasKit.Canvas.prototype.drawPath = function(path, paint) { CanvasKit.setCurrentContext(this._context); this._drawPath(path, paint); } CanvasKit.Canvas.prototype.drawPicture = function(pic) { CanvasKit.setCurrentContext(this._context); this._drawPicture(pic); } // points is a 1d array of length 2n representing n points where the even indices // will be treated as x coordinates and the odd indices will be treated as y coordinates. // Like other APIs, this accepts a malloced type array or malloc obj. CanvasKit.Canvas.prototype.drawPoints = function(mode, points, paint) { CanvasKit.setCurrentContext(this._context); var ptr = copy1dArray(points, 'HEAPF32'); this._drawPoints(mode, ptr, points.length / 2, paint); freeArraysThatAreNotMallocedByUsers(ptr, points); }; CanvasKit.Canvas.prototype.drawRRect = function(rrect, paint) { CanvasKit.setCurrentContext(this._context); var rPtr = copyRRectToWasm(rrect); this._drawRRect(rPtr, paint); }; CanvasKit.Canvas.prototype.drawRect = function(rect, paint) { CanvasKit.setCurrentContext(this._context); var rPtr = copyRectToWasm(rect); this._drawRect(rPtr, paint); }; CanvasKit.Canvas.prototype.drawRect4f = function(l, t, r, b, paint) { CanvasKit.setCurrentContext(this._context); this._drawRect4f(l, t, r, b, paint); } CanvasKit.Canvas.prototype.drawShadow = function(path, zPlaneParams, lightPos, lightRadius, ambientColor, spotColor, flags) { CanvasKit.setCurrentContext(this._context); var ambiPtr = copyColorToWasmNoScratch(ambientColor); var spotPtr = copyColorToWasmNoScratch(spotColor); // We use the return value from copy1dArray in case the passed in arrays are malloc'd. var zPlanePtr = copy1dArray(zPlaneParams, 'HEAPF32', _scratchThreeFloatsAPtr); var lightPosPtr = copy1dArray(lightPos, 'HEAPF32', _scratchThreeFloatsBPtr); this._drawShadow(path, zPlanePtr, lightPosPtr, lightRadius, ambiPtr, spotPtr, flags); freeArraysThatAreNotMallocedByUsers(ambiPtr, ambientColor); freeArraysThatAreNotMallocedByUsers(spotPtr, spotColor); }; CanvasKit.getShadowLocalBounds = function(ctm, path, zPlaneParams, lightPos, lightRadius, flags, optOutputRect) { var ctmPtr = copy3x3MatrixToWasm(ctm); // We use the return value from copy1dArray in case the passed in arrays are malloc'd. var zPlanePtr = copy1dArray(zPlaneParams, 'HEAPF32', _scratchThreeFloatsAPtr); var lightPosPtr = copy1dArray(lightPos, 'HEAPF32', _scratchThreeFloatsBPtr); var ok = this._getShadowLocalBounds(ctmPtr, path, zPlanePtr, lightPosPtr, lightRadius, flags, _scratchFourFloatsAPtr); if (!ok) { return null; } var ta = _scratchFourFloatsA['toTypedArray'](); if (optOutputRect) { optOutputRect.set(ta); return optOutputRect; } return ta.slice(); }; CanvasKit.Canvas.prototype.drawTextBlob = function(blob, x, y, paint) { CanvasKit.setCurrentContext(this._context); this._drawTextBlob(blob, x, y, paint); } CanvasKit.Canvas.prototype.drawVertices = function(verts, mode, paint) { CanvasKit.setCurrentContext(this._context); this._drawVertices(verts, mode, paint); } // getDeviceClipBounds returns an SkIRect CanvasKit.Canvas.prototype.getDeviceClipBounds = function(outputRect) { // _getDeviceClipBounds will copy the values into the pointer. this._getDeviceClipBounds(_scratchIRectPtr); return copyIRectFromWasm(_scratchIRect, outputRect); }; // getLocalToDevice returns a 4x4 matrix. CanvasKit.Canvas.prototype.getLocalToDevice = function() { // _getLocalToDevice will copy the values into the pointer. this._getLocalToDevice(_scratch4x4MatrixPtr); return copy4x4MatrixFromWasm(_scratch4x4MatrixPtr); }; // getTotalMatrix returns the current matrix as a 3x3 matrix. CanvasKit.Canvas.prototype.getTotalMatrix = function() { // _getTotalMatrix will copy the values into the pointer. this._getTotalMatrix(_scratch3x3MatrixPtr); // read them out into an array. TODO(kjlubick): If we change Matrix to be // typedArrays, then we should return a typed array here too. var rv = new Array(9); for (var i = 0; i < 9; i++) { rv[i] = CanvasKit.HEAPF32[_scratch3x3MatrixPtr/4 + i]; // divide by 4 to "cast" to float. } return rv; }; CanvasKit.Canvas.prototype.makeSurface = function(imageInfo) { var s = this._makeSurface(imageInfo); s._context = this._context; return s; }; CanvasKit.Canvas.prototype.readPixels = function(srcX, srcY, imageInfo, destMallocObj, bytesPerRow) { CanvasKit.setCurrentContext(this._context); return readPixels(this, srcX, srcY, imageInfo, destMallocObj, bytesPerRow); }; CanvasKit.Canvas.prototype.saveLayer = function(paint, boundsRect, backdrop, flags) { // bPtr will be 0 (nullptr) if boundsRect is undefined/null. var bPtr = copyRectToWasm(boundsRect); // These or clauses help emscripten, which does not deal with undefined well. return this._saveLayer(paint || null, bPtr, backdrop || null, flags || 0); }; // pixels should be a Uint8Array or a plain JS array. CanvasKit.Canvas.prototype.writePixels = function(pixels, srcWidth, srcHeight, destX, destY, alphaType, colorType, colorSpace) { if (pixels.byteLength % (srcWidth * srcHeight)) { throw 'pixels length must be a multiple of the srcWidth * srcHeight'; } CanvasKit.setCurrentContext(this._context); var bytesPerPixel = pixels.byteLength / (srcWidth * srcHeight); // supply defaults (which are compatible with HTMLCanvas's putImageData) alphaType = alphaType || CanvasKit.AlphaType.Unpremul; colorType = colorType || CanvasKit.ColorType.RGBA_8888; colorSpace = colorSpace || CanvasKit.ColorSpace.SRGB; var srcRowBytes = bytesPerPixel * srcWidth; var pptr = copy1dArray(pixels, 'HEAPU8'); var ok = this._writePixels({ 'width': srcWidth, 'height': srcHeight, 'colorType': colorType, 'alphaType': alphaType, 'colorSpace': colorSpace, }, pptr, srcRowBytes, destX, destY); freeArraysThatAreNotMallocedByUsers(pptr, pixels); return ok; }; CanvasKit.ColorFilter.MakeBlend = function(color4f, mode, colorSpace) { var cPtr = copyColorToWasm(color4f); colorSpace = colorSpace || CanvasKit.ColorSpace.SRGB; return CanvasKit.ColorFilter._MakeBlend(cPtr, mode, colorSpace); }; // colorMatrix is an ColorMatrix (e.g. Float32Array of length 20) CanvasKit.ColorFilter.MakeMatrix = function(colorMatrix) { if (!colorMatrix || colorMatrix.length !== 20) { throw 'invalid color matrix'; } var fptr = copy1dArray(colorMatrix, 'HEAPF32'); // We know skia memcopies the floats, so we can free our memory after the call returns. var m = CanvasKit.ColorFilter._makeMatrix(fptr); freeArraysThatAreNotMallocedByUsers(fptr, colorMatrix); return m; }; CanvasKit.ContourMeasure.prototype.getPosTan = function(distance, optionalOutput) { this._getPosTan(distance, _scratchFourFloatsAPtr); var ta = _scratchFourFloatsA['toTypedArray'](); if (optionalOutput) { optionalOutput.set(ta); return optionalOutput; } return ta.slice(); }; CanvasKit.ImageFilter.MakeDropShadow = function(dx, dy, sx, sy, color, input) { var cPtr = copyColorToWasm(color, _scratchColorPtr); return CanvasKit.ImageFilter._MakeDropShadow(dx, dy, sx, sy, cPtr, input); }; CanvasKit.ImageFilter.MakeDropShadowOnly = function(dx, dy, sx, sy, color, input) { var cPtr = copyColorToWasm(color, _scratchColorPtr); return CanvasKit.ImageFilter._MakeDropShadowOnly(dx, dy, sx, sy, cPtr, input); }; CanvasKit.ImageFilter.MakeImage = function(img, sampling, srcRect, dstRect) { var srcPtr = copyRectToWasm(srcRect, _scratchFourFloatsAPtr); var dstPtr = copyRectToWasm(dstRect, _scratchFourFloatsBPtr); if ('B' in sampling && 'C' in sampling) { return CanvasKit.ImageFilter._MakeImageCubic(img, sampling['B'], sampling['C'], srcPtr, dstPtr); } else { const filter = sampling['filter']; // 'filter' is a required field let mipmap = CanvasKit.MipmapMode.None; if ('mipmap' in sampling) { // 'mipmap' is optional mipmap = sampling['mipmap']; } return CanvasKit.ImageFilter._MakeImageOptions(img, filter, mipmap, srcPtr, dstPtr); } }; CanvasKit.ImageFilter.MakeMatrixTransform = function(matrix, sampling, input) { var matrPtr = copy3x3MatrixToWasm(matrix); if ('B' in sampling && 'C' in sampling) { return CanvasKit.ImageFilter._MakeMatrixTransformCubic(matrPtr, sampling['B'], sampling['C'], input); } else { const filter = sampling['filter']; // 'filter' is a required field let mipmap = CanvasKit.MipmapMode.None; if ('mipmap' in sampling) { // 'mipmap' is optional mipmap = sampling['mipmap']; } return CanvasKit.ImageFilter._MakeMatrixTransformOptions(matrPtr, filter, mipmap, input); } }; CanvasKit.Paint.prototype.getColor = function() { this._getColor(_scratchColorPtr); return copyColorFromWasm(_scratchColorPtr); }; CanvasKit.Paint.prototype.setColor = function(color4f, colorSpace) { colorSpace = colorSpace || null; // null will be replaced with sRGB in the C++ method. // emscripten wouldn't bind undefined to the sk_sp<ColorSpace> expected here. var cPtr = copyColorToWasm(color4f); this._setColor(cPtr, colorSpace); }; // The color components here are expected to be floating point values (nominally between // 0.0 and 1.0, but with wider color gamuts, the values could exceed this range). To convert // between standard 8 bit colors and floats, just divide by 255 before passing them in. CanvasKit.Paint.prototype.setColorComponents = function(r, g, b, a, colorSpace) { colorSpace = colorSpace || null; // null will be replaced with sRGB in the C++ method. // emscripten wouldn't bind undefined to the sk_sp<ColorSpace> expected here. var cPtr = copyColorComponentsToWasm(r, g, b, a); this._setColor(cPtr, colorSpace); }; CanvasKit.Path.prototype.getPoint = function(idx, optionalOutput) { // This will copy 2 floats into a space for 4 floats this._getPoint(idx, _scratchFourFloatsAPtr); var ta = _scratchFourFloatsA['toTypedArray'](); if (optionalOutput) { // We cannot call optionalOutput.set() because it is an error to call .set() with // a source bigger than the destination. optionalOutput[0] = ta[0]; optionalOutput[1] = ta[1]; return optionalOutput; } // Be sure to return a copy of just the first 2 values. return ta.slice(0, 2); }; CanvasKit.Picture.prototype.makeShader = function(tmx, tmy, mode, matr, rect) { var mPtr = copy3x3MatrixToWasm(matr); var rPtr = copyRectToWasm(rect); return this._makeShader(tmx, tmy, mode, mPtr, rPtr); }; CanvasKit.PictureRecorder.prototype.beginRecording = function(bounds) { var bPtr = copyRectToWasm(bounds); return this._beginRecording(bPtr); }; CanvasKit.Surface.prototype.getCanvas = function() { var c = this._getCanvas(); c._context = this._context; return c; }; CanvasKit.Surface.prototype.makeImageSnapshot = function(optionalBoundsRect) { CanvasKit.setCurrentContext(this._context); var bPtr = copyIRectToWasm(optionalBoundsRect); return this._makeImageSnapshot(bPtr); }; CanvasKit.Surface.prototype.makeSurface = function(imageInfo) { CanvasKit.setCurrentContext(this._context); var s = this._makeSurface(imageInfo); s._context = this._context; return s; }; CanvasKit.Surface.prototype._requestAnimationFrameInternal = function(callback, dirtyRect) { if (!this._cached_canvas) { this._cached_canvas = this.getCanvas(); } return requestAnimationFrame(function() { CanvasKit.setCurrentContext(this._context); callback(this._cached_canvas); // We do not dispose() of the Surface here, as the client will typically // call requestAnimationFrame again from within the supplied callback. // For drawing a single frame, prefer drawOnce(). this.flush(dirtyRect); }.bind(this)); }; if (!CanvasKit.Surface.prototype.requestAnimationFrame) { CanvasKit.Surface.prototype.requestAnimationFrame = CanvasKit.Surface.prototype._requestAnimationFrameInternal; } // drawOnce will dispose of the surface after drawing the frame using the provided // callback. CanvasKit.Surface.prototype._drawOnceInternal = function(callback, dirtyRect) { if (!this._cached_canvas) { this._cached_canvas = this.getCanvas(); } requestAnimationFrame(function() { CanvasKit.setCurrentContext(this._context); callback(this._cached_canvas); this.flush(dirtyRect); this.dispose(); }.bind(this)); }; if (!CanvasKit.Surface.prototype.drawOnce) { CanvasKit.Surface.prototype.drawOnce = CanvasKit.Surface.prototype._drawOnceInternal; } CanvasKit.PathEffect.MakeDash = function(intervals, phase) { if (!phase) { phase = 0; } if (!intervals.length || intervals.length % 2 === 1) { throw 'Intervals array must have even length'; } var ptr = copy1dArray(intervals, 'HEAPF32'); var dpe = CanvasKit.PathEffect._MakeDash(ptr, intervals.length, phase); freeArraysThatAreNotMallocedByUsers(ptr, intervals); return dpe; }; CanvasKit.PathEffect.MakeLine2D = function(width, matrix) { var matrixPtr = copy3x3MatrixToWasm(matrix); return CanvasKit.PathEffect._MakeLine2D(width, matrixPtr); }; CanvasKit.PathEffect.MakePath2D = function(matrix, path) { var matrixPtr = copy3x3MatrixToWasm(matrix); return CanvasKit.PathEffect._MakePath2D(matrixPtr, path); }; CanvasKit.Shader.MakeColor = function(color4f, colorSpace) { colorSpace = colorSpace || null; var cPtr = copyColorToWasm(color4f); return CanvasKit.Shader._MakeColor(cPtr, colorSpace); }; // TODO(kjlubick) remove deprecated names. CanvasKit.Shader.Blend = CanvasKit.Shader.MakeBlend; CanvasKit.Shader.Color = CanvasKit.Shader.MakeColor; CanvasKit.Shader.MakeLinearGradient = function(start, end, colors, pos, mode, localMatrix, flags, colorSpace) { colorSpace = colorSpace || null; var cPtrInfo = copyFlexibleColorArray(colors); var posPtr = copy1dArray(pos, 'HEAPF32'); flags = flags || 0; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); // Copy start and end to _scratchFourFloatsAPtr. var startEndPts = _scratchFourFloatsA['toTypedArray'](); startEndPts.set(start); startEndPts.set(end, 2); var lgs = CanvasKit.Shader._MakeLinearGradient(_scratchFourFloatsAPtr, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr, cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace); freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors); pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos); return lgs; }; CanvasKit.Shader.MakeRadialGradient = function(center, radius, colors, pos, mode, localMatrix, flags, colorSpace) { colorSpace = colorSpace || null; var cPtrInfo = copyFlexibleColorArray(colors); var posPtr = copy1dArray(pos, 'HEAPF32'); flags = flags || 0; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); var rgs = CanvasKit.Shader._MakeRadialGradient(center[0], center[1], radius, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr, cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace); freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors); pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos); return rgs; }; CanvasKit.Shader.MakeSweepGradient = function(cx, cy, colors, pos, mode, localMatrix, flags, startAngle, endAngle, colorSpace) { colorSpace = colorSpace || null; var cPtrInfo = copyFlexibleColorArray(colors); var posPtr = copy1dArray(pos, 'HEAPF32'); flags = flags || 0; startAngle = startAngle || 0; endAngle = endAngle || 360; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); var sgs = CanvasKit.Shader._MakeSweepGradient(cx, cy, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr, cPtrInfo.count, mode, startAngle, endAngle, flags, localMatrixPtr, colorSpace); freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors); pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos); return sgs; }; CanvasKit.Shader.MakeTwoPointConicalGradient = function(start, startRadius, end, endRadius, colors, pos, mode, localMatrix, flags, colorSpace) { colorSpace = colorSpace || null; var cPtrInfo = copyFlexibleColorArray(colors); var posPtr = copy1dArray(pos, 'HEAPF32'); flags = flags || 0; var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); // Copy start and end to _scratchFourFloatsAPtr. var startEndPts = _scratchFourFloatsA['toTypedArray'](); startEndPts.set(start); startEndPts.set(end, 2); var rgs = CanvasKit.Shader._MakeTwoPointConicalGradient(_scratchFourFloatsAPtr, startRadius, endRadius, cPtrInfo.colorPtr, cPtrInfo.colorType, posPtr, cPtrInfo.count, mode, flags, localMatrixPtr, colorSpace); freeArraysThatAreNotMallocedByUsers(cPtrInfo.colorPtr, colors); pos && freeArraysThatAreNotMallocedByUsers(posPtr, pos); return rgs; }; // Clients can pass in a Float32Array with length 4 to this and the results // will be copied into that array. Otherwise, a new TypedArray will be allocated // and returned. CanvasKit.Vertices.prototype.bounds = function(optionalOutputArray) { this._bounds(_scratchFourFloatsAPtr); var ta = _scratchFourFloatsA['toTypedArray'](); if (optionalOutputArray) { optionalOutputArray.set(ta); return optionalOutputArray; } return ta.slice(); }; // Run through the JS files that are added at compile time. if (CanvasKit._extraInitializations) { CanvasKit._extraInitializations.forEach(function(init) { init(); }); } }; // end CanvasKit.onRuntimeInitialized, that is, anything changing prototypes or dynamic. // Accepts an object holding two canvaskit colors. // { // ambient: [r, g, b, a], // spot: [r, g, b, a], // } // Returns the same format. Note, if malloced colors are passed in, the memory // housing the passed in colors passed in will be overwritten with the computed // tonal colors. CanvasKit.computeTonalColors = function(tonalColors) { // copy the colors into WASM var cPtrAmbi = copyColorToWasmNoScratch(tonalColors['ambient']); var cPtrSpot = copyColorToWasmNoScratch(tonalColors['spot']); // The output of this function will be the same pointers we passed in. this._computeTonalColors(cPtrAmbi, cPtrSpot); // Read the results out. var result = { 'ambient': copyColorFromWasm(cPtrAmbi), 'spot': copyColorFromWasm(cPtrSpot), }; // If the user passed us malloced colors in here, we don't want to clean them up. freeArraysThatAreNotMallocedByUsers(cPtrAmbi, tonalColors['ambient']); freeArraysThatAreNotMallocedByUsers(cPtrSpot, tonalColors['spot']); return result; }; CanvasKit.LTRBRect = function(l, t, r, b) { return Float32Array.of(l, t, r, b); }; CanvasKit.XYWHRect = function(x, y, w, h) { return Float32Array.of(x, y, x+w, y+h); }; CanvasKit.LTRBiRect = function(l, t, r, b) { return Int32Array.of(l, t, r, b); }; CanvasKit.XYWHiRect = function(x, y, w, h) { return Int32Array.of(x, y, x+w, y+h); }; // RRectXY returns a TypedArray representing an RRect with the given rect and a radiusX and // radiusY for all 4 corners. CanvasKit.RRectXY = function(rect, rx, ry) { return Float32Array.of( rect[0], rect[1], rect[2], rect[3], rx, ry, rx, ry, rx, ry, rx, ry, ); }; // data is a TypedArray or ArrayBuffer e.g. from fetch().then(resp.arrayBuffer()) CanvasKit.MakeAnimatedImageFromEncoded = function(data) { data = new Uint8Array(data); var iptr = CanvasKit._malloc(data.byteLength); CanvasKit.HEAPU8.set(data, iptr); var img = CanvasKit._decodeAnimatedImage(iptr, data.byteLength); if (!img) { Debug('Could not decode animated image'); return null; } return img; }; // data is a TypedArray or ArrayBuffer e.g. from fetch().then(resp.arrayBuffer()) CanvasKit.MakeImageFromEncoded = function(data) { data = new Uint8Array(data); var iptr = CanvasKit._malloc(data.byteLength); CanvasKit.HEAPU8.set(data, iptr); var img = CanvasKit._decodeImage(iptr, data.byteLength); if (!img) { Debug('Could not decode image'); return null; } return img; }; // A variable to hold a canvasElement which can be reused once created the first time. var memoizedCanvas2dElement = null; // Alternative to CanvasKit.MakeImageFromEncoded. Allows for CanvasKit users to take advantage of // browser APIs to decode images instead of using codecs included in the CanvasKit wasm binary. // Expects that the canvasImageSource has already loaded/decoded. // CanvasImageSource reference: https://developer.mozilla.org/en-US/docs/Web/API/CanvasImageSource CanvasKit.MakeImageFromCanvasImageSource = function(canvasImageSource) { var width = canvasImageSource.width; var height = canvasImageSource.height; if (!memoizedCanvas2dElement) { memoizedCanvas2dElement = document.createElement('canvas'); } memoizedCanvas2dElement.width = width; memoizedCanvas2dElement.height = height; var ctx2d = memoizedCanvas2dElement.getContext('2d', {willReadFrequently: true}); ctx2d.drawImage(canvasImageSource, 0, 0); var imageData = ctx2d.getImageData(0, 0, width, height); return CanvasKit.MakeImage({ 'width': width, 'height': height, 'alphaType': CanvasKit.AlphaType.Unpremul, 'colorType': CanvasKit.ColorType.RGBA_8888, 'colorSpace': CanvasKit.ColorSpace.SRGB }, imageData.data, 4 * width); }; // pixels may be an array but Uint8Array or Uint8ClampedArray is recommended, // with the bytes representing the pixel values. // (e.g. each set of 4 bytes could represent RGBA values for a single pixel). CanvasKit.MakeImage = function(info, pixels, bytesPerRow) { var pptr = CanvasKit._malloc(pixels.length); CanvasKit.HEAPU8.set(pixels, pptr); // We always want to copy the bytes into the WASM heap. // No need to _free pptr, Image takes it with SkData::MakeFromMalloc return CanvasKit._MakeImage(info, pptr, pixels.length, bytesPerRow); }; // Colors may be a Uint32Array of int colors, a Flat Float32Array of float colors // or a 2d Array of Float32Array(4) (deprecated) // the underlying Skia function accepts only int colors so it is recommended // to pass an array of int colors to avoid an extra conversion. CanvasKit.MakeVertices = function(mode, positions, textureCoordinates, colors, indices, isVolatile) { // Default isVolatile to true if not set isVolatile = isVolatile === undefined ? true : isVolatile; var idxCount = (indices && indices.length) || 0; var flags = 0; // These flags are from SkVertices.h and should be kept in sync with those. if (textureCoordinates && textureCoordinates.length) { flags |= (1 << 0); } if (colors && colors.length) { flags |= (1 << 1); } if (!isVolatile) { flags |= (1 << 2); } var builder = new CanvasKit._VerticesBuilder(mode, positions.length / 2, idxCount, flags); copy1dArray(positions, 'HEAPF32', builder.positions()); if (builder.texCoords()) { copy1dArray(textureCoordinates, 'HEAPF32', builder.texCoords()); } if (builder.colors()) { copy1dArray(assureIntColors(colors), 'HEAPU32', builder.colors()); } if (builder.indices()) { copy1dArray(indices, 'HEAPU16', builder.indices()); } // Create the vertices, which owns the memory that the builder had allocated. return builder.detach(); }; /* * Add some helpers for matrices. This is ported from SkMatrix.cpp and others * to save complexity and overhead of going back and forth between C++ and JS layers. * I would have liked to use something like DOMMatrix, except it * isn't widely supported (would need polyfills) and it doesn't * have a mapPoints() function (which could maybe be tacked on here). * If DOMMatrix catches on, it would be worth re-considering this usage. */ CanvasKit.Matrix = {}; function sdot() { // to be called with an even number of scalar args var acc = 0; for (var i=0; i < arguments.length-1; i+=2) { acc += arguments[i] * arguments[i+1]; } return acc; } // Private general matrix functions used in both 3x3s and 4x4s. // Return a square identity matrix of size n. var identityN = function(n) { var size = n*n; var m = new Array(size); while(size--) { m[size] = size%(n+1) === 0 ? 1.0 : 0.0; } return m; }; // Stride, a function for compactly representing several ways of copying an array into another. // Write vector `v` into matrix `m`. `m` is a matrix encoded as an array in row-major // order. Its width is passed as `width`. `v` is an array with length < (m.length/width). // An element of `v` is copied into `m` starting at `offset` and moving `colStride` cols right // each row. // // For example, a width of 4, offset of 3, and stride of -1 would put the vector here. // _ _ 0 _ // _ 1 _ _ // 2 _ _ _ // _ _ _ 3 // var stride = function(v, m, width, offset, colStride) { for (var i=0; i<v.length; i++) { m[i * width + // column (i * colStride + offset + width) % width // row ] = v[i]; } return m; }; CanvasKit.Matrix.identity = function() { return identityN(3); }; // Return the inverse (if it exists) of this matrix. // Otherwise, return null. CanvasKit.Matrix.invert = function(m) { // Find the determinant by the sarrus rule. https://en.wikipedia.org/wiki/Rule_of_Sarrus var det = m[0]*m[4]*m[8] + m[1]*m[5]*m[6] + m[2]*m[3]*m[7] - m[2]*m[4]*m[6] - m[1]*m[3]*m[8] - m[0]*m[5]*m[7]; if (!det) { Debug('Warning, uninvertible matrix'); return null; } // Return the inverse by the formula adj(m)/det. // adj (adjugate) of a 3x3 is the transpose of it's cofactor matrix. // a cofactor matrix is a matrix where each term is +-det(N) where matrix N is the 2x2 formed // by removing the row and column we're currently setting from the source. // the sign alternates in a checkerboard pattern with a `+` at the top left. // that's all been combined here into one expression. return [ (m[4]*m[8] - m[5]*m[7])/det, (m[2]*m[7] - m[1]*m[8])/det, (m[1]*m[5] - m[2]*m[4])/det, (m[5]*m[6] - m[3]*m[8])/det, (m[0]*m[8] - m[2]*m[6])/det, (m[2]*m[3] - m[0]*m[5])/det, (m[3]*m[7] - m[4]*m[6])/det, (m[1]*m[6] - m[0]*m[7])/det, (m[0]*m[4] - m[1]*m[3])/det, ]; }; // Maps the given points according to the passed in matrix. // Results are done in place. // See SkMatrix.h::mapPoints for the docs on the math. CanvasKit.Matrix.mapPoints = function(matrix, ptArr) { if (IsDebug && (ptArr.length % 2)) { throw 'mapPoints requires an even length arr'; } for (var i = 0; i < ptArr.length; i+=2) { var x = ptArr[i], y = ptArr[i+1]; // Gx+Hy+I var denom = matrix[6]*x + matrix[7]*y + matrix[8]; // Ax+By+C var xTrans = matrix[0]*x + matrix[1]*y + matrix[2]; // Dx+Ey+F var yTrans = matrix[3]*x + matrix[4]*y + matrix[5]; ptArr[i] = xTrans/denom; ptArr[i+1] = yTrans/denom; } return ptArr; }; function isnumber(val) { return !isNaN(val); } // generalized iterative algorithm for multiplying two matrices. function multiply(m1, m2, size) { if (IsDebug && (!m1.every(isnumber) || !m2.every(isnumber))) { throw 'Some members of matrices are NaN m1='+m1+', m2='+m2+''; } if (IsDebug && (m1.length !== m2.length)) { throw 'Undefined for matrices of different sizes. m1.length='+m1.length+', m2.length='+m2.length; } if (IsDebug && (size*size !== m1.length)) { throw 'Undefined for non-square matrices. array size was '+size; } var result = Array(m1.length); for (var r = 0; r < size; r++) { for (var c = 0; c < size; c++) { // accumulate a sum of m1[r,k]*m2[k, c] var acc = 0; for (var k = 0; k < size; k++) { acc += m1[size * r + k] * m2[size * k + c]; } result[r * size + c] = acc; } } return result; } // Accept an integer indicating the size of the matrices being multiplied (3 for 3x3), and any // number of matrices following it. function multiplyMany(size, listOfMatrices) { if (IsDebug && (listOfMatrices.length < 2)) { throw 'multiplication expected two or more matrices'; } var result = multiply(listOfMatrices[0], listOfMatrices[1], size); var next = 2; while (next < listOfMatrices.length) { result = multiply(result, listOfMatrices[next], size); next++; } return result; } // Accept any number 3x3 of matrices as arguments, multiply them together. // Matrix multiplication is associative but not commutative. the order of the arguments // matters, but it does not matter that this implementation multiplies them left to right. CanvasKit.Matrix.multiply = function() { return multiplyMany(3, arguments); }; // Return a matrix representing a rotation by n radians. // px, py optionally say which point the rotation should be around // with the default being (0, 0); CanvasKit.Matrix.rotated = function(radians, px, py) { px = px || 0; py = py || 0; var sinV = Math.sin(radians); var cosV = Math.cos(radians); return [ cosV, -sinV, sdot( sinV, py, 1 - cosV, px), sinV, cosV, sdot(-sinV, px, 1 - cosV, py), 0, 0, 1, ]; }; CanvasKit.Matrix.scaled = function(sx, sy, px, py) { px = px || 0; py = py || 0; var m = stride([sx, sy], identityN(3), 3, 0, 1); return stride([px-sx*px, py-sy*py], m, 3, 2, 0); }; CanvasKit.Matrix.skewed = function(kx, ky, px, py) { px = px || 0; py = py || 0; var m = stride([kx, ky], identityN(3), 3, 1, -1); return stride([-kx*px, -ky*py], m, 3, 2, 0); }; CanvasKit.Matrix.translated = function(dx, dy) { return stride(arguments, identityN(3), 3, 2, 0); }; // Functions for manipulating vectors. // Loosely based off of SkV3 in SkM44.h but skia also has SkVec2 and Skv4. This combines them and // works on vectors of any length. CanvasKit.Vector = {}; CanvasKit.Vector.dot = function(a, b) { if (IsDebug && (a.length !== b.length)) { throw 'Cannot perform dot product on arrays of different length ('+a.length+' vs '+b.length+')'; } return a.map(function(v, i) { return v*b[i] }).reduce(function(acc, cur) { return acc + cur; }); }; CanvasKit.Vector.lengthSquared = function(v) { return CanvasKit.Vector.dot(v, v); }; CanvasKit.Vector.length = function(v) { return Math.sqrt(CanvasKit.Vector.lengthSquared(v)); }; CanvasKit.Vector.mulScalar = function(v, s) { return v.map(function(i) { return i*s }); }; CanvasKit.Vector.add = function(a, b) { return a.map(function(v, i) { return v+b[i] }); }; CanvasKit.Vector.sub = function(a, b) { return a.map(function(v, i) { return v-b[i]; }); }; CanvasKit.Vector.dist = function(a, b) { return CanvasKit.Vector.length(CanvasKit.Vector.sub(a, b)); }; CanvasKit.Vector.normalize = function(v) { return CanvasKit.Vector.mulScalar(v, 1/CanvasKit.Vector.length(v)); }; CanvasKit.Vector.cross = function(a, b) { if (IsDebug && (a.length !== 3 || a.length !== 3)) { throw 'Cross product is only defined for 3-dimensional vectors (a.length='+a.length+', b.length='+b.length+')'; } return [ a[1]*b[2] - a[2]*b[1], a[2]*b[0] - a[0]*b[2], a[0]*b[1] - a[1]*b[0], ]; }; // Functions for creating and manipulating (row-major) 4x4 matrices. Accepted in place of // SkM44 in canvas methods, for the same reasons as the 3x3 matrices above. // ported from C++ code in SkM44.cpp CanvasKit.M44 = {}; // Create a 4x4 identity matrix CanvasKit.M44.identity = function() { return identityN(4); }; // Anything named vec below is an array of length 3 representing a vector/point in 3D space. // Create a 4x4 matrix representing a translate by the provided 3-vec CanvasKit.M44.translated = function(vec) { return stride(vec, identityN(4), 4, 3, 0); }; // Create a 4x4 matrix representing a scaling by the provided 3-vec CanvasKit.M44.scaled = function(vec) { return stride(vec, identityN(4), 4, 0, 1); }; // Create a 4x4 matrix representing a rotation about the provided axis 3-vec. // axis does not need to be normalized. CanvasKit.M44.rotated = function(axisVec, radians) { return CanvasKit.M44.rotatedUnitSinCos( CanvasKit.Vector.normalize(axisVec), Math.sin(radians), Math.cos(radians)); }; // Create a 4x4 matrix representing a rotation about the provided normalized axis 3-vec. // Rotation is provided redundantly as both sin and cos values. // This rotate can be used when you already have the cosAngle and sinAngle values // so you don't have to atan(cos/sin) to call roatated() which expects an angle in radians. // this does no checking! Behavior for invalid sin or cos values or non-normalized axis vectors // is incorrect. Prefer rotated(). CanvasKit.M44.rotatedUnitSinCos = function(axisVec, sinAngle, cosAngle) { var x = axisVec[0]; var y = axisVec[1]; var z = axisVec[2]; var c = cosAngle; var s = sinAngle; var t = 1 - c; return [ t*x*x + c, t*x*y - s*z, t*x*z + s*y, 0, t*x*y + s*z, t*y*y + c, t*y*z - s*x, 0, t*x*z - s*y, t*y*z + s*x, t*z*z + c, 0, 0, 0, 0, 1 ]; }; // Create a 4x4 matrix representing a camera at eyeVec, pointed at centerVec. CanvasKit.M44.lookat = function(eyeVec, centerVec, upVec) { var f = CanvasKit.Vector.normalize(CanvasKit.Vector.sub(centerVec, eyeVec)); var u = CanvasKit.Vector.normalize(upVec); var s = CanvasKit.Vector.normalize(CanvasKit.Vector.cross(f, u)); var m = CanvasKit.M44.identity(); // set each column's top three numbers stride(s, m, 4, 0, 0); stride(CanvasKit.Vector.cross(s, f), m, 4, 1, 0); stride(CanvasKit.Vector.mulScalar(f, -1), m, 4, 2, 0); stride(eyeVec, m, 4, 3, 0); var m2 = CanvasKit.M44.invert(m); if (m2 === null) { return CanvasKit.M44.identity(); } return m2; }; // Create a 4x4 matrix representing a perspective. All arguments are scalars. // angle is in radians. CanvasKit.M44.perspective = function(near, far, angle) { if (IsDebug && (far <= near)) { throw 'far must be greater than near when constructing M44 using perspective.'; } var dInv = 1 / (far - near); var halfAngle = angle / 2; var cot = Math.cos(halfAngle) / Math.sin(halfAngle); return [ cot, 0, 0, 0, 0, cot, 0, 0, 0, 0, (far+near)*dInv, 2*far*near*dInv, 0, 0, -1, 1, ]; }; // Returns the number at the given row and column in matrix m. CanvasKit.M44.rc = function(m, r, c) { return m[r*4+c]; }; // Accepts any number of 4x4 matrix arguments, multiplies them left to right. CanvasKit.M44.multiply = function() { return multiplyMany(4, arguments); }; // Invert the 4x4 matrix if it is invertible and return it. if not, return null. // taken from SkM44.cpp (altered to use row-major order) // m is not altered. CanvasKit.M44.invert = function(m) { if (IsDebug && !m.every(isnumber)) { throw 'some members of matrix are NaN m='+m; } var a00 = m[0]; var a01 = m[4]; var a02 = m[8]; var a03 = m[12]; var a10 = m[1]; var a11 = m[5]; var a12 = m[9]; var a13 = m[13]; var a20 = m[2]; var a21 = m[6]; var a22 = m[10]; var a23 = m[14]; var a30 = m[3]; var a31 = m[7]; var a32 = m[11]; var a33 = m[15]; var b00 = a00 * a11 - a01 * a10; var b01 = a00 * a12 - a02 * a10; var b02 = a00 * a13 - a03 * a10; var b03 = a01 * a12 - a02 * a11; var b04 = a01 * a13 - a03 * a11; var b05 = a02 * a13 - a03 * a12; var b06 = a20 * a31 - a21 * a30; var b07 = a20 * a32 - a22 * a30; var b08 = a20 * a33 - a23 * a30; var b09 = a21 * a32 - a22 * a31; var b10 = a21 * a33 - a23 * a31; var b11 = a22 * a33 - a23 * a32; // calculate determinate var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; var invdet = 1.0 / det; // bail out if the matrix is not invertible if (det === 0 || invdet === Infinity) { Debug('Warning, uninvertible matrix'); return null; } b00 *= invdet; b01 *= invdet; b02 *= invdet; b03 *= invdet; b04 *= invdet; b05 *= invdet; b06 *= invdet; b07 *= invdet; b08 *= invdet; b09 *= invdet; b10 *= invdet; b11 *= invdet; // store result in row major order var tmp = [ a11 * b11 - a12 * b10 + a13 * b09, a12 * b08 - a10 * b11 - a13 * b07, a10 * b10 - a11 * b08 + a13 * b06, a11 * b07 - a10 * b09 - a12 * b06, a02 * b10 - a01 * b11 - a03 * b09, a00 * b11 - a02 * b08 + a03 * b07, a01 * b08 - a00 * b10 - a03 * b06, a00 * b09 - a01 * b07 + a02 * b06, a31 * b05 - a32 * b04 + a33 * b03, a32 * b02 - a30 * b05 - a33 * b01, a30 * b04 - a31 * b02 + a33 * b00, a31 * b01 - a30 * b03 - a32 * b00, a22 * b04 - a21 * b05 - a23 * b03, a20 * b05 - a22 * b02 + a23 * b01, a21 * b02 - a20 * b04 - a23 * b00, a20 * b03 - a21 * b01 + a22 * b00, ]; if (!tmp.every(function(val) { return !isNaN(val) && val !== Infinity && val !== -Infinity; })) { Debug('inverted matrix contains infinities or NaN '+tmp); return null; } return tmp; }; CanvasKit.M44.transpose = function(m) { return [ m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15], ]; }; // Return the inverse of an SkM44. throw an error if it's not invertible CanvasKit.M44.mustInvert = function(m) { var m2 = CanvasKit.M44.invert(m); if (m2 === null) { throw 'Matrix not invertible'; } return m2; }; // returns a matrix that sets up a 3D perspective view from a given camera. // // area - a rect describing the viewport. (0, 0, canvas_width, canvas_height) suggested // zscale - a scalar describing the scale of the z axis. min(width, height)/2 suggested // cam - an object with the following attributes // const cam = { // 'eye' : [0, 0, 1 / Math.tan(Math.PI / 24) - 1], // a 3D point locating the camera // 'coa' : [0, 0, 0], // center of attention - the 3D point the camera is looking at. // 'up' : [0, 1, 0], // a unit vector pointing in the camera's up direction, because eye and // // coa alone leave roll unspecified. // 'near' : 0.02, // near clipping plane // 'far' : 4, // far clipping plane // 'angle': Math.PI / 12, // field of view in radians // }; CanvasKit.M44.setupCamera = function(area, zscale, cam) { var camera = CanvasKit.M44.lookat(cam['eye'], cam['coa'], cam['up']); var perspective = CanvasKit.M44.perspective(cam['near'], cam['far'], cam['angle']); var center = [(area[0] + area[2])/2, (area[1] + area[3])/2, 0]; var viewScale = [(area[2] - area[0])/2, (area[3] - area[1])/2, zscale]; var viewport = CanvasKit.M44.multiply( CanvasKit.M44.translated(center), CanvasKit.M44.scaled(viewScale)); return CanvasKit.M44.multiply( viewport, perspective, camera, CanvasKit.M44.mustInvert(viewport)); }; // An ColorMatrix is a 4x4 color matrix that transforms the 4 color channels // with a 1x4 matrix that post-translates those 4 channels. // For example, the following is the layout with the scale (S) and post-transform // (PT) items indicated. // RS, 0, 0, 0 | RPT // 0, GS, 0, 0 | GPT // 0, 0, BS, 0 | BPT // 0, 0, 0, AS | APT // // Much of this was hand-transcribed from SkColorMatrix.cpp, because it's easier to // deal with a Float32Array of length 20 than to try to expose the SkColorMatrix object. var rScale = 0; var gScale = 6; var bScale = 12; var aScale = 18; var rPostTrans = 4; var gPostTrans = 9; var bPostTrans = 14; var aPostTrans = 19; CanvasKit.ColorMatrix = {}; CanvasKit.ColorMatrix.identity = function() { var m = new Float32Array(20); m[rScale] = 1; m[gScale] = 1; m[bScale] = 1; m[aScale] = 1; return m; }; CanvasKit.ColorMatrix.scaled = function(rs, gs, bs, as) { var m = new Float32Array(20); m[rScale] = rs; m[gScale] = gs; m[bScale] = bs; m[aScale] = as; return m; }; var rotateIndices = [ [6, 7, 11, 12], [0, 10, 2, 12], [0, 1, 5, 6], ]; // axis should be 0, 1, 2 for r, g, b CanvasKit.ColorMatrix.rotated = function(axis, sine, cosine) { var m = CanvasKit.ColorMatrix.identity(); var indices = rotateIndices[axis]; m[indices[0]] = cosine; m[indices[1]] = sine; m[indices[2]] = -sine; m[indices[3]] = cosine; return m; }; // m is a ColorMatrix (i.e. a Float32Array), and this sets the 4 "special" // params that will translate the colors after they are multiplied by the 4x4 matrix. CanvasKit.ColorMatrix.postTranslate = function(m, dr, dg, db, da) { m[rPostTrans] += dr; m[gPostTrans] += dg; m[bPostTrans] += db; m[aPostTrans] += da; return m; }; // concat returns a new ColorMatrix that is the result of multiplying outer*inner CanvasKit.ColorMatrix.concat = function(outer, inner) { var m = new Float32Array(20); var index = 0; for (var j = 0; j < 20; j += 5) { for (var i = 0; i < 4; i++) { m[index++] = outer[j + 0] * inner[i + 0] + outer[j + 1] * inner[i + 5] + outer[j + 2] * inner[i + 10] + outer[j + 3] * inner[i + 15]; } m[index++] = outer[j + 0] * inner[4] + outer[j + 1] * inner[9] + outer[j + 2] * inner[14] + outer[j + 3] * inner[19] + outer[j + 4]; } return m; }; (function(CanvasKit){ CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; CanvasKit._extraInitializations.push(function() { CanvasKit.Paragraph.prototype.getRectsForRange = function(start, end, hStyle, wStyle) { /** * @type {Float32Array} */ var floatArray = this._getRectsForRange(start, end, hStyle, wStyle); return floatArrayToRects(floatArray); } CanvasKit.Paragraph.prototype.getRectsForPlaceholders = function() { /** * @type {Float32Array} */ var floatArray = this._getRectsForPlaceholders(); return floatArrayToRects(floatArray); } function floatArrayToRects(floatArray) { if (!floatArray || !floatArray.length) { return []; } var ret = []; for (var i = 0; i < floatArray.length; i+=5) { var rect = CanvasKit.LTRBRect(floatArray[i], floatArray[i+1], floatArray[i+2], floatArray[i+3]); var dir = CanvasKit.TextDirection.LTR; if (floatArray[i+4] === 0) { dir = CanvasKit.TextDirection.RTL; } ret.push({'rect': rect, 'dir': dir}); } CanvasKit._free(floatArray.byteOffset); return ret; } // Registers the font (provided as an arrayBuffer) with the alias `family`. CanvasKit.TypefaceFontProvider.prototype.registerFont = function(font, family) { var typeface = CanvasKit.Typeface.MakeFreeTypeFaceFromData(font); if (!typeface) { Debug('Could not decode font data'); // We do not need to free the data since the C++ will do that for us // when the font is deleted (or fails to decode); return null; } var familyPtr = cacheOrCopyString(family); this._registerFont(typeface, familyPtr); } // These helpers fill out all fields, because emscripten complains if we // have undefined and it expects, for example, a float. // TODO(kjlubick) For efficiency, we should probably just return opaque WASM objects so we do // not have to keep copying them across the wire. CanvasKit.ParagraphStyle = function(s) { // Use [''] to tell closure not to minify the names s['disableHinting'] = s['disableHinting'] || false; if (s['ellipsis']) { var str = s['ellipsis']; s['_ellipsisPtr'] = cacheOrCopyString(str); s['_ellipsisLen'] = lengthBytesUTF8(str) + 1; // add 1 for the null terminator. } else { s['_ellipsisPtr'] = nullptr; s['_ellipsisLen'] = 0; } if (s['heightMultiplier'] == null) { s['heightMultiplier'] = -1 } s['maxLines'] = s['maxLines'] || 0; s['replaceTabCharacters'] = s['replaceTabCharacters'] || false; s['strutStyle'] = strutStyle(s['strutStyle']); s['textAlign'] = s['textAlign'] || CanvasKit.TextAlign.Start; s['textDirection'] = s['textDirection'] || CanvasKit.TextDirection.LTR; s['textHeightBehavior'] = s['textHeightBehavior'] || CanvasKit.TextHeightBehavior.All; s['textStyle'] = CanvasKit.TextStyle(s['textStyle']); return s; }; function fontStyle(s) { s = s || {}; // Can't check for falsey as 0 width means "invisible". if (s['weight'] === undefined) { s['weight'] = CanvasKit.FontWeight.Normal; } s['width'] = s['width'] || CanvasKit.FontWidth.Normal; s['slant'] = s['slant'] || CanvasKit.FontSlant.Upright; return s; } function strutStyle(s) { s = s || {}; s['strutEnabled'] = s['strutEnabled'] || false; if (s['strutEnabled'] && Array.isArray(s['fontFamilies']) && s['fontFamilies'].length) { s['_fontFamiliesPtr'] = naiveCopyStrArray(s['fontFamilies']); s['_fontFamiliesLen'] = s['fontFamilies'].length; } else { s['_fontFamiliesPtr'] = nullptr; s['_fontFamiliesLen'] = 0; } s['fontStyle'] = fontStyle(s['fontStyle']); if (s['fontSize'] == null) { s['fontSize'] = -1 } if (s['heightMultiplier'] == null) { s['heightMultiplier'] = -1 } s['halfLeading'] = s['halfLeading'] || false; s['leading'] = s['leading'] || 0; s['forceStrutHeight'] = s['forceStrutHeight'] || false; return s; } CanvasKit.TextStyle = function(s) { // Use [''] to tell closure not to minify the names if (!s['color']) { s['color'] = CanvasKit.BLACK; } s['decoration'] = s['decoration'] || 0; s['decorationThickness'] = s['decorationThickness'] || 0; s['decorationStyle'] = s['decorationStyle'] || CanvasKit.DecorationStyle.Solid; s['textBaseline'] = s['textBaseline'] || CanvasKit.TextBaseline.Alphabetic; if (s['fontSize'] == null) { s['fontSize'] = -1 } s['letterSpacing'] = s['letterSpacing'] || 0; s['wordSpacing'] = s['wordSpacing'] || 0; if (s['heightMultiplier'] == null) { s['heightMultiplier'] = -1 } s['halfLeading'] = s['halfLeading'] || false; s['fontStyle'] = fontStyle(s['fontStyle']); // Properties which need to be Malloc'ed are set in `copyArrays`. return s; }; // returns a pointer to a place on the heap that has an array // of char* (effectively a char**). For now, this does the naive thing // and depends on the string being null-terminated. This should be used // for simple, well-formed things (e.g. font-families), not arbitrary // text that should be drawn. If we need this to handle more complex // strings, it should return two pointers, a pointer of the // string array and a pointer to an array of the strings byte lengths. function naiveCopyStrArray(strings) { if (!strings || !strings.length) { return nullptr; } var sPtrs = []; for (var i = 0; i < strings.length; i++) { var strPtr = cacheOrCopyString(strings[i]); sPtrs.push(strPtr); } return copy1dArray(sPtrs, 'HEAPU32'); } // maps string -> malloc'd pointer var stringCache = {}; // cacheOrCopyString copies a string from JS into WASM on the heap and returns the pointer // to the memory of the string. It is expected that a caller to this helper will *not* free // that memory, so it is cached. Thus, if a future call to this function with the same string // will return the cached pointer, preventing the memory usage from growing unbounded (in // a normal use case). function cacheOrCopyString(str) { if (stringCache[str]) { return stringCache[str]; } // Add 1 for null terminator, which we need when copying/converting var strLen = lengthBytesUTF8(str) + 1; var strPtr = CanvasKit._malloc(strLen); stringToUTF8(str, strPtr, strLen); stringCache[str] = strPtr; return strPtr; } // These scratch arrays are allocated once to copy the color data into, which saves us // having to free them after every invocation. var scratchForegroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats var scratchBackgroundColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats var scratchDecorationColorPtr = CanvasKit._malloc(4 * 4); // room for 4 32bit floats function copyArrays(textStyle) { // These color fields were arrays, but will set to WASM pointers before we pass this // object over the WASM interface. textStyle['_colorPtr'] = copyColorToWasm(textStyle['color']); textStyle['_foregroundColorPtr'] = nullptr; // nullptr is 0, from helper.js textStyle['_backgroundColorPtr'] = nullptr; textStyle['_decorationColorPtr'] = nullptr; if (textStyle['foregroundColor']) { textStyle['_foregroundColorPtr'] = copyColorToWasm(textStyle['foregroundColor'], scratchForegroundColorPtr); } if (textStyle['backgroundColor']) { textStyle['_backgroundColorPtr'] = copyColorToWasm(textStyle['backgroundColor'], scratchBackgroundColorPtr); } if (textStyle['decorationColor']) { textStyle['_decorationColorPtr'] = copyColorToWasm(textStyle['decorationColor'], scratchDecorationColorPtr); } if (Array.isArray(textStyle['fontFamilies']) && textStyle['fontFamilies'].length) { textStyle['_fontFamiliesPtr'] = naiveCopyStrArray(textStyle['fontFamilies']); textStyle['_fontFamiliesLen'] = textStyle['fontFamilies'].length; } else { textStyle['_fontFamiliesPtr'] = nullptr; textStyle['_fontFamiliesLen'] = 0; Debug('no font families provided, text may draw wrong or not at all'); } if (textStyle['locale']) { var str = textStyle['locale']; textStyle['_localePtr'] = cacheOrCopyString(str); textStyle['_localeLen'] = lengthBytesUTF8(str) + 1; // add 1 for the null terminator. } else { textStyle['_localePtr'] = nullptr; textStyle['_localeLen'] = 0; } if (Array.isArray(textStyle['shadows']) && textStyle['shadows'].length) { var shadows = textStyle['shadows']; var shadowColors = shadows.map(function (s) { return s['color'] || CanvasKit.BLACK; }); var shadowBlurRadii = shadows.map(function (s) { return s['blurRadius'] || 0.0; }); textStyle['_shadowLen'] = shadows.length; // 2 floats per point, 4 bytes per float var ptr = CanvasKit._malloc(shadows.length * 2 * 4); var adjustedPtr = ptr / 4; // 4 bytes per float for (var i = 0; i < shadows.length; i++) { var offset = shadows[i]['offset'] || [0, 0]; CanvasKit.HEAPF32[adjustedPtr] = offset[0]; CanvasKit.HEAPF32[adjustedPtr + 1] = offset[1]; adjustedPtr += 2; } textStyle['_shadowColorsPtr'] = copyFlexibleColorArray(shadowColors).colorPtr; textStyle['_shadowOffsetsPtr'] = ptr; textStyle['_shadowBlurRadiiPtr'] = copy1dArray(shadowBlurRadii, 'HEAPF32'); } else { textStyle['_shadowLen'] = 0; textStyle['_shadowColorsPtr'] = nullptr; textStyle['_shadowOffsetsPtr'] = nullptr; textStyle['_shadowBlurRadiiPtr'] = nullptr; } if (Array.isArray(textStyle['fontFeatures']) && textStyle['fontFeatures'].length) { var fontFeatures = textStyle['fontFeatures']; var fontFeatureNames = fontFeatures.map(function (s) { return s['name']; }); var fontFeatureValues = fontFeatures.map(function (s) { return s['value']; }); textStyle['_fontFeatureLen'] = fontFeatures.length; textStyle['_fontFeatureNamesPtr'] = naiveCopyStrArray(fontFeatureNames); textStyle['_fontFeatureValuesPtr'] = copy1dArray(fontFeatureValues, 'HEAPU32'); } else { textStyle['_fontFeatureLen'] = 0; textStyle['_fontFeatureNamesPtr'] = nullptr; textStyle['_fontFeatureValuesPtr'] = nullptr; } if (Array.isArray(textStyle['fontVariations']) && textStyle['fontVariations'].length) { var fontVariations = textStyle['fontVariations']; var fontVariationAxes = fontVariations.map(function (s) { return s['axis']; }); var fontVariationValues = fontVariations.map(function (s) { return s['value']; }); textStyle['_fontVariationLen'] = fontVariations.length; textStyle['_fontVariationAxesPtr'] = naiveCopyStrArray(fontVariationAxes); textStyle['_fontVariationValuesPtr'] = copy1dArray(fontVariationValues, 'HEAPF32'); } else { textStyle['_fontVariationLen'] = 0; textStyle['_fontVariationAxesPtr'] = nullptr; textStyle['_fontVariationValuesPtr'] = nullptr; } } function freeArrays(textStyle) { // The font family strings will get copied to a vector on the C++ side, which is owned by // the text style. CanvasKit._free(textStyle['_fontFamiliesPtr']); CanvasKit._free(textStyle['_shadowColorsPtr']); CanvasKit._free(textStyle['_shadowOffsetsPtr']); CanvasKit._free(textStyle['_shadowBlurRadiiPtr']); CanvasKit._free(textStyle['_fontFeatureNamesPtr']); CanvasKit._free(textStyle['_fontFeatureValuesPtr']); } CanvasKit.ParagraphBuilder.Make = function(paragraphStyle, fontManager) { copyArrays(paragraphStyle['textStyle']); var result = CanvasKit.ParagraphBuilder._Make(paragraphStyle, fontManager); freeArrays(paragraphStyle['textStyle']); return result; }; CanvasKit.ParagraphBuilder.MakeFromFontProvider = function(paragraphStyle, fontProvider) { copyArrays(paragraphStyle['textStyle']); var result = CanvasKit.ParagraphBuilder._MakeFromFontProvider(paragraphStyle, fontProvider); freeArrays(paragraphStyle['textStyle']); return result; }; CanvasKit.ParagraphBuilder.ShapeText = function(text, blocks, width) { let length = 0; for (const b of blocks) { length += b.length; } if (length !== text.length) { throw "Accumulated block lengths must equal text.length"; } return CanvasKit.ParagraphBuilder._ShapeText(text, blocks, width); }; CanvasKit.ParagraphBuilder.prototype.pushStyle = function(textStyle) { copyArrays(textStyle); this._pushStyle(textStyle); freeArrays(textStyle); }; CanvasKit.ParagraphBuilder.prototype.pushPaintStyle = function(textStyle, fg, bg) { copyArrays(textStyle); this._pushPaintStyle(textStyle, fg, bg); freeArrays(textStyle); }; CanvasKit.ParagraphBuilder.prototype.addPlaceholder = function(width, height, alignment, baseline, offset) { width = width || 0; height = height || 0; alignment = alignment || CanvasKit.PlaceholderAlignment.Baseline; baseline = baseline || CanvasKit.TextBaseline.Alphabetic; offset = offset || 0; this._addPlaceholder(width, height, alignment, baseline, offset); }; CanvasKit.ParagraphBuilder.prototype.buildWithClientInfo = function(bidiRegions, words, graphemeBreaks, lineBreaks) { var bPtr = copy1dArray(bidiRegions, 'HEAPU32'); var wPtr = copy1dArray(words, 'HEAPU32'); var gPtr = copy1dArray(graphemeBreaks, 'HEAPU32'); var lPtr = copy1dArray(lineBreaks, 'HEAPU32'); var para = this._buildWithClientInfo( bPtr, bidiRegions && bidiRegions.length || 0, wPtr, words && words.length || 0, gPtr, graphemeBreaks && graphemeBreaks.length || 0, lPtr, lineBreaks && lineBreaks.length || 0); freeArraysThatAreNotMallocedByUsers(bPtr, bidiRegions); freeArraysThatAreNotMallocedByUsers(wPtr, words); freeArraysThatAreNotMallocedByUsers(gPtr, graphemeBreaks); freeArraysThatAreNotMallocedByUsers(lPtr, lineBreaks); return para; }; }); }(Module)); // When this file is loaded in, the high level object is "Module"; // Adds in the code to use pathops with Path CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; CanvasKit._extraInitializations.push(function() { CanvasKit.Path.prototype.op = function(otherPath, op) { if (this._op(otherPath, op)) { return this; } return null; }; CanvasKit.Path.prototype.simplify = function() { if (this._simplify()) { return this; } return null; }; }); CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; CanvasKit._extraInitializations.push(function() { CanvasKit.Canvas.prototype.drawText = function(str, x, y, paint, font) { // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 var strLen = lengthBytesUTF8(str); // Add 1 for null terminator, which we need when copying/converting, but can ignore // when we call into Skia. var strPtr = CanvasKit._malloc(strLen + 1); stringToUTF8(str, strPtr, strLen + 1); this._drawSimpleText(strPtr, strLen, x, y, font, paint); CanvasKit._free(strPtr); }; CanvasKit.Canvas.prototype.drawGlyphs = function(glyphs, positions, x, y, font, paint) { if (!(glyphs.length*2 <= positions.length)) { throw 'Not enough positions for the array of gyphs'; } CanvasKit.setCurrentContext(this._context); const glyphs_ptr = copy1dArray(glyphs, 'HEAPU16'); const positions_ptr = copy1dArray(positions, 'HEAPF32'); this._drawGlyphs(glyphs.length, glyphs_ptr, positions_ptr, x, y, font, paint); freeArraysThatAreNotMallocedByUsers(positions_ptr, positions); freeArraysThatAreNotMallocedByUsers(glyphs_ptr, glyphs); }; // Glyphs should be a Uint16Array of glyph ids, e.g. provided by Font.getGlyphIDs. // If using a Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs() to get the right type. // The return value will be a Float32Array that is 4 times as long as the input array. For each // glyph, there will be 4 floats for left, top, right, bottom (relative to 0, 0) for that glyph. CanvasKit.Font.prototype.getGlyphBounds = function(glyphs, paint, optionalOutputArray) { var glyphPtr = copy1dArray(glyphs, 'HEAPU16'); var bytesPerRect = 4 * 4; var rectPtr = CanvasKit._malloc(glyphs.length * bytesPerRect); this._getGlyphWidthBounds(glyphPtr, glyphs.length, nullptr, rectPtr, paint || null); var rects = new Float32Array(CanvasKit.HEAPU8.buffer, rectPtr, glyphs.length * 4); freeArraysThatAreNotMallocedByUsers(glyphPtr, glyphs); if (optionalOutputArray) { optionalOutputArray.set(rects); CanvasKit._free(rectPtr); return optionalOutputArray; } var rv = Float32Array.from(rects); CanvasKit._free(rectPtr); return rv; }; CanvasKit.Font.prototype.getGlyphIDs = function(str, numGlyphIDs, optionalOutputArray) { if (!numGlyphIDs) { numGlyphIDs = str.length; } // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 // Add 1 for null terminator var strBytes = lengthBytesUTF8(str) + 1; var strPtr = CanvasKit._malloc(strBytes); stringToUTF8(str, strPtr, strBytes); // This includes the null terminator var bytesPerGlyph = 2; var glyphPtr = CanvasKit._malloc(numGlyphIDs * bytesPerGlyph); // We don't need to compute the id for the null terminator, so subtract 1. var actualIDs = this._getGlyphIDs(strPtr, strBytes - 1, numGlyphIDs, glyphPtr); CanvasKit._free(strPtr); if (actualIDs < 0) { Debug('Could not get glyphIDs'); CanvasKit._free(glyphPtr); return null; } var glyphs = new Uint16Array(CanvasKit.HEAPU8.buffer, glyphPtr, actualIDs); if (optionalOutputArray) { optionalOutputArray.set(glyphs); CanvasKit._free(glyphPtr); return optionalOutputArray; } var rv = Uint16Array.from(glyphs); CanvasKit._free(glyphPtr); return rv; }; CanvasKit.Font.prototype.getGlyphIntercepts = function(glyphs, positions, top, bottom) { var gPtr = copy1dArray(glyphs, 'HEAPU16'); var pPtr = copy1dArray(positions, 'HEAPF32'); return this._getGlyphIntercepts(gPtr, glyphs.length, !wasMalloced(glyphs), pPtr, positions.length, !wasMalloced(positions), top, bottom); }; // Glyphs should be a Uint16Array of glyph ids, e.g. provided by Font.getGlyphIDs. // If using a Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs() to get the right type. // The return value will be a Float32Array that has one width per input glyph. CanvasKit.Font.prototype.getGlyphWidths = function(glyphs, paint, optionalOutputArray) { var glyphPtr = copy1dArray(glyphs, 'HEAPU16'); var bytesPerWidth = 4; var widthPtr = CanvasKit._malloc(glyphs.length * bytesPerWidth); this._getGlyphWidthBounds(glyphPtr, glyphs.length, widthPtr, nullptr, paint || null); var widths = new Float32Array(CanvasKit.HEAPU8.buffer, widthPtr, glyphs.length); freeArraysThatAreNotMallocedByUsers(glyphPtr, glyphs); if (optionalOutputArray) { optionalOutputArray.set(widths); CanvasKit._free(widthPtr); return optionalOutputArray; } var rv = Float32Array.from(widths); CanvasKit._free(widthPtr); return rv; }; // arguments should all be arrayBuffers or be an array of arrayBuffers. CanvasKit.FontMgr.FromData = function() { if (!arguments.length) { Debug('Could not make FontMgr from no font sources'); return null; } var fonts = arguments; if (fonts.length === 1 && Array.isArray(fonts[0])) { fonts = arguments[0]; } if (!fonts.length) { Debug('Could not make FontMgr from no font sources'); return null; } var dPtrs = []; var sizes = []; for (var i = 0; i < fonts.length; i++) { var data = new Uint8Array(fonts[i]); var dptr = copy1dArray(data, 'HEAPU8'); dPtrs.push(dptr); sizes.push(data.byteLength); } // Pointers are 32 bit unsigned ints var datasPtr = copy1dArray(dPtrs, 'HEAPU32'); var sizesPtr = copy1dArray(sizes, 'HEAPU32'); var fm = CanvasKit.FontMgr._fromData(datasPtr, sizesPtr, fonts.length); // The FontMgr has taken ownership of the bytes we allocated in the for loop. CanvasKit._free(datasPtr); CanvasKit._free(sizesPtr); return fm; }; CanvasKit.Typeface.MakeFreeTypeFaceFromData = function(fontData) { var data = new Uint8Array(fontData); var fptr = copy1dArray(data, 'HEAPU8'); var font = CanvasKit.Typeface._MakeFreeTypeFaceFromData(fptr, data.byteLength); if (!font) { Debug('Could not decode font data'); // We do not need to free the data since the C++ will do that for us // when the font is deleted (or fails to decode); return null; } return font; }; CanvasKit.Typeface.prototype.getGlyphIDs = function(str, numGlyphIDs, optionalOutputArray) { if (!numGlyphIDs) { numGlyphIDs = str.length; } // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 // Add 1 for null terminator var strBytes = lengthBytesUTF8(str) + 1; var strPtr = CanvasKit._malloc(strBytes); stringToUTF8(str, strPtr, strBytes); // This includes the null terminator var bytesPerGlyph = 2; var glyphPtr = CanvasKit._malloc(numGlyphIDs * bytesPerGlyph); // We don't need to compute the id for the null terminator, so subtract 1. var actualIDs = this._getGlyphIDs(strPtr, strBytes - 1, numGlyphIDs, glyphPtr); CanvasKit._free(strPtr); if (actualIDs < 0) { Debug('Could not get glyphIDs'); CanvasKit._free(glyphPtr); return null; } var glyphs = new Uint16Array(CanvasKit.HEAPU8.buffer, glyphPtr, actualIDs); if (optionalOutputArray) { optionalOutputArray.set(glyphs); CanvasKit._free(glyphPtr); return optionalOutputArray; } var rv = Uint16Array.from(glyphs); CanvasKit._free(glyphPtr); return rv; }; CanvasKit.TextBlob.MakeOnPath = function(str, path, font, initialOffset) { if (!str || !str.length) { Debug('ignoring 0 length string'); return; } if (!path || !path.countPoints()) { Debug('ignoring empty path'); return; } if (path.countPoints() === 1) { Debug('path has 1 point, returning normal textblob'); return this.MakeFromText(str, font); } if (!initialOffset) { initialOffset = 0; } var ids = font.getGlyphIDs(str); var widths = font.getGlyphWidths(ids); var rsx = []; var meas = new CanvasKit.ContourMeasureIter(path, false, 1); var cont = meas.next(); var dist = initialOffset; var xycs = new Float32Array(4); for (var i = 0; i < str.length && cont; i++) { var width = widths[i]; dist += width/2; if (dist > cont.length()) { // jump to next contour cont.delete(); cont = meas.next(); if (!cont) { // We have come to the end of the path - terminate the string // right here. str = str.substring(0, i); break; } dist = width/2; } // Gives us the (x, y) coordinates as well as the cos/sin of the tangent // line at that position. cont.getPosTan(dist, xycs); var cx = xycs[0]; var cy = xycs[1]; var cosT = xycs[2]; var sinT = xycs[3]; var adjustedX = cx - (width/2 * cosT); var adjustedY = cy - (width/2 * sinT); rsx.push(cosT, sinT, adjustedX, adjustedY); dist += width/2; } var retVal = this.MakeFromRSXform(str, rsx, font); cont && cont.delete(); meas.delete(); return retVal; }; CanvasKit.TextBlob.MakeFromRSXform = function(str, rsxForms, font) { // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 // Add 1 for null terminator var strLen = lengthBytesUTF8(str) + 1; var strPtr = CanvasKit._malloc(strLen); // Add 1 for the null terminator. stringToUTF8(str, strPtr, strLen); var rPtr = copy1dArray(rsxForms, 'HEAPF32'); var blob = CanvasKit.TextBlob._MakeFromRSXform(strPtr, strLen - 1, rPtr, font); CanvasKit._free(strPtr); if (!blob) { Debug('Could not make textblob from string "' + str + '"'); return null; } return blob; }; // Glyphs should be a Uint32Array of glyph ids, e.g. provided by Font.getGlyphIDs. // If using a Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs() to get the right type. CanvasKit.TextBlob.MakeFromRSXformGlyphs = function(glyphs, rsxForms, font) { // Currently on the C++ side, glyph ids are 16bit, but there is an effort to change that. var glyphPtr = copy1dArray(glyphs, 'HEAPU16'); var bytesPerGlyph = 2; var rPtr = copy1dArray(rsxForms, 'HEAPF32'); var blob = CanvasKit.TextBlob._MakeFromRSXformGlyphs(glyphPtr, glyphs.length * bytesPerGlyph, rPtr, font); freeArraysThatAreNotMallocedByUsers(glyphPtr, glyphs); if (!blob) { Debug('Could not make textblob from glyphs "' + glyphs + '"'); return null; } return blob; }; // Glyphs should be a Uint32Array of glyph ids, e.g. provided by Font.getGlyphIDs. // If using a Malloc'd array, be sure to use CanvasKit.MallocGlyphIDs() to get the right type. CanvasKit.TextBlob.MakeFromGlyphs = function(glyphs, font) { // Currently on the C++ side, glyph ids are 16bit, but there is an effort to change that. var glyphPtr = copy1dArray(glyphs, 'HEAPU16'); var bytesPerGlyph = 2; var blob = CanvasKit.TextBlob._MakeFromGlyphs(glyphPtr, glyphs.length * bytesPerGlyph, font); freeArraysThatAreNotMallocedByUsers(glyphPtr, glyphs); if (!blob) { Debug('Could not make textblob from glyphs "' + glyphs + '"'); return null; } return blob; }; CanvasKit.TextBlob.MakeFromText = function(str, font) { // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 // Add 1 for null terminator var strLen = lengthBytesUTF8(str) + 1; var strPtr = CanvasKit._malloc(strLen); // Add 1 for the null terminator. stringToUTF8(str, strPtr, strLen); var blob = CanvasKit.TextBlob._MakeFromText(strPtr, strLen - 1, font); CanvasKit._free(strPtr); if (!blob) { Debug('Could not make textblob from string "' + str + '"'); return null; } return blob; }; // A helper to return the right type for GlyphIDs stored internally. When that changes, this // will also be changed, which will help avoid future breakages. CanvasKit.MallocGlyphIDs = function(numGlyphIDs) { return CanvasKit.Malloc(Uint16Array, numGlyphIDs); } }); CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; CanvasKit._extraInitializations.push(function() { // data is a TypedArray or ArrayBuffer e.g. from fetch().then(resp.arrayBuffer()) CanvasKit.MakePicture = function(data) { data = new Uint8Array(data); var iptr = CanvasKit._malloc(data.byteLength); CanvasKit.HEAPU8.set(data, iptr); // The skp takes ownership of the malloc'd data. var pic = CanvasKit._MakePicture(iptr, data.byteLength); // zhibin:skp start CanvasKit._free(iptr); // zhibin:skp end if (!pic) { Debug('Could not decode picture'); return null; } return pic; }; // zhibin:patcher MakePicture // data is a TypedArray or ArrayBuffer e.g. from fetch().then(resp.arrayBuffer()) CanvasKit.ApplyPatch = function (data, patch_data) { if(!data || !patch_data){ Debug('ApplyPatch could not patch null parameter.'); return null; } data = new Uint8Array(data); var iptr = CanvasKit._malloc(data.byteLength); CanvasKit.HEAPU8.set(data, iptr); var patch_iptr; var new_data; if (patch_data) { patch_data = new Uint8Array(patch_data); patch_iptr = CanvasKit._malloc(patch_data.byteLength); CanvasKit.HEAPU8.set(patch_data, patch_iptr); //Debug('Set patch data'); new_data= CanvasKit._ApplyPatch(iptr, data.byteLength, patch_iptr, patch_data.byteLength); } CanvasKit._free(iptr); CanvasKit._free(patch_iptr); if(!new_data) Debug('Patch result is null.'); return new_data; }; // zhibin:MakePictureFromSkps MakePicture CanvasKit.MakePictureFromeSkps = function () { if (!arguments.length) { Debug('Could not make Picture from no skp sources'); return null; } var snapshot = arguments; // [ [0,0],[skp] ] // 函数是只有一个参数 if (snapshot.length === 1 && Array.isArray(snapshot[0])) { snapshot = arguments[0]; } if (!snapshot.length) { Debug('Could not make SkPicture from no picture sources'); return null; } var dPtrs = []; var sizes = []; const num_of_transform = 16; var transformPtr = CanvasKit._malloc(snapshot.length * num_of_transform * 4); var adjustedPtr = transformPtr / 4; // 4 bytes per float for (var i = 0; i < snapshot.length; i++) { var item = snapshot[i]; var transform = item[0]; var skp64 = item[1]; // transform var trans = transform || [0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0]; for (var z = 0; z < num_of_transform; z++) { CanvasKit.HEAPF32[adjustedPtr + z] = trans[z]; } adjustedPtr += num_of_transform; //处理base64的skp //下面两句分配内存 var data = new Uint8Array(skp64); var dptr = copy1dArray(data, 'HEAPU8'); dPtrs.push(dptr); sizes.push(data.byteLength); } // Pointers are 32 bit unsigned ints var datasPtr = copy1dArray(dPtrs, 'HEAPU32'); var sizesPtr = copy1dArray(sizes, 'HEAPU32'); // make picure from data var picture = CanvasKit._MakePictureFromeSkps(transformPtr, datasPtr, sizesPtr, snapshot.length); // The _MakePictureFromeSkps has taken ownership of the bytes we allocated in the for loop. CanvasKit._free(transformPtr); CanvasKit._free(datasPtr); CanvasKit._free(sizesPtr); return picture; }; //zhibin:skp_image_stripped CanvasKit.MakePictureWithImages = function (data, images) { //1. set skp data data = new Uint8Array(data); var iptr = CanvasKit._malloc(data.byteLength); CanvasKit.HEAPU8.set(data, iptr); //2. set image list var pic; if (!images) { // The skp takes ownership of the malloc'd data. pic = CanvasKit._MakePictureWithImages(iptr, data.byteLength, 0, nullptr, nullptr, nullptr); } else { var imageIdPtrs = []; // image id list var imageDataPtrs = [];// list to save the image data. var imagesizes = [];// list to save the size of each image. images.forEach((image, id) => { //process image var imageLen = lengthBytesUTF8(image) + 1; var img_iptr = CanvasKit._malloc(imageLen); stringToUTF8(image, img_iptr, imageLen); imageDataPtrs.push(img_iptr); imagesizes.push(imageLen); // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 // Add 1 for null terminator // process id var strLen = lengthBytesUTF8(id) + 1; var strPtr = CanvasKit._malloc(strLen); stringToUTF8(id, strPtr, strLen); imageIdPtrs.push(strPtr); }); // Not entirely sure if it matters, but the uintptr_t are 32 bits // we want to copy our array of uintptr_t into the right size memory. var namesPtr = copy1dArray(imageIdPtrs, "HEAPU32"); var imagesPtr = copy1dArray(imageDataPtrs, "HEAPU32"); var imagesizesPtr = copy1dArray(imagesizes, "HEAPU32"); pic = CanvasKit._MakePictureWithImages(iptr, data.byteLength, images.size, namesPtr, imagesPtr, imagesizesPtr ); // The C++ code has made copies of the image and string data, so free our copies. CanvasKit._free(namesPtr); CanvasKit._free(imagesPtr); CanvasKit._free(imagesizesPtr); } //3. // The skp takes ownership of the malloc'd data. CanvasKit._free(iptr); if (!pic) { Debug('Could not decode picture'); return null; } return pic; }; //--- skp_image_stripped //zhibin:image pool CanvasKit.ImagePool.prototype.insert = function (id, image) { if(!id || !image){ Debug('MakePictureWithImages could not insert null parameter.'); return null; } //id var idLen = lengthBytesUTF8(id) + 1; var idPtr = CanvasKit._malloc(idLen); stringToUTF8(id, idPtr, idLen); //image var imageLen = lengthBytesUTF8(image) + 1; var imagePtr = CanvasKit._malloc(imageLen); stringToUTF8(image, imagePtr, imageLen); this._insert( idPtr, imagePtr ); // The C++ code has made copies of the image and string data, so free our copies. CanvasKit._free(idPtr); CanvasKit._free(imagePtr); }; CanvasKit.ImagePool.prototype.erase = function (id) { if(!id){ Debug('erase could not erase null parameter.'); return null; } //id var idLen = lengthBytesUTF8(id) + 1; var idPtr = CanvasKit._malloc(idLen); stringToUTF8(id, idPtr, idLen); this._erase(idPtr); CanvasKit._free(idPtr); } //--- image pool }); CanvasKit._extraInitializations = CanvasKit._extraInitializations || []; CanvasKit._extraInitializations.push(function() { // sksl is the shader code. // errorCallback is a function that will be called with an error string if the // effect cannot be made. If not provided, the error will be logged. CanvasKit.RuntimeEffect.Make = function(sksl, errorCallback) { // The easiest way to pass a function into C++ code is to wrap it in an object and // treat it as an emscripten::val on the other side. var callbackObj = { 'onError': errorCallback || function(err) { console.log('RuntimeEffect error', err); }, }; return CanvasKit.RuntimeEffect._Make(sksl, callbackObj); }; CanvasKit.RuntimeEffect.prototype.makeShader = function(floats, localMatrix) { // If the uniforms were set in a MallocObj, we don't want the shader to take ownership of // them (and free the memory when the shader is freed). var shouldOwnUniforms = !floats['_ck']; var fptr = copy1dArray(floats, 'HEAPF32'); var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); // Our array has 4 bytes per float, so be sure to account for that before // sending it over the wire. return this._makeShader(fptr, floats.length * 4, shouldOwnUniforms, localMatrixPtr); } // childrenWithShaders is an array of other shaders (e.g. Image.makeShader()) CanvasKit.RuntimeEffect.prototype.makeShaderWithChildren = function(floats, childrenShaders, localMatrix) { // If the uniforms were set in a MallocObj, we don't want the shader to take ownership of // them (and free the memory when the shader is freed). var shouldOwnUniforms = !floats['_ck']; var fptr = copy1dArray(floats, 'HEAPF32'); var localMatrixPtr = copy3x3MatrixToWasm(localMatrix); var barePointers = []; for (var i = 0; i < childrenShaders.length; i++) { // childrenShaders are emscriptens smart pointer type. We want to get the bare pointer // and send that over the wire, so it can be re-wrapped as an sk_sp. barePointers.push(childrenShaders[i].$$.ptr); } var childrenPointers = copy1dArray(barePointers, 'HEAPU32'); // Our array has 4 bytes per float, so be sure to account for that before // sending it over the wire. return this._makeShaderWithChildren(fptr, floats.length * 4, shouldOwnUniforms, childrenPointers, barePointers.length, localMatrixPtr); } }); // Adds compile-time JS functions to augment the CanvasKit interface. // Specifically, the code that emulates the HTML Canvas interface // (which is called HTMLCanvas or similar to avoid confusion with // SkCanvas). (function() { // This allows us to expose internal functions (e.g. color // parsing) for unit-testing, even in the minified version. // Our tests are not minified like CanvasKit is, so the names // would get lost otherwise. CanvasKit._testing = {}; // This intentionally dangles because we want all the htmlcanvas // JS code to be in the same scope, but JS doesn't support // namespaces like C++ does. Thus, we simply include this // preamble.js file, all the source .js files and then postamble.js // to bundle everything in the same scope. // General purpose utility functions go in this file. function allAreFinite(args) { for (var i = 0; i < args.length; i++) { if (args[i] !== undefined && !Number.isFinite(args[i])) { return false; } } return true; } function toBase64String(bytes) { if (typeof Buffer !== 'undefined') { // Are we on node? return Buffer.from(bytes).toString('base64'); } else { // From https://stackoverflow.com/a/25644409 // because the naive solution of // btoa(String.fromCharCode.apply(null, bytes)); // would occasionally throw "Maximum call stack size exceeded" var CHUNK_SIZE = 0x8000; //arbitrary number var index = 0; var length = bytes.length; var result = ''; var slice; while (index < length) { slice = bytes.slice(index, Math.min(index + CHUNK_SIZE, length)); result += String.fromCharCode.apply(null, slice); index += CHUNK_SIZE; } return btoa(result); } } // Functions dealing with parsing/stringifying color go here. // Create the following with // node ./htmlcanvas/_namedcolors.js --expose-wasm // JS/closure doesn't have a constexpr like thing which // would really help here. Since we don't, we pre-compute // the map, which saves (a tiny amount of) startup time // and (a small amount of) code size. /* @dict */ var colorMap = { 'aliceblue': Float32Array.of(0.941, 0.973, 1.000, 1.000), 'antiquewhite': Float32Array.of(0.980, 0.922, 0.843, 1.000), 'aqua': Float32Array.of(0.000, 1.000, 1.000, 1.000), 'aquamarine': Float32Array.of(0.498, 1.000, 0.831, 1.000), 'azure': Float32Array.of(0.941, 1.000, 1.000, 1.000), 'beige': Float32Array.of(0.961, 0.961, 0.863, 1.000), 'bisque': Float32Array.of(1.000, 0.894, 0.769, 1.000), 'black': Float32Array.of(0.000, 0.000, 0.000, 1.000), 'blanchedalmond': Float32Array.of(1.000, 0.922, 0.804, 1.000), 'blue': Float32Array.of(0.000, 0.000, 1.000, 1.000), 'blueviolet': Float32Array.of(0.541, 0.169, 0.886, 1.000), 'brown': Float32Array.of(0.647, 0.165, 0.165, 1.000), 'burlywood': Float32Array.of(0.871, 0.722, 0.529, 1.000), 'cadetblue': Float32Array.of(0.373, 0.620, 0.627, 1.000), 'chartreuse': Float32Array.of(0.498, 1.000, 0.000, 1.000), 'chocolate': Float32Array.of(0.824, 0.412, 0.118, 1.000), 'coral': Float32Array.of(1.000, 0.498, 0.314, 1.000), 'cornflowerblue': Float32Array.of(0.392, 0.584, 0.929, 1.000), 'cornsilk': Float32Array.of(1.000, 0.973, 0.863, 1.000), 'crimson': Float32Array.of(0.863, 0.078, 0.235, 1.000), 'cyan': Float32Array.of(0.000, 1.000, 1.000, 1.000), 'darkblue': Float32Array.of(0.000, 0.000, 0.545, 1.000), 'darkcyan': Float32Array.of(0.000, 0.545, 0.545, 1.000), 'darkgoldenrod': Float32Array.of(0.722, 0.525, 0.043, 1.000), 'darkgray': Float32Array.of(0.663, 0.663, 0.663, 1.000), 'darkgreen': Float32Array.of(0.000, 0.392, 0.000, 1.000), 'darkgrey': Float32Array.of(0.663, 0.663, 0.663, 1.000), 'darkkhaki': Float32Array.of(0.741, 0.718, 0.420, 1.000), 'darkmagenta': Float32Array.of(0.545, 0.000, 0.545, 1.000), 'darkolivegreen': Float32Array.of(0.333, 0.420, 0.184, 1.000), 'darkorange': Float32Array.of(1.000, 0.549, 0.000, 1.000), 'darkorchid': Float32Array.of(0.600, 0.196, 0.800, 1.000), 'darkred': Float32Array.of(0.545, 0.000, 0.000, 1.000), 'darksalmon': Float32Array.of(0.914, 0.588, 0.478, 1.000), 'darkseagreen': Float32Array.of(0.561, 0.737, 0.561, 1.000), 'darkslateblue': Float32Array.of(0.282, 0.239, 0.545, 1.000), 'darkslategray': Float32Array.of(0.184, 0.310, 0.310, 1.000), 'darkslategrey': Float32Array.of(0.184, 0.310, 0.310, 1.000), 'darkturquoise': Float32Array.of(0.000, 0.808, 0.820, 1.000), 'darkviolet': Float32Array.of(0.580, 0.000, 0.827, 1.000), 'deeppink': Float32Array.of(1.000, 0.078, 0.576, 1.000), 'deepskyblue': Float32Array.of(0.000, 0.749, 1.000, 1.000), 'dimgray': Float32Array.of(0.412, 0.412, 0.412, 1.000), 'dimgrey': Float32Array.of(0.412, 0.412, 0.412, 1.000), 'dodgerblue': Float32Array.of(0.118, 0.565, 1.000, 1.000), 'firebrick': Float32Array.of(0.698, 0.133, 0.133, 1.000), 'floralwhite': Float32Array.of(1.000, 0.980, 0.941, 1.000), 'forestgreen': Float32Array.of(0.133, 0.545, 0.133, 1.000), 'fuchsia': Float32Array.of(1.000, 0.000, 1.000, 1.000), 'gainsboro': Float32Array.of(0.863, 0.863, 0.863, 1.000), 'ghostwhite': Float32Array.of(0.973, 0.973, 1.000, 1.000), 'gold': Float32Array.of(1.000, 0.843, 0.000, 1.000), 'goldenrod': Float32Array.of(0.855, 0.647, 0.125, 1.000), 'gray': Float32Array.of(0.502, 0.502, 0.502, 1.000), 'green': Float32Array.of(0.000, 0.502, 0.000, 1.000), 'greenyellow': Float32Array.of(0.678, 1.000, 0.184, 1.000), 'grey': Float32Array.of(0.502, 0.502, 0.502, 1.000), 'honeydew': Float32Array.of(0.941, 1.000, 0.941, 1.000), 'hotpink': Float32Array.of(1.000, 0.412, 0.706, 1.000), 'indianred': Float32Array.of(0.804, 0.361, 0.361, 1.000), 'indigo': Float32Array.of(0.294, 0.000, 0.510, 1.000), 'ivory': Float32Array.of(1.000, 1.000, 0.941, 1.000), 'khaki': Float32Array.of(0.941, 0.902, 0.549, 1.000), 'lavender': Float32Array.of(0.902, 0.902, 0.980, 1.000), 'lavenderblush': Float32Array.of(1.000, 0.941, 0.961, 1.000), 'lawngreen': Float32Array.of(0.486, 0.988, 0.000, 1.000), 'lemonchiffon': Float32Array.of(1.000, 0.980, 0.804, 1.000), 'lightblue': Float32Array.of(0.678, 0.847, 0.902, 1.000), 'lightcoral': Float32Array.of(0.941, 0.502, 0.502, 1.000), 'lightcyan': Float32Array.of(0.878, 1.000, 1.000, 1.000), 'lightgoldenrodyellow': Float32Array.of(0.980, 0.980, 0.824, 1.000), 'lightgray': Float32Array.of(0.827, 0.827, 0.827, 1.000), 'lightgreen': Float32Array.of(0.565, 0.933, 0.565, 1.000), 'lightgrey': Float32Array.of(0.827, 0.827, 0.827, 1.000), 'lightpink': Float32Array.of(1.000, 0.714, 0.757, 1.000), 'lightsalmon': Float32Array.of(1.000, 0.627, 0.478, 1.000), 'lightseagreen': Float32Array.of(0.125, 0.698, 0.667, 1.000), 'lightskyblue': Float32Array.of(0.529, 0.808, 0.980, 1.000), 'lightslategray': Float32Array.of(0.467, 0.533, 0.600, 1.000), 'lightslategrey': Float32Array.of(0.467, 0.533, 0.600, 1.000), 'lightsteelblue': Float32Array.of(0.690, 0.769, 0.871, 1.000), 'lightyellow': Float32Array.of(1.000, 1.000, 0.878, 1.000), 'lime': Float32Array.of(0.000, 1.000, 0.000, 1.000), 'limegreen': Float32Array.of(0.196, 0.804, 0.196, 1.000), 'linen': Float32Array.of(0.980, 0.941, 0.902, 1.000), 'magenta': Float32Array.of(1.000, 0.000, 1.000, 1.000), 'maroon': Float32Array.of(0.502, 0.000, 0.000, 1.000), 'mediumaquamarine': Float32Array.of(0.400, 0.804, 0.667, 1.000), 'mediumblue': Float32Array.of(0.000, 0.000, 0.804, 1.000), 'mediumorchid': Float32Array.of(0.729, 0.333, 0.827, 1.000), 'mediumpurple': Float32Array.of(0.576, 0.439, 0.859, 1.000), 'mediumseagreen': Float32Array.of(0.235, 0.702, 0.443, 1.000), 'mediumslateblue': Float32Array.of(0.482, 0.408, 0.933, 1.000), 'mediumspringgreen': Float32Array.of(0.000, 0.980, 0.604, 1.000), 'mediumturquoise': Float32Array.of(0.282, 0.820, 0.800, 1.000), 'mediumvioletred': Float32Array.of(0.780, 0.082, 0.522, 1.000), 'midnightblue': Float32Array.of(0.098, 0.098, 0.439, 1.000), 'mintcream': Float32Array.of(0.961, 1.000, 0.980, 1.000), 'mistyrose': Float32Array.of(1.000, 0.894, 0.882, 1.000), 'moccasin': Float32Array.of(1.000, 0.894, 0.710, 1.000), 'navajowhite': Float32Array.of(1.000, 0.871, 0.678, 1.000), 'navy': Float32Array.of(0.000, 0.000, 0.502, 1.000), 'oldlace': Float32Array.of(0.992, 0.961, 0.902, 1.000), 'olive': Float32Array.of(0.502, 0.502, 0.000, 1.000), 'olivedrab': Float32Array.of(0.420, 0.557, 0.137, 1.000), 'orange': Float32Array.of(1.000, 0.647, 0.000, 1.000), 'orangered': Float32Array.of(1.000, 0.271, 0.000, 1.000), 'orchid': Float32Array.of(0.855, 0.439, 0.839, 1.000), 'palegoldenrod': Float32Array.of(0.933, 0.910, 0.667, 1.000), 'palegreen': Float32Array.of(0.596, 0.984, 0.596, 1.000), 'paleturquoise': Float32Array.of(0.686, 0.933, 0.933, 1.000), 'palevioletred': Float32Array.of(0.859, 0.439, 0.576, 1.000), 'papayawhip': Float32Array.of(1.000, 0.937, 0.835, 1.000), 'peachpuff': Float32Array.of(1.000, 0.855, 0.725, 1.000), 'peru': Float32Array.of(0.804, 0.522, 0.247, 1.000), 'pink': Float32Array.of(1.000, 0.753, 0.796, 1.000), 'plum': Float32Array.of(0.867, 0.627, 0.867, 1.000), 'powderblue': Float32Array.of(0.690, 0.878, 0.902, 1.000), 'purple': Float32Array.of(0.502, 0.000, 0.502, 1.000), 'rebeccapurple': Float32Array.of(0.400, 0.200, 0.600, 1.000), 'red': Float32Array.of(1.000, 0.000, 0.000, 1.000), 'rosybrown': Float32Array.of(0.737, 0.561, 0.561, 1.000), 'royalblue': Float32Array.of(0.255, 0.412, 0.882, 1.000), 'saddlebrown': Float32Array.of(0.545, 0.271, 0.075, 1.000), 'salmon': Float32Array.of(0.980, 0.502, 0.447, 1.000), 'sandybrown': Float32Array.of(0.957, 0.643, 0.376, 1.000), 'seagreen': Float32Array.of(0.180, 0.545, 0.341, 1.000), 'seashell': Float32Array.of(1.000, 0.961, 0.933, 1.000), 'sienna': Float32Array.of(0.627, 0.322, 0.176, 1.000), 'silver': Float32Array.of(0.753, 0.753, 0.753, 1.000), 'skyblue': Float32Array.of(0.529, 0.808, 0.922, 1.000), 'slateblue': Float32Array.of(0.416, 0.353, 0.804, 1.000), 'slategray': Float32Array.of(0.439, 0.502, 0.565, 1.000), 'slategrey': Float32Array.of(0.439, 0.502, 0.565, 1.000), 'snow': Float32Array.of(1.000, 0.980, 0.980, 1.000), 'springgreen': Float32Array.of(0.000, 1.000, 0.498, 1.000), 'steelblue': Float32Array.of(0.275, 0.510, 0.706, 1.000), 'tan': Float32Array.of(0.824, 0.706, 0.549, 1.000), 'teal': Float32Array.of(0.000, 0.502, 0.502, 1.000), 'thistle': Float32Array.of(0.847, 0.749, 0.847, 1.000), 'tomato': Float32Array.of(1.000, 0.388, 0.278, 1.000), 'transparent': Float32Array.of(0.000, 0.000, 0.000, 0.000), 'turquoise': Float32Array.of(0.251, 0.878, 0.816, 1.000), 'violet': Float32Array.of(0.933, 0.510, 0.933, 1.000), 'wheat': Float32Array.of(0.961, 0.871, 0.702, 1.000), 'white': Float32Array.of(1.000, 1.000, 1.000, 1.000), 'whitesmoke': Float32Array.of(0.961, 0.961, 0.961, 1.000), 'yellow': Float32Array.of(1.000, 1.000, 0.000, 1.000), 'yellowgreen': Float32Array.of(0.604, 0.804, 0.196, 1.000), } function colorToString(skcolor) { // https://html.spec.whatwg.org/multipage/canvas.html#serialisation-of-a-color var components = CanvasKit.getColorComponents(skcolor); var r = components[0]; var g = components[1]; var b = components[2]; var a = components[3]; if (a === 1.0) { // hex r = r.toString(16).toLowerCase(); g = g.toString(16).toLowerCase(); b = b.toString(16).toLowerCase(); r = (r.length === 1 ? '0'+r: r); g = (g.length === 1 ? '0'+g: g); b = (b.length === 1 ? '0'+b: b); return '#'+r+g+b; } else { a = (a === 0 || a === 1) ? a : a.toFixed(8); return 'rgba('+r+', '+g+', '+b+', '+a+')'; } } function parseColor(colorStr) { return CanvasKit.parseColorString(colorStr, colorMap); } CanvasKit._testing['parseColor'] = parseColor; CanvasKit._testing['colorToString'] = colorToString; // Functions dealing with parsing/stringifying fonts go here. var fontStringRegex = new RegExp( '(italic|oblique|normal|)\\s*' + // style '(small-caps|normal|)\\s*' + // variant '(bold|bolder|lighter|[1-9]00|normal|)\\s*' + // weight '([\\d\\.]+)' + // size '(px|pt|pc|in|cm|mm|%|em|ex|ch|rem|q)' + // unit // line-height is ignored here, as per the spec '(.+)' // family ); function stripWhitespace(str) { return str.replace(/^\s+|\s+$/, ''); } var defaultHeight = 16; // Based off of node-canvas's parseFont // returns font size in px, which represents the em width. function parseFontString(fontStr) { var font = fontStringRegex.exec(fontStr); if (!font) { Debug('Invalid font string ' + fontStr); return null; } var size = parseFloat(font[4]); var sizePx = defaultHeight; var unit = font[5]; switch (unit) { case 'em': case 'rem': sizePx = size * defaultHeight; break; case 'pt': sizePx = size * 4/3; break; case 'px': sizePx = size; break; case 'pc': sizePx = size * defaultHeight; break; case 'in': sizePx = size * 96; break; case 'cm': sizePx = size * 96.0 / 2.54; break; case 'mm': sizePx = size * (96.0 / 25.4); break; case 'q': // quarter millimeters sizePx = size * (96.0 / 25.4 / 4); break; case '%': sizePx = size * (defaultHeight / 75); break; } return { 'style': font[1], 'variant': font[2], 'weight': font[3], 'sizePx': sizePx, 'family': font[6].trim() }; } function getTypeface(fontstr) { var descriptors = parseFontString(fontstr); var typeface = getFromFontCache(descriptors); descriptors['typeface'] = typeface; return descriptors; } // null means use the default typeface (which is currently NotoMono) var fontCache = { 'Noto Mono': { '*': null, // is used if we have this font family, but not the right style/variant/weight }, 'monospace': { '*': null, } }; // descriptors is like https://developer.mozilla.org/en-US/docs/Web/API/FontFace/FontFace // The ones currently supported are family, style, variant, weight. function addToFontCache(typeface, descriptors) { var key = (descriptors['style'] || 'normal') + '|' + (descriptors['variant'] || 'normal') + '|' + (descriptors['weight'] || 'normal'); var fam = descriptors['family']; if (!fontCache[fam]) { // preload with a fallback to this typeface fontCache[fam] = { '*': typeface, }; } fontCache[fam][key] = typeface; } function getFromFontCache(descriptors) { var key = (descriptors['style'] || 'normal') + '|' + (descriptors['variant'] || 'normal') + '|' + (descriptors['weight'] || 'normal'); var fam = descriptors['family']; if (!fontCache[fam]) { return null; } return fontCache[fam][key] || fontCache[fam]['*']; } CanvasKit._testing['parseFontString'] = parseFontString; function CanvasRenderingContext2D(skcanvas) { this._canvas = skcanvas; this._paint = new CanvasKit.Paint(); this._paint.setAntiAlias(true); this._paint.setStrokeMiter(10); this._paint.setStrokeCap(CanvasKit.StrokeCap.Butt); this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Miter); this._fontString = '10px monospace'; this._font = new CanvasKit.Font(null, 10); this._font.setSubpixel(true); this._strokeStyle = CanvasKit.BLACK; this._fillStyle = CanvasKit.BLACK; this._shadowBlur = 0; this._shadowColor = CanvasKit.TRANSPARENT; this._shadowOffsetX = 0; this._shadowOffsetY = 0; this._globalAlpha = 1; this._strokeWidth = 1; this._lineDashOffset = 0; this._lineDashList = []; // aka BlendMode this._globalCompositeOperation = CanvasKit.BlendMode.SrcOver; this._paint.setStrokeWidth(this._strokeWidth); this._paint.setBlendMode(this._globalCompositeOperation); this._currentPath = new CanvasKit.Path(); this._currentTransform = CanvasKit.Matrix.identity(); // Use this for save/restore this._canvasStateStack = []; // Keep a reference to all the effects (e.g. gradients, patterns) // that were allocated for cleanup in _dispose. this._toCleanUp = []; this._dispose = function() { this._currentPath.delete(); this._paint.delete(); this._font.delete(); this._toCleanUp.forEach(function(c) { c._dispose(); }); // Don't delete this._canvas as it will be disposed // by the surface of which it is based. }; // This always accepts DOMMatrix/SVGMatrix or any other // object that has properties a,b,c,d,e,f defined. // Returns a DOM-Matrix like dictionary Object.defineProperty(this, 'currentTransform', { enumerable: true, get: function() { return { 'a' : this._currentTransform[0], 'c' : this._currentTransform[1], 'e' : this._currentTransform[2], 'b' : this._currentTransform[3], 'd' : this._currentTransform[4], 'f' : this._currentTransform[5], }; }, // @param {DOMMatrix} matrix set: function(matrix) { if (matrix.a) { // if we see a property named 'a', guess that b-f will // also be there. this.setTransform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f); } } }); Object.defineProperty(this, 'fillStyle', { enumerable: true, get: function() { if (isCanvasKitColor(this._fillStyle)) { return colorToString(this._fillStyle); } return this._fillStyle; }, set: function(newStyle) { if (typeof newStyle === 'string') { this._fillStyle = parseColor(newStyle); } else if (newStyle._getShader) { // It's an effect that has a shader. this._fillStyle = newStyle } } }); Object.defineProperty(this, 'font', { enumerable: true, get: function() { return this._fontString; }, set: function(newFont) { var tf = getTypeface(newFont); if (tf) { // tf is a "dict" according to closure, that is, the field // names are not minified. Thus, we need to access it via // bracket notation to tell closure not to minify these names. this._font.setSize(tf['sizePx']); this._font.setTypeface(tf['typeface']); this._fontString = newFont; } } }); Object.defineProperty(this, 'globalAlpha', { enumerable: true, get: function() { return this._globalAlpha; }, set: function(newAlpha) { // ignore invalid values, as per the spec if (!isFinite(newAlpha) || newAlpha < 0 || newAlpha > 1) { return; } this._globalAlpha = newAlpha; } }); Object.defineProperty(this, 'globalCompositeOperation', { enumerable: true, get: function() { switch (this._globalCompositeOperation) { // composite-mode case CanvasKit.BlendMode.SrcOver: return 'source-over'; case CanvasKit.BlendMode.DstOver: return 'destination-over'; case CanvasKit.BlendMode.Src: return 'copy'; case CanvasKit.BlendMode.Dst: return 'destination'; case CanvasKit.BlendMode.Clear: return 'clear'; case CanvasKit.BlendMode.SrcIn: return 'source-in'; case CanvasKit.BlendMode.DstIn: return 'destination-in'; case CanvasKit.BlendMode.SrcOut: return 'source-out'; case CanvasKit.BlendMode.DstOut: return 'destination-out'; case CanvasKit.BlendMode.SrcATop: return 'source-atop'; case CanvasKit.BlendMode.DstATop: return 'destination-atop'; case CanvasKit.BlendMode.Xor: return 'xor'; case CanvasKit.BlendMode.Plus: return 'lighter'; case CanvasKit.BlendMode.Multiply: return 'multiply'; case CanvasKit.BlendMode.Screen: return 'screen'; case CanvasKit.BlendMode.Overlay: return 'overlay'; case CanvasKit.BlendMode.Darken: return 'darken'; case CanvasKit.BlendMode.Lighten: return 'lighten'; case CanvasKit.BlendMode.ColorDodge: return 'color-dodge'; case CanvasKit.BlendMode.ColorBurn: return 'color-burn'; case CanvasKit.BlendMode.HardLight: return 'hard-light'; case CanvasKit.BlendMode.SoftLight: return 'soft-light'; case CanvasKit.BlendMode.Difference: return 'difference'; case CanvasKit.BlendMode.Exclusion: return 'exclusion'; case CanvasKit.BlendMode.Hue: return 'hue'; case CanvasKit.BlendMode.Saturation: return 'saturation'; case CanvasKit.BlendMode.Color: return 'color'; case CanvasKit.BlendMode.Luminosity: return 'luminosity'; } }, set: function(newMode) { switch (newMode) { // composite-mode case 'source-over': this._globalCompositeOperation = CanvasKit.BlendMode.SrcOver; break; case 'destination-over': this._globalCompositeOperation = CanvasKit.BlendMode.DstOver; break; case 'copy': this._globalCompositeOperation = CanvasKit.BlendMode.Src; break; case 'destination': this._globalCompositeOperation = CanvasKit.BlendMode.Dst; break; case 'clear': this._globalCompositeOperation = CanvasKit.BlendMode.Clear; break; case 'source-in': this._globalCompositeOperation = CanvasKit.BlendMode.SrcIn; break; case 'destination-in': this._globalCompositeOperation = CanvasKit.BlendMode.DstIn; break; case 'source-out': this._globalCompositeOperation = CanvasKit.BlendMode.SrcOut; break; case 'destination-out': this._globalCompositeOperation = CanvasKit.BlendMode.DstOut; break; case 'source-atop': this._globalCompositeOperation = CanvasKit.BlendMode.SrcATop; break; case 'destination-atop': this._globalCompositeOperation = CanvasKit.BlendMode.DstATop; break; case 'xor': this._globalCompositeOperation = CanvasKit.BlendMode.Xor; break; case 'lighter': this._globalCompositeOperation = CanvasKit.BlendMode.Plus; break; case 'plus-lighter': this._globalCompositeOperation = CanvasKit.BlendMode.Plus; break; case 'plus-darker': throw 'plus-darker is not supported'; // blend-mode case 'multiply': this._globalCompositeOperation = CanvasKit.BlendMode.Multiply; break; case 'screen': this._globalCompositeOperation = CanvasKit.BlendMode.Screen; break; case 'overlay': this._globalCompositeOperation = CanvasKit.BlendMode.Overlay; break; case 'darken': this._globalCompositeOperation = CanvasKit.BlendMode.Darken; break; case 'lighten': this._globalCompositeOperation = CanvasKit.BlendMode.Lighten; break; case 'color-dodge': this._globalCompositeOperation = CanvasKit.BlendMode.ColorDodge; break; case 'color-burn': this._globalCompositeOperation = CanvasKit.BlendMode.ColorBurn; break; case 'hard-light': this._globalCompositeOperation = CanvasKit.BlendMode.HardLight; break; case 'soft-light': this._globalCompositeOperation = CanvasKit.BlendMode.SoftLight; break; case 'difference': this._globalCompositeOperation = CanvasKit.BlendMode.Difference; break; case 'exclusion': this._globalCompositeOperation = CanvasKit.BlendMode.Exclusion; break; case 'hue': this._globalCompositeOperation = CanvasKit.BlendMode.Hue; break; case 'saturation': this._globalCompositeOperation = CanvasKit.BlendMode.Saturation; break; case 'color': this._globalCompositeOperation = CanvasKit.BlendMode.Color; break; case 'luminosity': this._globalCompositeOperation = CanvasKit.BlendMode.Luminosity; break; default: return; } this._paint.setBlendMode(this._globalCompositeOperation); } }); Object.defineProperty(this, 'imageSmoothingEnabled', { enumerable: true, get: function() { return true; }, set: function(a) { // ignored, we always use high quality image smoothing. } }); Object.defineProperty(this, 'imageSmoothingQuality', { enumerable: true, get: function() { return 'high'; }, set: function(a) { // ignored, we always use high quality image smoothing. } }); Object.defineProperty(this, 'lineCap', { enumerable: true, get: function() { switch (this._paint.getStrokeCap()) { case CanvasKit.StrokeCap.Butt: return 'butt'; case CanvasKit.StrokeCap.Round: return 'round'; case CanvasKit.StrokeCap.Square: return 'square'; } }, set: function(newCap) { switch (newCap) { case 'butt': this._paint.setStrokeCap(CanvasKit.StrokeCap.Butt); return; case 'round': this._paint.setStrokeCap(CanvasKit.StrokeCap.Round); return; case 'square': this._paint.setStrokeCap(CanvasKit.StrokeCap.Square); return; } } }); Object.defineProperty(this, 'lineDashOffset', { enumerable: true, get: function() { return this._lineDashOffset; }, set: function(newOffset) { if (!isFinite(newOffset)) { return; } this._lineDashOffset = newOffset; } }); Object.defineProperty(this, 'lineJoin', { enumerable: true, get: function() { switch (this._paint.getStrokeJoin()) { case CanvasKit.StrokeJoin.Miter: return 'miter'; case CanvasKit.StrokeJoin.Round: return 'round'; case CanvasKit.StrokeJoin.Bevel: return 'bevel'; } }, set: function(newJoin) { switch (newJoin) { case 'miter': this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Miter); return; case 'round': this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Round); return; case 'bevel': this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Bevel); return; } } }); Object.defineProperty(this, 'lineWidth', { enumerable: true, get: function() { return this._paint.getStrokeWidth(); }, set: function(newWidth) { if (newWidth <= 0 || !newWidth) { // Spec says to ignore NaN/Inf/0/negative values return; } this._strokeWidth = newWidth; this._paint.setStrokeWidth(newWidth); } }); Object.defineProperty(this, 'miterLimit', { enumerable: true, get: function() { return this._paint.getStrokeMiter(); }, set: function(newLimit) { if (newLimit <= 0 || !newLimit) { // Spec says to ignore NaN/Inf/0/negative values return; } this._paint.setStrokeMiter(newLimit); } }); Object.defineProperty(this, 'shadowBlur', { enumerable: true, get: function() { return this._shadowBlur; }, set: function(newBlur) { // ignore negative, inf and NAN (but not 0) as per the spec. if (newBlur < 0 || !isFinite(newBlur)) { return; } this._shadowBlur = newBlur; } }); Object.defineProperty(this, 'shadowColor', { enumerable: true, get: function() { return colorToString(this._shadowColor); }, set: function(newColor) { this._shadowColor = parseColor(newColor); } }); Object.defineProperty(this, 'shadowOffsetX', { enumerable: true, get: function() { return this._shadowOffsetX; }, set: function(newOffset) { if (!isFinite(newOffset)) { return; } this._shadowOffsetX = newOffset; } }); Object.defineProperty(this, 'shadowOffsetY', { enumerable: true, get: function() { return this._shadowOffsetY; }, set: function(newOffset) { if (!isFinite(newOffset)) { return; } this._shadowOffsetY = newOffset; } }); Object.defineProperty(this, 'strokeStyle', { enumerable: true, get: function() { return colorToString(this._strokeStyle); }, set: function(newStyle) { if (typeof newStyle === 'string') { this._strokeStyle = parseColor(newStyle); } else if (newStyle._getShader) { // It's probably an effect. this._strokeStyle = newStyle } } }); this.arc = function(x, y, radius, startAngle, endAngle, ccw) { arc(this._currentPath, x, y, radius, startAngle, endAngle, ccw); }; this.arcTo = function(x1, y1, x2, y2, radius) { arcTo(this._currentPath, x1, y1, x2, y2, radius); }; // As per the spec this doesn't begin any paths, it only // clears out any previous paths. this.beginPath = function() { this._currentPath.delete(); this._currentPath = new CanvasKit.Path(); }; this.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { bezierCurveTo(this._currentPath, cp1x, cp1y, cp2x, cp2y, x, y); }; this.clearRect = function(x, y, width, height) { this._paint.setStyle(CanvasKit.PaintStyle.Fill); this._paint.setBlendMode(CanvasKit.BlendMode.Clear); this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), this._paint); this._paint.setBlendMode(this._globalCompositeOperation); }; this.clip = function(path, fillRule) { if (typeof path === 'string') { // shift the args if a Path2D is supplied fillRule = path; path = this._currentPath; } else if (path && path._getPath) { path = path._getPath(); } if (!path) { path = this._currentPath; } var clip = path.copy(); if (fillRule && fillRule.toLowerCase() === 'evenodd') { clip.setFillType(CanvasKit.FillType.EvenOdd); } else { clip.setFillType(CanvasKit.FillType.Winding); } this._canvas.clipPath(clip, CanvasKit.ClipOp.Intersect, true); clip.delete(); }; this.closePath = function() { closePath(this._currentPath); }; this.createImageData = function() { // either takes in 1 or 2 arguments: // - imagedata on which to copy *width* and *height* only // - width, height if (arguments.length === 1) { var oldData = arguments[0]; var byteLength = 4 * oldData.width * oldData.height; return new ImageData(new Uint8ClampedArray(byteLength), oldData.width, oldData.height); } else if (arguments.length === 2) { var width = arguments[0]; var height = arguments[1]; var byteLength = 4 * width * height; return new ImageData(new Uint8ClampedArray(byteLength), width, height); } else { throw 'createImageData expects 1 or 2 arguments, got '+arguments.length; } }; this.createLinearGradient = function(x1, y1, x2, y2) { if (!allAreFinite(arguments)) { return; } var lcg = new LinearCanvasGradient(x1, y1, x2, y2); this._toCleanUp.push(lcg); return lcg; }; this.createPattern = function(image, repetition) { var cp = new CanvasPattern(image, repetition); this._toCleanUp.push(cp); return cp; }; this.createRadialGradient = function(x1, y1, r1, x2, y2, r2) { if (!allAreFinite(arguments)) { return; } var rcg = new RadialCanvasGradient(x1, y1, r1, x2, y2, r2); this._toCleanUp.push(rcg); return rcg; }; this.drawImage = function(img) { // 3 potential sets of arguments // - image, dx, dy // - image, dx, dy, dWidth, dHeight // - image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight // use the fillPaint, which has the globalAlpha in it // which drawImageRect will use. if (img instanceof HTMLImage) { img = img.getSkImage(); } var iPaint = this._fillPaint(); if (arguments.length === 3 || arguments.length === 5) { var destRect = CanvasKit.XYWHRect(arguments[1], arguments[2], arguments[3] || img.width(), arguments[4] || img.height()); var srcRect = CanvasKit.XYWHRect(0, 0, img.width(), img.height()); } else if (arguments.length === 9){ var destRect = CanvasKit.XYWHRect(arguments[5], arguments[6], arguments[7], arguments[8]); var srcRect = CanvasKit.XYWHRect(arguments[1], arguments[2], arguments[3], arguments[4]); } else { throw 'invalid number of args for drawImage, need 3, 5, or 9; got '+ arguments.length; } this._canvas.drawImageRect(img, srcRect, destRect, iPaint, false); iPaint.dispose(); }; this.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, ccw) { ellipse(this._currentPath, x, y, radiusX, radiusY, rotation, startAngle, endAngle, ccw); }; // A helper to copy the current paint, ready for filling // This applies the global alpha. // Call dispose() after to clean up. this._fillPaint = function() { var paint = this._paint.copy(); paint.setStyle(CanvasKit.PaintStyle.Fill); if (isCanvasKitColor(this._fillStyle)) { var alphaColor = CanvasKit.multiplyByAlpha(this._fillStyle, this._globalAlpha); paint.setColor(alphaColor); } else { var shader = this._fillStyle._getShader(this._currentTransform); paint.setColor(CanvasKit.Color(0,0,0, this._globalAlpha)); paint.setShader(shader); } paint.dispose = function() { // If there are some helper effects in the future, clean them up // here. In any case, we have .dispose() to make _fillPaint behave // like _strokePaint and _shadowPaint. this.delete(); }; return paint; }; this.fill = function(path, fillRule) { if (typeof path === 'string') { // shift the args if a Path2D is supplied fillRule = path; path = this._currentPath; } else if (path && path._getPath) { path = path._getPath(); } if (fillRule === 'evenodd') { this._currentPath.setFillType(CanvasKit.FillType.EvenOdd); } else if (fillRule === 'nonzero' || !fillRule) { this._currentPath.setFillType(CanvasKit.FillType.Winding); } else { throw 'invalid fill rule'; } if (!path) { path = this._currentPath; } var fillPaint = this._fillPaint(); var shadowPaint = this._shadowPaint(fillPaint); if (shadowPaint) { this._canvas.save(); this._applyShadowOffsetMatrix(); this._canvas.drawPath(path, shadowPaint); this._canvas.restore(); shadowPaint.dispose(); } this._canvas.drawPath(path, fillPaint); fillPaint.dispose(); }; this.fillRect = function(x, y, width, height) { var fillPaint = this._fillPaint(); var shadowPaint = this._shadowPaint(fillPaint); if (shadowPaint) { this._canvas.save(); this._applyShadowOffsetMatrix(); this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), shadowPaint); this._canvas.restore(); shadowPaint.dispose(); } this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), fillPaint); fillPaint.dispose(); }; this.fillText = function(text, x, y, maxWidth) { // TODO do something with maxWidth, probably involving measure var fillPaint = this._fillPaint(); var blob = CanvasKit.TextBlob.MakeFromText(text, this._font); var shadowPaint = this._shadowPaint(fillPaint); if (shadowPaint) { this._canvas.save(); this._applyShadowOffsetMatrix(); this._canvas.drawTextBlob(blob, x, y, shadowPaint); this._canvas.restore(); shadowPaint.dispose(); } this._canvas.drawTextBlob(blob, x, y, fillPaint); blob.delete(); fillPaint.dispose(); }; this.getImageData = function(x, y, w, h) { var pixels = this._canvas.readPixels(x, y, { 'width': w, 'height': h, 'colorType': CanvasKit.ColorType.RGBA_8888, 'alphaType': CanvasKit.AlphaType.Unpremul, 'colorSpace': CanvasKit.ColorSpace.SRGB, }); if (!pixels) { return null; } // This essentially re-wraps the pixels from a Uint8Array to // a Uint8ClampedArray (without making a copy of pixels). return new ImageData( new Uint8ClampedArray(pixels.buffer), w, h); }; this.getLineDash = function() { return this._lineDashList.slice(); }; this._mapToLocalCoordinates = function(pts) { var inverted = CanvasKit.Matrix.invert(this._currentTransform); CanvasKit.Matrix.mapPoints(inverted, pts); return pts; }; this.isPointInPath = function(x, y, fillmode) { var args = arguments; if (args.length === 3) { var path = this._currentPath; } else if (args.length === 4) { var path = args[0]; x = args[1]; y = args[2]; fillmode = args[3]; } else { throw 'invalid arg count, need 3 or 4, got ' + args.length; } if (!isFinite(x) || !isFinite(y)) { return false; } fillmode = fillmode || 'nonzero'; if (!(fillmode === 'nonzero' || fillmode === 'evenodd')) { return false; } // x and y are in canvas coordinates (i.e. unaffected by CTM) var pts = this._mapToLocalCoordinates([x, y]); x = pts[0]; y = pts[1]; path.setFillType(fillmode === 'nonzero' ? CanvasKit.FillType.Winding : CanvasKit.FillType.EvenOdd); return path.contains(x, y); }; this.isPointInStroke = function(x, y) { var args = arguments; if (args.length === 2) { var path = this._currentPath; } else if (args.length === 3) { var path = args[0]; x = args[1]; y = args[2]; } else { throw 'invalid arg count, need 2 or 3, got ' + args.length; } if (!isFinite(x) || !isFinite(y)) { return false; } var pts = this._mapToLocalCoordinates([x, y]); x = pts[0]; y = pts[1]; var temp = path.copy(); // fillmode is always nonzero temp.setFillType(CanvasKit.FillType.Winding); temp.stroke({'width': this.lineWidth, 'miter_limit': this.miterLimit, 'cap': this._paint.getStrokeCap(), 'join': this._paint.getStrokeJoin(), 'precision': 0.3, // this is what Chrome uses to compute this }); var retVal = temp.contains(x, y); temp.delete(); return retVal; }; this.lineTo = function(x, y) { lineTo(this._currentPath, x, y); }; this.measureText = function(text) { const ids = this._font.getGlyphIDs(text); const widths = this._font.getGlyphWidths(ids); let totalWidth = 0; for (const w of widths) { totalWidth += w; } return { "width": totalWidth, }; }; this.moveTo = function(x, y) { moveTo(this._currentPath, x, y); }; this.putImageData = function(imageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight) { if (!allAreFinite([x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight])) { return; } if (dirtyX === undefined) { // fast, simple path for basic call this._canvas.writePixels(imageData.data, imageData.width, imageData.height, x, y); return; } dirtyX = dirtyX || 0; dirtyY = dirtyY || 0; dirtyWidth = dirtyWidth || imageData.width; dirtyHeight = dirtyHeight || imageData.height; // as per https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-putimagedata if (dirtyWidth < 0) { dirtyX = dirtyX+dirtyWidth; dirtyWidth = Math.abs(dirtyWidth); } if (dirtyHeight < 0) { dirtyY = dirtyY+dirtyHeight; dirtyHeight = Math.abs(dirtyHeight); } if (dirtyX < 0) { dirtyWidth = dirtyWidth + dirtyX; dirtyX = 0; } if (dirtyY < 0) { dirtyHeight = dirtyHeight + dirtyY; dirtyY = 0; } if (dirtyWidth <= 0 || dirtyHeight <= 0) { return; } var img = CanvasKit.MakeImage({ 'width': imageData.width, 'height': imageData.height, 'alphaType': CanvasKit.AlphaType.Unpremul, 'colorType': CanvasKit.ColorType.RGBA_8888, 'colorSpace': CanvasKit.ColorSpace.SRGB }, imageData.data, 4 * imageData.width); var src = CanvasKit.XYWHRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight); var dst = CanvasKit.XYWHRect(x+dirtyX, y+dirtyY, dirtyWidth, dirtyHeight); var inverted = CanvasKit.Matrix.invert(this._currentTransform); this._canvas.save(); // putImageData() operates in device space. this._canvas.concat(inverted); this._canvas.drawImageRect(img, src, dst, null, false); this._canvas.restore(); img.delete(); }; this.quadraticCurveTo = function(cpx, cpy, x, y) { quadraticCurveTo(this._currentPath, cpx, cpy, x, y); }; this.rect = function(x, y, width, height) { rect(this._currentPath, x, y, width, height); }; this.resetTransform = function() { // Apply the current transform to the path and then reset // to the identity. Essentially "commit" the transform. this._currentPath.transform(this._currentTransform); var inverted = CanvasKit.Matrix.invert(this._currentTransform); this._canvas.concat(inverted); // This should be identity, modulo floating point drift. this._currentTransform = this._canvas.getTotalMatrix(); }; this.restore = function() { var newState = this._canvasStateStack.pop(); if (!newState) { return; } // "commit" the current transform. We pop, then apply the inverse of the // popped state, which has the effect of applying just the delta of // transforms between old and new. var combined = CanvasKit.Matrix.multiply( this._currentTransform, CanvasKit.Matrix.invert(newState.ctm) ); this._currentPath.transform(combined); this._paint.delete(); this._paint = newState.paint; this._lineDashList = newState.ldl; this._strokeWidth = newState.sw; this._strokeStyle = newState.ss; this._fillStyle = newState.fs; this._shadowOffsetX = newState.sox; this._shadowOffsetY = newState.soy; this._shadowBlur = newState.sb; this._shadowColor = newState.shc; this._globalAlpha = newState.ga; this._globalCompositeOperation = newState.gco; this._lineDashOffset = newState.ldo; this._fontString = newState.fontstr; //TODO: textAlign, textBaseline // restores the clip and ctm this._canvas.restore(); this._currentTransform = this._canvas.getTotalMatrix(); }; this.rotate = function(radians) { if (!isFinite(radians)) { return; } // retroactively apply the inverse of this transform to the previous // path so it cancels out when we apply the transform at draw time. var inverted = CanvasKit.Matrix.rotated(-radians); this._currentPath.transform(inverted); this._canvas.rotate(radiansToDegrees(radians), 0, 0); this._currentTransform = this._canvas.getTotalMatrix(); }; this.save = function() { if (this._fillStyle._copy) { var fs = this._fillStyle._copy(); this._toCleanUp.push(fs); } else { var fs = this._fillStyle; } if (this._strokeStyle._copy) { var ss = this._strokeStyle._copy(); this._toCleanUp.push(ss); } else { var ss = this._strokeStyle; } this._canvasStateStack.push({ ctm: this._currentTransform.slice(), ldl: this._lineDashList.slice(), sw: this._strokeWidth, ss: ss, fs: fs, sox: this._shadowOffsetX, soy: this._shadowOffsetY, sb: this._shadowBlur, shc: this._shadowColor, ga: this._globalAlpha, ldo: this._lineDashOffset, gco: this._globalCompositeOperation, paint: this._paint.copy(), fontstr: this._fontString, //TODO: textAlign, textBaseline }); // Saves the clip this._canvas.save(); }; this.scale = function(sx, sy) { if (!allAreFinite(arguments)) { return; } // retroactively apply the inverse of this transform to the previous // path so it cancels out when we apply the transform at draw time. var inverted = CanvasKit.Matrix.scaled(1/sx, 1/sy); this._currentPath.transform(inverted); this._canvas.scale(sx, sy); this._currentTransform = this._canvas.getTotalMatrix(); }; this.setLineDash = function(dashes) { for (var i = 0; i < dashes.length; i++) { if (!isFinite(dashes[i]) || dashes[i] < 0) { Debug('dash list must have positive, finite values'); return; } } if (dashes.length % 2 === 1) { // as per the spec, concatenate 2 copies of dashes // to give it an even number of elements. Array.prototype.push.apply(dashes, dashes); } this._lineDashList = dashes; }; this.setTransform = function(a, b, c, d, e, f) { if (!(allAreFinite(arguments))) { return; } this.resetTransform(); this.transform(a, b, c, d, e, f); }; // We need to apply the shadowOffsets on the device coordinates, so we undo // the CTM, apply the offsets, then re-apply the CTM. this._applyShadowOffsetMatrix = function() { var inverted = CanvasKit.Matrix.invert(this._currentTransform); this._canvas.concat(inverted); this._canvas.concat(CanvasKit.Matrix.translated(this._shadowOffsetX, this._shadowOffsetY)); this._canvas.concat(this._currentTransform); }; // Returns the shadow paint for the current settings or null if there // should be no shadow. This ends up being a copy of the given // paint with a blur maskfilter and the correct color. this._shadowPaint = function(basePaint) { // multiply first to see if the alpha channel goes to 0 after multiplication. var alphaColor = CanvasKit.multiplyByAlpha(this._shadowColor, this._globalAlpha); // if alpha is zero, no shadows if (!CanvasKit.getColorComponents(alphaColor)[3]) { return null; } // one of these must also be non-zero (otherwise the shadow is // completely hidden. And the spec says so). if (!(this._shadowBlur || this._shadowOffsetY || this._shadowOffsetX)) { return null; } var shadowPaint = basePaint.copy(); shadowPaint.setColor(alphaColor); var blurEffect = CanvasKit.MaskFilter.MakeBlur(CanvasKit.BlurStyle.Normal, BlurRadiusToSigma(this._shadowBlur), false); shadowPaint.setMaskFilter(blurEffect); // hack up a "destructor" which also cleans up the blurEffect. Otherwise, // we leak the blurEffect (since smart pointers don't help us in JS land). shadowPaint.dispose = function() { blurEffect.delete(); this.delete(); }; return shadowPaint; }; // A helper to get a copy of the current paint, ready for stroking. // This applies the global alpha and the dashedness. // Call dispose() after to clean up. this._strokePaint = function() { var paint = this._paint.copy(); paint.setStyle(CanvasKit.PaintStyle.Stroke); if (isCanvasKitColor(this._strokeStyle)) { var alphaColor = CanvasKit.multiplyByAlpha(this._strokeStyle, this._globalAlpha); paint.setColor(alphaColor); } else { var shader = this._strokeStyle._getShader(this._currentTransform); paint.setColor(CanvasKit.Color(0,0,0, this._globalAlpha)); paint.setShader(shader); } paint.setStrokeWidth(this._strokeWidth); if (this._lineDashList.length) { var dashedEffect = CanvasKit.PathEffect.MakeDash(this._lineDashList, this._lineDashOffset); paint.setPathEffect(dashedEffect); } paint.dispose = function() { dashedEffect && dashedEffect.delete(); this.delete(); }; return paint; }; this.stroke = function(path) { path = path ? path._getPath() : this._currentPath; var strokePaint = this._strokePaint(); var shadowPaint = this._shadowPaint(strokePaint); if (shadowPaint) { this._canvas.save(); this._applyShadowOffsetMatrix(); this._canvas.drawPath(path, shadowPaint); this._canvas.restore(); shadowPaint.dispose(); } this._canvas.drawPath(path, strokePaint); strokePaint.dispose(); }; this.strokeRect = function(x, y, width, height) { var strokePaint = this._strokePaint(); var shadowPaint = this._shadowPaint(strokePaint); if (shadowPaint) { this._canvas.save(); this._applyShadowOffsetMatrix(); this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), shadowPaint); this._canvas.restore(); shadowPaint.dispose(); } this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), strokePaint); strokePaint.dispose(); }; this.strokeText = function(text, x, y, maxWidth) { // TODO do something with maxWidth, probably involving measure var strokePaint = this._strokePaint(); var blob = CanvasKit.TextBlob.MakeFromText(text, this._font); var shadowPaint = this._shadowPaint(strokePaint); if (shadowPaint) { this._canvas.save(); this._applyShadowOffsetMatrix(); this._canvas.drawTextBlob(blob, x, y, shadowPaint); this._canvas.restore(); shadowPaint.dispose(); } this._canvas.drawTextBlob(blob, x, y, strokePaint); blob.delete(); strokePaint.dispose(); }; this.translate = function(dx, dy) { if (!allAreFinite(arguments)) { return; } // retroactively apply the inverse of this transform to the previous // path so it cancels out when we apply the transform at draw time. var inverted = CanvasKit.Matrix.translated(-dx, -dy); this._currentPath.transform(inverted); this._canvas.translate(dx, dy); this._currentTransform = this._canvas.getTotalMatrix(); }; this.transform = function(a, b, c, d, e, f) { var newTransform = [a, c, e, b, d, f, 0, 0, 1]; // retroactively apply the inverse of this transform to the previous // path so it cancels out when we apply the transform at draw time. var inverted = CanvasKit.Matrix.invert(newTransform); this._currentPath.transform(inverted); this._canvas.concat(newTransform); this._currentTransform = this._canvas.getTotalMatrix(); }; // Not supported operations (e.g. for Web only) this.addHitRegion = function() {}; this.clearHitRegions = function() {}; this.drawFocusIfNeeded = function() {}; this.removeHitRegion = function() {}; this.scrollPathIntoView = function() {}; Object.defineProperty(this, 'canvas', { value: null, writable: false }); } function BlurRadiusToSigma(radius) { // Blink (Chrome) does the following, for legacy reasons, even though it // is against the spec. https://bugs.chromium.org/p/chromium/issues/detail?id=179006 // This may change in future releases. // This code is staying here in case any clients are interested in using it // to match Blink "exactly". // if (radius <= 0) // return 0; // return 0.288675 * radius + 0.5; // // This is what the spec says, which is how Firefox and others operate. return radius/2; } CanvasKit.MakeCanvas = function(width, height) { var surf = CanvasKit.MakeSurface(width, height); if (surf) { return new HTMLCanvas(surf); } return null; }; function HTMLCanvas(skSurface) { this._surface = skSurface; this._context = new CanvasRenderingContext2D(skSurface.getCanvas()); this._toCleanup = []; // Data is either an ArrayBuffer, a TypedArray, or a Node Buffer this.decodeImage = function(data) { var img = CanvasKit.MakeImageFromEncoded(data); if (!img) { throw 'Invalid input'; } this._toCleanup.push(img); return new HTMLImage(img); }; this.loadFont = function(buffer, descriptors) { var newFont = CanvasKit.Typeface.MakeFreeTypeFaceFromData(buffer); if (!newFont) { Debug('font could not be processed', descriptors); return null; } this._toCleanup.push(newFont); addToFontCache(newFont, descriptors); }; this.makePath2D = function(path) { var p2d = new Path2D(path); this._toCleanup.push(p2d._getPath()); return p2d; }; // A normal <canvas> requires that clients call getContext this.getContext = function(type) { if (type === '2d') { return this._context; } return null; }; this.toDataURL = function(codec, quality) { // TODO(kjlubick): maybe support other codecs (webp?) // For now, just to png and jpeg this._surface.flush(); var img = this._surface.makeImageSnapshot(); if (!img) { Debug('no snapshot'); return; } codec = codec || 'image/png'; var format = CanvasKit.ImageFormat.PNG; if (codec === 'image/jpeg') { format = CanvasKit.ImageFormat.JPEG; } quality = quality || 0.92; var imgBytes = img.encodeToBytes(format, quality); if (!imgBytes) { Debug('encoding failure'); return } img.delete(); return 'data:' + codec + ';base64,' + toBase64String(imgBytes); }; this.dispose = function() { this._context._dispose(); this._toCleanup.forEach(function(i) { i.delete(); }); this._surface.dispose(); } } function HTMLImage(skImage) { this._skImage = skImage; // These are writable but have no effect, just like HTMLImageElement this.width = skImage.width(); this.height = skImage.height(); this.naturalWidth = this.width; this.naturalHeight = this.height; this.getSkImage = function() { return skImage; } } function ImageData(arr, width, height) { if (!width || height === 0) { throw 'invalid dimensions, width and height must be non-zero'; } if (arr.length % 4) { throw 'arr must be a multiple of 4'; } height = height || arr.length/(4*width); Object.defineProperty(this, 'data', { value: arr, writable: false }); Object.defineProperty(this, 'height', { value: height, writable: false }); Object.defineProperty(this, 'width', { value: width, writable: false }); } CanvasKit.ImageData = function() { if (arguments.length === 2) { var width = arguments[0]; var height = arguments[1]; var byteLength = 4 * width * height; return new ImageData(new Uint8ClampedArray(byteLength), width, height); } else if (arguments.length === 3) { var arr = arguments[0]; if (arr.prototype.constructor !== Uint8ClampedArray ) { throw 'bytes must be given as a Uint8ClampedArray'; } var width = arguments[1]; var height = arguments[2]; if (arr % 4) { throw 'bytes must be given in a multiple of 4'; } if (arr % width) { throw 'bytes must divide evenly by width'; } if (height && (height !== (arr / (width * 4)))) { throw 'invalid height given'; } height = arr / (width * 4); return new ImageData(arr, width, height); } else { throw 'invalid number of arguments - takes 2 or 3, saw ' + arguments.length; } } function LinearCanvasGradient(x1, y1, x2, y2) { this._shader = null; this._colors = []; this._pos = []; this.addColorStop = function(offset, color) { if (offset < 0 || offset > 1 || !isFinite(offset)) { throw 'offset must be between 0 and 1 inclusively'; } color = parseColor(color); // From the spec: If multiple stops are added at the same offset on a // gradient, then they must be placed in the order added, with the first // one closest to the start of the gradient, and each subsequent one // infinitesimally further along towards the end point (in effect // causing all but the first and last stop added at each point to be // ignored). // To implement that, if an offset is already in the list, // we just overwrite its color (since the user can't remove Color stops // after the fact). var idx = this._pos.indexOf(offset); if (idx !== -1) { this._colors[idx] = color; } else { // insert it in sorted order for (idx = 0; idx < this._pos.length; idx++) { if (this._pos[idx] > offset) { break; } } this._pos .splice(idx, 0, offset); this._colors.splice(idx, 0, color); } } this._copy = function() { var lcg = new LinearCanvasGradient(x1, y1, x2, y2); lcg._colors = this._colors.slice(); lcg._pos = this._pos.slice(); return lcg; } this._dispose = function() { if (this._shader) { this._shader.delete(); this._shader = null; } } this._getShader = function(currentTransform) { // From the spec: "The points in the linear gradient must be transformed // as described by the current transformation matrix when rendering." var pts = [x1, y1, x2, y2]; CanvasKit.Matrix.mapPoints(currentTransform, pts); var sx1 = pts[0]; var sy1 = pts[1]; var sx2 = pts[2]; var sy2 = pts[3]; this._dispose(); this._shader = CanvasKit.Shader.MakeLinearGradient([sx1, sy1], [sx2, sy2], this._colors, this._pos, CanvasKit.TileMode.Clamp); return this._shader; } } // CanvasPath methods, which all take an Path object as the first param function arc(skpath, x, y, radius, startAngle, endAngle, ccw) { // As per https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arc // arc is essentially a simpler version of ellipse. ellipse(skpath, x, y, radius, radius, 0, startAngle, endAngle, ccw); } function arcTo(skpath, x1, y1, x2, y2, radius) { if (!allAreFinite([x1, y1, x2, y2, radius])) { return; } if (radius < 0) { throw 'radii cannot be negative'; } if (skpath.isEmpty()) { skpath.moveTo(x1, y1); } skpath.arcToTangent(x1, y1, x2, y2, radius); } function bezierCurveTo(skpath, cp1x, cp1y, cp2x, cp2y, x, y) { if (!allAreFinite([cp1x, cp1y, cp2x, cp2y, x, y])) { return; } if (skpath.isEmpty()) { skpath.moveTo(cp1x, cp1y); } skpath.cubicTo(cp1x, cp1y, cp2x, cp2y, x, y); } function closePath(skpath) { if (skpath.isEmpty()) { return; } // Check to see if we are not just a single point var bounds = skpath.getBounds(); if ((bounds[3] - bounds[1]) || (bounds[2] - bounds[0])) { skpath.close(); } } function _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle) { var sweepDegrees = radiansToDegrees(endAngle - startAngle); var startDegrees = radiansToDegrees(startAngle); var oval = CanvasKit.LTRBRect(x - radiusX, y - radiusY, x + radiusX, y + radiusY); // draw in 2 180 degree segments because trying to draw all 360 degrees at once // draws nothing. if (almostEqual(Math.abs(sweepDegrees), 360)) { var halfSweep = sweepDegrees/2; skpath.arcToOval(oval, startDegrees, halfSweep, false); skpath.arcToOval(oval, startDegrees + halfSweep, halfSweep, false); return; } skpath.arcToOval(oval, startDegrees, sweepDegrees, false); } function ellipse(skpath, x, y, radiusX, radiusY, rotation, startAngle, endAngle, ccw) { if (!allAreFinite([x, y, radiusX, radiusY, rotation, startAngle, endAngle])) { return; } if (radiusX < 0 || radiusY < 0) { throw 'radii cannot be negative'; } // based off of CanonicalizeAngle in Chrome var tao = 2 * Math.PI; var newStartAngle = startAngle % tao; if (newStartAngle < 0) { newStartAngle += tao; } var delta = newStartAngle - startAngle; startAngle = newStartAngle; endAngle += delta; // Based off of AdjustEndAngle in Chrome. if (!ccw && (endAngle - startAngle) >= tao) { // Draw complete ellipse endAngle = startAngle + tao; } else if (ccw && (startAngle - endAngle) >= tao) { // Draw complete ellipse endAngle = startAngle - tao; } else if (!ccw && startAngle > endAngle) { endAngle = startAngle + (tao - (startAngle - endAngle) % tao); } else if (ccw && startAngle < endAngle) { endAngle = startAngle - (tao - (endAngle - startAngle) % tao); } // Based off of Chrome's implementation in // https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/path.cc // of note, can't use addArc or addOval because they close the arc, which // the spec says not to do (unless the user explicitly calls closePath). // This throws off points being in/out of the arc. if (!rotation) { _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle); return; } var rotated = CanvasKit.Matrix.rotated(rotation, x, y); var rotatedInvert = CanvasKit.Matrix.rotated(-rotation, x, y); skpath.transform(rotatedInvert); _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle); skpath.transform(rotated); } function lineTo(skpath, x, y) { if (!allAreFinite([x, y])) { return; } // A lineTo without a previous point has a moveTo inserted before it if (skpath.isEmpty()) { skpath.moveTo(x, y); } skpath.lineTo(x, y); } function moveTo(skpath, x, y) { if (!allAreFinite([x, y])) { return; } skpath.moveTo(x, y); } function quadraticCurveTo(skpath, cpx, cpy, x, y) { if (!allAreFinite([cpx, cpy, x, y])) { return; } if (skpath.isEmpty()) { skpath.moveTo(cpx, cpy); } skpath.quadTo(cpx, cpy, x, y); } function rect(skpath, x, y, width, height) { var rect = CanvasKit.XYWHRect(x, y, width, height); if (!allAreFinite(rect)) { return; } // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect skpath.addRect(rect); } function Path2D(path) { this._path = null; if (typeof path === 'string') { this._path = CanvasKit.Path.MakeFromSVGString(path); } else if (path && path._getPath) { this._path = path._getPath().copy(); } else { this._path = new CanvasKit.Path(); } this._getPath = function() { return this._path; } this.addPath = function(path2d, transform) { if (!transform) { transform = { 'a': 1, 'c': 0, 'e': 0, 'b': 0, 'd': 1, 'f': 0, }; } this._path.addPath(path2d._getPath(), [transform.a, transform.c, transform.e, transform.b, transform.d, transform.f]); } this.arc = function(x, y, radius, startAngle, endAngle, ccw) { arc(this._path, x, y, radius, startAngle, endAngle, ccw); } this.arcTo = function(x1, y1, x2, y2, radius) { arcTo(this._path, x1, y1, x2, y2, radius); } this.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { bezierCurveTo(this._path, cp1x, cp1y, cp2x, cp2y, x, y); } this.closePath = function() { closePath(this._path); } this.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, ccw) { ellipse(this._path, x, y, radiusX, radiusY, rotation, startAngle, endAngle, ccw); } this.lineTo = function(x, y) { lineTo(this._path, x, y); } this.moveTo = function(x, y) { moveTo(this._path, x, y); } this.quadraticCurveTo = function(cpx, cpy, x, y) { quadraticCurveTo(this._path, cpx, cpy, x, y); } this.rect = function(x, y, width, height) { rect(this._path, x, y, width, height); } } function CanvasPattern(image, repetition) { this._shader = null; // image should be an Image returned from HTMLCanvas.decodeImage() if (image instanceof HTMLImage) { image = image.getSkImage(); } this._image = image; this._transform = CanvasKit.Matrix.identity(); if (repetition === '') { repetition = 'repeat'; } switch(repetition) { case 'repeat-x': this._tileX = CanvasKit.TileMode.Repeat; // Skia's 'clamp' mode repeats the last row/column // which looks very very strange. // Decal mode does just transparent copying, which // is exactly what the spec wants. this._tileY = CanvasKit.TileMode.Decal; break; case 'repeat-y': this._tileX = CanvasKit.TileMode.Decal; this._tileY = CanvasKit.TileMode.Repeat; break; case 'repeat': this._tileX = CanvasKit.TileMode.Repeat; this._tileY = CanvasKit.TileMode.Repeat; break; case 'no-repeat': this._tileX = CanvasKit.TileMode.Decal; this._tileY = CanvasKit.TileMode.Decal; break; default: throw 'invalid repetition mode ' + repetition; } // Takes a DOMMatrix like object. e.g. the identity would be: // {a:1, b: 0, c: 0, d: 1, e: 0, f: 0} // @param {DOMMatrix} m this.setTransform = function(m) { var t = [m.a, m.c, m.e, m.b, m.d, m.f, 0, 0, 1]; if (allAreFinite(t)) { this._transform = t; } }; this._copy = function() { var cp = new CanvasPattern(); cp._tileX = this._tileX; cp._tileY = this._tileY; return cp; }; this._dispose = function() { if (this._shader) { this._shader.delete(); this._shader = null; } }; this._getShader = function(currentTransform) { // Ignore currentTransform since it will be applied later this._dispose(); // A shader with cubic sampling options is high quality. this._shader = this._image.makeShaderCubic(this._tileX, this._tileY, 1/3, 1/3, this._transform); return this._shader; } } // Note, Skia has a different notion of a "radial" gradient. // Skia has a twoPointConical gradient that is the same as the // canvas's RadialGradient. function RadialCanvasGradient(x1, y1, r1, x2, y2, r2) { this._shader = null; this._colors = []; this._pos = []; this.addColorStop = function(offset, color) { if (offset < 0 || offset > 1 || !isFinite(offset)) { throw 'offset must be between 0 and 1 inclusively'; } color = parseColor(color); // From the spec: If multiple stops are added at the same offset on a // gradient, then they must be placed in the order added, with the first // one closest to the start of the gradient, and each subsequent one // infinitesimally further along towards the end point (in effect // causing all but the first and last stop added at each point to be // ignored). // To implement that, if an offset is already in the list, // we just overwrite its color (since the user can't remove Color stops // after the fact). var idx = this._pos.indexOf(offset); if (idx !== -1) { this._colors[idx] = color; } else { // insert it in sorted order for (idx = 0; idx < this._pos.length; idx++) { if (this._pos[idx] > offset) { break; } } this._pos .splice(idx, 0, offset); this._colors.splice(idx, 0, color); } } this._copy = function() { var rcg = new RadialCanvasGradient(x1, y1, r1, x2, y2, r2); rcg._colors = this._colors.slice(); rcg._pos = this._pos.slice(); return rcg; } this._dispose = function() { if (this._shader) { this._shader.delete(); this._shader = null; } } this._getShader = function(currentTransform) { // From the spec: "The points in the linear gradient must be transformed // as described by the current transformation matrix when rendering." var pts = [x1, y1, x2, y2]; CanvasKit.Matrix.mapPoints(currentTransform, pts); var sx1 = pts[0]; var sy1 = pts[1]; var sx2 = pts[2]; var sy2 = pts[3]; var sx = currentTransform[0]; var sy = currentTransform[4]; var scaleFactor = (Math.abs(sx) + Math.abs(sy))/2; var sr1 = r1 * scaleFactor; var sr2 = r2 * scaleFactor; this._dispose(); this._shader = CanvasKit.Shader.MakeTwoPointConicalGradient( [sx1, sy1], sr1, [sx2, sy2], sr2, this._colors, this._pos, CanvasKit.TileMode.Clamp); return this._shader; } } // This closes the scope started in preamble.js }()); // This closes the scope started in preamble.js }(Module)); // When this file is loaded in, the high level object is "Module"; // Sometimes an existing Module object exists with properties // meant to overwrite the default module functionality. Here // we collect those properties and reapply _after_ we configure // the current environment's defaults to avoid having to be so // defensive during initialization. var moduleOverrides = Object.assign({}, Module); var arguments_ = []; var thisProgram = './this.program'; var quit_ = (status, toThrow) => { throw toThrow; }; // Determine the runtime environment we are in. You can customize this by // setting the ENVIRONMENT setting at compile time (see settings.js). // Attempt to auto-detect the environment var ENVIRONMENT_IS_WEB = typeof window == 'object'; var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function'; // N.b. Electron.js environment is simultaneously a NODE-environment, but // also a web environment. var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string'; var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; if (Module['ENVIRONMENT']) { throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)'); } // `/` should be present at the end if `scriptDirectory` is not empty var scriptDirectory = ''; function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory); } return scriptDirectory + path; } // Hooks that are implemented differently in different runtime environments. var read_, readAsync, readBinary, setWindowTitle; // Normally we don't log exceptions but instead let them bubble out the top // level where the embedding environment (e.g. the browser) can handle // them. // However under v8 and node we sometimes exit the process direcly in which case // its up to use us to log the exception before exiting. // If we fix https://github.com/emscripten-core/emscripten/issues/15080 // this may no longer be needed under node. function logExceptionOnExit(e) { if (e instanceof ExitStatus) return; let toLog = e; if (e && typeof e == 'object' && e.stack) { toLog = [e, e.stack]; } err('exiting due to exception: ' + toLog); } var fs; var nodePath; var requireNodeFS; if (ENVIRONMENT_IS_NODE) { if (!(typeof process == 'object' && typeof require == 'function')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); if (ENVIRONMENT_IS_WORKER) { scriptDirectory = require('path').dirname(scriptDirectory) + '/'; } else { scriptDirectory = __dirname + '/'; } // include: node_shell_read.js requireNodeFS = () => { // Use nodePath as the indicator for these not being initialized, // since in some environments a global fs may have already been // created. if (!nodePath) { fs = require('fs'); nodePath = require('path'); } }; read_ = function shell_read(filename, binary) { requireNodeFS(); filename = nodePath['normalize'](filename); return fs.readFileSync(filename, binary ? undefined : 'utf8'); }; readBinary = (filename) => { var ret = read_(filename, true); if (!ret.buffer) { ret = new Uint8Array(ret); } assert(ret.buffer); return ret; }; readAsync = (filename, onload, onerror) => { requireNodeFS(); filename = nodePath['normalize'](filename); fs.readFile(filename, function(err, data) { if (err) onerror(err); else onload(data.buffer); }); }; // end include: node_shell_read.js if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/'); } arguments_ = process['argv'].slice(2); // MODULARIZE will export the module in the proper place outside, we don't need to export here // Without this older versions of node (< v15) will log unhandled rejections // but return 0, which is not normally the desired behaviour. This is // not be needed with node v15 and about because it is now the default // behaviour: // See https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode process['on']('unhandledRejection', function(reason) { throw reason; }); quit_ = (status, toThrow) => { if (keepRuntimeAlive()) { process['exitCode'] = status; throw toThrow; } logExceptionOnExit(toThrow); process['exit'](status); }; Module['inspect'] = function () { return '[Emscripten Module object]'; }; } else if (ENVIRONMENT_IS_SHELL) { if ((typeof process == 'object' && typeof require === 'function') || typeof window == 'object' || typeof importScripts == 'function') throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); if (typeof read != 'undefined') { read_ = function shell_read(f) { return read(f); }; } readBinary = function readBinary(f) { let data; if (typeof readbuffer == 'function') { return new Uint8Array(readbuffer(f)); } data = read(f, 'binary'); assert(typeof data == 'object'); return data; }; readAsync = function readAsync(f, onload, onerror) { setTimeout(() => onload(readBinary(f)), 0); }; if (typeof scriptArgs != 'undefined') { arguments_ = scriptArgs; } else if (typeof arguments != 'undefined') { arguments_ = arguments; } if (typeof quit == 'function') { quit_ = (status, toThrow) => { logExceptionOnExit(toThrow); quit(status); }; } if (typeof print != 'undefined') { // Prefer to use print/printErr where they exist, as they usually work better. if (typeof console == 'undefined') console = /** @type{!Console} */({}); console.log = /** @type{!function(this:Console, ...*): undefined} */ (print); console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr != 'undefined' ? printErr : print); } } else // Note that this includes Node.js workers when relevant (pthreads is enabled). // Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and // ENVIRONMENT_IS_NODE. if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled scriptDirectory = self.location.href; } else if (typeof document != 'undefined' && document.currentScript) { // web scriptDirectory = document.currentScript.src; } // When MODULARIZE, this JS may be executed later, after document.currentScript // is gone, so we saved it, and we use it here instead of any other info. if (_scriptDir) { scriptDirectory = _scriptDir; } // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. // otherwise, slice off the final part of the url to find the script directory. // if scriptDirectory does not contain a slash, lastIndexOf will return -1, // and scriptDirectory will correctly be replaced with an empty string. // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #), // they are removed because they could contain a slash. if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf('/')+1); } else { scriptDirectory = ''; } if (!(typeof window == 'object' || typeof importScripts == 'function')) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); // Differentiate the Web Worker from the Node Worker case, as reading must // be done differently. { // include: web_or_worker_shell_read.js read_ = (url) => { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.send(null); return xhr.responseText; } if (ENVIRONMENT_IS_WORKER) { readBinary = (url) => { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.responseType = 'arraybuffer'; xhr.send(null); return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response)); }; } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 onload(xhr.response); return; } onerror(); }; xhr.onerror = onerror; xhr.send(null); } // end include: web_or_worker_shell_read.js } setWindowTitle = (title) => document.title = title; } else { throw new Error('environment detection error'); } var out = Module['print'] || console.log.bind(console); var err = Module['printErr'] || console.warn.bind(console); // Merge back in the overrides Object.assign(Module, moduleOverrides); // Free the object hierarchy contained in the overrides, this lets the GC // reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. moduleOverrides = null; checkIncomingModuleAPI(); // Emit code to handle expected values on the Module object. This applies Module.x // to the proper local x. This has two benefits: first, we only emit it if it is // expected to arrive, and second, by using a local everywhere else that can be // minified. if (Module['arguments']) arguments_ = Module['arguments'];legacyModuleProp('arguments', 'arguments_'); if (Module['thisProgram']) thisProgram = Module['thisProgram'];legacyModuleProp('thisProgram', 'thisProgram'); if (Module['quit']) quit_ = Module['quit'];legacyModuleProp('quit', 'quit_'); // perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message // Assertions on removed incoming Module JS APIs. assert(typeof Module['memoryInitializerPrefixURL'] == 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['pthreadMainPrefixURL'] == 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['cdInitializerPrefixURL'] == 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['filePackagePrefixURL'] == 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['read'] == 'undefined', 'Module.read option was removed (modify read_ in JS)'); assert(typeof Module['readAsync'] == 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)'); assert(typeof Module['readBinary'] == 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)'); assert(typeof Module['setWindowTitle'] == 'undefined', 'Module.setWindowTitle option was removed (modify setWindowTitle in JS)'); assert(typeof Module['TOTAL_MEMORY'] == 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY'); legacyModuleProp('read', 'read_'); legacyModuleProp('readAsync', 'readAsync'); legacyModuleProp('readBinary', 'readBinary'); legacyModuleProp('setWindowTitle', 'setWindowTitle'); var IDBFS = 'IDBFS is no longer included by default; build with -lidbfs.js'; var PROXYFS = 'PROXYFS is no longer included by default; build with -lproxyfs.js'; var WORKERFS = 'WORKERFS is no longer included by default; build with -lworkerfs.js'; var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js'; function alignMemory() { abort('`alignMemory` is now a library function and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line'); } assert(!ENVIRONMENT_IS_SHELL, "shell environment detected but not enabled at build time. Add 'shell' to `-sENVIRONMENT` to enable."); var STACK_ALIGN = 16; var POINTER_SIZE = 4; function getNativeTypeSize(type) { switch (type) { case 'i1': case 'i8': case 'u8': return 1; case 'i16': case 'u16': return 2; case 'i32': case 'u32': return 4; case 'i64': case 'u64': return 8; case 'float': return 4; case 'double': return 8; default: { if (type[type.length - 1] === '*') { return POINTER_SIZE; } else if (type[0] === 'i') { const bits = Number(type.substr(1)); assert(bits % 8 === 0, 'getNativeTypeSize invalid bits ' + bits + ', type ' + type); return bits / 8; } else { return 0; } } } } function warnOnce(text) { if (!warnOnce.shown) warnOnce.shown = {}; if (!warnOnce.shown[text]) { warnOnce.shown[text] = 1; err(text); } } // include: runtime_functions.js // This gives correct answers for everything less than 2^{14} = 16384 // I hope nobody is contemplating functions with 16384 arguments... function uleb128Encode(n) { assert(n < 16384); if (n < 128) { return [n]; } return [(n % 128) | 128, n >> 7]; } // Converts a signature like 'vii' into a description of the wasm types, like // { parameters: ['i32', 'i32'], results: [] }. function sigToWasmTypes(sig) { var typeNames = { 'i': 'i32', 'j': 'i64', 'f': 'f32', 'd': 'f64', 'p': 'i32', }; var type = { parameters: [], results: sig[0] == 'v' ? [] : [typeNames[sig[0]]] }; for (var i = 1; i < sig.length; ++i) { assert(sig[i] in typeNames, 'invalid signature char: ' + sig[i]); type.parameters.push(typeNames[sig[i]]); } return type; } // Wraps a JS function as a wasm function with a given signature. function convertJsFunctionToWasm(func, sig) { // If the type reflection proposal is available, use the new // "WebAssembly.Function" constructor. // Otherwise, construct a minimal wasm module importing the JS function and // re-exporting it. if (typeof WebAssembly.Function == "function") { return new WebAssembly.Function(sigToWasmTypes(sig), func); } // The module is static, with the exception of the type section, which is // generated based on the signature passed in. var typeSection = [ 0x01, // count: 1 0x60, // form: func ]; var sigRet = sig.slice(0, 1); var sigParam = sig.slice(1); var typeCodes = { 'i': 0x7f, // i32 'p': 0x7f, // i32 'j': 0x7e, // i64 'f': 0x7d, // f32 'd': 0x7c, // f64 }; // Parameters, length + signatures typeSection = typeSection.concat(uleb128Encode(sigParam.length)); for (var i = 0; i < sigParam.length; ++i) { assert(sigParam[i] in typeCodes, 'invalid signature char: ' + sigParam[i]); typeSection.push(typeCodes[sigParam[i]]); } // Return values, length + signatures // With no multi-return in MVP, either 0 (void) or 1 (anything else) if (sigRet == 'v') { typeSection.push(0x00); } else { typeSection = typeSection.concat([0x01, typeCodes[sigRet]]); } // Write the section code and overall length of the type section into the // section header typeSection = [0x01 /* Type section code */].concat( uleb128Encode(typeSection.length), typeSection ); // Rest of the module is static var bytes = new Uint8Array([ 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") 0x01, 0x00, 0x00, 0x00, // version: 1 ].concat(typeSection, [ 0x02, 0x07, // import section // (import "e" "f" (func 0 (type 0))) 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00, 0x07, 0x05, // export section // (export "f" (func 0 (type 0))) 0x01, 0x01, 0x66, 0x00, 0x00, ])); // We can compile this wasm module synchronously because it is very small. // This accepts an import (at "e.f"), that it reroutes to an export (at "f") var module = new WebAssembly.Module(bytes); var instance = new WebAssembly.Instance(module, { 'e': { 'f': func } }); var wrappedFunc = instance.exports['f']; return wrappedFunc; } var freeTableIndexes = []; // Weak map of functions in the table to their indexes, created on first use. var functionsInTableMap; function getEmptyTableSlot() { // Reuse a free index if there is one, otherwise grow. if (freeTableIndexes.length) { return freeTableIndexes.pop(); } // Grow the table try { wasmTable.grow(1); } catch (err) { if (!(err instanceof RangeError)) { throw err; } throw 'Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.'; } return wasmTable.length - 1; } function updateTableMap(offset, count) { for (var i = offset; i < offset + count; i++) { var item = getWasmTableEntry(i); // Ignore null values. if (item) { functionsInTableMap.set(item, i); } } } /** * Add a function to the table. * 'sig' parameter is required if the function being added is a JS function. * @param {string=} sig */ function addFunction(func, sig) { assert(typeof func != 'undefined'); // Check if the function is already in the table, to ensure each function // gets a unique index. First, create the map if this is the first use. if (!functionsInTableMap) { functionsInTableMap = new WeakMap(); updateTableMap(0, wasmTable.length); } if (functionsInTableMap.has(func)) { return functionsInTableMap.get(func); } // It's not in the table, add it now. var ret = getEmptyTableSlot(); // Set the new value. try { // Attempting to call this with JS function will cause of table.set() to fail setWasmTableEntry(ret, func); } catch (err) { if (!(err instanceof TypeError)) { throw err; } assert(typeof sig != 'undefined', 'Missing signature argument to addFunction: ' + func); var wrapped = convertJsFunctionToWasm(func, sig); setWasmTableEntry(ret, wrapped); } functionsInTableMap.set(func, ret); return ret; } function removeFunction(index) { functionsInTableMap.delete(getWasmTableEntry(index)); freeTableIndexes.push(index); } // end include: runtime_functions.js // include: runtime_debug.js function legacyModuleProp(prop, newName) { if (!Object.getOwnPropertyDescriptor(Module, prop)) { Object.defineProperty(Module, prop, { configurable: true, get: function() { abort('Module.' + prop + ' has been replaced with plain ' + newName + ' (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)'); } }); } } function ignoredModuleProp(prop) { if (Object.getOwnPropertyDescriptor(Module, prop)) { abort('`Module.' + prop + '` was supplied but `' + prop + '` not included in INCOMING_MODULE_JS_API'); } } function unexportedMessage(sym, isFSSybol) { var msg = "'" + sym + "' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the FAQ)"; if (isFSSybol) { msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; } return msg; } function unexportedRuntimeSymbol(sym, isFSSybol) { if (!Object.getOwnPropertyDescriptor(Module, sym)) { Object.defineProperty(Module, sym, { configurable: true, get: function() { abort(unexportedMessage(sym, isFSSybol)); } }); } } function unexportedRuntimeFunction(sym, isFSSybol) { if (!Object.getOwnPropertyDescriptor(Module, sym)) { Module[sym] = () => abort(unexportedMessage(sym, isFSSybol)); } } // end include: runtime_debug.js var tempRet0 = 0; var setTempRet0 = (value) => { tempRet0 = value; }; var getTempRet0 = () => tempRet0; // === Preamble library stuff === // Documentation for the public APIs defined in this file must be updated in: // site/source/docs/api_reference/preamble.js.rst // A prebuilt local version of the documentation is available at: // site/build/text/docs/api_reference/preamble.js.txt // You can also build docs locally as HTML or other formats in site/ // An online HTML version (which may be of a different version of Emscripten) // is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html var wasmBinary; if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary'); var noExitRuntime = Module['noExitRuntime'] || true;legacyModuleProp('noExitRuntime', 'noExitRuntime'); if (typeof WebAssembly != 'object') { abort('no native wasm support detected'); } // Wasm globals var wasmMemory; //======================================== // Runtime essentials //======================================== // whether we are quitting the application. no code should run after this. // set in exit() and abort() var ABORT = false; // set by exit() and abort(). Passed to 'onExit' handler. // NOTE: This is also used as the process return code code in shell environments // but only when noExitRuntime is false. var EXITSTATUS; /** @type {function(*, string=)} */ function assert(condition, text) { if (!condition) { abort('Assertion failed' + (text ? ': ' + text : '')); } } // Returns the C function with a specified identifier (for C++, you need to do manual name mangling) function getCFunc(ident) { var func = Module['_' + ident]; // closure exported function assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported'); return func; } // C calling interface. /** @param {string|null=} returnType @param {Array=} argTypes @param {Arguments|Array=} args @param {Object=} opts */ function ccall(ident, returnType, argTypes, args, opts) { // For fast lookup of conversion functions var toC = { 'string': function(str) { var ret = 0; if (str !== null && str !== undefined && str !== 0) { // null string // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' var len = (str.length << 2) + 1; ret = stackAlloc(len); stringToUTF8(str, ret, len); } return ret; }, 'array': function(arr) { var ret = stackAlloc(arr.length); writeArrayToMemory(arr, ret); return ret; } }; function convertReturnValue(ret) { if (returnType === 'string') { return UTF8ToString(ret); } if (returnType === 'boolean') return Boolean(ret); return ret; } var func = getCFunc(ident); var cArgs = []; var stack = 0; assert(returnType !== 'array', 'Return type should not be "array".'); if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]]; if (converter) { if (stack === 0) stack = stackSave(); cArgs[i] = converter(args[i]); } else { cArgs[i] = args[i]; } } } var ret = func.apply(null, cArgs); function onDone(ret) { if (stack !== 0) stackRestore(stack); return convertReturnValue(ret); } ret = onDone(ret); return ret; } /** @param {string=} returnType @param {Array=} argTypes @param {Object=} opts */ function cwrap(ident, returnType, argTypes, opts) { return function() { return ccall(ident, returnType, argTypes, arguments, opts); } } // We used to include malloc/free by default in the past. Show a helpful error in // builds with assertions. // include: runtime_strings.js // runtime_strings.js: Strings related runtime functions that are part of both MINIMAL_RUNTIME and regular runtime. var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined; // Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns // a copy of that string as a Javascript String object. /** * heapOrArray is either a regular array, or a JavaScript typed array view. * @param {number} idx * @param {number=} maxBytesToRead * @return {string} */ function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead; var endPtr = idx; // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. // (As a tiny code save trick, compare endPtr against endIdx using a negation, so that undefined means Infinity) while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); } else { var str = ''; // If building with TextDecoder, we have already computed the string length above, so test loop end condition against that while (idx < endPtr) { // For UTF8 byte structure, see: // http://en.wikipedia.org/wiki/UTF-8#Description // https://www.ietf.org/rfc/rfc2279.txt // https://tools.ietf.org/html/rfc3629 var u0 = heapOrArray[idx++]; if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } var u1 = heapOrArray[idx++] & 63; if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } var u2 = heapOrArray[idx++] & 63; if ((u0 & 0xF0) == 0xE0) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; } else { if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte 0x' + u0.toString(16) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!'); u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); } if (u0 < 0x10000) { str += String.fromCharCode(u0); } else { var ch = u0 - 0x10000; str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); } } } return str; } // Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the emscripten HEAP, returns a // copy of that string as a Javascript String object. // maxBytesToRead: an optional length that specifies the maximum number of bytes to read. You can omit // this parameter to scan the string until the first \0 byte. If maxBytesToRead is // passed, and the string at [ptr, ptr+maxBytesToReadr[ contains a null byte in the // middle, then the string will cut short at that byte index (i.e. maxBytesToRead will // not produce a string of exact length [ptr, ptr+maxBytesToRead[) // N.B. mixing frequent uses of UTF8ToString() with and without maxBytesToRead may // throw JS JIT optimizations off, so it is worth to consider consistently using one // style or the other. /** * @param {number} ptr * @param {number=} maxBytesToRead * @return {string} */ function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; } // Copies the given Javascript String object 'str' to the given byte array at address 'outIdx', // encoded in UTF8 form and null-terminated. The copy will require at most str.length*4+1 bytes of space in the HEAP. // Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. // Parameters: // str: the Javascript string to copy. // heap: the array to copy to. Each index in this array is assumed to be one 8-byte element. // outIdx: The starting offset in the array to begin the copying. // maxBytesToWrite: The maximum number of bytes this function can write to the array. // This count should include the null terminator, // i.e. if maxBytesToWrite=1, only the null terminator will be written and nothing else. // maxBytesToWrite=0 does not write any bytes to the output, not even the null terminator. // Returns the number of bytes written, EXCLUDING the null terminator. function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes. return 0; var startIdx = outIdx; var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. // See http://unicode.org/faq/utf_bom.html#utf16-3 // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 var u = str.charCodeAt(i); // possibly a lead surrogate if (u >= 0xD800 && u <= 0xDFFF) { var u1 = str.charCodeAt(++i); u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF); } if (u <= 0x7F) { if (outIdx >= endIdx) break; heap[outIdx++] = u; } else if (u <= 0x7FF) { if (outIdx + 1 >= endIdx) break; heap[outIdx++] = 0xC0 | (u >> 6); heap[outIdx++] = 0x80 | (u & 63); } else if (u <= 0xFFFF) { if (outIdx + 2 >= endIdx) break; heap[outIdx++] = 0xE0 | (u >> 12); heap[outIdx++] = 0x80 | ((u >> 6) & 63); heap[outIdx++] = 0x80 | (u & 63); } else { if (outIdx + 3 >= endIdx) break; if (u > 0x10FFFF) warnOnce('Invalid Unicode code point 0x' + u.toString(16) + ' encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x10FFFF).'); heap[outIdx++] = 0xF0 | (u >> 18); heap[outIdx++] = 0x80 | ((u >> 12) & 63); heap[outIdx++] = 0x80 | ((u >> 6) & 63); heap[outIdx++] = 0x80 | (u & 63); } } // Null-terminate the pointer to the buffer. heap[outIdx] = 0; return outIdx - startIdx; } // Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in UTF8 form. The copy will require at most str.length*4+1 bytes of space in the HEAP. // Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. // Returns the number of bytes written, EXCLUDING the null terminator. function stringToUTF8(str, outPtr, maxBytesToWrite) { assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); return stringToUTF8Array(str, HEAPU8,outPtr, maxBytesToWrite); } // Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. function lengthBytesUTF8(str) { var len = 0; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. // See http://unicode.org/faq/utf_bom.html#utf16-3 var u = str.charCodeAt(i); // possibly a lead surrogate if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); if (u <= 0x7F) ++len; else if (u <= 0x7FF) len += 2; else if (u <= 0xFFFF) len += 3; else len += 4; } return len; } // end include: runtime_strings.js // include: runtime_strings_extra.js // runtime_strings_extra.js: Strings related runtime functions that are available only in regular runtime. // Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns // a copy of that string as a Javascript String object. function AsciiToString(ptr) { var str = ''; while (1) { var ch = HEAPU8[((ptr++)>>0)]; if (!ch) return str; str += String.fromCharCode(ch); } } // Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in ASCII form. The copy will require at most str.length+1 bytes of space in the HEAP. function stringToAscii(str, outPtr) { return writeAsciiToMemory(str, outPtr, false); } // Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns // a copy of that string as a Javascript String object. var UTF16Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf-16le') : undefined; function UTF16ToString(ptr, maxBytesToRead) { assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!'); var endPtr = ptr; // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. var idx = endPtr >> 1; var maxIdx = idx + maxBytesToRead / 2; // If maxBytesToRead is not passed explicitly, it will be undefined, and this // will always evaluate to true. This saves on code size. while (!(idx >= maxIdx) && HEAPU16[idx]) ++idx; endPtr = idx << 1; if (endPtr - ptr > 32 && UTF16Decoder) { return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr)); } else { var str = ''; // If maxBytesToRead is not passed explicitly, it will be undefined, and the for-loop's condition // will always evaluate to true. The loop is then terminated on the first null char. for (var i = 0; !(i >= maxBytesToRead / 2); ++i) { var codeUnit = HEAP16[(((ptr)+(i*2))>>1)]; if (codeUnit == 0) break; // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through. str += String.fromCharCode(codeUnit); } return str; } } // Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in UTF16 form. The copy will require at most str.length*4+2 bytes of space in the HEAP. // Use the function lengthBytesUTF16() to compute the exact number of bytes (excluding null terminator) that this function will write. // Parameters: // str: the Javascript string to copy. // outPtr: Byte address in Emscripten HEAP where to write the string to. // maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null // terminator, i.e. if maxBytesToWrite=2, only the null terminator will be written and nothing else. // maxBytesToWrite<2 does not write any bytes to the output, not even the null terminator. // Returns the number of bytes written, EXCLUDING the null terminator. function stringToUTF16(str, outPtr, maxBytesToWrite) { assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!'); assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. if (maxBytesToWrite === undefined) { maxBytesToWrite = 0x7FFFFFFF; } if (maxBytesToWrite < 2) return 0; maxBytesToWrite -= 2; // Null terminator. var startPtr = outPtr; var numCharsToWrite = (maxBytesToWrite < str.length*2) ? (maxBytesToWrite / 2) : str.length; for (var i = 0; i < numCharsToWrite; ++i) { // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP. var codeUnit = str.charCodeAt(i); // possibly a lead surrogate HEAP16[((outPtr)>>1)] = codeUnit; outPtr += 2; } // Null-terminate the pointer to the HEAP. HEAP16[((outPtr)>>1)] = 0; return outPtr - startPtr; } // Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. function lengthBytesUTF16(str) { return str.length*2; } function UTF32ToString(ptr, maxBytesToRead) { assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!'); var i = 0; var str = ''; // If maxBytesToRead is not passed explicitly, it will be undefined, and this // will always evaluate to true. This saves on code size. while (!(i >= maxBytesToRead / 4)) { var utf32 = HEAP32[(((ptr)+(i*4))>>2)]; if (utf32 == 0) break; ++i; // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing. // See http://unicode.org/faq/utf_bom.html#utf16-3 if (utf32 >= 0x10000) { var ch = utf32 - 0x10000; str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); } else { str += String.fromCharCode(utf32); } } return str; } // Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in UTF32 form. The copy will require at most str.length*4+4 bytes of space in the HEAP. // Use the function lengthBytesUTF32() to compute the exact number of bytes (excluding null terminator) that this function will write. // Parameters: // str: the Javascript string to copy. // outPtr: Byte address in Emscripten HEAP where to write the string to. // maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null // terminator, i.e. if maxBytesToWrite=4, only the null terminator will be written and nothing else. // maxBytesToWrite<4 does not write any bytes to the output, not even the null terminator. // Returns the number of bytes written, EXCLUDING the null terminator. function stringToUTF32(str, outPtr, maxBytesToWrite) { assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!'); assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. if (maxBytesToWrite === undefined) { maxBytesToWrite = 0x7FFFFFFF; } if (maxBytesToWrite < 4) return 0; var startPtr = outPtr; var endPtr = startPtr + maxBytesToWrite - 4; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. // See http://unicode.org/faq/utf_bom.html#utf16-3 var codeUnit = str.charCodeAt(i); // possibly a lead surrogate if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) { var trailSurrogate = str.charCodeAt(++i); codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF); } HEAP32[((outPtr)>>2)] = codeUnit; outPtr += 4; if (outPtr + 4 > endPtr) break; } // Null-terminate the pointer to the HEAP. HEAP32[((outPtr)>>2)] = 0; return outPtr - startPtr; } // Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. function lengthBytesUTF32(str) { var len = 0; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. // See http://unicode.org/faq/utf_bom.html#utf16-3 var codeUnit = str.charCodeAt(i); if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate. len += 4; } return len; } // Allocate heap space for a JS string, and write it there. // It is the responsibility of the caller to free() that memory. function allocateUTF8(str) { var size = lengthBytesUTF8(str) + 1; var ret = _malloc(size); if (ret) stringToUTF8Array(str, HEAP8, ret, size); return ret; } // Allocate stack space for a JS string, and write it there. function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1; var ret = stackAlloc(size); stringToUTF8Array(str, HEAP8, ret, size); return ret; } // Deprecated: This function should not be called because it is unsafe and does not provide // a maximum length limit of how many bytes it is allowed to write. Prefer calling the // function stringToUTF8Array() instead, which takes in a maximum length that can be used // to be secure from out of bounds writes. /** @deprecated @param {boolean=} dontAddNull */ function writeStringToMemory(string, buffer, dontAddNull) { warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); var /** @type {number} */ lastChar, /** @type {number} */ end; if (dontAddNull) { // stringToUTF8Array always appends null. If we don't want to do that, remember the // character that existed at the location where the null will be placed, and restore // that after the write (below). end = buffer + lengthBytesUTF8(string); lastChar = HEAP8[end]; } stringToUTF8(string, buffer, Infinity); if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. } function writeArrayToMemory(array, buffer) { assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') HEAP8.set(array, buffer); } /** @param {boolean=} dontAddNull */ function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { assert(str.charCodeAt(i) === (str.charCodeAt(i) & 0xff)); HEAP8[((buffer++)>>0)] = str.charCodeAt(i); } // Null-terminate the pointer to the HEAP. if (!dontAddNull) HEAP8[((buffer)>>0)] = 0; } // end include: runtime_strings_extra.js // Memory management var HEAP, /** @type {!ArrayBuffer} */ buffer, /** @type {!Int8Array} */ HEAP8, /** @type {!Uint8Array} */ HEAPU8, /** @type {!Int16Array} */ HEAP16, /** @type {!Uint16Array} */ HEAPU16, /** @type {!Int32Array} */ HEAP32, /** @type {!Uint32Array} */ HEAPU32, /** @type {!Float32Array} */ HEAPF32, /** @type {!Float64Array} */ HEAPF64; function updateGlobalBufferAndViews(buf) { buffer = buf; Module['HEAP8'] = HEAP8 = new Int8Array(buf); Module['HEAP16'] = HEAP16 = new Int16Array(buf); Module['HEAP32'] = HEAP32 = new Int32Array(buf); Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf); Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf); Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf); Module['HEAPF32'] = HEAPF32 = new Float32Array(buf); Module['HEAPF64'] = HEAPF64 = new Float64Array(buf); } var TOTAL_STACK = 5242880; if (Module['TOTAL_STACK']) assert(TOTAL_STACK === Module['TOTAL_STACK'], 'the stack size can no longer be determined at runtime') var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 536870912;legacyModuleProp('INITIAL_MEMORY', 'INITIAL_MEMORY'); assert(INITIAL_MEMORY >= TOTAL_STACK, 'INITIAL_MEMORY should be larger than TOTAL_STACK, was ' + INITIAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); // check for full engine support (use string 'subarray' to avoid closure compiler confusion) assert(typeof Int32Array != 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray != undefined && Int32Array.prototype.set != undefined, 'JS engine does not provide full typed array support'); // If memory is defined in wasm, the user can't provide it. assert(!Module['wasmMemory'], 'Use of `wasmMemory` detected. Use -sIMPORTED_MEMORY to define wasmMemory externally'); assert(INITIAL_MEMORY == 536870912, 'Detected runtime INITIAL_MEMORY setting. Use -sIMPORTED_MEMORY to define wasmMemory dynamically'); // include: runtime_init_table.js // In regular non-RELOCATABLE mode the table is exported // from the wasm module and this will be assigned once // the exports are available. var wasmTable; // end include: runtime_init_table.js // include: runtime_stack_check.js // Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. function writeStackCookie() { var max = _emscripten_stack_get_end(); assert((max & 3) == 0); // The stack grow downwards towards _emscripten_stack_get_end. // We write cookies to the final two words in the stack and detect if they are // ever overwritten. HEAP32[((max)>>2)] = 0x2135467; HEAP32[(((max)+(4))>>2)] = 0x89BACDFE; // Also test the global address 0 for integrity. HEAPU32[0] = 0x63736d65; /* 'emsc' */ } function checkStackCookie() { if (ABORT) return; var max = _emscripten_stack_get_end(); var cookie1 = HEAPU32[((max)>>2)]; var cookie2 = HEAPU32[(((max)+(4))>>2)]; if (cookie1 != 0x2135467 || cookie2 != 0x89BACDFE) { abort('Stack overflow! Stack cookie has been overwritten at 0x' + max.toString(16) + ', expected hex dwords 0x89BACDFE and 0x2135467, but received 0x' + cookie2.toString(16) + ' 0x' + cookie1.toString(16)); } // Also test the global address 0 for integrity. if (HEAPU32[0] !== 0x63736d65 /* 'emsc' */) abort('Runtime error: The application has corrupted its heap memory area (address zero)!'); } // end include: runtime_stack_check.js // include: runtime_assertions.js // Endianness check (function() { var h16 = new Int16Array(1); var h8 = new Int8Array(h16.buffer); h16[0] = 0x6373; if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)'; })(); // end include: runtime_assertions.js var __ATPRERUN__ = []; // functions called before the runtime is initialized var __ATINIT__ = []; // functions called during startup var __ATEXIT__ = []; // functions called during shutdown var __ATPOSTRUN__ = []; // functions called after the main() is called var runtimeInitialized = false; function keepRuntimeAlive() { return noExitRuntime; } function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()); } } callRuntimeCallbacks(__ATPRERUN__); } function initRuntime() { assert(!runtimeInitialized); runtimeInitialized = true; checkStackCookie(); callRuntimeCallbacks(__ATINIT__); } function postRun() { checkStackCookie(); if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()); } } callRuntimeCallbacks(__ATPOSTRUN__); } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb); } function addOnInit(cb) { __ATINIT__.unshift(cb); } function addOnExit(cb) { } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb); } // include: runtime_math.js // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc assert(Math.imul, 'This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); assert(Math.fround, 'This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); assert(Math.clz32, 'This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); assert(Math.trunc, 'This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); // end include: runtime_math.js // A counter of dependencies for calling run(). If we need to // do asynchronous work before running, increment this and // decrement it. Incrementing must happen in a place like // Module.preRun (used by emcc to add file preloading). // Note that you can add dependencies in preRun, even though // it happens right before run - run will be postponed until // the dependencies are met. var runDependencies = 0; var runDependencyWatcher = null; var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled var runDependencyTracking = {}; function getUniqueRunDependency(id) { var orig = id; while (1) { if (!runDependencyTracking[id]) return id; id = orig + Math.random(); } } function addRunDependency(id) { runDependencies++; if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies); } if (id) { assert(!runDependencyTracking[id]); runDependencyTracking[id] = 1; if (runDependencyWatcher === null && typeof setInterval != 'undefined') { // Check for missing dependencies every few seconds runDependencyWatcher = setInterval(function() { if (ABORT) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; return; } var shown = false; for (var dep in runDependencyTracking) { if (!shown) { shown = true; err('still waiting on run dependencies:'); } err('dependency: ' + dep); } if (shown) { err('(end of list)'); } }, 10000); } } else { err('warning: run dependency added without ID'); } } function removeRunDependency(id) { runDependencies--; if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies); } if (id) { assert(runDependencyTracking[id]); delete runDependencyTracking[id]; } else { err('warning: run dependency removed without ID'); } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; } if (dependenciesFulfilled) { var callback = dependenciesFulfilled; dependenciesFulfilled = null; callback(); // can add another dependenciesFulfilled } } } /** @param {string|number=} what */ function abort(what) { { if (Module['onAbort']) { Module['onAbort'](what); } } what = 'Aborted(' + what + ')'; // TODO(sbc): Should we remove printing and leave it up to whoever // catches the exception? err(what); ABORT = true; EXITSTATUS = 1; // Use a wasm runtime error, because a JS error might be seen as a foreign // exception, which means we'd run destructors on it. We need the error to // simply make the program stop. // FIXME This approach does not work in Wasm EH because it currently does not assume // all RuntimeErrors are from traps; it decides whether a RuntimeError is from // a trap or not based on a hidden field within the object. So at the moment // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that // allows this in the wasm spec. // Suppress closure compiler warning here. Closure compiler's builtin extern // defintion for WebAssembly.RuntimeError claims it takes no arguments even // though it can. // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what); readyPromiseReject(e); // Throw the error whether or not MODULARIZE is set because abort is used // in code paths apart from instantiation where an exception is expected // to be thrown when abort is called. throw e; } // {{MEM_INITIALIZER}} // include: memoryprofiler.js // end include: memoryprofiler.js // show errors on likely calls to FS when it was not included var FS = { error: function() { abort('Filesystem support (FS) was not included. The problem is that you are using files from JS, but files were not used from C/C++, so filesystem support was not auto-included. You can force-include filesystem support with -sFORCE_FILESYSTEM'); }, init: function() { FS.error() }, createDataFile: function() { FS.error() }, createPreloadedFile: function() { FS.error() }, createLazyFile: function() { FS.error() }, open: function() { FS.error() }, mkdev: function() { FS.error() }, registerDevice: function() { FS.error() }, analyzePath: function() { FS.error() }, loadFilesFromDB: function() { FS.error() }, ErrnoError: function ErrnoError() { FS.error() }, }; Module['FS_createDataFile'] = FS.createDataFile; Module['FS_createPreloadedFile'] = FS.createPreloadedFile; // include: URIUtils.js // Prefix of data URIs emitted by SINGLE_FILE and related options. var dataURIPrefix = 'data:application/octet-stream;base64,'; // Indicates whether filename is a base64 data URI. function isDataURI(filename) { // Prefix of data URIs emitted by SINGLE_FILE and related options. return filename.startsWith(dataURIPrefix); } // Indicates whether filename is delivered via file protocol (as opposed to http/https) function isFileURI(filename) { return filename.startsWith('file://'); } // end include: URIUtils.js /** @param {boolean=} fixedasm */ function createExportWrapper(name, fixedasm) { return function() { var displayName = name; var asm = fixedasm; if (!fixedasm) { asm = Module['asm']; } assert(runtimeInitialized, 'native function `' + displayName + '` called before runtime initialization'); if (!asm[name]) { assert(asm[name], 'exported native function `' + displayName + '` not found'); } return asm[name].apply(null, arguments); }; } var wasmBinaryFile; wasmBinaryFile = 'canvaskit.wasm'; if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile); } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary); } if (readBinary) { return readBinary(file); } else { throw "both async and sync fetching of the wasm failed"; } } catch (err) { abort(err); } } function getBinaryPromise() { // If we don't have the binary yet, try to to load it asynchronously. // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url. // See https://github.com/github/fetch/pull/92#issuecomment-140665932 // Cordova or Electron apps are typically loaded from a file:// url. // So use fetch if it is available and the url is not a file, otherwise fall back to XHR. if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' && !isFileURI(wasmBinaryFile) ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; } return response['arrayBuffer'](); }).catch(function () { return getBinary(wasmBinaryFile); }); } else { if (readAsync) { // fetch is not available or url is file => try XHR (readAsync uses XHR internally) return new Promise(function(resolve, reject) { readAsync(wasmBinaryFile, function(response) { resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))) }, reject) }); } } } // Otherwise, getBinary should be able to get it synchronously return Promise.resolve().then(function() { return getBinary(wasmBinaryFile); }); } // Create the wasm instance. // Receives the wasm imports, returns the exports. function createWasm() { // prepare imports var info = { 'env': asmLibraryArg, 'wasi_snapshot_preview1': asmLibraryArg, }; // Load the wasm module and create an instance of using native support in the JS engine. // handle a generated wasm instance, receiving its exports and // performing other necessary setup /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { var exports = instance.exports; Module['asm'] = exports; wasmMemory = Module['asm']['memory']; assert(wasmMemory, "memory not found in wasm exports"); // This assertion doesn't hold when emscripten is run in --post-link // mode. // TODO(sbc): Read INITIAL_MEMORY out of the wasm file in post-link mode. //assert(wasmMemory.buffer.byteLength === 536870912); updateGlobalBufferAndViews(wasmMemory.buffer); wasmTable = Module['asm']['__indirect_function_table']; assert(wasmTable, "table not found in wasm exports"); addOnInit(Module['asm']['__wasm_call_ctors']); removeRunDependency('wasm-instantiate'); } // we can't run yet (except in a pthread, where we have a custom sync instantiator) addRunDependency('wasm-instantiate'); // Prefer streaming instantiation if available. // Async compilation can be confusing when an error on the page overwrites Module // (for example, if the order of elements is wrong, and the one defining Module is // later), so we save Module and check it later. var trueModule = Module; function receiveInstantiationResult(result) { // 'result' is a ResultObject object which has both the module and instance. // receiveInstance() will swap in the exports (to Module.asm) so they can be called assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); trueModule = null; // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. // When the regression is fixed, can restore the above USE_PTHREADS-enabled path. receiveInstance(result['instance']); } function instantiateArrayBuffer(receiver) { return getBinaryPromise().then(function(binary) { return WebAssembly.instantiate(binary, info); }).then(function (instance) { return instance; }).then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason); // Warn on some common problems. if (isFileURI(wasmBinaryFile)) { err('warning: Loading from a file URI (' + wasmBinaryFile + ') is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing'); } abort(reason); }); } function instantiateAsync() { if (!wasmBinary && typeof WebAssembly.instantiateStreaming == 'function' && !isDataURI(wasmBinaryFile) && // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. !isFileURI(wasmBinaryFile) && // Avoid instantiateStreaming() on Node.js environment for now, as while // Node.js v18.1.0 implements it, it does not have a full fetch() // implementation yet. // // Reference: // https://github.com/emscripten-core/emscripten/pull/16917 !ENVIRONMENT_IS_NODE && typeof fetch == 'function') { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { // Suppress closure warning here since the upstream definition for // instantiateStreaming only allows Promise<Repsponse> rather than // an actual Response. // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed. /** @suppress {checkTypes} */ var result = WebAssembly.instantiateStreaming(response, info); return result.then( receiveInstantiationResult, function(reason) { // We expect the most common failure cause to be a bad MIME type for the binary, // in which case falling back to ArrayBuffer instantiation should work. err('wasm streaming compile failed: ' + reason); err('falling back to ArrayBuffer instantiation'); return instantiateArrayBuffer(receiveInstantiationResult); }); }); } else { return instantiateArrayBuffer(receiveInstantiationResult); } } // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel // to any other async startup actions they are performing. // Also pthreads and wasm workers initialize the wasm instance through this path. if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance); return exports; } catch(e) { err('Module.instantiateWasm callback failed with error: ' + e); return false; } } // If instantiation fails, reject the module ready promise. instantiateAsync().catch(readyPromiseReject); return {}; // no exports yet; we'll fill them in later } // Globals used by JS i64 conversions (see makeSetValue) var tempDouble; var tempI64; // === Body === var ASM_CONSTS = { }; function callRuntimeCallbacks(callbacks) { while (callbacks.length > 0) { // Pass the module as the first argument. callbacks.shift()(Module); } } function withStackSave(f) { var stack = stackSave(); var ret = f(); stackRestore(stack); return ret; } function demangle(func) { // If demangle has failed before, stop demangling any further function names // This avoids an infinite recursion with malloc()->abort()->stackTrace()->demangle()->malloc()->... demangle.recursionGuard = (demangle.recursionGuard|0)+1; if (demangle.recursionGuard > 1) return func; var __cxa_demangle_func = Module['___cxa_demangle'] || Module['__cxa_demangle']; assert(__cxa_demangle_func); return withStackSave(function() { try { var s = func; if (s.startsWith('__Z')) s = s.substr(1); var len = lengthBytesUTF8(s)+1; var buf = stackAlloc(len); stringToUTF8(s, buf, len); var status = stackAlloc(4); var ret = __cxa_demangle_func(buf, 0, 0, status); if (HEAP32[((status)>>2)] === 0 && ret) { return UTF8ToString(ret); } // otherwise, libcxxabi failed } catch(e) { } finally { _free(ret); if (demangle.recursionGuard < 2) --demangle.recursionGuard; } // failure when using libcxxabi, don't demangle return func; }); } function demangleAll(text) { var regex = /\b_Z[\w\d_]+/g; return text.replace(regex, function(x) { var y = demangle(x); return x === y ? x : (y + ' [' + x + ']'); }); } /** * @param {number} ptr * @param {string} type */ function getValue(ptr, type = 'i8') { if (type.endsWith('*')) type = '*'; switch (type) { case 'i1': return HEAP8[((ptr)>>0)]; case 'i8': return HEAP8[((ptr)>>0)]; case 'i16': return HEAP16[((ptr)>>1)]; case 'i32': return HEAP32[((ptr)>>2)]; case 'i64': return HEAP32[((ptr)>>2)]; case 'float': return HEAPF32[((ptr)>>2)]; case 'double': return HEAPF64[((ptr)>>3)]; case '*': return HEAPU32[((ptr)>>2)]; default: abort('invalid type for getValue: ' + type); } return null; } var wasmTableMirror = []; function getWasmTableEntry(funcPtr) { var func = wasmTableMirror[funcPtr]; if (!func) { if (funcPtr >= wasmTableMirror.length) wasmTableMirror.length = funcPtr + 1; wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); } assert(wasmTable.get(funcPtr) == func, "JavaScript-side Wasm function table mirror is out of date!"); return func; } function handleException(e) { // Certain exception types we do not treat as errors since they are used for // internal control flow. // 1. ExitStatus, which is thrown by exit() // 2. "unwind", which is thrown by emscripten_unwind_to_js_event_loop() and others // that wish to return to JS event loop. if (e instanceof ExitStatus || e == 'unwind') { return EXITSTATUS; } quit_(1, e); } function jsStackTrace() { var error = new Error(); if (!error.stack) { // IE10+ special cases: It does have callstack info, but it is only // populated if an Error object is thrown, so try that as a special-case. try { throw new Error(); } catch(e) { error = e; } if (!error.stack) { return '(no stack trace available)'; } } return error.stack.toString(); } /** * @param {number} ptr * @param {number} value * @param {string} type */ function setValue(ptr, value, type = 'i8') { if (type.endsWith('*')) type = '*'; switch (type) { case 'i1': HEAP8[((ptr)>>0)] = value; break; case 'i8': HEAP8[((ptr)>>0)] = value; break; case 'i16': HEAP16[((ptr)>>1)] = value; break; case 'i32': HEAP32[((ptr)>>2)] = value; break; case 'i64': (tempI64 = [value>>>0,(tempDouble=value,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((ptr)>>2)] = tempI64[0],HEAP32[(((ptr)+(4))>>2)] = tempI64[1]); break; case 'float': HEAPF32[((ptr)>>2)] = value; break; case 'double': HEAPF64[((ptr)>>3)] = value; break; case '*': HEAPU32[((ptr)>>2)] = value; break; default: abort('invalid type for setValue: ' + type); } } function setWasmTableEntry(idx, func) { wasmTable.set(idx, func); // With ABORT_ON_WASM_EXCEPTIONS wasmTable.get is overriden to return wrapped // functions so we need to call it here to retrieve the potential wrapper correctly // instead of just storing 'func' directly into wasmTableMirror wasmTableMirror[idx] = wasmTable.get(idx); } function stackTrace() { var js = jsStackTrace(); if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace'](); return demangleAll(js); } function ___assert_fail(condition, filename, line, func) { abort('Assertion failed: ' + UTF8ToString(condition) + ', at: ' + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']); } function setErrNo(value) { HEAP32[((___errno_location())>>2)] = value; return value; } var SYSCALLS = {varargs:undefined,get:function() { assert(SYSCALLS.varargs != undefined); SYSCALLS.varargs += 4; var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)]; return ret; },getStr:function(ptr) { var ret = UTF8ToString(ptr); return ret; }}; function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs; return 0; } function ___syscall_fstat64(fd, buf) { abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM'); } function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs; return 0; } function ___syscall_lstat64(path, buf) { abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM'); } function ___syscall_newfstatat(dirfd, path, buf, flags) { abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM'); } function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs; abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM'); } function ___syscall_stat64(path, buf) { abort('it should not be possible to operate on streams when !SYSCALLS_REQUIRE_FILESYSTEM'); } var structRegistrations = {}; function runDestructors(destructors) { while (destructors.length) { var ptr = destructors.pop(); var del = destructors.pop(); del(ptr); } } function simpleReadValueFromPointer(pointer) { return this['fromWireType'](HEAP32[((pointer)>>2)]); } var awaitingDependencies = {}; var registeredTypes = {}; var typeDependencies = {}; var char_0 = 48; var char_9 = 57; function makeLegalFunctionName(name) { if (undefined === name) { return '_unknown'; } name = name.replace(/[^a-zA-Z0-9_]/g, '$'); var f = name.charCodeAt(0); if (f >= char_0 && f <= char_9) { return '_' + name; } return name; } function createNamedFunction(name, body) { name = makeLegalFunctionName(name); return function() { "use strict"; return body.apply(this, arguments); }; } function extendError(baseErrorType, errorName) { var errorClass = createNamedFunction(errorName, function(message) { this.name = errorName; this.message = message; var stack = (new Error(message)).stack; if (stack !== undefined) { this.stack = this.toString() + '\n' + stack.replace(/^Error(:[^\n]*)?\n/, ''); } }); errorClass.prototype = Object.create(baseErrorType.prototype); errorClass.prototype.constructor = errorClass; errorClass.prototype.toString = function() { if (this.message === undefined) { return this.name; } else { return this.name + ': ' + this.message; } }; return errorClass; } var InternalError = undefined; function throwInternalError(message) { throw new InternalError(message); } function whenDependentTypesAreResolved(myTypes, dependentTypes, getTypeConverters) { myTypes.forEach(function(type) { typeDependencies[type] = dependentTypes; }); function onComplete(typeConverters) { var myTypeConverters = getTypeConverters(typeConverters); if (myTypeConverters.length !== myTypes.length) { throwInternalError('Mismatched type converter count'); } for (var i = 0; i < myTypes.length; ++i) { registerType(myTypes[i], myTypeConverters[i]); } } var typeConverters = new Array(dependentTypes.length); var unregisteredTypes = []; var registered = 0; dependentTypes.forEach((dt, i) => { if (registeredTypes.hasOwnProperty(dt)) { typeConverters[i] = registeredTypes[dt]; } else { unregisteredTypes.push(dt); if (!awaitingDependencies.hasOwnProperty(dt)) { awaitingDependencies[dt] = []; } awaitingDependencies[dt].push(() => { typeConverters[i] = registeredTypes[dt]; ++registered; if (registered === unregisteredTypes.length) { onComplete(typeConverters); } }); } }); if (0 === unregisteredTypes.length) { onComplete(typeConverters); } } function __embind_finalize_value_object(structType) { var reg = structRegistrations[structType]; delete structRegistrations[structType]; var rawConstructor = reg.rawConstructor; var rawDestructor = reg.rawDestructor; var fieldRecords = reg.fields; var fieldTypes = fieldRecords.map((field) => field.getterReturnType). concat(fieldRecords.map((field) => field.setterArgumentType)); whenDependentTypesAreResolved([structType], fieldTypes, (fieldTypes) => { var fields = {}; fieldRecords.forEach((field, i) => { var fieldName = field.fieldName; var getterReturnType = fieldTypes[i]; var getter = field.getter; var getterContext = field.getterContext; var setterArgumentType = fieldTypes[i + fieldRecords.length]; var setter = field.setter; var setterContext = field.setterContext; fields[fieldName] = { read: (ptr) => { return getterReturnType['fromWireType']( getter(getterContext, ptr)); }, write: (ptr, o) => { var destructors = []; setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o)); runDestructors(destructors); } }; }); return [{ name: reg.name, 'fromWireType': function(ptr) { var rv = {}; for (var i in fields) { rv[i] = fields[i].read(ptr); } rawDestructor(ptr); return rv; }, 'toWireType': function(destructors, o) { // todo: Here we have an opportunity for -O3 level "unsafe" optimizations: // assume all fields are present without checking. for (var fieldName in fields) { if (!(fieldName in o)) { throw new TypeError('Missing field: "' + fieldName + '"'); } } var ptr = rawConstructor(); for (fieldName in fields) { fields[fieldName].write(ptr, o[fieldName]); } if (destructors !== null) { destructors.push(rawDestructor, ptr); } return ptr; }, 'argPackAdvance': 8, 'readValueFromPointer': simpleReadValueFromPointer, destructorFunction: rawDestructor, }]; }); } function __embind_register_bigint(primitiveType, name, size, minRange, maxRange) {} function getShiftFromSize(size) { switch (size) { case 1: return 0; case 2: return 1; case 4: return 2; case 8: return 3; default: throw new TypeError('Unknown type size: ' + size); } } function embind_init_charCodes() { var codes = new Array(256); for (var i = 0; i < 256; ++i) { codes[i] = String.fromCharCode(i); } embind_charCodes = codes; } var embind_charCodes = undefined; function readLatin1String(ptr) { var ret = ""; var c = ptr; while (HEAPU8[c]) { ret += embind_charCodes[HEAPU8[c++]]; } return ret; } var BindingError = undefined; function throwBindingError(message) { throw new BindingError(message); } /** @param {Object=} options */ function registerType(rawType, registeredInstance, options = {}) { if (!('argPackAdvance' in registeredInstance)) { throw new TypeError('registerType registeredInstance requires argPackAdvance'); } var name = registeredInstance.name; if (!rawType) { throwBindingError('type "' + name + '" must have a positive integer typeid pointer'); } if (registeredTypes.hasOwnProperty(rawType)) { if (options.ignoreDuplicateRegistrations) { return; } else { throwBindingError("Cannot register type '" + name + "' twice"); } } registeredTypes[rawType] = registeredInstance; delete typeDependencies[rawType]; if (awaitingDependencies.hasOwnProperty(rawType)) { var callbacks = awaitingDependencies[rawType]; delete awaitingDependencies[rawType]; callbacks.forEach((cb) => cb()); } } function __embind_register_bool(rawType, name, size, trueValue, falseValue) { var shift = getShiftFromSize(size); name = readLatin1String(name); registerType(rawType, { name: name, 'fromWireType': function(wt) { // ambiguous emscripten ABI: sometimes return values are // true or false, and sometimes integers (0 or 1) return !!wt; }, 'toWireType': function(destructors, o) { return o ? trueValue : falseValue; }, 'argPackAdvance': 8, 'readValueFromPointer': function(pointer) { // TODO: if heap is fixed (like in asm.js) this could be executed outside var heap; if (size === 1) { heap = HEAP8; } else if (size === 2) { heap = HEAP16; } else if (size === 4) { heap = HEAP32; } else { throw new TypeError("Unknown boolean type size: " + name); } return this['fromWireType'](heap[pointer >> shift]); }, destructorFunction: null, // This type does not need a destructor }); } function ClassHandle_isAliasOf(other) { if (!(this instanceof ClassHandle)) { return false; } if (!(other instanceof ClassHandle)) { return false; } var leftClass = this.$$.ptrType.registeredClass; var left = this.$$.ptr; var rightClass = other.$$.ptrType.registeredClass; var right = other.$$.ptr; while (leftClass.baseClass) { left = leftClass.upcast(left); leftClass = leftClass.baseClass; } while (rightClass.baseClass) { right = rightClass.upcast(right); rightClass = rightClass.baseClass; } return leftClass === rightClass && left === right; } function shallowCopyInternalPointer(o) { return { count: o.count, deleteScheduled: o.deleteScheduled, preservePointerOnDelete: o.preservePointerOnDelete, ptr: o.ptr, ptrType: o.ptrType, smartPtr: o.smartPtr, smartPtrType: o.smartPtrType, }; } function throwInstanceAlreadyDeleted(obj) { function getInstanceTypeName(handle) { return handle.$$.ptrType.registeredClass.name; } throwBindingError(getInstanceTypeName(obj) + ' instance already deleted'); } var finalizationRegistry = false; function detachFinalizer(handle) {} function runDestructor($$) { if ($$.smartPtr) { $$.smartPtrType.rawDestructor($$.smartPtr); } else { $$.ptrType.registeredClass.rawDestructor($$.ptr); } } function releaseClassHandle($$) { $$.count.value -= 1; var toDelete = 0 === $$.count.value; if (toDelete) { runDestructor($$); } } function downcastPointer(ptr, ptrClass, desiredClass) { if (ptrClass === desiredClass) { return ptr; } if (undefined === desiredClass.baseClass) { return null; // no conversion } var rv = downcastPointer(ptr, ptrClass, desiredClass.baseClass); if (rv === null) { return null; } return desiredClass.downcast(rv); } var registeredPointers = {}; function getInheritedInstanceCount() { return Object.keys(registeredInstances).length; } function getLiveInheritedInstances() { var rv = []; for (var k in registeredInstances) { if (registeredInstances.hasOwnProperty(k)) { rv.push(registeredInstances[k]); } } return rv; } var deletionQueue = []; function flushPendingDeletes() { while (deletionQueue.length) { var obj = deletionQueue.pop(); obj.$$.deleteScheduled = false; obj['delete'](); } } var delayFunction = undefined; function setDelayFunction(fn) { delayFunction = fn; if (deletionQueue.length && delayFunction) { delayFunction(flushPendingDeletes); } } function init_embind() { Module['getInheritedInstanceCount'] = getInheritedInstanceCount; Module['getLiveInheritedInstances'] = getLiveInheritedInstances; Module['flushPendingDeletes'] = flushPendingDeletes; Module['setDelayFunction'] = setDelayFunction; } var registeredInstances = {}; function getBasestPointer(class_, ptr) { if (ptr === undefined) { throwBindingError('ptr should not be undefined'); } while (class_.baseClass) { ptr = class_.upcast(ptr); class_ = class_.baseClass; } return ptr; } function getInheritedInstance(class_, ptr) { ptr = getBasestPointer(class_, ptr); return registeredInstances[ptr]; } function makeClassHandle(prototype, record) { if (!record.ptrType || !record.ptr) { throwInternalError('makeClassHandle requires ptr and ptrType'); } var hasSmartPtrType = !!record.smartPtrType; var hasSmartPtr = !!record.smartPtr; if (hasSmartPtrType !== hasSmartPtr) { throwInternalError('Both smartPtrType and smartPtr must be specified'); } record.count = { value: 1 }; return attachFinalizer(Object.create(prototype, { $$: { value: record, }, })); } function RegisteredPointer_fromWireType(ptr) { // ptr is a raw pointer (or a raw smartpointer) // rawPointer is a maybe-null raw pointer var rawPointer = this.getPointee(ptr); if (!rawPointer) { this.destructor(ptr); return null; } var registeredInstance = getInheritedInstance(this.registeredClass, rawPointer); if (undefined !== registeredInstance) { // JS object has been neutered, time to repopulate it if (0 === registeredInstance.$$.count.value) { registeredInstance.$$.ptr = rawPointer; registeredInstance.$$.smartPtr = ptr; return registeredInstance['clone'](); } else { // else, just increment reference count on existing object // it already has a reference to the smart pointer var rv = registeredInstance['clone'](); this.destructor(ptr); return rv; } } function makeDefaultHandle() { if (this.isSmartPointer) { return makeClassHandle(this.registeredClass.instancePrototype, { ptrType: this.pointeeType, ptr: rawPointer, smartPtrType: this, smartPtr: ptr, }); } else { return makeClassHandle(this.registeredClass.instancePrototype, { ptrType: this, ptr: ptr, }); } } var actualType = this.registeredClass.getActualType(rawPointer); var registeredPointerRecord = registeredPointers[actualType]; if (!registeredPointerRecord) { return makeDefaultHandle.call(this); } var toType; if (this.isConst) { toType = registeredPointerRecord.constPointerType; } else { toType = registeredPointerRecord.pointerType; } var dp = downcastPointer( rawPointer, this.registeredClass, toType.registeredClass); if (dp === null) { return makeDefaultHandle.call(this); } if (this.isSmartPointer) { return makeClassHandle(toType.registeredClass.instancePrototype, { ptrType: toType, ptr: dp, smartPtrType: this, smartPtr: ptr, }); } else { return makeClassHandle(toType.registeredClass.instancePrototype, { ptrType: toType, ptr: dp, }); } } function attachFinalizer(handle) { if ('undefined' === typeof FinalizationRegistry) { attachFinalizer = (handle) => handle; return handle; } // If the running environment has a FinalizationRegistry (see // https://github.com/tc39/proposal-weakrefs), then attach finalizers // for class handles. We check for the presence of FinalizationRegistry // at run-time, not build-time. finalizationRegistry = new FinalizationRegistry((info) => { console.warn(info.leakWarning.stack.replace(/^Error: /, '')); releaseClassHandle(info.$$); }); attachFinalizer = (handle) => { var $$ = handle.$$; var hasSmartPtr = !!$$.smartPtr; if (hasSmartPtr) { // We should not call the destructor on raw pointers in case other code expects the pointee to live var info = { $$: $$ }; // Create a warning as an Error instance in advance so that we can store // the current stacktrace and point to it when / if a leak is detected. // This is more useful than the empty stacktrace of `FinalizationRegistry` // callback. var cls = $$.ptrType.registeredClass; info.leakWarning = new Error("Embind found a leaked C++ instance " + cls.name + " <0x" + $$.ptr.toString(16) + ">.\n" + "We'll free it automatically in this case, but this functionality is not reliable across various environments.\n" + "Make sure to invoke .delete() manually once you're done with the instance instead.\n" + "Originally allocated"); // `.stack` will add "at ..." after this sentence if ('captureStackTrace' in Error) { Error.captureStackTrace(info.leakWarning, RegisteredPointer_fromWireType); } finalizationRegistry.register(handle, info, handle); } return handle; }; detachFinalizer = (handle) => finalizationRegistry.unregister(handle); return attachFinalizer(handle); } function ClassHandle_clone() { if (!this.$$.ptr) { throwInstanceAlreadyDeleted(this); } if (this.$$.preservePointerOnDelete) { this.$$.count.value += 1; return this; } else { var clone = attachFinalizer(Object.create(Object.getPrototypeOf(this), { $$: { value: shallowCopyInternalPointer(this.$$), } })); clone.$$.count.value += 1; clone.$$.deleteScheduled = false; return clone; } } function ClassHandle_delete() { if (!this.$$.ptr) { throwInstanceAlreadyDeleted(this); } if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) { throwBindingError('Object already scheduled for deletion'); } detachFinalizer(this); releaseClassHandle(this.$$); if (!this.$$.preservePointerOnDelete) { this.$$.smartPtr = undefined; this.$$.ptr = undefined; } } function ClassHandle_isDeleted() { return !this.$$.ptr; } function ClassHandle_deleteLater() { if (!this.$$.ptr) { throwInstanceAlreadyDeleted(this); } if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) { throwBindingError('Object already scheduled for deletion'); } deletionQueue.push(this); if (deletionQueue.length === 1 && delayFunction) { delayFunction(flushPendingDeletes); } this.$$.deleteScheduled = true; return this; } function init_ClassHandle() { ClassHandle.prototype['isAliasOf'] = ClassHandle_isAliasOf; ClassHandle.prototype['clone'] = ClassHandle_clone; ClassHandle.prototype['delete'] = ClassHandle_delete; ClassHandle.prototype['isDeleted'] = ClassHandle_isDeleted; ClassHandle.prototype['deleteLater'] = ClassHandle_deleteLater; } function ClassHandle() { } function ensureOverloadTable(proto, methodName, humanName) { if (undefined === proto[methodName].overloadTable) { var prevFunc = proto[methodName]; // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. proto[methodName] = function() { // TODO This check can be removed in -O3 level "unsafe" optimizations. if (!proto[methodName].overloadTable.hasOwnProperty(arguments.length)) { throwBindingError("Function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + proto[methodName].overloadTable + ")!"); } return proto[methodName].overloadTable[arguments.length].apply(this, arguments); }; // Move the previous function into the overload table. proto[methodName].overloadTable = []; proto[methodName].overloadTable[prevFunc.argCount] = prevFunc; } } /** @param {number=} numArguments */ function exposePublicSymbol(name, value, numArguments) { if (Module.hasOwnProperty(name)) { if (undefined === numArguments || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments])) { throwBindingError("Cannot register public name '" + name + "' twice"); } // We are exposing a function with the same name as an existing function. Create an overload table and a function selector // that routes between the two. ensureOverloadTable(Module, name, name); if (Module.hasOwnProperty(numArguments)) { throwBindingError("Cannot register multiple overloads of a function with the same number of arguments (" + numArguments + ")!"); } // Add the new function into the overload table. Module[name].overloadTable[numArguments] = value; } else { Module[name] = value; if (undefined !== numArguments) { Module[name].numArguments = numArguments; } } } /** @constructor */ function RegisteredClass(name, constructor, instancePrototype, rawDestructor, baseClass, getActualType, upcast, downcast) { this.name = name; this.constructor = constructor; this.instancePrototype = instancePrototype; this.rawDestructor = rawDestructor; this.baseClass = baseClass; this.getActualType = getActualType; this.upcast = upcast; this.downcast = downcast; this.pureVirtualFunctions = []; } function upcastPointer(ptr, ptrClass, desiredClass) { while (ptrClass !== desiredClass) { if (!ptrClass.upcast) { throwBindingError("Expected null or instance of " + desiredClass.name + ", got an instance of " + ptrClass.name); } ptr = ptrClass.upcast(ptr); ptrClass = ptrClass.baseClass; } return ptr; } function constNoSmartPtrRawPointerToWireType(destructors, handle) { if (handle === null) { if (this.isReference) { throwBindingError('null is not a valid ' + this.name); } return 0; } if (!handle.$$) { throwBindingError('Cannot pass "' + embindRepr(handle) + '" as a ' + this.name); } if (!handle.$$.ptr) { throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name); } var handleClass = handle.$$.ptrType.registeredClass; var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); return ptr; } function genericPointerToWireType(destructors, handle) { var ptr; if (handle === null) { if (this.isReference) { throwBindingError('null is not a valid ' + this.name); } if (this.isSmartPointer) { ptr = this.rawConstructor(); if (destructors !== null) { destructors.push(this.rawDestructor, ptr); } return ptr; } else { return 0; } } if (!handle.$$) { throwBindingError('Cannot pass "' + embindRepr(handle) + '" as a ' + this.name); } if (!handle.$$.ptr) { throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name); } if (!this.isConst && handle.$$.ptrType.isConst) { throwBindingError('Cannot convert argument of type ' + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + ' to parameter type ' + this.name); } var handleClass = handle.$$.ptrType.registeredClass; ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); if (this.isSmartPointer) { // TODO: this is not strictly true // We could support BY_EMVAL conversions from raw pointers to smart pointers // because the smart pointer can hold a reference to the handle if (undefined === handle.$$.smartPtr) { throwBindingError('Passing raw pointer to smart pointer is illegal'); } switch (this.sharingPolicy) { case 0: // NONE // no upcasting if (handle.$$.smartPtrType === this) { ptr = handle.$$.smartPtr; } else { throwBindingError('Cannot convert argument of type ' + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + ' to parameter type ' + this.name); } break; case 1: // INTRUSIVE ptr = handle.$$.smartPtr; break; case 2: // BY_EMVAL if (handle.$$.smartPtrType === this) { ptr = handle.$$.smartPtr; } else { var clonedHandle = handle['clone'](); ptr = this.rawShare( ptr, Emval.toHandle(function() { clonedHandle['delete'](); }) ); if (destructors !== null) { destructors.push(this.rawDestructor, ptr); } } break; default: throwBindingError('Unsupporting sharing policy'); } } return ptr; } function nonConstNoSmartPtrRawPointerToWireType(destructors, handle) { if (handle === null) { if (this.isReference) { throwBindingError('null is not a valid ' + this.name); } return 0; } if (!handle.$$) { throwBindingError('Cannot pass "' + embindRepr(handle) + '" as a ' + this.name); } if (!handle.$$.ptr) { throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name); } if (handle.$$.ptrType.isConst) { throwBindingError('Cannot convert argument of type ' + handle.$$.ptrType.name + ' to parameter type ' + this.name); } var handleClass = handle.$$.ptrType.registeredClass; var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass); return ptr; } function RegisteredPointer_getPointee(ptr) { if (this.rawGetPointee) { ptr = this.rawGetPointee(ptr); } return ptr; } function RegisteredPointer_destructor(ptr) { if (this.rawDestructor) { this.rawDestructor(ptr); } } function RegisteredPointer_deleteObject(handle) { if (handle !== null) { handle['delete'](); } } function init_RegisteredPointer() { RegisteredPointer.prototype.getPointee = RegisteredPointer_getPointee; RegisteredPointer.prototype.destructor = RegisteredPointer_destructor; RegisteredPointer.prototype['argPackAdvance'] = 8; RegisteredPointer.prototype['readValueFromPointer'] = simpleReadValueFromPointer; RegisteredPointer.prototype['deleteObject'] = RegisteredPointer_deleteObject; RegisteredPointer.prototype['fromWireType'] = RegisteredPointer_fromWireType; } /** @constructor @param {*=} pointeeType, @param {*=} sharingPolicy, @param {*=} rawGetPointee, @param {*=} rawConstructor, @param {*=} rawShare, @param {*=} rawDestructor, */ function RegisteredPointer( name, registeredClass, isReference, isConst, // smart pointer properties isSmartPointer, pointeeType, sharingPolicy, rawGetPointee, rawConstructor, rawShare, rawDestructor ) { this.name = name; this.registeredClass = registeredClass; this.isReference = isReference; this.isConst = isConst; // smart pointer properties this.isSmartPointer = isSmartPointer; this.pointeeType = pointeeType; this.sharingPolicy = sharingPolicy; this.rawGetPointee = rawGetPointee; this.rawConstructor = rawConstructor; this.rawShare = rawShare; this.rawDestructor = rawDestructor; if (!isSmartPointer && registeredClass.baseClass === undefined) { if (isConst) { this['toWireType'] = constNoSmartPtrRawPointerToWireType; this.destructorFunction = null; } else { this['toWireType'] = nonConstNoSmartPtrRawPointerToWireType; this.destructorFunction = null; } } else { this['toWireType'] = genericPointerToWireType; // Here we must leave this.destructorFunction undefined, since whether genericPointerToWireType returns // a pointer that needs to be freed up is runtime-dependent, and cannot be evaluated at registration time. // TODO: Create an alternative mechanism that allows removing the use of var destructors = []; array in // craftInvokerFunction altogether. } } /** @param {number=} numArguments */ function replacePublicSymbol(name, value, numArguments) { if (!Module.hasOwnProperty(name)) { throwInternalError('Replacing nonexistant public symbol'); } // If there's an overload table for this symbol, replace the symbol in the overload table instead. if (undefined !== Module[name].overloadTable && undefined !== numArguments) { Module[name].overloadTable[numArguments] = value; } else { Module[name] = value; Module[name].argCount = numArguments; } } function dynCallLegacy(sig, ptr, args) { assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); if (args && args.length) { // j (64-bit integer) must be passed in as two numbers [low 32, high 32]. assert(args.length === sig.substring(1).replace(/j/g, '--').length); } else { assert(sig.length == 1); } var f = Module['dynCall_' + sig]; return args && args.length ? f.apply(null, [ptr].concat(args)) : f.call(null, ptr); } /** @param {Object=} args */ function dynCall(sig, ptr, args) { // Without WASM_BIGINT support we cannot directly call function with i64 as // part of thier signature, so we rely the dynCall functions generated by // wasm-emscripten-finalize if (sig.includes('j')) { return dynCallLegacy(sig, ptr, args); } assert(getWasmTableEntry(ptr), 'missing table entry in dynCall: ' + ptr); var rtn = getWasmTableEntry(ptr).apply(null, args); return rtn; } function getDynCaller(sig, ptr) { assert(sig.includes('j') || sig.includes('p'), 'getDynCaller should only be called with i64 sigs') var argCache = []; return function() { argCache.length = 0; Object.assign(argCache, arguments); return dynCall(sig, ptr, argCache); }; } function embind__requireFunction(signature, rawFunction) { signature = readLatin1String(signature); function makeDynCaller() { if (signature.includes('j')) { return getDynCaller(signature, rawFunction); } return getWasmTableEntry(rawFunction); } var fp = makeDynCaller(); if (typeof fp != "function") { throwBindingError("unknown function pointer with signature " + signature + ": " + rawFunction); } return fp; } var UnboundTypeError = undefined; function getTypeName(type) { var ptr = ___getTypeName(type); var rv = readLatin1String(ptr); _free(ptr); return rv; } function throwUnboundTypeError(message, types) { var unboundTypes = []; var seen = {}; function visit(type) { if (seen[type]) { return; } if (registeredTypes[type]) { return; } if (typeDependencies[type]) { typeDependencies[type].forEach(visit); return; } unboundTypes.push(type); seen[type] = true; } types.forEach(visit); throw new UnboundTypeError(message + ': ' + unboundTypes.map(getTypeName).join([', '])); } function __embind_register_class(rawType, rawPointerType, rawConstPointerType, baseClassRawType, getActualTypeSignature, getActualType, upcastSignature, upcast, downcastSignature, downcast, name, destructorSignature, rawDestructor) { name = readLatin1String(name); getActualType = embind__requireFunction(getActualTypeSignature, getActualType); if (upcast) { upcast = embind__requireFunction(upcastSignature, upcast); } if (downcast) { downcast = embind__requireFunction(downcastSignature, downcast); } rawDestructor = embind__requireFunction(destructorSignature, rawDestructor); var legalFunctionName = makeLegalFunctionName(name); exposePublicSymbol(legalFunctionName, function() { // this code cannot run if baseClassRawType is zero throwUnboundTypeError('Cannot construct ' + name + ' due to unbound types', [baseClassRawType]); }); whenDependentTypesAreResolved( [rawType, rawPointerType, rawConstPointerType], baseClassRawType ? [baseClassRawType] : [], function(base) { base = base[0]; var baseClass; var basePrototype; if (baseClassRawType) { baseClass = base.registeredClass; basePrototype = baseClass.instancePrototype; } else { basePrototype = ClassHandle.prototype; } var constructor = createNamedFunction(legalFunctionName, function() { if (Object.getPrototypeOf(this) !== instancePrototype) { throw new BindingError("Use 'new' to construct " + name); } if (undefined === registeredClass.constructor_body) { throw new BindingError(name + " has no accessible constructor"); } var body = registeredClass.constructor_body[arguments.length]; if (undefined === body) { throw new BindingError("Tried to invoke ctor of " + name + " with invalid number of parameters (" + arguments.length + ") - expected (" + Object.keys(registeredClass.constructor_body).toString() + ") parameters instead!"); } return body.apply(this, arguments); }); var instancePrototype = Object.create(basePrototype, { constructor: { value: constructor }, }); constructor.prototype = instancePrototype; var registeredClass = new RegisteredClass(name, constructor, instancePrototype, rawDestructor, baseClass, getActualType, upcast, downcast); var referenceConverter = new RegisteredPointer(name, registeredClass, true, false, false); var pointerConverter = new RegisteredPointer(name + '*', registeredClass, false, false, false); var constPointerConverter = new RegisteredPointer(name + ' const*', registeredClass, false, true, false); registeredPointers[rawType] = { pointerType: pointerConverter, constPointerType: constPointerConverter }; replacePublicSymbol(legalFunctionName, constructor); return [referenceConverter, pointerConverter, constPointerConverter]; } ); } function new_(constructor, argumentList) { if (!(constructor instanceof Function)) { throw new TypeError('new_ called with constructor type ' + typeof(constructor) + " which is not a function"); } if (constructor === Function) { throw new Error('new_ cannot create a new Function with DYNAMIC_EXECUTION == 0.'); } /* * Previously, the following line was just: * function dummy() {}; * Unfortunately, Chrome was preserving 'dummy' as the object's name, even * though at creation, the 'dummy' has the correct constructor name. Thus, * objects created with IMVU.new would show up in the debugger as 'dummy', * which isn't very helpful. Using IMVU.createNamedFunction addresses the * issue. Doublely-unfortunately, there's no way to write a test for this * behavior. -NRD 2013.02.22 */ var dummy = createNamedFunction(constructor.name || 'unknownFunctionName', function(){}); dummy.prototype = constructor.prototype; var obj = new dummy; var r = constructor.apply(obj, argumentList); return (r instanceof Object) ? r : obj; } function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cppTargetFunc) { // humanName: a human-readable string name for the function to be generated. // argTypes: An array that contains the embind type objects for all types in the function signature. // argTypes[0] is the type object for the function return value. // argTypes[1] is the type object for function this object/class type, or null if not crafting an invoker for a class method. // argTypes[2...] are the actual function parameters. // classType: The embind type object for the class to be bound, or null if this is not a method of a class. // cppInvokerFunc: JS Function object to the C++-side function that interops into C++ code. // cppTargetFunc: Function pointer (an integer to FUNCTION_TABLE) to the target C++ function the cppInvokerFunc will end up calling. var argCount = argTypes.length; if (argCount < 2) { throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!"); } var isClassMethodFunc = (argTypes[1] !== null && classType !== null); // Free functions with signature "void function()" do not need an invoker that marshalls between wire types. // TODO: This omits argument count check - enable only at -O3 or similar. // if (ENABLE_UNSAFE_OPTS && argCount == 2 && argTypes[0].name == "void" && !isClassMethodFunc) { // return FUNCTION_TABLE[fn]; // } // Determine if we need to use a dynamic stack to store the destructors for the function parameters. // TODO: Remove this completely once all function invokers are being dynamically generated. var needsDestructorStack = false; for (var i = 1; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) { // The type does not define a destructor function - must use dynamic stack needsDestructorStack = true; break; } } var returns = (argTypes[0].name !== "void"); var expectedArgCount = argCount - 2; var argsWired = new Array(expectedArgCount); var invokerFuncArgs = []; var destructors = []; return function() { if (arguments.length !== expectedArgCount) { throwBindingError('function ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + expectedArgCount + ' args!'); } destructors.length = 0; var thisWired; invokerFuncArgs.length = isClassMethodFunc ? 2 : 1; invokerFuncArgs[0] = cppTargetFunc; if (isClassMethodFunc) { thisWired = argTypes[1]['toWireType'](destructors, this); invokerFuncArgs[1] = thisWired; } for (var i = 0; i < expectedArgCount; ++i) { argsWired[i] = argTypes[i + 2]['toWireType'](destructors, arguments[i]); invokerFuncArgs.push(argsWired[i]); } var rv = cppInvokerFunc.apply(null, invokerFuncArgs); function onDone(rv) { if (needsDestructorStack) { runDestructors(destructors); } else { for (var i = isClassMethodFunc ? 1 : 2; i < argTypes.length; i++) { var param = i === 1 ? thisWired : argsWired[i - 2]; if (argTypes[i].destructorFunction !== null) { argTypes[i].destructorFunction(param); } } } if (returns) { return argTypes[0]['fromWireType'](rv); } } return onDone(rv); }; } function heap32VectorToArray(count, firstElement) { var array = []; for (var i = 0; i < count; i++) { // TODO(https://github.com/emscripten-core/emscripten/issues/17310): // Find a way to hoist the `>> 2` or `>> 3` out of this loop. array.push(HEAPU32[(((firstElement)+(i * 4))>>2)]); } return array; } function __embind_register_class_class_function(rawClassType, methodName, argCount, rawArgTypesAddr, invokerSignature, rawInvoker, fn) { var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = readLatin1String(methodName); rawInvoker = embind__requireFunction(invokerSignature, rawInvoker); whenDependentTypesAreResolved([], [rawClassType], function(classType) { classType = classType[0]; var humanName = classType.name + '.' + methodName; function unboundTypesHandler() { throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); } if (methodName.startsWith("@@")) { methodName = Symbol[methodName.substring(2)]; } var proto = classType.registeredClass.constructor; if (undefined === proto[methodName]) { // This is the first function to be registered with this name. unboundTypesHandler.argCount = argCount-1; proto[methodName] = unboundTypesHandler; } else { // There was an existing function with the same name registered. Set up // a function overload routing table. ensureOverloadTable(proto, methodName, humanName); proto[methodName].overloadTable[argCount-1] = unboundTypesHandler; } whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { // Replace the initial unbound-types-handler stub with the proper // function. If multiple overloads are registered, the function handlers // go into an overload table. var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn); if (undefined === proto[methodName].overloadTable) { func.argCount = argCount-1; proto[methodName] = func; } else { proto[methodName].overloadTable[argCount-1] = func; } return []; }); return []; }); } function __embind_register_class_constructor( rawClassType, argCount, rawArgTypesAddr, invokerSignature, invoker, rawConstructor ) { assert(argCount > 0); var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); invoker = embind__requireFunction(invokerSignature, invoker); var args = [rawConstructor]; var destructors = []; whenDependentTypesAreResolved([], [rawClassType], function(classType) { classType = classType[0]; var humanName = 'constructor ' + classType.name; if (undefined === classType.registeredClass.constructor_body) { classType.registeredClass.constructor_body = []; } if (undefined !== classType.registeredClass.constructor_body[argCount - 1]) { throw new BindingError("Cannot register multiple constructors with identical number of parameters (" + (argCount-1) + ") for class '" + classType.name + "'! Overload resolution is currently only performed using the parameter count, not actual type info!"); } classType.registeredClass.constructor_body[argCount - 1] = () => { throwUnboundTypeError('Cannot construct ' + classType.name + ' due to unbound types', rawArgTypes); }; whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { // Insert empty slot for context type (argTypes[1]). argTypes.splice(1, 0, null); classType.registeredClass.constructor_body[argCount - 1] = craftInvokerFunction(humanName, argTypes, null, invoker, rawConstructor); return []; }); return []; }); } function __embind_register_class_function(rawClassType, methodName, argCount, rawArgTypesAddr, // [ReturnType, ThisType, Args...] invokerSignature, rawInvoker, context, isPureVirtual) { var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); methodName = readLatin1String(methodName); rawInvoker = embind__requireFunction(invokerSignature, rawInvoker); whenDependentTypesAreResolved([], [rawClassType], function(classType) { classType = classType[0]; var humanName = classType.name + '.' + methodName; if (methodName.startsWith("@@")) { methodName = Symbol[methodName.substring(2)]; } if (isPureVirtual) { classType.registeredClass.pureVirtualFunctions.push(methodName); } function unboundTypesHandler() { throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); } var proto = classType.registeredClass.instancePrototype; var method = proto[methodName]; if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount - 2)) { // This is the first overload to be registered, OR we are replacing a // function in the base class with a function in the derived class. unboundTypesHandler.argCount = argCount - 2; unboundTypesHandler.className = classType.name; proto[methodName] = unboundTypesHandler; } else { // There was an existing function with the same name registered. Set up // a function overload routing table. ensureOverloadTable(proto, methodName, humanName); proto[methodName].overloadTable[argCount - 2] = unboundTypesHandler; } whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) { var memberFunction = craftInvokerFunction(humanName, argTypes, classType, rawInvoker, context); // Replace the initial unbound-handler-stub function with the appropriate member function, now that all types // are resolved. If multiple overloads are registered for this function, the function goes into an overload table. if (undefined === proto[methodName].overloadTable) { // Set argCount in case an overload is registered later memberFunction.argCount = argCount - 2; proto[methodName] = memberFunction; } else { proto[methodName].overloadTable[argCount - 2] = memberFunction; } return []; }); return []; }); } function __embind_register_constant(name, type, value) { name = readLatin1String(name); whenDependentTypesAreResolved([], [type], function(type) { type = type[0]; Module[name] = type['fromWireType'](value); return []; }); } var emval_free_list = []; var emval_handle_array = [{},{value:undefined},{value:null},{value:true},{value:false}]; function __emval_decref(handle) { if (handle > 4 && 0 === --emval_handle_array[handle].refcount) { emval_handle_array[handle] = undefined; emval_free_list.push(handle); } } function count_emval_handles() { var count = 0; for (var i = 5; i < emval_handle_array.length; ++i) { if (emval_handle_array[i] !== undefined) { ++count; } } return count; } function get_first_emval() { for (var i = 5; i < emval_handle_array.length; ++i) { if (emval_handle_array[i] !== undefined) { return emval_handle_array[i]; } } return null; } function init_emval() { Module['count_emval_handles'] = count_emval_handles; Module['get_first_emval'] = get_first_emval; } var Emval = {toValue:(handle) => { if (!handle) { throwBindingError('Cannot use deleted val. handle = ' + handle); } return emval_handle_array[handle].value; },toHandle:(value) => { switch (value) { case undefined: return 1; case null: return 2; case true: return 3; case false: return 4; default:{ var handle = emval_free_list.length ? emval_free_list.pop() : emval_handle_array.length; emval_handle_array[handle] = {refcount: 1, value: value}; return handle; } } }}; function __embind_register_emval(rawType, name) { name = readLatin1String(name); registerType(rawType, { name: name, 'fromWireType': function(handle) { var rv = Emval.toValue(handle); __emval_decref(handle); return rv; }, 'toWireType': function(destructors, value) { return Emval.toHandle(value); }, 'argPackAdvance': 8, 'readValueFromPointer': simpleReadValueFromPointer, destructorFunction: null, // This type does not need a destructor // TODO: do we need a deleteObject here? write a test where // emval is passed into JS via an interface }); } function enumReadValueFromPointer(name, shift, signed) { switch (shift) { case 0: return function(pointer) { var heap = signed ? HEAP8 : HEAPU8; return this['fromWireType'](heap[pointer]); }; case 1: return function(pointer) { var heap = signed ? HEAP16 : HEAPU16; return this['fromWireType'](heap[pointer >> 1]); }; case 2: return function(pointer) { var heap = signed ? HEAP32 : HEAPU32; return this['fromWireType'](heap[pointer >> 2]); }; default: throw new TypeError("Unknown integer type: " + name); } } function __embind_register_enum(rawType, name, size, isSigned) { var shift = getShiftFromSize(size); name = readLatin1String(name); function ctor() {} ctor.values = {}; registerType(rawType, { name: name, constructor: ctor, 'fromWireType': function(c) { return this.constructor.values[c]; }, 'toWireType': function(destructors, c) { return c.value; }, 'argPackAdvance': 8, 'readValueFromPointer': enumReadValueFromPointer(name, shift, isSigned), destructorFunction: null, }); exposePublicSymbol(name, ctor); } function requireRegisteredType(rawType, humanName) { var impl = registeredTypes[rawType]; if (undefined === impl) { throwBindingError(humanName + " has unknown type " + getTypeName(rawType)); } return impl; } function __embind_register_enum_value(rawEnumType, name, enumValue) { var enumType = requireRegisteredType(rawEnumType, 'enum'); name = readLatin1String(name); var Enum = enumType.constructor; var Value = Object.create(enumType.constructor.prototype, { value: {value: enumValue}, constructor: {value: createNamedFunction(enumType.name + '_' + name, function() {})}, }); Enum.values[enumValue] = Value; Enum[name] = Value; } function embindRepr(v) { if (v === null) { return 'null'; } var t = typeof v; if (t === 'object' || t === 'array' || t === 'function') { return v.toString(); } else { return '' + v; } } function floatReadValueFromPointer(name, shift) { switch (shift) { case 2: return function(pointer) { return this['fromWireType'](HEAPF32[pointer >> 2]); }; case 3: return function(pointer) { return this['fromWireType'](HEAPF64[pointer >> 3]); }; default: throw new TypeError("Unknown float type: " + name); } } function __embind_register_float(rawType, name, size) { var shift = getShiftFromSize(size); name = readLatin1String(name); registerType(rawType, { name: name, 'fromWireType': function(value) { return value; }, 'toWireType': function(destructors, value) { if (typeof value != "number" && typeof value != "boolean") { throw new TypeError('Cannot convert "' + embindRepr(value) + '" to ' + this.name); } // The VM will perform JS to Wasm value conversion, according to the spec: // https://www.w3.org/TR/wasm-js-api-1/#towebassemblyvalue return value; }, 'argPackAdvance': 8, 'readValueFromPointer': floatReadValueFromPointer(name, shift), destructorFunction: null, // This type does not need a destructor }); } function __embind_register_function(name, argCount, rawArgTypesAddr, signature, rawInvoker, fn) { var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); name = readLatin1String(name); rawInvoker = embind__requireFunction(signature, rawInvoker); exposePublicSymbol(name, function() { throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes); }, argCount - 1); whenDependentTypesAreResolved([], argTypes, function(argTypes) { var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); replacePublicSymbol(name, craftInvokerFunction(name, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn), argCount - 1); return []; }); } function integerReadValueFromPointer(name, shift, signed) { // integers are quite common, so generate very specialized functions switch (shift) { case 0: return signed ? function readS8FromPointer(pointer) { return HEAP8[pointer]; } : function readU8FromPointer(pointer) { return HEAPU8[pointer]; }; case 1: return signed ? function readS16FromPointer(pointer) { return HEAP16[pointer >> 1]; } : function readU16FromPointer(pointer) { return HEAPU16[pointer >> 1]; }; case 2: return signed ? function readS32FromPointer(pointer) { return HEAP32[pointer >> 2]; } : function readU32FromPointer(pointer) { return HEAPU32[pointer >> 2]; }; default: throw new TypeError("Unknown integer type: " + name); } } function __embind_register_integer(primitiveType, name, size, minRange, maxRange) { name = readLatin1String(name); // LLVM doesn't have signed and unsigned 32-bit types, so u32 literals come // out as 'i32 -1'. Always treat those as max u32. if (maxRange === -1) { maxRange = 4294967295; } var shift = getShiftFromSize(size); var fromWireType = (value) => value; if (minRange === 0) { var bitshift = 32 - 8*size; fromWireType = (value) => (value << bitshift) >>> bitshift; } var isUnsignedType = (name.includes('unsigned')); var checkAssertions = (value, toTypeName) => { if (typeof value != "number" && typeof value != "boolean") { throw new TypeError('Cannot convert "' + embindRepr(value) + '" to ' + toTypeName); } if (value < minRange || value > maxRange) { throw new TypeError('Passing a number "' + embindRepr(value) + '" from JS side to C/C++ side to an argument of type "' + name + '", which is outside the valid range [' + minRange + ', ' + maxRange + ']!'); } } var toWireType; if (isUnsignedType) { toWireType = function(destructors, value) { checkAssertions(value, this.name); return value >>> 0; } } else { toWireType = function(destructors, value) { checkAssertions(value, this.name); // The VM will perform JS to Wasm value conversion, according to the spec: // https://www.w3.org/TR/wasm-js-api-1/#towebassemblyvalue return value; } } registerType(primitiveType, { name: name, 'fromWireType': fromWireType, 'toWireType': toWireType, 'argPackAdvance': 8, 'readValueFromPointer': integerReadValueFromPointer(name, shift, minRange !== 0), destructorFunction: null, // This type does not need a destructor }); } function __embind_register_memory_view(rawType, dataTypeIndex, name) { var typeMapping = [ Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, ]; var TA = typeMapping[dataTypeIndex]; function decodeMemoryView(handle) { handle = handle >> 2; var heap = HEAPU32; var size = heap[handle]; // in elements var data = heap[handle + 1]; // byte offset into emscripten heap return new TA(buffer, data, size); } name = readLatin1String(name); registerType(rawType, { name: name, 'fromWireType': decodeMemoryView, 'argPackAdvance': 8, 'readValueFromPointer': decodeMemoryView, }, { ignoreDuplicateRegistrations: true, }); } function __embind_register_smart_ptr(rawType, rawPointeeType, name, sharingPolicy, getPointeeSignature, rawGetPointee, constructorSignature, rawConstructor, shareSignature, rawShare, destructorSignature, rawDestructor) { name = readLatin1String(name); rawGetPointee = embind__requireFunction(getPointeeSignature, rawGetPointee); rawConstructor = embind__requireFunction(constructorSignature, rawConstructor); rawShare = embind__requireFunction(shareSignature, rawShare); rawDestructor = embind__requireFunction(destructorSignature, rawDestructor); whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) { pointeeType = pointeeType[0]; var registeredPointer = new RegisteredPointer(name, pointeeType.registeredClass, false, false, // smart pointer properties true, pointeeType, sharingPolicy, rawGetPointee, rawConstructor, rawShare, rawDestructor); return [registeredPointer]; }); } function __embind_register_std_string(rawType, name) { name = readLatin1String(name); var stdStringIsUTF8 //process only std::string bindings with UTF8 support, in contrast to e.g. std::basic_string<unsigned char> = (name === "std::string"); registerType(rawType, { name: name, 'fromWireType': function(value) { var length = HEAPU32[((value)>>2)]; var payload = value + 4; var str; if (stdStringIsUTF8) { var decodeStartPtr = payload; // Looping here to support possible embedded '0' bytes for (var i = 0; i <= length; ++i) { var currentBytePtr = payload + i; if (i == length || HEAPU8[currentBytePtr] == 0) { var maxRead = currentBytePtr - decodeStartPtr; var stringSegment = UTF8ToString(decodeStartPtr, maxRead); if (str === undefined) { str = stringSegment; } else { str += String.fromCharCode(0); str += stringSegment; } decodeStartPtr = currentBytePtr + 1; } } } else { var a = new Array(length); for (var i = 0; i < length; ++i) { a[i] = String.fromCharCode(HEAPU8[payload + i]); } str = a.join(''); } _free(value); return str; }, 'toWireType': function(destructors, value) { if (value instanceof ArrayBuffer) { value = new Uint8Array(value); } var length; var valueIsOfTypeString = (typeof value == 'string'); if (!(valueIsOfTypeString || value instanceof Uint8Array || value instanceof Uint8ClampedArray || value instanceof Int8Array)) { throwBindingError('Cannot pass non-string to std::string'); } if (stdStringIsUTF8 && valueIsOfTypeString) { length = lengthBytesUTF8(value); } else { length = value.length; } // assumes 4-byte alignment var base = _malloc(4 + length + 1); var ptr = base + 4; HEAPU32[((base)>>2)] = length; if (stdStringIsUTF8 && valueIsOfTypeString) { stringToUTF8(value, ptr, length + 1); } else { if (valueIsOfTypeString) { for (var i = 0; i < length; ++i) { var charCode = value.charCodeAt(i); if (charCode > 255) { _free(ptr); throwBindingError('String has UTF-16 code units that do not fit in 8 bits'); } HEAPU8[ptr + i] = charCode; } } else { for (var i = 0; i < length; ++i) { HEAPU8[ptr + i] = value[i]; } } } if (destructors !== null) { destructors.push(_free, base); } return base; }, 'argPackAdvance': 8, 'readValueFromPointer': simpleReadValueFromPointer, destructorFunction: function(ptr) { _free(ptr); }, }); } function __embind_register_std_wstring(rawType, charSize, name) { name = readLatin1String(name); var decodeString, encodeString, getHeap, lengthBytesUTF, shift; if (charSize === 2) { decodeString = UTF16ToString; encodeString = stringToUTF16; lengthBytesUTF = lengthBytesUTF16; getHeap = () => HEAPU16; shift = 1; } else if (charSize === 4) { decodeString = UTF32ToString; encodeString = stringToUTF32; lengthBytesUTF = lengthBytesUTF32; getHeap = () => HEAPU32; shift = 2; } registerType(rawType, { name: name, 'fromWireType': function(value) { // Code mostly taken from _embind_register_std_string fromWireType var length = HEAPU32[value >> 2]; var HEAP = getHeap(); var str; var decodeStartPtr = value + 4; // Looping here to support possible embedded '0' bytes for (var i = 0; i <= length; ++i) { var currentBytePtr = value + 4 + i * charSize; if (i == length || HEAP[currentBytePtr >> shift] == 0) { var maxReadBytes = currentBytePtr - decodeStartPtr; var stringSegment = decodeString(decodeStartPtr, maxReadBytes); if (str === undefined) { str = stringSegment; } else { str += String.fromCharCode(0); str += stringSegment; } decodeStartPtr = currentBytePtr + charSize; } } _free(value); return str; }, 'toWireType': function(destructors, value) { if (!(typeof value == 'string')) { throwBindingError('Cannot pass non-string to C++ string type ' + name); } // assumes 4-byte alignment var length = lengthBytesUTF(value); var ptr = _malloc(4 + length + charSize); HEAPU32[ptr >> 2] = length >> shift; encodeString(value, ptr + 4, length + charSize); if (destructors !== null) { destructors.push(_free, ptr); } return ptr; }, 'argPackAdvance': 8, 'readValueFromPointer': simpleReadValueFromPointer, destructorFunction: function(ptr) { _free(ptr); }, }); } function __embind_register_value_object( rawType, name, constructorSignature, rawConstructor, destructorSignature, rawDestructor ) { structRegistrations[rawType] = { name: readLatin1String(name), rawConstructor: embind__requireFunction(constructorSignature, rawConstructor), rawDestructor: embind__requireFunction(destructorSignature, rawDestructor), fields: [], }; } function __embind_register_value_object_field( structType, fieldName, getterReturnType, getterSignature, getter, getterContext, setterArgumentType, setterSignature, setter, setterContext ) { structRegistrations[structType].fields.push({ fieldName: readLatin1String(fieldName), getterReturnType: getterReturnType, getter: embind__requireFunction(getterSignature, getter), getterContext: getterContext, setterArgumentType: setterArgumentType, setter: embind__requireFunction(setterSignature, setter), setterContext: setterContext, }); } function __embind_register_void(rawType, name) { name = readLatin1String(name); registerType(rawType, { isVoid: true, // void return values can be optimized out sometimes name: name, 'argPackAdvance': 0, 'fromWireType': function() { return undefined; }, 'toWireType': function(destructors, o) { // TODO: assert if anything else is given? return undefined; }, }); } function __emscripten_date_now() { return Date.now(); } var nowIsMonotonic = true;; function __emscripten_get_now_is_monotonic() { return nowIsMonotonic; } function __emscripten_throw_longjmp() { throw Infinity; } function __emval_as(handle, returnType, destructorsRef) { handle = Emval.toValue(handle); returnType = requireRegisteredType(returnType, 'emval::as'); var destructors = []; var rd = Emval.toHandle(destructors); HEAPU32[((destructorsRef)>>2)] = rd; return returnType['toWireType'](destructors, handle); } function emval_allocateDestructors(destructorsRef) { var destructors = []; HEAPU32[((destructorsRef)>>2)] = Emval.toHandle(destructors); return destructors; } var emval_symbols = {}; function getStringOrSymbol(address) { var symbol = emval_symbols[address]; if (symbol === undefined) { return readLatin1String(address); } return symbol; } var emval_methodCallers = []; function __emval_call_method(caller, handle, methodName, destructorsRef, args) { caller = emval_methodCallers[caller]; handle = Emval.toValue(handle); methodName = getStringOrSymbol(methodName); return caller(handle, methodName, emval_allocateDestructors(destructorsRef), args); } function __emval_call_void_method(caller, handle, methodName, args) { caller = emval_methodCallers[caller]; handle = Emval.toValue(handle); methodName = getStringOrSymbol(methodName); caller(handle, methodName, null, args); } function emval_get_global() { if (typeof globalThis == 'object') { return globalThis; } function testGlobal(obj) { obj['$$$embind_global$$$'] = obj; var success = typeof $$$embind_global$$$ == 'object' && obj['$$$embind_global$$$'] == obj; if (!success) { delete obj['$$$embind_global$$$']; } return success; } if (typeof $$$embind_global$$$ == 'object') { return $$$embind_global$$$; } if (typeof global == 'object' && testGlobal(global)) { $$$embind_global$$$ = global; } else if (typeof self == 'object' && testGlobal(self)) { $$$embind_global$$$ = self; // This works for both "window" and "self" (Web Workers) global objects } if (typeof $$$embind_global$$$ == 'object') { return $$$embind_global$$$; } throw Error('unable to get global object.'); } function __emval_get_global(name) { if (name===0) { return Emval.toHandle(emval_get_global()); } else { name = getStringOrSymbol(name); return Emval.toHandle(emval_get_global()[name]); } } function emval_addMethodCaller(caller) { var id = emval_methodCallers.length; emval_methodCallers.push(caller); return id; } function emval_lookupTypes(argCount, argTypes) { var a = new Array(argCount); for (var i = 0; i < argCount; ++i) { a[i] = requireRegisteredType(HEAPU32[(((argTypes)+(i * POINTER_SIZE))>>2)], "parameter " + i); } return a; } var emval_registeredMethods = []; function __emval_get_method_caller(argCount, argTypes) { var types = emval_lookupTypes(argCount, argTypes); var retType = types[0]; var signatureName = retType.name + "_$" + types.slice(1).map(function (t) { return t.name; }).join("_") + "$"; var returnId = emval_registeredMethods[signatureName]; if (returnId !== undefined) { return returnId; } var argN = new Array(argCount - 1); var invokerFunction = (handle, name, destructors, args) => { var offset = 0; for (var i = 0; i < argCount - 1; ++i) { argN[i] = types[i + 1]['readValueFromPointer'](args + offset); offset += types[i + 1]['argPackAdvance']; } var rv = handle[name].apply(handle, argN); for (var i = 0; i < argCount - 1; ++i) { if (types[i + 1].deleteObject) { types[i + 1].deleteObject(argN[i]); } } if (!retType.isVoid) { return retType['toWireType'](destructors, rv); } }; returnId = emval_addMethodCaller(invokerFunction); emval_registeredMethods[signatureName] = returnId; return returnId; } function __emval_get_property(handle, key) { handle = Emval.toValue(handle); key = Emval.toValue(key); return Emval.toHandle(handle[key]); } function __emval_incref(handle) { if (handle > 4) { emval_handle_array[handle].refcount += 1; } } function craftEmvalAllocator(argCount) { /*This function returns a new function that looks like this: function emval_allocator_3(constructor, argTypes, args) { var argType0 = requireRegisteredType(HEAP32[(argTypes >> 2)], "parameter 0"); var arg0 = argType0['readValueFromPointer'](args); var argType1 = requireRegisteredType(HEAP32[(argTypes >> 2) + 1], "parameter 1"); var arg1 = argType1['readValueFromPointer'](args + 8); var argType2 = requireRegisteredType(HEAP32[(argTypes >> 2) + 2], "parameter 2"); var arg2 = argType2['readValueFromPointer'](args + 16); var obj = new constructor(arg0, arg1, arg2); return Emval.toHandle(obj); } */ var argsList = new Array(argCount + 1); return function(constructor, argTypes, args) { argsList[0] = constructor; for (var i = 0; i < argCount; ++i) { var argType = requireRegisteredType(HEAPU32[(((argTypes)+(i * POINTER_SIZE))>>2)], 'parameter ' + i); argsList[i + 1] = argType['readValueFromPointer'](args); args += argType['argPackAdvance']; } var obj = new (constructor.bind.apply(constructor, argsList)); return Emval.toHandle(obj); }; } var emval_newers = {}; function __emval_new(handle, argCount, argTypes, args) { handle = Emval.toValue(handle); var newer = emval_newers[argCount]; if (!newer) { newer = craftEmvalAllocator(argCount); emval_newers[argCount] = newer; } return newer(handle, argTypes, args); } function __emval_new_array() { return Emval.toHandle([]); } function __emval_new_cstring(v) { return Emval.toHandle(getStringOrSymbol(v)); } function __emval_new_object() { return Emval.toHandle({}); } function __emval_not(object) { object = Emval.toValue(object); return !object; } function __emval_run_destructors(handle) { var destructors = Emval.toValue(handle); runDestructors(destructors); __emval_decref(handle); } function __emval_set_property(handle, key, value) { handle = Emval.toValue(handle); key = Emval.toValue(key); value = Emval.toValue(value); handle[key] = value; } function __emval_take_value(type, arg) { type = requireRegisteredType(type, '_emval_take_value'); var v = type['readValueFromPointer'](arg); return Emval.toHandle(v); } function __mmap_js(len, prot, flags, fd, off, allocated) { return -52; } function __munmap_js(addr, len, prot, flags, fd, offset) { } function _abort() { abort('native code called abort()'); } var _emscripten_get_now;if (ENVIRONMENT_IS_NODE) { _emscripten_get_now = () => { var t = process['hrtime'](); return t[0] * 1e3 + t[1] / 1e6; }; } else _emscripten_get_now = () => performance.now(); ; function __webgl_enable_ANGLE_instanced_arrays(ctx) { // Extension available in WebGL 1 from Firefox 26 and Google Chrome 30 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('ANGLE_instanced_arrays'); if (ext) { ctx['vertexAttribDivisor'] = function(index, divisor) { ext['vertexAttribDivisorANGLE'](index, divisor); }; ctx['drawArraysInstanced'] = function(mode, first, count, primcount) { ext['drawArraysInstancedANGLE'](mode, first, count, primcount); }; ctx['drawElementsInstanced'] = function(mode, count, type, indices, primcount) { ext['drawElementsInstancedANGLE'](mode, count, type, indices, primcount); }; return 1; } } function __webgl_enable_OES_vertex_array_object(ctx) { // Extension available in WebGL 1 from Firefox 25 and WebKit 536.28/desktop Safari 6.0.3 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('OES_vertex_array_object'); if (ext) { ctx['createVertexArray'] = function() { return ext['createVertexArrayOES'](); }; ctx['deleteVertexArray'] = function(vao) { ext['deleteVertexArrayOES'](vao); }; ctx['bindVertexArray'] = function(vao) { ext['bindVertexArrayOES'](vao); }; ctx['isVertexArray'] = function(vao) { return ext['isVertexArrayOES'](vao); }; return 1; } } function __webgl_enable_WEBGL_draw_buffers(ctx) { // Extension available in WebGL 1 from Firefox 28 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('WEBGL_draw_buffers'); if (ext) { ctx['drawBuffers'] = function(n, bufs) { ext['drawBuffersWEBGL'](n, bufs); }; return 1; } } function __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx) { // Closure is expected to be allowed to minify the '.dibvbi' property, so not accessing it quoted. return !!(ctx.dibvbi = ctx.getExtension('WEBGL_draw_instanced_base_vertex_base_instance')); } function __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(ctx) { // Closure is expected to be allowed to minify the '.mdibvbi' property, so not accessing it quoted. return !!(ctx.mdibvbi = ctx.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance')); } function __webgl_enable_WEBGL_multi_draw(ctx) { // Closure is expected to be allowed to minify the '.multiDrawWebgl' property, so not accessing it quoted. return !!(ctx.multiDrawWebgl = ctx.getExtension('WEBGL_multi_draw')); } var GL = {counter:1,buffers:[],programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},queries:[],samplers:[],transformFeedbacks:[],syncs:[],stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode) { if (!GL.lastError) { GL.lastError = errorCode; } },getNewId:function(table) { var ret = GL.counter++; for (var i = table.length; i < ret; i++) { table[i] = null; } return ret; },getSource:function(shader, count, string, length) { var source = ''; for (var i = 0; i < count; ++i) { var len = length ? HEAP32[(((length)+(i*4))>>2)] : -1; source += UTF8ToString(HEAP32[(((string)+(i*4))>>2)], len < 0 ? undefined : len); } return source; },validateGLObjectID:function(objectHandleArray, objectID, callerFunctionName, objectReadableType) { if (objectID != 0) { if (objectHandleArray[objectID] === null) { err(callerFunctionName + ' called with an already deleted ' + objectReadableType + ' ID ' + objectID + '!'); } else if (!(objectID in objectHandleArray)) { err(callerFunctionName + ' called with a nonexisting ' + objectReadableType + ' ID ' + objectID + '!'); } } },validateVertexAttribPointer:function(dimension, dataType, stride, offset) { var sizeBytes = 1; switch (dataType) { case 0x1400 /* GL_BYTE */: case 0x1401 /* GL_UNSIGNED_BYTE */: sizeBytes = 1; break; case 0x1402 /* GL_SHORT */: case 0x1403 /* GL_UNSIGNED_SHORT */: sizeBytes = 2; break; case 0x1404 /* GL_INT */: case 0x1405 /* GL_UNSIGNED_INT */: case 0x1406 /* GL_FLOAT */: sizeBytes = 4; break; case 0x140A /* GL_DOUBLE */: sizeBytes = 8; break; default: if (GL.currentContext.version >= 2) { if (dataType == 0x8368 /* GL_UNSIGNED_INT_2_10_10_10_REV */ || dataType == 0x8D9F /* GL_INT_2_10_10_10_REV */) { sizeBytes = 4; break; } else if (dataType == 0x140B /* GL_HALF_FLOAT */) { sizeBytes = 2; break; } else { // else fall through } } err('Invalid vertex attribute data type GLenum ' + dataType + ' passed to GL function!'); } if (dimension == 0x80E1 /* GL_BGRA */) { err('WebGL does not support size=GL_BGRA in a call to glVertexAttribPointer! Please use size=4 and type=GL_UNSIGNED_BYTE instead!'); } else if (dimension < 1 || dimension > 4) { err('Invalid dimension='+dimension+' in call to glVertexAttribPointer, must be 1,2,3 or 4.'); } if (stride < 0 || stride > 255) { err('Invalid stride='+stride+' in call to glVertexAttribPointer. Note that maximum supported stride in WebGL is 255!'); } if (offset % sizeBytes != 0) { err('GL spec section 6.4 error: vertex attribute data offset of ' + offset + ' bytes should have been a multiple of the data type size that was used: GLenum ' + dataType + ' has size of ' + sizeBytes + ' bytes!'); } if (stride % sizeBytes != 0) { err('GL spec section 6.4 error: vertex attribute data stride of ' + stride + ' bytes should have been a multiple of the data type size that was used: GLenum ' + dataType + ' has size of ' + sizeBytes + ' bytes!'); } },createContext:function(/** @type {HTMLCanvasElement} */ canvas, webGLContextAttributes) { // BUG: Workaround Safari WebGL issue: After successfully acquiring WebGL context on a canvas, // calling .getContext() will always return that context independent of which 'webgl' or 'webgl2' // context version was passed. See https://bugs.webkit.org/show_bug.cgi?id=222758 and // https://github.com/emscripten-core/emscripten/issues/13295. // TODO: Once the bug is fixed and shipped in Safari, adjust the Safari version field in above check. if (!canvas.getContextSafariWebGL2Fixed) { canvas.getContextSafariWebGL2Fixed = canvas.getContext; /** @type {function(this:HTMLCanvasElement, string, (Object|null)=): (Object|null)} */ function fixedGetContext(ver, attrs) { var gl = canvas.getContextSafariWebGL2Fixed(ver, attrs); return ((ver == 'webgl') == (gl instanceof WebGLRenderingContext)) ? gl : null; } canvas.getContext = fixedGetContext; } var ctx = (webGLContextAttributes.majorVersion > 1) ? canvas.getContext("webgl2", webGLContextAttributes) : (canvas.getContext("webgl", webGLContextAttributes) // https://caniuse.com/#feat=webgl ); if (!ctx) return 0; var handle = GL.registerContext(ctx, webGLContextAttributes); return handle; },registerContext:function(ctx, webGLContextAttributes) { // without pthreads a context is just an integer ID var handle = GL.getNewId(GL.contexts); var context = { handle: handle, attributes: webGLContextAttributes, version: webGLContextAttributes.majorVersion, GLctx: ctx }; // Store the created context object so that we can access the context given a canvas without having to pass the parameters again. if (ctx.canvas) ctx.canvas.GLctxObject = context; GL.contexts[handle] = context; if (typeof webGLContextAttributes.enableExtensionsByDefault == 'undefined' || webGLContextAttributes.enableExtensionsByDefault) { GL.initExtensions(context); } return handle; },makeContextCurrent:function(contextHandle) { GL.currentContext = GL.contexts[contextHandle]; // Active Emscripten GL layer context object. Module.ctx = GLctx = GL.currentContext && GL.currentContext.GLctx; // Active WebGL context object. return !(contextHandle && !GLctx); },getContext:function(contextHandle) { return GL.contexts[contextHandle]; },deleteContext:function(contextHandle) { if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = null; if (typeof JSEvents == 'object') JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); // Release all JS event handlers on the DOM element that the GL context is associated with since the context is now deleted. if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises. GL.contexts[contextHandle] = null; },initExtensions:function(context) { // If this function is called without a specific context object, init the extensions of the currently active context. if (!context) context = GL.currentContext; if (context.initExtensionsDone) return; context.initExtensionsDone = true; var GLctx = context.GLctx; // Detect the presence of a few extensions manually, this GL interop layer itself will need to know if they exist. // Extensions that are only available in WebGL 1 (the calls will be no-ops if called on a WebGL 2 context active) __webgl_enable_ANGLE_instanced_arrays(GLctx); __webgl_enable_OES_vertex_array_object(GLctx); __webgl_enable_WEBGL_draw_buffers(GLctx); // Extensions that are available from WebGL >= 2 (no-op if called on a WebGL 1 context active) __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx); __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx); // On WebGL 2, EXT_disjoint_timer_query is replaced with an alternative // that's based on core APIs, and exposes only the queryCounterEXT() // entrypoint. if (context.version >= 2) { GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query_webgl2"); } // However, Firefox exposes the WebGL 1 version on WebGL 2 as well and // thus we look for the WebGL 1 version again if the WebGL 2 version // isn't present. https://bugzilla.mozilla.org/show_bug.cgi?id=1328882 if (context.version < 2 || !GLctx.disjointTimerQueryExt) { GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); } __webgl_enable_WEBGL_multi_draw(GLctx); // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. var exts = GLctx.getSupportedExtensions() || []; exts.forEach(function(ext) { // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders are not enabled by default. if (!ext.includes('lose_context') && !ext.includes('debug')) { // Call .getExtension() to enable that extension permanently. GLctx.getExtension(ext); } }); }}; function _emscripten_glActiveTexture(x0) { GLctx['activeTexture'](x0) } function _emscripten_glAttachShader(program, shader) { GL.validateGLObjectID(GL.programs, program, 'glAttachShader', 'program'); GL.validateGLObjectID(GL.shaders, shader, 'glAttachShader', 'shader'); GLctx.attachShader(GL.programs[program], GL.shaders[shader]); } function _emscripten_glBindAttribLocation(program, index, name) { GL.validateGLObjectID(GL.programs, program, 'glBindAttribLocation', 'program'); GLctx.bindAttribLocation(GL.programs[program], index, UTF8ToString(name)); } function _emscripten_glBindBuffer(target, buffer) { GL.validateGLObjectID(GL.buffers, buffer, 'glBindBuffer', 'buffer'); if (target == 0x88EB /*GL_PIXEL_PACK_BUFFER*/) { // In WebGL 2 glReadPixels entry point, we need to use a different WebGL 2 API function call when a buffer is bound to // GL_PIXEL_PACK_BUFFER_BINDING point, so must keep track whether that binding point is non-null to know what is // the proper API function to call. GLctx.currentPixelPackBufferBinding = buffer; } else if (target == 0x88EC /*GL_PIXEL_UNPACK_BUFFER*/) { // In WebGL 2 gl(Compressed)Tex(Sub)Image[23]D entry points, we need to // use a different WebGL 2 API function call when a buffer is bound to // GL_PIXEL_UNPACK_BUFFER_BINDING point, so must keep track whether that // binding point is non-null to know what is the proper API function to // call. GLctx.currentPixelUnpackBufferBinding = buffer; } GLctx.bindBuffer(target, GL.buffers[buffer]); } function _emscripten_glBindFramebuffer(target, framebuffer) { GL.validateGLObjectID(GL.framebuffers, framebuffer, 'glBindFramebuffer', 'framebuffer'); GLctx.bindFramebuffer(target, GL.framebuffers[framebuffer]); } function _emscripten_glBindRenderbuffer(target, renderbuffer) { GL.validateGLObjectID(GL.renderbuffers, renderbuffer, 'glBindRenderbuffer', 'renderbuffer'); GLctx.bindRenderbuffer(target, GL.renderbuffers[renderbuffer]); } function _emscripten_glBindSampler(unit, sampler) { GL.validateGLObjectID(GL.samplers, sampler, 'glBindSampler', 'sampler'); GLctx['bindSampler'](unit, GL.samplers[sampler]); } function _emscripten_glBindTexture(target, texture) { GL.validateGLObjectID(GL.textures, texture, 'glBindTexture', 'texture'); GLctx.bindTexture(target, GL.textures[texture]); } function _emscripten_glBindVertexArray(vao) { assert(GLctx['bindVertexArray'], 'Must have WebGL2 or OES_vertex_array_object to use vao'); GLctx['bindVertexArray'](GL.vaos[vao]); } function _emscripten_glBindVertexArrayOES(vao) { assert(GLctx['bindVertexArray'], 'Must have WebGL2 or OES_vertex_array_object to use vao'); GLctx['bindVertexArray'](GL.vaos[vao]); } function _emscripten_glBlendColor(x0, x1, x2, x3) { GLctx['blendColor'](x0, x1, x2, x3) } function _emscripten_glBlendEquation(x0) { GLctx['blendEquation'](x0) } function _emscripten_glBlendFunc(x0, x1) { GLctx['blendFunc'](x0, x1) } function _emscripten_glBlitFramebuffer(x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) { GLctx['blitFramebuffer'](x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) } function _emscripten_glBufferData(target, size, data, usage) { if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. // If size is zero, WebGL would interpret uploading the whole input arraybuffer (starting from given offset), which would // not make sense in WebAssembly, so avoid uploading if size is zero. However we must still call bufferData to establish a // backing storage of zero bytes. if (data && size) { GLctx.bufferData(target, HEAPU8, usage, data, size); } else { GLctx.bufferData(target, size, usage); } } else { // N.b. here first form specifies a heap subarray, second form an integer size, so the ?: code here is polymorphic. It is advised to avoid // randomly mixing both uses in calling code, to avoid any potential JS engine JIT issues. GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage); } } function _emscripten_glBufferSubData(target, offset, size, data) { if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. size && GLctx.bufferSubData(target, offset, HEAPU8, data, size); return; } GLctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size)); } function _emscripten_glCheckFramebufferStatus(x0) { return GLctx['checkFramebufferStatus'](x0) } function _emscripten_glClear(x0) { GLctx['clear'](x0) } function _emscripten_glClearColor(x0, x1, x2, x3) { GLctx['clearColor'](x0, x1, x2, x3) } function _emscripten_glClearStencil(x0) { GLctx['clearStencil'](x0) } function convertI32PairToI53(lo, hi) { // This function should not be getting called with too large unsigned numbers // in high part (if hi >= 0x7FFFFFFFF, one should have been calling // convertU32PairToI53()) assert(hi === (hi|0)); return (lo >>> 0) + hi * 4294967296; } function _emscripten_glClientWaitSync(sync, flags, timeoutLo, timeoutHi) { // WebGL2 vs GLES3 differences: in GLES3, the timeout parameter is a uint64, where 0xFFFFFFFFFFFFFFFFULL means GL_TIMEOUT_IGNORED. // In JS, there's no 64-bit value types, so instead timeout is taken to be signed, and GL_TIMEOUT_IGNORED is given value -1. // Inherently the value accepted in the timeout is lossy, and can't take in arbitrary u64 bit pattern (but most likely doesn't matter) // See https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.15 return GLctx.clientWaitSync(GL.syncs[sync], flags, convertI32PairToI53(timeoutLo, timeoutHi)); } function _emscripten_glColorMask(red, green, blue, alpha) { GLctx.colorMask(!!red, !!green, !!blue, !!alpha); } function _emscripten_glCompileShader(shader) { GL.validateGLObjectID(GL.shaders, shader, 'glCompileShader', 'shader'); GLctx.compileShader(GL.shaders[shader]); } function _emscripten_glCompressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, data) { if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelUnpackBufferBinding || !imageSize) { GLctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, imageSize, data); } else { GLctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, HEAPU8, data, imageSize); } return; } GLctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, data ? HEAPU8.subarray((data), (data+imageSize)) : null); } function _emscripten_glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data) { if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelUnpackBufferBinding || !imageSize) { GLctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, format, imageSize, data); } else { GLctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, format, HEAPU8, data, imageSize); } return; } GLctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, format, data ? HEAPU8.subarray((data), (data+imageSize)) : null); } function _emscripten_glCopyBufferSubData(x0, x1, x2, x3, x4) { GLctx['copyBufferSubData'](x0, x1, x2, x3, x4) } function _emscripten_glCopyTexSubImage2D(x0, x1, x2, x3, x4, x5, x6, x7) { GLctx['copyTexSubImage2D'](x0, x1, x2, x3, x4, x5, x6, x7) } function _emscripten_glCreateProgram() { var id = GL.getNewId(GL.programs); var program = GLctx.createProgram(); // Store additional information needed for each shader program: program.name = id; // Lazy cache results of glGetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH/GL_ACTIVE_ATTRIBUTE_MAX_LENGTH/GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH) program.maxUniformLength = program.maxAttributeLength = program.maxUniformBlockNameLength = 0; program.uniformIdCounter = 1; GL.programs[id] = program; return id; } function _emscripten_glCreateShader(shaderType) { var id = GL.getNewId(GL.shaders); GL.shaders[id] = GLctx.createShader(shaderType); return id; } function _emscripten_glCullFace(x0) { GLctx['cullFace'](x0) } function _emscripten_glDeleteBuffers(n, buffers) { for (var i = 0; i < n; i++) { var id = HEAP32[(((buffers)+(i*4))>>2)]; var buffer = GL.buffers[id]; // From spec: "glDeleteBuffers silently ignores 0's and names that do not // correspond to existing buffer objects." if (!buffer) continue; GLctx.deleteBuffer(buffer); buffer.name = 0; GL.buffers[id] = null; if (id == GLctx.currentPixelPackBufferBinding) GLctx.currentPixelPackBufferBinding = 0; if (id == GLctx.currentPixelUnpackBufferBinding) GLctx.currentPixelUnpackBufferBinding = 0; } } function _emscripten_glDeleteFramebuffers(n, framebuffers) { for (var i = 0; i < n; ++i) { var id = HEAP32[(((framebuffers)+(i*4))>>2)]; var framebuffer = GL.framebuffers[id]; if (!framebuffer) continue; // GL spec: "glDeleteFramebuffers silently ignores 0s and names that do not correspond to existing framebuffer objects". GLctx.deleteFramebuffer(framebuffer); framebuffer.name = 0; GL.framebuffers[id] = null; } } function _emscripten_glDeleteProgram(id) { if (!id) return; var program = GL.programs[id]; if (!program) { // glDeleteProgram actually signals an error when deleting a nonexisting object, unlike some other GL delete functions. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } GLctx.deleteProgram(program); program.name = 0; GL.programs[id] = null; } function _emscripten_glDeleteRenderbuffers(n, renderbuffers) { for (var i = 0; i < n; i++) { var id = HEAP32[(((renderbuffers)+(i*4))>>2)]; var renderbuffer = GL.renderbuffers[id]; if (!renderbuffer) continue; // GL spec: "glDeleteRenderbuffers silently ignores 0s and names that do not correspond to existing renderbuffer objects". GLctx.deleteRenderbuffer(renderbuffer); renderbuffer.name = 0; GL.renderbuffers[id] = null; } } function _emscripten_glDeleteSamplers(n, samplers) { for (var i = 0; i < n; i++) { var id = HEAP32[(((samplers)+(i*4))>>2)]; var sampler = GL.samplers[id]; if (!sampler) continue; GLctx['deleteSampler'](sampler); sampler.name = 0; GL.samplers[id] = null; } } function _emscripten_glDeleteShader(id) { if (!id) return; var shader = GL.shaders[id]; if (!shader) { // glDeleteShader actually signals an error when deleting a nonexisting object, unlike some other GL delete functions. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } GLctx.deleteShader(shader); GL.shaders[id] = null; } function _emscripten_glDeleteSync(id) { if (!id) return; var sync = GL.syncs[id]; if (!sync) { // glDeleteSync signals an error when deleting a nonexisting object, unlike some other GL delete functions. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } GLctx.deleteSync(sync); sync.name = 0; GL.syncs[id] = null; } function _emscripten_glDeleteTextures(n, textures) { for (var i = 0; i < n; i++) { var id = HEAP32[(((textures)+(i*4))>>2)]; var texture = GL.textures[id]; if (!texture) continue; // GL spec: "glDeleteTextures silently ignores 0s and names that do not correspond to existing textures". GLctx.deleteTexture(texture); texture.name = 0; GL.textures[id] = null; } } function _emscripten_glDeleteVertexArrays(n, vaos) { assert(GLctx['deleteVertexArray'], 'Must have WebGL2 or OES_vertex_array_object to use vao'); for (var i = 0; i < n; i++) { var id = HEAP32[(((vaos)+(i*4))>>2)]; GLctx['deleteVertexArray'](GL.vaos[id]); GL.vaos[id] = null; } } function _emscripten_glDeleteVertexArraysOES(n, vaos) { assert(GLctx['deleteVertexArray'], 'Must have WebGL2 or OES_vertex_array_object to use vao'); for (var i = 0; i < n; i++) { var id = HEAP32[(((vaos)+(i*4))>>2)]; GLctx['deleteVertexArray'](GL.vaos[id]); GL.vaos[id] = null; } } function _emscripten_glDepthMask(flag) { GLctx.depthMask(!!flag); } function _emscripten_glDisable(x0) { GLctx['disable'](x0) } function _emscripten_glDisableVertexAttribArray(index) { GLctx.disableVertexAttribArray(index); } function _emscripten_glDrawArrays(mode, first, count) { GLctx.drawArrays(mode, first, count); } function _emscripten_glDrawArraysInstanced(mode, first, count, primcount) { assert(GLctx['drawArraysInstanced'], 'Must have ANGLE_instanced_arrays extension or WebGL 2 to use WebGL instancing'); GLctx['drawArraysInstanced'](mode, first, count, primcount); } function _emscripten_glDrawArraysInstancedBaseInstanceWEBGL(mode, first, count, instanceCount, baseInstance) { GLctx.dibvbi['drawArraysInstancedBaseInstanceWEBGL'](mode, first, count, instanceCount, baseInstance); } var tempFixedLengthArray = []; function _emscripten_glDrawBuffers(n, bufs) { assert(GLctx['drawBuffers'], 'Must have WebGL2 or WEBGL_draw_buffers extension to use drawBuffers'); assert(n < tempFixedLengthArray.length, 'Invalid count of numBuffers=' + n + ' passed to glDrawBuffers (that many draw buffer points do not exist in GL)'); var bufArray = tempFixedLengthArray[n]; for (var i = 0; i < n; i++) { bufArray[i] = HEAP32[(((bufs)+(i*4))>>2)]; } GLctx['drawBuffers'](bufArray); } function _emscripten_glDrawElements(mode, count, type, indices) { GLctx.drawElements(mode, count, type, indices); } function _emscripten_glDrawElementsInstanced(mode, count, type, indices, primcount) { assert(GLctx['drawElementsInstanced'], 'Must have ANGLE_instanced_arrays extension or WebGL 2 to use WebGL instancing'); GLctx['drawElementsInstanced'](mode, count, type, indices, primcount); } function _emscripten_glDrawElementsInstancedBaseVertexBaseInstanceWEBGL(mode, count, type, offset, instanceCount, baseVertex, baseinstance) { GLctx.dibvbi['drawElementsInstancedBaseVertexBaseInstanceWEBGL'](mode, count, type, offset, instanceCount, baseVertex, baseinstance); } function _glDrawElements(mode, count, type, indices) { GLctx.drawElements(mode, count, type, indices); } function _emscripten_glDrawRangeElements(mode, start, end, count, type, indices) { // TODO: This should be a trivial pass-though function registered at the bottom of this page as // glFuncs[6][1] += ' drawRangeElements'; // but due to https://bugzilla.mozilla.org/show_bug.cgi?id=1202427, // we work around by ignoring the range. _glDrawElements(mode, count, type, indices); } function _emscripten_glEnable(x0) { GLctx['enable'](x0) } function _emscripten_glEnableVertexAttribArray(index) { GLctx.enableVertexAttribArray(index); } function _emscripten_glFenceSync(condition, flags) { var sync = GLctx.fenceSync(condition, flags); if (sync) { var id = GL.getNewId(GL.syncs); sync.name = id; GL.syncs[id] = sync; return id; } else { return 0; // Failed to create a sync object } } function _emscripten_glFinish() { GLctx['finish']() } function _emscripten_glFlush() { GLctx['flush']() } function _emscripten_glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer) { GL.validateGLObjectID(GL.renderbuffers, renderbuffer, 'glFramebufferRenderbuffer', 'renderbuffer'); GLctx.framebufferRenderbuffer(target, attachment, renderbuffertarget, GL.renderbuffers[renderbuffer]); } function _emscripten_glFramebufferTexture2D(target, attachment, textarget, texture, level) { GL.validateGLObjectID(GL.textures, texture, 'glFramebufferTexture2D', 'texture'); GLctx.framebufferTexture2D(target, attachment, textarget, GL.textures[texture], level); } function _emscripten_glFrontFace(x0) { GLctx['frontFace'](x0) } function __glGenObject(n, buffers, createFunction, objectTable , functionName ) { for (var i = 0; i < n; i++) { var buffer = GLctx[createFunction](); var id = buffer && GL.getNewId(objectTable); if (buffer) { buffer.name = id; objectTable[id] = buffer; } else { GL.recordError(0x502 /* GL_INVALID_OPERATION */); err('GL_INVALID_OPERATION in ' + functionName + ': GLctx.' + createFunction + ' returned null - most likely GL context is lost!'); } HEAP32[(((buffers)+(i*4))>>2)] = id; } } function _emscripten_glGenBuffers(n, buffers) { __glGenObject(n, buffers, 'createBuffer', GL.buffers , 'glGenBuffers' ); } function _emscripten_glGenFramebuffers(n, ids) { __glGenObject(n, ids, 'createFramebuffer', GL.framebuffers , 'glGenFramebuffers' ); } function _emscripten_glGenRenderbuffers(n, renderbuffers) { __glGenObject(n, renderbuffers, 'createRenderbuffer', GL.renderbuffers , 'glGenRenderbuffers' ); } function _emscripten_glGenSamplers(n, samplers) { __glGenObject(n, samplers, 'createSampler', GL.samplers , 'glGenSamplers' ); } function _emscripten_glGenTextures(n, textures) { __glGenObject(n, textures, 'createTexture', GL.textures , 'glGenTextures' ); } function _emscripten_glGenVertexArrays(n, arrays) { assert(GLctx['createVertexArray'], 'Must have WebGL2 or OES_vertex_array_object to use vao'); __glGenObject(n, arrays, 'createVertexArray', GL.vaos , 'glGenVertexArrays' ); } function _emscripten_glGenVertexArraysOES(n, arrays) { assert(GLctx['createVertexArray'], 'Must have WebGL2 or OES_vertex_array_object to use vao'); __glGenObject(n, arrays, 'createVertexArray', GL.vaos , 'glGenVertexArrays' ); } function _emscripten_glGenerateMipmap(x0) { GLctx['generateMipmap'](x0) } function _emscripten_glGetBufferParameteriv(target, value, data) { if (!data) { // GLES2 specification does not specify how to behave if data is a null pointer. Since calling this function does not make sense // if data == null, issue a GL error to notify user about it. err('GL_INVALID_VALUE in glGetBufferParameteriv(target=' + target + ', value=' + value + ', data=0): Function called with null out pointer!'); GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } HEAP32[((data)>>2)] = GLctx.getBufferParameter(target, value); } function _emscripten_glGetError() { var error = GLctx.getError() || GL.lastError; GL.lastError = 0/*GL_NO_ERROR*/; return error; } function readI53FromI64(ptr) { return HEAPU32[ptr>>2] + HEAP32[ptr+4>>2] * 4294967296; } function readI53FromU64(ptr) { return HEAPU32[ptr>>2] + HEAPU32[ptr+4>>2] * 4294967296; } function writeI53ToI64(ptr, num) { HEAPU32[ptr>>2] = num; HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296; var deserialized = (num >= 0) ? readI53FromU64(ptr) : readI53FromI64(ptr); if (deserialized != num) warnOnce('writeI53ToI64() out of range: serialized JS Number ' + num + ' to Wasm heap as bytes lo=0x' + HEAPU32[ptr>>2].toString(16) + ', hi=0x' + HEAPU32[ptr+4>>2].toString(16) + ', which deserializes back to ' + deserialized + ' instead!'); } function emscriptenWebGLGet(name_, p, type) { // Guard against user passing a null pointer. // Note that GLES2 spec does not say anything about how passing a null pointer should be treated. // Testing on desktop core GL 3, the application crashes on glGetIntegerv to a null pointer, but // better to report an error instead of doing anything random. if (!p) { err('GL_INVALID_VALUE in glGet' + type + 'v(name=' + name_ + ': Function called with null out pointer!'); GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } var ret = undefined; switch (name_) { // Handle a few trivial GLES values case 0x8DFA: // GL_SHADER_COMPILER ret = 1; break; case 0x8DF8: // GL_SHADER_BINARY_FORMATS if (type != 0 && type != 1) { GL.recordError(0x500); // GL_INVALID_ENUM err('GL_INVALID_ENUM in glGet' + type + 'v(GL_SHADER_BINARY_FORMATS): Invalid parameter type!'); } return; // Do not write anything to the out pointer, since no binary formats are supported. case 0x87FE: // GL_NUM_PROGRAM_BINARY_FORMATS case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS ret = 0; break; case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length), // so implement it ourselves to allow C++ GLES2 code get the length. var formats = GLctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/); ret = formats ? formats.length : 0; break; case 0x821D: // GL_NUM_EXTENSIONS if (GL.currentContext.version < 2) { GL.recordError(0x502 /* GL_INVALID_OPERATION */); // Calling GLES3/WebGL2 function with a GLES2/WebGL1 context return; } // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. var exts = GLctx.getSupportedExtensions() || []; ret = 2 * exts.length; // each extension is duplicated, first in unprefixed WebGL form, and then a second time with "GL_" prefix. break; case 0x821B: // GL_MAJOR_VERSION case 0x821C: // GL_MINOR_VERSION if (GL.currentContext.version < 2) { GL.recordError(0x500); // GL_INVALID_ENUM return; } ret = name_ == 0x821B ? 3 : 0; // return version 3.0 break; } if (ret === undefined) { var result = GLctx.getParameter(name_); switch (typeof result) { case "number": ret = result; break; case "boolean": ret = result ? 1 : 0; break; case "string": GL.recordError(0x500); // GL_INVALID_ENUM err('GL_INVALID_ENUM in glGet' + type + 'v(' + name_ + ') on a name which returns a string!'); return; case "object": if (result === null) { // null is a valid result for some (e.g., which buffer is bound - perhaps nothing is bound), but otherwise // can mean an invalid name_, which we need to report as an error switch (name_) { case 0x8894: // ARRAY_BUFFER_BINDING case 0x8B8D: // CURRENT_PROGRAM case 0x8895: // ELEMENT_ARRAY_BUFFER_BINDING case 0x8CA6: // FRAMEBUFFER_BINDING or DRAW_FRAMEBUFFER_BINDING case 0x8CA7: // RENDERBUFFER_BINDING case 0x8069: // TEXTURE_BINDING_2D case 0x85B5: // WebGL 2 GL_VERTEX_ARRAY_BINDING, or WebGL 1 extension OES_vertex_array_object GL_VERTEX_ARRAY_BINDING_OES case 0x8F36: // COPY_READ_BUFFER_BINDING or COPY_READ_BUFFER case 0x8F37: // COPY_WRITE_BUFFER_BINDING or COPY_WRITE_BUFFER case 0x88ED: // PIXEL_PACK_BUFFER_BINDING case 0x88EF: // PIXEL_UNPACK_BUFFER_BINDING case 0x8CAA: // READ_FRAMEBUFFER_BINDING case 0x8919: // SAMPLER_BINDING case 0x8C1D: // TEXTURE_BINDING_2D_ARRAY case 0x806A: // TEXTURE_BINDING_3D case 0x8E25: // TRANSFORM_FEEDBACK_BINDING case 0x8C8F: // TRANSFORM_FEEDBACK_BUFFER_BINDING case 0x8A28: // UNIFORM_BUFFER_BINDING case 0x8514: { // TEXTURE_BINDING_CUBE_MAP ret = 0; break; } default: { GL.recordError(0x500); // GL_INVALID_ENUM err('GL_INVALID_ENUM in glGet' + type + 'v(' + name_ + ') and it returns null!'); return; } } } else if (result instanceof Float32Array || result instanceof Uint32Array || result instanceof Int32Array || result instanceof Array) { for (var i = 0; i < result.length; ++i) { switch (type) { case 0: HEAP32[(((p)+(i*4))>>2)] = result[i]; break; case 2: HEAPF32[(((p)+(i*4))>>2)] = result[i]; break; case 4: HEAP8[(((p)+(i))>>0)] = result[i] ? 1 : 0; break; default: throw 'internal glGet error, bad type: ' + type; } } return; } else { try { ret = result.name | 0; } catch(e) { GL.recordError(0x500); // GL_INVALID_ENUM err('GL_INVALID_ENUM in glGet' + type + 'v: Unknown object returned from WebGL getParameter(' + name_ + ')! (error: ' + e + ')'); return; } } break; default: GL.recordError(0x500); // GL_INVALID_ENUM err('GL_INVALID_ENUM in glGet' + type + 'v: Native code calling glGet' + type + 'v(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); return; } } switch (type) { case 1: writeI53ToI64(p, ret); break; case 0: HEAP32[((p)>>2)] = ret; break; case 2: HEAPF32[((p)>>2)] = ret; break; case 4: HEAP8[((p)>>0)] = ret ? 1 : 0; break; default: throw 'internal glGet error, bad type: ' + type; } } function _emscripten_glGetFloatv(name_, p) { emscriptenWebGLGet(name_, p, 2); } function _emscripten_glGetFramebufferAttachmentParameteriv(target, attachment, pname, params) { var result = GLctx.getFramebufferAttachmentParameter(target, attachment, pname); if (result instanceof WebGLRenderbuffer || result instanceof WebGLTexture) { result = result.name | 0; } HEAP32[((params)>>2)] = result; } function _emscripten_glGetIntegerv(name_, p) { emscriptenWebGLGet(name_, p, 0); } function _emscripten_glGetProgramInfoLog(program, maxLength, length, infoLog) { GL.validateGLObjectID(GL.programs, program, 'glGetProgramInfoLog', 'program'); var log = GLctx.getProgramInfoLog(GL.programs[program]); if (log === null) log = '(unknown error)'; var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; } function _emscripten_glGetProgramiv(program, pname, p) { if (!p) { // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. err('GL_INVALID_VALUE in glGetProgramiv(program=' + program + ', pname=' + pname + ', p=0): Function called with null out pointer!'); GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } GL.validateGLObjectID(GL.programs, program, 'glGetProgramiv', 'program'); if (program >= GL.counter) { err('GL_INVALID_VALUE in glGetProgramiv(program=' + program + ', pname=' + pname + ', p=0x' + p.toString(16) + '): The specified program object name was not generated by GL!'); GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } program = GL.programs[program]; if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH var log = GLctx.getProgramInfoLog(program); if (log === null) log = '(unknown error)'; HEAP32[((p)>>2)] = log.length + 1; } else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) { if (!program.maxUniformLength) { for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) { program.maxUniformLength = Math.max(program.maxUniformLength, GLctx.getActiveUniform(program, i).name.length+1); } } HEAP32[((p)>>2)] = program.maxUniformLength; } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) { if (!program.maxAttributeLength) { for (var i = 0; i < GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); ++i) { program.maxAttributeLength = Math.max(program.maxAttributeLength, GLctx.getActiveAttrib(program, i).name.length+1); } } HEAP32[((p)>>2)] = program.maxAttributeLength; } else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) { if (!program.maxUniformBlockNameLength) { for (var i = 0; i < GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); ++i) { program.maxUniformBlockNameLength = Math.max(program.maxUniformBlockNameLength, GLctx.getActiveUniformBlockName(program, i).length+1); } } HEAP32[((p)>>2)] = program.maxUniformBlockNameLength; } else { HEAP32[((p)>>2)] = GLctx.getProgramParameter(program, pname); } } function _emscripten_glGetRenderbufferParameteriv(target, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if params == null, issue a GL error to notify user about it. err('GL_INVALID_VALUE in glGetRenderbufferParameteriv(target=' + target + ', pname=' + pname + ', params=0): Function called with null out pointer!'); GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } HEAP32[((params)>>2)] = GLctx.getRenderbufferParameter(target, pname); } function _emscripten_glGetShaderInfoLog(shader, maxLength, length, infoLog) { GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderInfoLog', 'shader'); var log = GLctx.getShaderInfoLog(GL.shaders[shader]); if (log === null) log = '(unknown error)'; var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; } function _emscripten_glGetShaderPrecisionFormat(shaderType, precisionType, range, precision) { var result = GLctx.getShaderPrecisionFormat(shaderType, precisionType); HEAP32[((range)>>2)] = result.rangeMin; HEAP32[(((range)+(4))>>2)] = result.rangeMax; HEAP32[((precision)>>2)] = result.precision; } function _emscripten_glGetShaderiv(shader, pname, p) { if (!p) { // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. err('GL_INVALID_VALUE in glGetShaderiv(shader=' + shader + ', pname=' + pname + ', p=0): Function called with null out pointer!'); GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } GL.validateGLObjectID(GL.shaders, shader, 'glGetShaderiv', 'shader'); if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH var log = GLctx.getShaderInfoLog(GL.shaders[shader]); if (log === null) log = '(unknown error)'; // The GLES2 specification says that if the shader has an empty info log, // a value of 0 is returned. Otherwise the log has a null char appended. // (An empty string is falsey, so we can just check that instead of // looking at log.length.) var logLength = log ? log.length + 1 : 0; HEAP32[((p)>>2)] = logLength; } else if (pname == 0x8B88) { // GL_SHADER_SOURCE_LENGTH var source = GLctx.getShaderSource(GL.shaders[shader]); // source may be a null, or the empty string, both of which are falsey // values that we report a 0 length for. var sourceLength = source ? source.length + 1 : 0; HEAP32[((p)>>2)] = sourceLength; } else { HEAP32[((p)>>2)] = GLctx.getShaderParameter(GL.shaders[shader], pname); } } function stringToNewUTF8(jsString) { var length = lengthBytesUTF8(jsString)+1; var cString = _malloc(length); stringToUTF8(jsString, cString, length); return cString; } function _emscripten_glGetString(name_) { var ret = GL.stringCache[name_]; if (!ret) { switch (name_) { case 0x1F03 /* GL_EXTENSIONS */: var exts = GLctx.getSupportedExtensions() || []; // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. exts = exts.concat(exts.map(function(e) { return "GL_" + e; })); ret = stringToNewUTF8(exts.join(' ')); break; case 0x1F00 /* GL_VENDOR */: case 0x1F01 /* GL_RENDERER */: case 0x9245 /* UNMASKED_VENDOR_WEBGL */: case 0x9246 /* UNMASKED_RENDERER_WEBGL */: var s = GLctx.getParameter(name_); if (!s) { GL.recordError(0x500/*GL_INVALID_ENUM*/); err('GL_INVALID_ENUM in glGetString: Received empty parameter for query name ' + name_ + '!'); // This occurs e.g. if one attempts GL_UNMASKED_VENDOR_WEBGL when it is not supported. } ret = s && stringToNewUTF8(s); break; case 0x1F02 /* GL_VERSION */: var glVersion = GLctx.getParameter(0x1F02 /*GL_VERSION*/); // return GLES version string corresponding to the version of the WebGL context if (GL.currentContext.version >= 2) glVersion = 'OpenGL ES 3.0 (' + glVersion + ')'; else { glVersion = 'OpenGL ES 2.0 (' + glVersion + ')'; } ret = stringToNewUTF8(glVersion); break; case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */: var glslVersion = GLctx.getParameter(0x8B8C /*GL_SHADING_LANGUAGE_VERSION*/); // extract the version number 'N.M' from the string 'WebGL GLSL ES N.M ...' var ver_re = /^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/; var ver_num = glslVersion.match(ver_re); if (ver_num !== null) { if (ver_num[1].length == 3) ver_num[1] = ver_num[1] + '0'; // ensure minor version has 2 digits glslVersion = 'OpenGL ES GLSL ES ' + ver_num[1] + ' (' + glslVersion + ')'; } ret = stringToNewUTF8(glslVersion); break; default: GL.recordError(0x500/*GL_INVALID_ENUM*/); err('GL_INVALID_ENUM in glGetString: Unknown parameter ' + name_ + '!'); // fall through } GL.stringCache[name_] = ret; } return ret; } function _emscripten_glGetStringi(name, index) { if (GL.currentContext.version < 2) { GL.recordError(0x502 /* GL_INVALID_OPERATION */); // Calling GLES3/WebGL2 function with a GLES2/WebGL1 context return 0; } var stringiCache = GL.stringiCache[name]; if (stringiCache) { if (index < 0 || index >= stringiCache.length) { GL.recordError(0x501/*GL_INVALID_VALUE*/); err('GL_INVALID_VALUE in glGetStringi: index out of range (' + index + ')!'); return 0; } return stringiCache[index]; } switch (name) { case 0x1F03 /* GL_EXTENSIONS */: var exts = GLctx.getSupportedExtensions() || []; // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. exts = exts.concat(exts.map(function(e) { return "GL_" + e; })); exts = exts.map(function(e) { return stringToNewUTF8(e); }); stringiCache = GL.stringiCache[name] = exts; if (index < 0 || index >= stringiCache.length) { GL.recordError(0x501/*GL_INVALID_VALUE*/); err('GL_INVALID_VALUE in glGetStringi: index out of range (' + index + ') in a call to GL_EXTENSIONS!'); return 0; } return stringiCache[index]; default: GL.recordError(0x500/*GL_INVALID_ENUM*/); err('GL_INVALID_ENUM in glGetStringi: Unknown parameter ' + name + '!'); return 0; } } /** @suppress {checkTypes} */ function jstoi_q(str) { return parseInt(str); } /** @noinline */ function webglGetLeftBracePos(name) { return name.slice(-1) == ']' && name.lastIndexOf('['); } function webglPrepareUniformLocationsBeforeFirstUse(program) { var uniformLocsById = program.uniformLocsById, // Maps GLuint -> WebGLUniformLocation uniformSizeAndIdsByName = program.uniformSizeAndIdsByName, // Maps name -> [uniform array length, GLuint] i, j; // On the first time invocation of glGetUniformLocation on this shader program: // initialize cache data structures and discover which uniforms are arrays. if (!uniformLocsById) { // maps GLint integer locations to WebGLUniformLocations program.uniformLocsById = uniformLocsById = {}; // maps integer locations back to uniform name strings, so that we can lazily fetch uniform array locations program.uniformArrayNamesById = {}; for (i = 0; i < GLctx.getProgramParameter(program, 0x8B86/*GL_ACTIVE_UNIFORMS*/); ++i) { var u = GLctx.getActiveUniform(program, i); var nm = u.name; var sz = u.size; var lb = webglGetLeftBracePos(nm); var arrayName = lb > 0 ? nm.slice(0, lb) : nm; // Assign a new location. var id = program.uniformIdCounter; program.uniformIdCounter += sz; // Eagerly get the location of the uniformArray[0] base element. // The remaining indices >0 will be left for lazy evaluation to // improve performance. Those may never be needed to fetch, if the // application fills arrays always in full starting from the first // element of the array. uniformSizeAndIdsByName[arrayName] = [sz, id]; // Store placeholder integers in place that highlight that these // >0 index locations are array indices pending population. for(j = 0; j < sz; ++j) { uniformLocsById[id] = j; program.uniformArrayNamesById[id++] = arrayName; } } } } function _emscripten_glGetUniformLocation(program, name) { GL.validateGLObjectID(GL.programs, program, 'glGetUniformLocation', 'program'); name = UTF8ToString(name); assert(!name.includes(' '), 'Uniform names passed to glGetUniformLocation() should not contain spaces! (received "' + name + '")'); if (program = GL.programs[program]) { webglPrepareUniformLocationsBeforeFirstUse(program); var uniformLocsById = program.uniformLocsById; // Maps GLuint -> WebGLUniformLocation var arrayIndex = 0; var uniformBaseName = name; // Invariant: when populating integer IDs for uniform locations, we must maintain the precondition that // arrays reside in contiguous addresses, i.e. for a 'vec4 colors[10];', colors[4] must be at location colors[0]+4. // However, user might call glGetUniformLocation(program, "colors") for an array, so we cannot discover based on the user // input arguments whether the uniform we are dealing with is an array. The only way to discover which uniforms are arrays // is to enumerate over all the active uniforms in the program. var leftBrace = webglGetLeftBracePos(name); // If user passed an array accessor "[index]", parse the array index off the accessor. if (leftBrace > 0) { assert(name.slice(leftBrace + 1).length == 1 || !isNaN(jstoi_q(name.slice(leftBrace + 1))), 'Malformed input parameter name "' + name + '" passed to glGetUniformLocation!'); arrayIndex = jstoi_q(name.slice(leftBrace + 1)) >>> 0; // "index]", coerce parseInt(']') with >>>0 to treat "foo[]" as "foo[0]" and foo[-1] as unsigned out-of-bounds. uniformBaseName = name.slice(0, leftBrace); } // Have we cached the location of this uniform before? var sizeAndId = program.uniformSizeAndIdsByName[uniformBaseName]; // A pair [array length, GLint of the uniform location] // If an uniform with this name exists, and if its index is within the array limits (if it's even an array), // query the WebGLlocation, or return an existing cached location. if (sizeAndId && arrayIndex < sizeAndId[0]) { arrayIndex += sizeAndId[1]; // Add the base location of the uniform to the array index offset. if ((uniformLocsById[arrayIndex] = uniformLocsById[arrayIndex] || GLctx.getUniformLocation(program, name))) { return arrayIndex; } } } else { // N.b. we are currently unable to distinguish between GL program IDs that never existed vs GL program IDs that have been deleted, // so report GL_INVALID_VALUE in both cases. GL.recordError(0x501 /* GL_INVALID_VALUE */); } return -1; } function _emscripten_glInvalidateFramebuffer(target, numAttachments, attachments) { assert(numAttachments < tempFixedLengthArray.length, 'Invalid count of numAttachments=' + numAttachments + ' passed to glInvalidateFramebuffer (that many attachment points do not exist in GL)'); var list = tempFixedLengthArray[numAttachments]; for (var i = 0; i < numAttachments; i++) { list[i] = HEAP32[(((attachments)+(i*4))>>2)]; } GLctx['invalidateFramebuffer'](target, list); } function _emscripten_glInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height) { assert(numAttachments < tempFixedLengthArray.length, 'Invalid count of numAttachments=' + numAttachments + ' passed to glInvalidateSubFramebuffer (that many attachment points do not exist in GL)'); var list = tempFixedLengthArray[numAttachments]; for (var i = 0; i < numAttachments; i++) { list[i] = HEAP32[(((attachments)+(i*4))>>2)]; } GLctx['invalidateSubFramebuffer'](target, list, x, y, width, height); } function _emscripten_glIsSync(sync) { return GLctx.isSync(GL.syncs[sync]); } function _emscripten_glIsTexture(id) { var texture = GL.textures[id]; if (!texture) return 0; return GLctx.isTexture(texture); } function _emscripten_glLineWidth(x0) { GLctx['lineWidth'](x0) } function _emscripten_glLinkProgram(program) { GL.validateGLObjectID(GL.programs, program, 'glLinkProgram', 'program'); program = GL.programs[program]; GLctx.linkProgram(program); // Invalidate earlier computed uniform->ID mappings, those have now become stale program.uniformLocsById = 0; // Mark as null-like so that glGetUniformLocation() knows to populate this again. program.uniformSizeAndIdsByName = {}; } function _emscripten_glMultiDrawArraysInstancedBaseInstanceWEBGL(mode, firsts, counts, instanceCounts, baseInstances, drawCount) { GLctx.mdibvbi['multiDrawArraysInstancedBaseInstanceWEBGL']( mode, HEAP32, firsts >> 2, HEAP32, counts >> 2, HEAP32, instanceCounts >> 2, HEAPU32, baseInstances >> 2, drawCount); } function _emscripten_glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(mode, counts, type, offsets, instanceCounts, baseVertices, baseInstances, drawCount) { GLctx.mdibvbi['multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL']( mode, HEAP32, counts >> 2, type, HEAP32, offsets >> 2, HEAP32, instanceCounts >> 2, HEAP32, baseVertices >> 2, HEAPU32, baseInstances >> 2, drawCount); } function _emscripten_glPixelStorei(pname, param) { if (pname == 0xCF5 /* GL_UNPACK_ALIGNMENT */) { GL.unpackAlignment = param; } GLctx.pixelStorei(pname, param); } function _emscripten_glReadBuffer(x0) { GLctx['readBuffer'](x0) } function computeUnpackAlignedImageSize(width, height, sizePerPixel, alignment) { function roundedToNextMultipleOf(x, y) { assert((y & (y-1)) === 0, 'Unpack alignment must be a power of 2! (Allowed values per WebGL spec are 1, 2, 4 or 8)'); return (x + y - 1) & -y; } var plainRowSize = width * sizePerPixel; var alignedRowSize = roundedToNextMultipleOf(plainRowSize, alignment); return height * alignedRowSize; } function __colorChannelsInGlTextureFormat(format) { // Micro-optimizations for size: map format to size by subtracting smallest enum value (0x1902) from all values first. // Also omit the most common size value (1) from the list, which is assumed by formats not on the list. var colorChannels = { // 0x1902 /* GL_DEPTH_COMPONENT */ - 0x1902: 1, // 0x1906 /* GL_ALPHA */ - 0x1902: 1, 5: 3, 6: 4, // 0x1909 /* GL_LUMINANCE */ - 0x1902: 1, 8: 2, 29502: 3, 29504: 4, // 0x1903 /* GL_RED */ - 0x1902: 1, 26917: 2, 26918: 2, // 0x8D94 /* GL_RED_INTEGER */ - 0x1902: 1, 29846: 3, 29847: 4 }; if (!colorChannels[format - 0x1902] && format != 0x1902 /* GL_DEPTH_COMPONENT */ && format != 0x1906 /* GL_ALPHA */ && format != 0x1909 /* GL_LUMINANCE */ && format != 0x1903 /* GL_RED */ && format != 0x8D94 /* GL_RED_INTEGER */) { err('Invalid format=0x' + format.toString(16) + ' passed to function _colorChannelsInGlTextureFormat()!'); } return colorChannels[format - 0x1902]||1; } function heapObjectForWebGLType(type) { // Micro-optimization for size: Subtract lowest GL enum number (0x1400/* GL_BYTE */) from type to compare // smaller values for the heap, for shorter generated code size. // Also the type HEAPU16 is not tested for explicitly, but any unrecognized type will return out HEAPU16. // (since most types are HEAPU16) type -= 0x1400; if (type == 0) return HEAP8; if (type == 1) return HEAPU8; if (type == 2) return HEAP16; if (type == 4) return HEAP32; if (type == 6) return HEAPF32; if (type == 5 || type == 28922 || type == 28520 || type == 30779 || type == 30782 ) return HEAPU32; if (type != 3 && type != 11 && type != 27699 && type != 27700 && type != 28515 && type != 31073) { err('Invalid WebGL type 0x' + (type+0x1400).toString() + ' passed to $heapObjectForWebGLType!'); } return HEAPU16; } function heapAccessShiftForWebGLHeap(heap) { return 31 - Math.clz32(heap.BYTES_PER_ELEMENT); } function emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) { var heap = heapObjectForWebGLType(type); var shift = heapAccessShiftForWebGLHeap(heap); var byteSize = 1<<shift; var sizePerPixel = __colorChannelsInGlTextureFormat(format) * byteSize; var bytes = computeUnpackAlignedImageSize(width, height, sizePerPixel, GL.unpackAlignment); assert((pixels >> shift) << shift == pixels, 'Pointer to texture data passed to texture get function must be aligned to the byte size of the pixel type!'); return heap.subarray(pixels >> shift, pixels + bytes >> shift); } function _emscripten_glReadPixels(x, y, width, height, format, type, pixels) { if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelPackBufferBinding) { GLctx.readPixels(x, y, width, height, format, type, pixels); } else { var heap = heapObjectForWebGLType(type); GLctx.readPixels(x, y, width, height, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); } return; } var pixelData = emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, format); if (!pixelData) { GL.recordError(0x500/*GL_INVALID_ENUM*/); err('GL_INVALID_ENUM in glReadPixels: Unrecognized combination of type=' + type + ' and format=' + format + '!'); return; } GLctx.readPixels(x, y, width, height, format, type, pixelData); } function _emscripten_glRenderbufferStorage(x0, x1, x2, x3) { GLctx['renderbufferStorage'](x0, x1, x2, x3) } function _emscripten_glRenderbufferStorageMultisample(x0, x1, x2, x3, x4) { GLctx['renderbufferStorageMultisample'](x0, x1, x2, x3, x4) } function _emscripten_glSamplerParameterf(sampler, pname, param) { GL.validateGLObjectID(GL.samplers, sampler, 'glBindSampler', 'sampler'); GLctx['samplerParameterf'](GL.samplers[sampler], pname, param); } function _emscripten_glSamplerParameteri(sampler, pname, param) { GL.validateGLObjectID(GL.samplers, sampler, 'glBindSampler', 'sampler'); GLctx['samplerParameteri'](GL.samplers[sampler], pname, param); } function _emscripten_glSamplerParameteriv(sampler, pname, params) { GL.validateGLObjectID(GL.samplers, sampler, 'glBindSampler', 'sampler'); var param = HEAP32[((params)>>2)]; GLctx['samplerParameteri'](GL.samplers[sampler], pname, param); } function _emscripten_glScissor(x0, x1, x2, x3) { GLctx['scissor'](x0, x1, x2, x3) } function _emscripten_glShaderSource(shader, count, string, length) { GL.validateGLObjectID(GL.shaders, shader, 'glShaderSource', 'shader'); var source = GL.getSource(shader, count, string, length); GLctx.shaderSource(GL.shaders[shader], source); } function _emscripten_glStencilFunc(x0, x1, x2) { GLctx['stencilFunc'](x0, x1, x2) } function _emscripten_glStencilFuncSeparate(x0, x1, x2, x3) { GLctx['stencilFuncSeparate'](x0, x1, x2, x3) } function _emscripten_glStencilMask(x0) { GLctx['stencilMask'](x0) } function _emscripten_glStencilMaskSeparate(x0, x1) { GLctx['stencilMaskSeparate'](x0, x1) } function _emscripten_glStencilOp(x0, x1, x2) { GLctx['stencilOp'](x0, x1, x2) } function _emscripten_glStencilOpSeparate(x0, x1, x2, x3) { GLctx['stencilOpSeparate'](x0, x1, x2, x3) } function _emscripten_glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels) { if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelUnpackBufferBinding) { GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels); } else if (pixels) { var heap = heapObjectForWebGLType(type); GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); } else { GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, null); } return; } GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) : null); } function _emscripten_glTexParameterf(x0, x1, x2) { GLctx['texParameterf'](x0, x1, x2) } function _emscripten_glTexParameterfv(target, pname, params) { var param = HEAPF32[((params)>>2)]; GLctx.texParameterf(target, pname, param); } function _emscripten_glTexParameteri(x0, x1, x2) { GLctx['texParameteri'](x0, x1, x2) } function _emscripten_glTexParameteriv(target, pname, params) { var param = HEAP32[((params)>>2)]; GLctx.texParameteri(target, pname, param); } function _emscripten_glTexStorage2D(x0, x1, x2, x3, x4) { GLctx['texStorage2D'](x0, x1, x2, x3, x4) } function _emscripten_glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels) { if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. if (GLctx.currentPixelUnpackBufferBinding) { GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); } else if (pixels) { var heap = heapObjectForWebGLType(type); GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, heap, pixels >> heapAccessShiftForWebGLHeap(heap)); } else { GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, null); } return; } var pixelData = null; if (pixels) pixelData = emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, 0); GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixelData); } function webglGetUniformLocation(location) { var p = GLctx.currentProgram; if (p) { var webglLoc = p.uniformLocsById[location]; // p.uniformLocsById[location] stores either an integer, or a WebGLUniformLocation. // If an integer, we have not yet bound the location, so do it now. The integer value specifies the array index // we should bind to. if (typeof webglLoc == 'number') { p.uniformLocsById[location] = webglLoc = GLctx.getUniformLocation(p, p.uniformArrayNamesById[location] + (webglLoc > 0 ? '[' + webglLoc + ']' : '')); } // Else an already cached WebGLUniformLocation, return it. return webglLoc; } else { GL.recordError(0x502/*GL_INVALID_OPERATION*/); } } function _emscripten_glUniform1f(location, v0) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform1f', 'location'); GLctx.uniform1f(webglGetUniformLocation(location), v0); } var miniTempWebGLFloatBuffers = []; function _emscripten_glUniform1fv(location, count, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform1fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniform1fv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniform1fv(webglGetUniformLocation(location), HEAPF32, value>>2, count); return; } if (count <= 288) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[count-1]; for (var i = 0; i < count; ++i) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*4)>>2); } GLctx.uniform1fv(webglGetUniformLocation(location), view); } function _emscripten_glUniform1i(location, v0) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform1i', 'location'); GLctx.uniform1i(webglGetUniformLocation(location), v0); } var __miniTempWebGLIntBuffers = []; function _emscripten_glUniform1iv(location, count, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform1iv', 'location'); assert((value & 3) == 0, 'Pointer to integer data passed to glUniform1iv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniform1iv(webglGetUniformLocation(location), HEAP32, value>>2, count); return; } if (count <= 288) { // avoid allocation when uploading few enough uniforms var view = __miniTempWebGLIntBuffers[count-1]; for (var i = 0; i < count; ++i) { view[i] = HEAP32[(((value)+(4*i))>>2)]; } } else { var view = HEAP32.subarray((value)>>2, (value+count*4)>>2); } GLctx.uniform1iv(webglGetUniformLocation(location), view); } function _emscripten_glUniform2f(location, v0, v1) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform2f', 'location'); GLctx.uniform2f(webglGetUniformLocation(location), v0, v1); } function _emscripten_glUniform2fv(location, count, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform2fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniform2fv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniform2fv(webglGetUniformLocation(location), HEAPF32, value>>2, count*2); return; } if (count <= 144) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[2*count-1]; for (var i = 0; i < 2*count; i += 2) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*8)>>2); } GLctx.uniform2fv(webglGetUniformLocation(location), view); } function _emscripten_glUniform2i(location, v0, v1) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform2i', 'location'); GLctx.uniform2i(webglGetUniformLocation(location), v0, v1); } function _emscripten_glUniform2iv(location, count, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform2iv', 'location'); assert((value & 3) == 0, 'Pointer to integer data passed to glUniform2iv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniform2iv(webglGetUniformLocation(location), HEAP32, value>>2, count*2); return; } if (count <= 144) { // avoid allocation when uploading few enough uniforms var view = __miniTempWebGLIntBuffers[2*count-1]; for (var i = 0; i < 2*count; i += 2) { view[i] = HEAP32[(((value)+(4*i))>>2)]; view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; } } else { var view = HEAP32.subarray((value)>>2, (value+count*8)>>2); } GLctx.uniform2iv(webglGetUniformLocation(location), view); } function _emscripten_glUniform3f(location, v0, v1, v2) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform3f', 'location'); GLctx.uniform3f(webglGetUniformLocation(location), v0, v1, v2); } function _emscripten_glUniform3fv(location, count, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform3fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniform3fv must be aligned to four bytes!' + value); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniform3fv(webglGetUniformLocation(location), HEAPF32, value>>2, count*3); return; } if (count <= 96) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[3*count-1]; for (var i = 0; i < 3*count; i += 3) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*12)>>2); } GLctx.uniform3fv(webglGetUniformLocation(location), view); } function _emscripten_glUniform3i(location, v0, v1, v2) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform3i', 'location'); GLctx.uniform3i(webglGetUniformLocation(location), v0, v1, v2); } function _emscripten_glUniform3iv(location, count, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform3iv', 'location'); assert((value & 3) == 0, 'Pointer to integer data passed to glUniform3iv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniform3iv(webglGetUniformLocation(location), HEAP32, value>>2, count*3); return; } if (count <= 96) { // avoid allocation when uploading few enough uniforms var view = __miniTempWebGLIntBuffers[3*count-1]; for (var i = 0; i < 3*count; i += 3) { view[i] = HEAP32[(((value)+(4*i))>>2)]; view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAP32[(((value)+(4*i+8))>>2)]; } } else { var view = HEAP32.subarray((value)>>2, (value+count*12)>>2); } GLctx.uniform3iv(webglGetUniformLocation(location), view); } function _emscripten_glUniform4f(location, v0, v1, v2, v3) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform4f', 'location'); GLctx.uniform4f(webglGetUniformLocation(location), v0, v1, v2, v3); } function _emscripten_glUniform4fv(location, count, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform4fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniform4fv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniform4fv(webglGetUniformLocation(location), HEAPF32, value>>2, count*4); return; } if (count <= 72) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[4*count-1]; // hoist the heap out of the loop for size and for pthreads+growth. var heap = HEAPF32; value >>= 2; for (var i = 0; i < 4 * count; i += 4) { var dst = value + i; view[i] = heap[dst]; view[i + 1] = heap[dst + 1]; view[i + 2] = heap[dst + 2]; view[i + 3] = heap[dst + 3]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*16)>>2); } GLctx.uniform4fv(webglGetUniformLocation(location), view); } function _emscripten_glUniform4i(location, v0, v1, v2, v3) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform4i', 'location'); GLctx.uniform4i(webglGetUniformLocation(location), v0, v1, v2, v3); } function _emscripten_glUniform4iv(location, count, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniform4iv', 'location'); assert((value & 3) == 0, 'Pointer to integer data passed to glUniform4iv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniform4iv(webglGetUniformLocation(location), HEAP32, value>>2, count*4); return; } if (count <= 72) { // avoid allocation when uploading few enough uniforms var view = __miniTempWebGLIntBuffers[4*count-1]; for (var i = 0; i < 4*count; i += 4) { view[i] = HEAP32[(((value)+(4*i))>>2)]; view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAP32[(((value)+(4*i+8))>>2)]; view[i+3] = HEAP32[(((value)+(4*i+12))>>2)]; } } else { var view = HEAP32.subarray((value)>>2, (value+count*16)>>2); } GLctx.uniform4iv(webglGetUniformLocation(location), view); } function _emscripten_glUniformMatrix2fv(location, count, transpose, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix2fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix2fv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniformMatrix2fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*4); return; } if (count <= 72) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[4*count-1]; for (var i = 0; i < 4*count; i += 4) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*16)>>2); } GLctx.uniformMatrix2fv(webglGetUniformLocation(location), !!transpose, view); } function _emscripten_glUniformMatrix3fv(location, count, transpose, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix3fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix3fv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniformMatrix3fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*9); return; } if (count <= 32) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[9*count-1]; for (var i = 0; i < 9*count; i += 9) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)]; view[i+4] = HEAPF32[(((value)+(4*i+16))>>2)]; view[i+5] = HEAPF32[(((value)+(4*i+20))>>2)]; view[i+6] = HEAPF32[(((value)+(4*i+24))>>2)]; view[i+7] = HEAPF32[(((value)+(4*i+28))>>2)]; view[i+8] = HEAPF32[(((value)+(4*i+32))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*36)>>2); } GLctx.uniformMatrix3fv(webglGetUniformLocation(location), !!transpose, view); } function _emscripten_glUniformMatrix4fv(location, count, transpose, value) { GL.validateGLObjectID(GLctx.currentProgram.uniformLocsById, location, 'glUniformMatrix4fv', 'location'); assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix4fv must be aligned to four bytes!'); if (GL.currentContext.version >= 2) { // WebGL 2 provides new garbage-free entry points to call to WebGL. Use those always when possible. count && GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, HEAPF32, value>>2, count*16); return; } if (count <= 18) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[16*count-1]; // hoist the heap out of the loop for size and for pthreads+growth. var heap = HEAPF32; value >>= 2; for (var i = 0; i < 16 * count; i += 16) { var dst = value + i; view[i] = heap[dst]; view[i + 1] = heap[dst + 1]; view[i + 2] = heap[dst + 2]; view[i + 3] = heap[dst + 3]; view[i + 4] = heap[dst + 4]; view[i + 5] = heap[dst + 5]; view[i + 6] = heap[dst + 6]; view[i + 7] = heap[dst + 7]; view[i + 8] = heap[dst + 8]; view[i + 9] = heap[dst + 9]; view[i + 10] = heap[dst + 10]; view[i + 11] = heap[dst + 11]; view[i + 12] = heap[dst + 12]; view[i + 13] = heap[dst + 13]; view[i + 14] = heap[dst + 14]; view[i + 15] = heap[dst + 15]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*64)>>2); } GLctx.uniformMatrix4fv(webglGetUniformLocation(location), !!transpose, view); } function _emscripten_glUseProgram(program) { GL.validateGLObjectID(GL.programs, program, 'glUseProgram', 'program'); program = GL.programs[program]; GLctx.useProgram(program); // Record the currently active program so that we can access the uniform // mapping table of that program. GLctx.currentProgram = program; } function _emscripten_glVertexAttrib1f(x0, x1) { GLctx['vertexAttrib1f'](x0, x1) } function _emscripten_glVertexAttrib2fv(index, v) { assert((v & 3) == 0, 'Pointer to float data passed to glVertexAttrib2fv must be aligned to four bytes!'); assert(v != 0, 'Null pointer passed to glVertexAttrib2fv!'); GLctx.vertexAttrib2f(index, HEAPF32[v>>2], HEAPF32[v+4>>2]); } function _emscripten_glVertexAttrib3fv(index, v) { assert((v & 3) == 0, 'Pointer to float data passed to glVertexAttrib3fv must be aligned to four bytes!'); assert(v != 0, 'Null pointer passed to glVertexAttrib3fv!'); GLctx.vertexAttrib3f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2]); } function _emscripten_glVertexAttrib4fv(index, v) { assert((v & 3) == 0, 'Pointer to float data passed to glVertexAttrib4fv must be aligned to four bytes!'); assert(v != 0, 'Null pointer passed to glVertexAttrib4fv!'); GLctx.vertexAttrib4f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2], HEAPF32[v+12>>2]); } function _emscripten_glVertexAttribDivisor(index, divisor) { assert(GLctx['vertexAttribDivisor'], 'Must have ANGLE_instanced_arrays extension or WebGL 2 to use WebGL instancing'); GLctx['vertexAttribDivisor'](index, divisor); } function _emscripten_glVertexAttribIPointer(index, size, type, stride, ptr) { GL.validateVertexAttribPointer(size, type, stride, ptr); GLctx['vertexAttribIPointer'](index, size, type, stride, ptr); } function _emscripten_glVertexAttribPointer(index, size, type, normalized, stride, ptr) { GL.validateVertexAttribPointer(size, type, stride, ptr); GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); } function _emscripten_glViewport(x0, x1, x2, x3) { GLctx['viewport'](x0, x1, x2, x3) } function _emscripten_glWaitSync(sync, flags, timeoutLo, timeoutHi) { // See WebGL2 vs GLES3 difference on GL_TIMEOUT_IGNORED above (https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.15) GLctx.waitSync(GL.syncs[sync], flags, convertI32PairToI53(timeoutLo, timeoutHi)); } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num); } function getHeapMax() { // Stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate // full 4GB Wasm memories, the size will wrap back to 0 bytes in Wasm side // for any code that deals with heap sizes, which would require special // casing all heap size related code to treat 0 specially. return 2147483648; } function emscripten_realloc_buffer(size) { try { // round size grow request up to wasm page size (fixed 64KB per spec) wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16); // .grow() takes a delta compared to the previous size updateGlobalBufferAndViews(wasmMemory.buffer); return 1 /*success*/; } catch(e) { err('emscripten_realloc_buffer: Attempted to grow heap from ' + buffer.byteLength + ' bytes to ' + size + ' bytes, but got error: ' + e); } // implicit 0 return to save code size (caller will cast "undefined" into 0 // anyhow) } function _emscripten_resize_heap(requestedSize) { var oldSize = HEAPU8.length; requestedSize = requestedSize >>> 0; // With multithreaded builds, races can happen (another thread might increase the size // in between), so return a failure, and let the caller retry. assert(requestedSize > oldSize); // Memory resize rules: // 1. Always increase heap size to at least the requested size, rounded up // to next page multiple. // 2a. If MEMORY_GROWTH_LINEAR_STEP == -1, excessively resize the heap // geometrically: increase the heap size according to // MEMORY_GROWTH_GEOMETRIC_STEP factor (default +20%), At most // overreserve by MEMORY_GROWTH_GEOMETRIC_CAP bytes (default 96MB). // 2b. If MEMORY_GROWTH_LINEAR_STEP != -1, excessively resize the heap // linearly: increase the heap size by at least // MEMORY_GROWTH_LINEAR_STEP bytes. // 3. Max size for the heap is capped at 2048MB-WASM_PAGE_SIZE, or by // MAXIMUM_MEMORY, or by ASAN limit, depending on which is smallest // 4. If we were unable to allocate as much memory, it may be due to // over-eager decision to excessively reserve due to (3) above. // Hence if an allocation fails, cut down on the amount of excess // growth, in an attempt to succeed to perform a smaller allocation. // A limit is set for how much we can grow. We should not exceed that // (the wasm binary specifies it, so if we tried, we'd fail anyhow). var maxHeapSize = getHeapMax(); if (requestedSize > maxHeapSize) { err('Cannot enlarge memory, asked to go up to ' + requestedSize + ' bytes, but the limit is ' + maxHeapSize + ' bytes!'); return false; } let alignUp = (x, multiple) => x + (multiple - x % multiple) % multiple; // Loop through potential heap size increases. If we attempt a too eager // reservation that fails, cut down on the attempted size and reserve a // smaller bump instead. (max 3 times, chosen somewhat arbitrarily) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize + 134217728 / cutDown; // ensure linear growth var newSize = Math.min(maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536)); var replacement = emscripten_realloc_buffer(newSize); if (replacement) { return true; } } err('Failed to grow the heap from ' + oldSize + ' bytes to ' + newSize + ' bytes, not enough memory!'); return false; } function _emscripten_webgl_do_get_current_context() { return GL.currentContext ? GL.currentContext.handle : 0; } function _emscripten_webgl_get_current_context( ) { return _emscripten_webgl_do_get_current_context(); } var ENV = {}; function getExecutableName() { return thisProgram || './this.program'; } function getEnvStrings() { if (!getEnvStrings.strings) { // Default values. // Browser language detection #8751 var lang = ((typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C').replace('-', '_') + '.UTF-8'; var env = { /* 'USER': 'web_user', 'LOGNAME': 'web_user', 'PATH': '/', 'PWD': '/', 'HOME': '/home/web_user', */ //zhibin:remove for security leak. 'LANG': lang, '_': getExecutableName() }; // Apply the user-provided values, if any. for (var x in ENV) { // x is a key in ENV; if ENV[x] is undefined, that means it was // explicitly set to be so. We allow user code to do that to // force variables with default values to remain unset. if (ENV[x] === undefined) delete env[x]; else env[x] = ENV[x]; } var strings = []; for (var x in env) { strings.push(x + '=' + env[x]); } getEnvStrings.strings = strings; } return getEnvStrings.strings; } function _environ_get(__environ, environ_buf) { var bufSize = 0; getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize; HEAPU32[(((__environ)+(i*4))>>2)] = ptr; writeAsciiToMemory(string, ptr); bufSize += string.length + 1; }); return 0; } function _environ_sizes_get(penviron_count, penviron_buf_size) { var strings = getEnvStrings(); HEAPU32[((penviron_count)>>2)] = strings.length; var bufSize = 0; strings.forEach(function(string) { bufSize += string.length + 1; }); HEAPU32[((penviron_buf_size)>>2)] = bufSize; return 0; } function _exit(status) { // void _exit(int status); // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html exit(status); } function _fd_close(fd) { abort('fd_close called without SYSCALLS_REQUIRE_FILESYSTEM'); } function convertI32PairToI53Checked(lo, hi) { assert(lo == (lo >>> 0) || lo == (lo|0)); // lo should either be a i32 or a u32 assert(hi === (hi|0)); // hi should be a i32 return ((hi + 0x200000) >>> 0 < 0x400001 - !!lo) ? (lo >>> 0) + hi * 4294967296 : NaN; } function _fd_pread(fd, iov, iovcnt, offset_low, offset_high, pnum) { abort('fd_pread called without SYSCALLS_REQUIRE_FILESYSTEM'); } function _fd_read(fd, iov, iovcnt, pnum) { abort('fd_read called without SYSCALLS_REQUIRE_FILESYSTEM'); } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { return 70; } var printCharBuffers = [null,[],[]]; function printChar(stream, curr) { var buffer = printCharBuffers[stream]; assert(buffer); if (curr === 0 || curr === 10) { (stream === 1 ? out : err)(UTF8ArrayToString(buffer, 0)); buffer.length = 0; } else { buffer.push(curr); } } function flush_NO_FILESYSTEM() { // flush anything remaining in the buffers during shutdown _fflush(0); if (printCharBuffers[1].length) printChar(1, 10); if (printCharBuffers[2].length) printChar(2, 10); } function _fd_write(fd, iov, iovcnt, pnum) { // hack to support printf in SYSCALLS_REQUIRE_FILESYSTEM=0 var num = 0; for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[((iov)>>2)]; var len = HEAPU32[(((iov)+(4))>>2)]; iov += 8; for (var j = 0; j < len; j++) { printChar(fd, HEAPU8[ptr+j]); } num += len; } HEAPU32[((pnum)>>2)] = num; return 0; } function _getTempRet0() { return getTempRet0(); } function _setTempRet0(val) { setTempRet0(val); } function __isLeapYear(year) { return year%4 === 0 && (year%100 !== 0 || year%400 === 0); } function __arraySum(array, index) { var sum = 0; for (var i = 0; i <= index; sum += array[i++]) { // no-op } return sum; } var __MONTH_DAYS_LEAP = [31,29,31,30,31,30,31,31,30,31,30,31]; var __MONTH_DAYS_REGULAR = [31,28,31,30,31,30,31,31,30,31,30,31]; function __addDays(date, days) { var newDate = new Date(date.getTime()); while (days > 0) { var leap = __isLeapYear(newDate.getFullYear()); var currentMonth = newDate.getMonth(); var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth]; if (days > daysInCurrentMonth-newDate.getDate()) { // we spill over to next month days -= (daysInCurrentMonth-newDate.getDate()+1); newDate.setDate(1); if (currentMonth < 11) { newDate.setMonth(currentMonth+1) } else { newDate.setMonth(0); newDate.setFullYear(newDate.getFullYear()+1); } } else { // we stay in current month newDate.setDate(newDate.getDate()+days); return newDate; } } return newDate; } function _strftime(s, maxsize, format, tm) { // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html var tm_zone = HEAP32[(((tm)+(40))>>2)]; var date = { tm_sec: HEAP32[((tm)>>2)], tm_min: HEAP32[(((tm)+(4))>>2)], tm_hour: HEAP32[(((tm)+(8))>>2)], tm_mday: HEAP32[(((tm)+(12))>>2)], tm_mon: HEAP32[(((tm)+(16))>>2)], tm_year: HEAP32[(((tm)+(20))>>2)], tm_wday: HEAP32[(((tm)+(24))>>2)], tm_yday: HEAP32[(((tm)+(28))>>2)], tm_isdst: HEAP32[(((tm)+(32))>>2)], tm_gmtoff: HEAP32[(((tm)+(36))>>2)], tm_zone: tm_zone ? UTF8ToString(tm_zone) : '' }; var pattern = UTF8ToString(format); // expand format var EXPANSION_RULES_1 = { '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013 '%D': '%m/%d/%y', // Equivalent to %m / %d / %y '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d '%h': '%b', // Equivalent to %b '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation '%R': '%H:%M', // Replaced by the time in 24-hour notation '%T': '%H:%M:%S', // Replaced by the time '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation '%X': '%H:%M:%S', // Replaced by the locale's appropriate time representation // Modified Conversion Specifiers '%Ec': '%c', // Replaced by the locale's alternative appropriate date and time representation. '%EC': '%C', // Replaced by the name of the base year (period) in the locale's alternative representation. '%Ex': '%m/%d/%y', // Replaced by the locale's alternative date representation. '%EX': '%H:%M:%S', // Replaced by the locale's alternative time representation. '%Ey': '%y', // Replaced by the offset from %EC (year only) in the locale's alternative representation. '%EY': '%Y', // Replaced by the full alternative year representation. '%Od': '%d', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading zeros if there is any alternative symbol for zero; otherwise, with leading <space> characters. '%Oe': '%e', // Replaced by the day of the month, using the locale's alternative numeric symbols, filled as needed with leading <space> characters. '%OH': '%H', // Replaced by the hour (24-hour clock) using the locale's alternative numeric symbols. '%OI': '%I', // Replaced by the hour (12-hour clock) using the locale's alternative numeric symbols. '%Om': '%m', // Replaced by the month using the locale's alternative numeric symbols. '%OM': '%M', // Replaced by the minutes using the locale's alternative numeric symbols. '%OS': '%S', // Replaced by the seconds using the locale's alternative numeric symbols. '%Ou': '%u', // Replaced by the weekday as a number in the locale's alternative representation (Monday=1). '%OU': '%U', // Replaced by the week number of the year (Sunday as the first day of the week, rules corresponding to %U ) using the locale's alternative numeric symbols. '%OV': '%V', // Replaced by the week number of the year (Monday as the first day of the week, rules corresponding to %V ) using the locale's alternative numeric symbols. '%Ow': '%w', // Replaced by the number of the weekday (Sunday=0) using the locale's alternative numeric symbols. '%OW': '%W', // Replaced by the week number of the year (Monday as the first day of the week) using the locale's alternative numeric symbols. '%Oy': '%y', // Replaced by the year (offset from %C ) using the locale's alternative numeric symbols. }; for (var rule in EXPANSION_RULES_1) { pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]); } var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; function leadingSomething(value, digits, character) { var str = typeof value == 'number' ? value.toString() : (value || ''); while (str.length < digits) { str = character[0]+str; } return str; } function leadingNulls(value, digits) { return leadingSomething(value, digits, '0'); } function compareByDay(date1, date2) { function sgn(value) { return value < 0 ? -1 : (value > 0 ? 1 : 0); } var compare; if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) { if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) { compare = sgn(date1.getDate()-date2.getDate()); } } return compare; } function getFirstWeekStartDate(janFourth) { switch (janFourth.getDay()) { case 0: // Sunday return new Date(janFourth.getFullYear()-1, 11, 29); case 1: // Monday return janFourth; case 2: // Tuesday return new Date(janFourth.getFullYear(), 0, 3); case 3: // Wednesday return new Date(janFourth.getFullYear(), 0, 2); case 4: // Thursday return new Date(janFourth.getFullYear(), 0, 1); case 5: // Friday return new Date(janFourth.getFullYear()-1, 11, 31); case 6: // Saturday return new Date(janFourth.getFullYear()-1, 11, 30); } } function getWeekBasedYear(date) { var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4); var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4); var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { // this date is after the start of the first week of this year if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { return thisDate.getFullYear()+1; } else { return thisDate.getFullYear(); } } else { return thisDate.getFullYear()-1; } } var EXPANSION_RULES_2 = { '%a': function(date) { return WEEKDAYS[date.tm_wday].substring(0,3); }, '%A': function(date) { return WEEKDAYS[date.tm_wday]; }, '%b': function(date) { return MONTHS[date.tm_mon].substring(0,3); }, '%B': function(date) { return MONTHS[date.tm_mon]; }, '%C': function(date) { var year = date.tm_year+1900; return leadingNulls((year/100)|0,2); }, '%d': function(date) { return leadingNulls(date.tm_mday, 2); }, '%e': function(date) { return leadingSomething(date.tm_mday, 2, ' '); }, '%g': function(date) { // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. // In this system, weeks begin on a Monday and week 1 of the year is the week that includes // January 4th, which is also the week that includes the first Thursday of the year, and // is also the first week that contains at least four days in the year. // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of // the last week of the preceding year; thus, for Saturday 2nd January 1999, // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, // or 31st is a Monday, it and any following days are part of week 1 of the following year. // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01. return getWeekBasedYear(date).toString().substring(2); }, '%G': function(date) { return getWeekBasedYear(date); }, '%H': function(date) { return leadingNulls(date.tm_hour, 2); }, '%I': function(date) { var twelveHour = date.tm_hour; if (twelveHour == 0) twelveHour = 12; else if (twelveHour > 12) twelveHour -= 12; return leadingNulls(twelveHour, 2); }, '%j': function(date) { // Day of the year (001-366) return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon-1), 3); }, '%m': function(date) { return leadingNulls(date.tm_mon+1, 2); }, '%M': function(date) { return leadingNulls(date.tm_min, 2); }, '%n': function() { return '\n'; }, '%p': function(date) { if (date.tm_hour >= 0 && date.tm_hour < 12) { return 'AM'; } else { return 'PM'; } }, '%S': function(date) { return leadingNulls(date.tm_sec, 2); }, '%t': function() { return '\t'; }, '%u': function(date) { return date.tm_wday || 7; }, '%U': function(date) { var days = date.tm_yday + 7 - date.tm_wday; return leadingNulls(Math.floor(days / 7), 2); }, '%V': function(date) { // Replaced by the week number of the year (Monday as the first day of the week) // as a decimal number [01,53]. If the week containing 1 January has four // or more days in the new year, then it is considered week 1. // Otherwise, it is the last week of the previous year, and the next week is week 1. // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] var val = Math.floor((date.tm_yday + 7 - (date.tm_wday + 6) % 7 ) / 7); // If 1 Jan is just 1-3 days past Monday, the previous week // is also in this year. if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { val++; } if (!val) { val = 52; // If 31 December of prev year a Thursday, or Friday of a // leap year, then the prev year has 53 weeks. var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7; if (dec31 == 4 || (dec31 == 5 && __isLeapYear(date.tm_year%400-1))) { val++; } } else if (val == 53) { // If 1 January is not a Thursday, and not a Wednesday of a // leap year, then this year has only 52 weeks. var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7; if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year))) val = 1; } return leadingNulls(val, 2); }, '%w': function(date) { return date.tm_wday; }, '%W': function(date) { var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7); return leadingNulls(Math.floor(days / 7), 2); }, '%y': function(date) { // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] return (date.tm_year+1900).toString().substring(2); }, '%Y': function(date) { // Replaced by the year as a decimal number (for example, 1997). [ tm_year] return date.tm_year+1900; }, '%z': function(date) { // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ). // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). var off = date.tm_gmtoff; var ahead = off >= 0; off = Math.abs(off) / 60; // convert from minutes into hhmm format (which means 60 minutes = 100 units) off = (off / 60)*100 + (off % 60); return (ahead ? '+' : '-') + String("0000" + off).slice(-4); }, '%Z': function(date) { return date.tm_zone; }, '%%': function() { return '%'; } }; // Replace %% with a pair of NULLs (which cannot occur in a C string), then // re-inject them after processing. pattern = pattern.replace(/%%/g, '\0\0') for (var rule in EXPANSION_RULES_2) { if (pattern.includes(rule)) { pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date)); } } pattern = pattern.replace(/\0\0/g, '%') var bytes = intArrayFromString(pattern, false); if (bytes.length > maxsize) { return 0; } writeArrayToMemory(bytes, s); return bytes.length-1; } function _strftime_l(s, maxsize, format, tm) { return _strftime(s, maxsize, format, tm); // no locale support yet } InternalError = Module['InternalError'] = extendError(Error, 'InternalError');; embind_init_charCodes(); BindingError = Module['BindingError'] = extendError(Error, 'BindingError');; init_ClassHandle(); init_embind();; init_RegisteredPointer(); UnboundTypeError = Module['UnboundTypeError'] = extendError(Error, 'UnboundTypeError');; init_emval();; var GLctx;; for (var i = 0; i < 32; ++i) tempFixedLengthArray.push(new Array(i));; var miniTempWebGLFloatBuffersStorage = new Float32Array(288); for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i+1); } ; var __miniTempWebGLIntBuffersStorage = new Int32Array(288); for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { __miniTempWebGLIntBuffers[i] = __miniTempWebGLIntBuffersStorage.subarray(0, i+1); } ; var ASSERTIONS = true; /** @type {function(string, boolean=, number=)} */ function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy)+1; var u8array = new Array(len); var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); if (dontAddNull) u8array.length = numBytesWritten; return u8array; } function intArrayToString(array) { var ret = []; for (var i = 0; i < array.length; i++) { var chr = array[i]; if (chr > 0xFF) { if (ASSERTIONS) { assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ') at offset ' + i + ' not in 0x00-0xFF.'); } chr &= 0xFF; } ret.push(String.fromCharCode(chr)); } return ret.join(''); } function checkIncomingModuleAPI() { ignoredModuleProp('fetchSettings'); } var asmLibraryArg = { "__assert_fail": ___assert_fail, "__syscall_fcntl64": ___syscall_fcntl64, "__syscall_fstat64": ___syscall_fstat64, "__syscall_ioctl": ___syscall_ioctl, "__syscall_lstat64": ___syscall_lstat64, "__syscall_newfstatat": ___syscall_newfstatat, "__syscall_openat": ___syscall_openat, "__syscall_stat64": ___syscall_stat64, "_embind_finalize_value_object": __embind_finalize_value_object, "_embind_register_bigint": __embind_register_bigint, "_embind_register_bool": __embind_register_bool, "_embind_register_class": __embind_register_class, "_embind_register_class_class_function": __embind_register_class_class_function, "_embind_register_class_constructor": __embind_register_class_constructor, "_embind_register_class_function": __embind_register_class_function, "_embind_register_constant": __embind_register_constant, "_embind_register_emval": __embind_register_emval, "_embind_register_enum": __embind_register_enum, "_embind_register_enum_value": __embind_register_enum_value, "_embind_register_float": __embind_register_float, "_embind_register_function": __embind_register_function, "_embind_register_integer": __embind_register_integer, "_embind_register_memory_view": __embind_register_memory_view, "_embind_register_smart_ptr": __embind_register_smart_ptr, "_embind_register_std_string": __embind_register_std_string, "_embind_register_std_wstring": __embind_register_std_wstring, "_embind_register_value_object": __embind_register_value_object, "_embind_register_value_object_field": __embind_register_value_object_field, "_embind_register_void": __embind_register_void, "_emscripten_date_now": __emscripten_date_now, "_emscripten_get_now_is_monotonic": __emscripten_get_now_is_monotonic, "_emscripten_throw_longjmp": __emscripten_throw_longjmp, "_emval_as": __emval_as, "_emval_call_method": __emval_call_method, "_emval_call_void_method": __emval_call_void_method, "_emval_decref": __emval_decref, "_emval_get_global": __emval_get_global, "_emval_get_method_caller": __emval_get_method_caller, "_emval_get_property": __emval_get_property, "_emval_incref": __emval_incref, "_emval_new": __emval_new, "_emval_new_array": __emval_new_array, "_emval_new_cstring": __emval_new_cstring, "_emval_new_object": __emval_new_object, "_emval_not": __emval_not, "_emval_run_destructors": __emval_run_destructors, "_emval_set_property": __emval_set_property, "_emval_take_value": __emval_take_value, "_mmap_js": __mmap_js, "_munmap_js": __munmap_js, "abort": _abort, "emscripten_get_now": _emscripten_get_now, "emscripten_glActiveTexture": _emscripten_glActiveTexture, "emscripten_glAttachShader": _emscripten_glAttachShader, "emscripten_glBindAttribLocation": _emscripten_glBindAttribLocation, "emscripten_glBindBuffer": _emscripten_glBindBuffer, "emscripten_glBindFramebuffer": _emscripten_glBindFramebuffer, "emscripten_glBindRenderbuffer": _emscripten_glBindRenderbuffer, "emscripten_glBindSampler": _emscripten_glBindSampler, "emscripten_glBindTexture": _emscripten_glBindTexture, "emscripten_glBindVertexArray": _emscripten_glBindVertexArray, "emscripten_glBindVertexArrayOES": _emscripten_glBindVertexArrayOES, "emscripten_glBlendColor": _emscripten_glBlendColor, "emscripten_glBlendEquation": _emscripten_glBlendEquation, "emscripten_glBlendFunc": _emscripten_glBlendFunc, "emscripten_glBlitFramebuffer": _emscripten_glBlitFramebuffer, "emscripten_glBufferData": _emscripten_glBufferData, "emscripten_glBufferSubData": _emscripten_glBufferSubData, "emscripten_glCheckFramebufferStatus": _emscripten_glCheckFramebufferStatus, "emscripten_glClear": _emscripten_glClear, "emscripten_glClearColor": _emscripten_glClearColor, "emscripten_glClearStencil": _emscripten_glClearStencil, "emscripten_glClientWaitSync": _emscripten_glClientWaitSync, "emscripten_glColorMask": _emscripten_glColorMask, "emscripten_glCompileShader": _emscripten_glCompileShader, "emscripten_glCompressedTexImage2D": _emscripten_glCompressedTexImage2D, "emscripten_glCompressedTexSubImage2D": _emscripten_glCompressedTexSubImage2D, "emscripten_glCopyBufferSubData": _emscripten_glCopyBufferSubData, "emscripten_glCopyTexSubImage2D": _emscripten_glCopyTexSubImage2D, "emscripten_glCreateProgram": _emscripten_glCreateProgram, "emscripten_glCreateShader": _emscripten_glCreateShader, "emscripten_glCullFace": _emscripten_glCullFace, "emscripten_glDeleteBuffers": _emscripten_glDeleteBuffers, "emscripten_glDeleteFramebuffers": _emscripten_glDeleteFramebuffers, "emscripten_glDeleteProgram": _emscripten_glDeleteProgram, "emscripten_glDeleteRenderbuffers": _emscripten_glDeleteRenderbuffers, "emscripten_glDeleteSamplers": _emscripten_glDeleteSamplers, "emscripten_glDeleteShader": _emscripten_glDeleteShader, "emscripten_glDeleteSync": _emscripten_glDeleteSync, "emscripten_glDeleteTextures": _emscripten_glDeleteTextures, "emscripten_glDeleteVertexArrays": _emscripten_glDeleteVertexArrays, "emscripten_glDeleteVertexArraysOES": _emscripten_glDeleteVertexArraysOES, "emscripten_glDepthMask": _emscripten_glDepthMask, "emscripten_glDisable": _emscripten_glDisable, "emscripten_glDisableVertexAttribArray": _emscripten_glDisableVertexAttribArray, "emscripten_glDrawArrays": _emscripten_glDrawArrays, "emscripten_glDrawArraysInstanced": _emscripten_glDrawArraysInstanced, "emscripten_glDrawArraysInstancedBaseInstanceWEBGL": _emscripten_glDrawArraysInstancedBaseInstanceWEBGL, "emscripten_glDrawBuffers": _emscripten_glDrawBuffers, "emscripten_glDrawElements": _emscripten_glDrawElements, "emscripten_glDrawElementsInstanced": _emscripten_glDrawElementsInstanced, "emscripten_glDrawElementsInstancedBaseVertexBaseInstanceWEBGL": _emscripten_glDrawElementsInstancedBaseVertexBaseInstanceWEBGL, "emscripten_glDrawRangeElements": _emscripten_glDrawRangeElements, "emscripten_glEnable": _emscripten_glEnable, "emscripten_glEnableVertexAttribArray": _emscripten_glEnableVertexAttribArray, "emscripten_glFenceSync": _emscripten_glFenceSync, "emscripten_glFinish": _emscripten_glFinish, "emscripten_glFlush": _emscripten_glFlush, "emscripten_glFramebufferRenderbuffer": _emscripten_glFramebufferRenderbuffer, "emscripten_glFramebufferTexture2D": _emscripten_glFramebufferTexture2D, "emscripten_glFrontFace": _emscripten_glFrontFace, "emscripten_glGenBuffers": _emscripten_glGenBuffers, "emscripten_glGenFramebuffers": _emscripten_glGenFramebuffers, "emscripten_glGenRenderbuffers": _emscripten_glGenRenderbuffers, "emscripten_glGenSamplers": _emscripten_glGenSamplers, "emscripten_glGenTextures": _emscripten_glGenTextures, "emscripten_glGenVertexArrays": _emscripten_glGenVertexArrays, "emscripten_glGenVertexArraysOES": _emscripten_glGenVertexArraysOES, "emscripten_glGenerateMipmap": _emscripten_glGenerateMipmap, "emscripten_glGetBufferParameteriv": _emscripten_glGetBufferParameteriv, "emscripten_glGetError": _emscripten_glGetError, "emscripten_glGetFloatv": _emscripten_glGetFloatv, "emscripten_glGetFramebufferAttachmentParameteriv": _emscripten_glGetFramebufferAttachmentParameteriv, "emscripten_glGetIntegerv": _emscripten_glGetIntegerv, "emscripten_glGetProgramInfoLog": _emscripten_glGetProgramInfoLog, "emscripten_glGetProgramiv": _emscripten_glGetProgramiv, "emscripten_glGetRenderbufferParameteriv": _emscripten_glGetRenderbufferParameteriv, "emscripten_glGetShaderInfoLog": _emscripten_glGetShaderInfoLog, "emscripten_glGetShaderPrecisionFormat": _emscripten_glGetShaderPrecisionFormat, "emscripten_glGetShaderiv": _emscripten_glGetShaderiv, "emscripten_glGetString": _emscripten_glGetString, "emscripten_glGetStringi": _emscripten_glGetStringi, "emscripten_glGetUniformLocation": _emscripten_glGetUniformLocation, "emscripten_glInvalidateFramebuffer": _emscripten_glInvalidateFramebuffer, "emscripten_glInvalidateSubFramebuffer": _emscripten_glInvalidateSubFramebuffer, "emscripten_glIsSync": _emscripten_glIsSync, "emscripten_glIsTexture": _emscripten_glIsTexture, "emscripten_glLineWidth": _emscripten_glLineWidth, "emscripten_glLinkProgram": _emscripten_glLinkProgram, "emscripten_glMultiDrawArraysInstancedBaseInstanceWEBGL": _emscripten_glMultiDrawArraysInstancedBaseInstanceWEBGL, "emscripten_glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL": _emscripten_glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL, "emscripten_glPixelStorei": _emscripten_glPixelStorei, "emscripten_glReadBuffer": _emscripten_glReadBuffer, "emscripten_glReadPixels": _emscripten_glReadPixels, "emscripten_glRenderbufferStorage": _emscripten_glRenderbufferStorage, "emscripten_glRenderbufferStorageMultisample": _emscripten_glRenderbufferStorageMultisample, "emscripten_glSamplerParameterf": _emscripten_glSamplerParameterf, "emscripten_glSamplerParameteri": _emscripten_glSamplerParameteri, "emscripten_glSamplerParameteriv": _emscripten_glSamplerParameteriv, "emscripten_glScissor": _emscripten_glScissor, "emscripten_glShaderSource": _emscripten_glShaderSource, "emscripten_glStencilFunc": _emscripten_glStencilFunc, "emscripten_glStencilFuncSeparate": _emscripten_glStencilFuncSeparate, "emscripten_glStencilMask": _emscripten_glStencilMask, "emscripten_glStencilMaskSeparate": _emscripten_glStencilMaskSeparate, "emscripten_glStencilOp": _emscripten_glStencilOp, "emscripten_glStencilOpSeparate": _emscripten_glStencilOpSeparate, "emscripten_glTexImage2D": _emscripten_glTexImage2D, "emscripten_glTexParameterf": _emscripten_glTexParameterf, "emscripten_glTexParameterfv": _emscripten_glTexParameterfv, "emscripten_glTexParameteri": _emscripten_glTexParameteri, "emscripten_glTexParameteriv": _emscripten_glTexParameteriv, "emscripten_glTexStorage2D": _emscripten_glTexStorage2D, "emscripten_glTexSubImage2D": _emscripten_glTexSubImage2D, "emscripten_glUniform1f": _emscripten_glUniform1f, "emscripten_glUniform1fv": _emscripten_glUniform1fv, "emscripten_glUniform1i": _emscripten_glUniform1i, "emscripten_glUniform1iv": _emscripten_glUniform1iv, "emscripten_glUniform2f": _emscripten_glUniform2f, "emscripten_glUniform2fv": _emscripten_glUniform2fv, "emscripten_glUniform2i": _emscripten_glUniform2i, "emscripten_glUniform2iv": _emscripten_glUniform2iv, "emscripten_glUniform3f": _emscripten_glUniform3f, "emscripten_glUniform3fv": _emscripten_glUniform3fv, "emscripten_glUniform3i": _emscripten_glUniform3i, "emscripten_glUniform3iv": _emscripten_glUniform3iv, "emscripten_glUniform4f": _emscripten_glUniform4f, "emscripten_glUniform4fv": _emscripten_glUniform4fv, "emscripten_glUniform4i": _emscripten_glUniform4i, "emscripten_glUniform4iv": _emscripten_glUniform4iv, "emscripten_glUniformMatrix2fv": _emscripten_glUniformMatrix2fv, "emscripten_glUniformMatrix3fv": _emscripten_glUniformMatrix3fv, "emscripten_glUniformMatrix4fv": _emscripten_glUniformMatrix4fv, "emscripten_glUseProgram": _emscripten_glUseProgram, "emscripten_glVertexAttrib1f": _emscripten_glVertexAttrib1f, "emscripten_glVertexAttrib2fv": _emscripten_glVertexAttrib2fv, "emscripten_glVertexAttrib3fv": _emscripten_glVertexAttrib3fv, "emscripten_glVertexAttrib4fv": _emscripten_glVertexAttrib4fv, "emscripten_glVertexAttribDivisor": _emscripten_glVertexAttribDivisor, "emscripten_glVertexAttribIPointer": _emscripten_glVertexAttribIPointer, "emscripten_glVertexAttribPointer": _emscripten_glVertexAttribPointer, "emscripten_glViewport": _emscripten_glViewport, "emscripten_glWaitSync": _emscripten_glWaitSync, "emscripten_memcpy_big": _emscripten_memcpy_big, "emscripten_resize_heap": _emscripten_resize_heap, "emscripten_webgl_get_current_context": _emscripten_webgl_get_current_context, "environ_get": _environ_get, "environ_sizes_get": _environ_sizes_get, "exit": _exit, "fd_close": _fd_close, "fd_pread": _fd_pread, "fd_read": _fd_read, "fd_seek": _fd_seek, "fd_write": _fd_write, "getTempRet0": _getTempRet0, "invoke_ii": invoke_ii, "invoke_iii": invoke_iii, "invoke_iiii": invoke_iiii, "invoke_iiiii": invoke_iiiii, "invoke_iiiiii": invoke_iiiiii, "invoke_iiiiiii": invoke_iiiiiii, "invoke_iiiiiiiiii": invoke_iiiiiiiiii, "invoke_v": invoke_v, "invoke_vi": invoke_vi, "invoke_vii": invoke_vii, "invoke_viii": invoke_viii, "invoke_viiii": invoke_viiii, "invoke_viiiii": invoke_viiiii, "invoke_viiiiiii": invoke_viiiiiii, "invoke_viiiiiiiii": invoke_viiiiiiiii, "setTempRet0": _setTempRet0, "strftime_l": _strftime_l }; var asm = createWasm(); /** @type {function(...*):?} */ var ___wasm_call_ctors = Module["___wasm_call_ctors"] = createExportWrapper("__wasm_call_ctors"); /** @type {function(...*):?} */ var _malloc = Module["_malloc"] = createExportWrapper("malloc"); /** @type {function(...*):?} */ var ___errno_location = Module["___errno_location"] = createExportWrapper("__errno_location"); /** @type {function(...*):?} */ var _free = Module["_free"] = createExportWrapper("free"); /** @type {function(...*):?} */ var _fflush = Module["_fflush"] = createExportWrapper("fflush"); /** @type {function(...*):?} */ var _saveSetjmp = Module["_saveSetjmp"] = createExportWrapper("saveSetjmp"); /** @type {function(...*):?} */ var ___getTypeName = Module["___getTypeName"] = createExportWrapper("__getTypeName"); /** @type {function(...*):?} */ var ___embind_register_native_and_builtin_types = Module["___embind_register_native_and_builtin_types"] = createExportWrapper("__embind_register_native_and_builtin_types"); /** @type {function(...*):?} */ var ___dl_seterr = Module["___dl_seterr"] = createExportWrapper("__dl_seterr"); /** @type {function(...*):?} */ var _setThrew = Module["_setThrew"] = createExportWrapper("setThrew"); /** @type {function(...*):?} */ var _emscripten_stack_init = Module["_emscripten_stack_init"] = function() { return (_emscripten_stack_init = Module["_emscripten_stack_init"] = Module["asm"]["emscripten_stack_init"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _emscripten_stack_get_free = Module["_emscripten_stack_get_free"] = function() { return (_emscripten_stack_get_free = Module["_emscripten_stack_get_free"] = Module["asm"]["emscripten_stack_get_free"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _emscripten_stack_get_base = Module["_emscripten_stack_get_base"] = function() { return (_emscripten_stack_get_base = Module["_emscripten_stack_get_base"] = Module["asm"]["emscripten_stack_get_base"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _emscripten_stack_get_end = Module["_emscripten_stack_get_end"] = function() { return (_emscripten_stack_get_end = Module["_emscripten_stack_get_end"] = Module["asm"]["emscripten_stack_get_end"]).apply(null, arguments); }; /** @type {function(...*):?} */ var stackSave = Module["stackSave"] = createExportWrapper("stackSave"); /** @type {function(...*):?} */ var stackRestore = Module["stackRestore"] = createExportWrapper("stackRestore"); /** @type {function(...*):?} */ var stackAlloc = Module["stackAlloc"] = createExportWrapper("stackAlloc"); /** @type {function(...*):?} */ var ___cxa_demangle = Module["___cxa_demangle"] = createExportWrapper("__cxa_demangle"); /** @type {function(...*):?} */ var dynCall_viji = Module["dynCall_viji"] = createExportWrapper("dynCall_viji"); /** @type {function(...*):?} */ var dynCall_vijiii = Module["dynCall_vijiii"] = createExportWrapper("dynCall_vijiii"); /** @type {function(...*):?} */ var dynCall_viiiiij = Module["dynCall_viiiiij"] = createExportWrapper("dynCall_viiiiij"); /** @type {function(...*):?} */ var dynCall_jiiiijiiiii = Module["dynCall_jiiiijiiiii"] = createExportWrapper("dynCall_jiiiijiiiii"); /** @type {function(...*):?} */ var dynCall_viiij = Module["dynCall_viiij"] = createExportWrapper("dynCall_viiij"); /** @type {function(...*):?} */ var dynCall_jii = Module["dynCall_jii"] = createExportWrapper("dynCall_jii"); /** @type {function(...*):?} */ var dynCall_vij = Module["dynCall_vij"] = createExportWrapper("dynCall_vij"); /** @type {function(...*):?} */ var dynCall_iiij = Module["dynCall_iiij"] = createExportWrapper("dynCall_iiij"); /** @type {function(...*):?} */ var dynCall_viij = Module["dynCall_viij"] = createExportWrapper("dynCall_viij"); /** @type {function(...*):?} */ var dynCall_iiiij = Module["dynCall_iiiij"] = createExportWrapper("dynCall_iiiij"); /** @type {function(...*):?} */ var dynCall_ji = Module["dynCall_ji"] = createExportWrapper("dynCall_ji"); /** @type {function(...*):?} */ var dynCall_iij = Module["dynCall_iij"] = createExportWrapper("dynCall_iij"); /** @type {function(...*):?} */ var dynCall_iiijii = Module["dynCall_iiijii"] = createExportWrapper("dynCall_iiijii"); /** @type {function(...*):?} */ var dynCall_jiiiii = Module["dynCall_jiiiii"] = createExportWrapper("dynCall_jiiiii"); /** @type {function(...*):?} */ var dynCall_jiiiiii = Module["dynCall_jiiiiii"] = createExportWrapper("dynCall_jiiiiii"); /** @type {function(...*):?} */ var dynCall_jiiiiji = Module["dynCall_jiiiiji"] = createExportWrapper("dynCall_jiiiiji"); /** @type {function(...*):?} */ var dynCall_iijj = Module["dynCall_iijj"] = createExportWrapper("dynCall_iijj"); /** @type {function(...*):?} */ var dynCall_iiiji = Module["dynCall_iiiji"] = createExportWrapper("dynCall_iiiji"); /** @type {function(...*):?} */ var dynCall_iiji = Module["dynCall_iiji"] = createExportWrapper("dynCall_iiji"); /** @type {function(...*):?} */ var dynCall_iijjiii = Module["dynCall_iijjiii"] = createExportWrapper("dynCall_iijjiii"); /** @type {function(...*):?} */ var dynCall_vijjjii = Module["dynCall_vijjjii"] = createExportWrapper("dynCall_vijjjii"); /** @type {function(...*):?} */ var dynCall_jiji = Module["dynCall_jiji"] = createExportWrapper("dynCall_jiji"); /** @type {function(...*):?} */ var dynCall_viijii = Module["dynCall_viijii"] = createExportWrapper("dynCall_viijii"); /** @type {function(...*):?} */ var dynCall_iiiiij = Module["dynCall_iiiiij"] = createExportWrapper("dynCall_iiiiij"); /** @type {function(...*):?} */ var dynCall_iiiiijj = Module["dynCall_iiiiijj"] = createExportWrapper("dynCall_iiiiijj"); /** @type {function(...*):?} */ var dynCall_iiiiiijj = Module["dynCall_iiiiiijj"] = createExportWrapper("dynCall_iiiiiijj"); function invoke_ii(index,a1) { var sp = stackSave(); try { return getWasmTableEntry(index)(a1); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_iii(index,a1,a2) { var sp = stackSave(); try { return getWasmTableEntry(index)(a1,a2); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_vi(index,a1) { var sp = stackSave(); try { getWasmTableEntry(index)(a1); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_viii(index,a1,a2,a3) { var sp = stackSave(); try { getWasmTableEntry(index)(a1,a2,a3); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7) { var sp = stackSave(); try { getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6) { var sp = stackSave(); try { return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_iiii(index,a1,a2,a3) { var sp = stackSave(); try { return getWasmTableEntry(index)(a1,a2,a3); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_vii(index,a1,a2) { var sp = stackSave(); try { getWasmTableEntry(index)(a1,a2); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_viiii(index,a1,a2,a3,a4) { var sp = stackSave(); try { getWasmTableEntry(index)(a1,a2,a3,a4); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_viiiii(index,a1,a2,a3,a4,a5) { var sp = stackSave(); try { getWasmTableEntry(index)(a1,a2,a3,a4,a5); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_iiiii(index,a1,a2,a3,a4) { var sp = stackSave(); try { return getWasmTableEntry(index)(a1,a2,a3,a4); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_iiiiii(index,a1,a2,a3,a4,a5) { var sp = stackSave(); try { return getWasmTableEntry(index)(a1,a2,a3,a4,a5); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_viiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9) { var sp = stackSave(); try { getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_v(index) { var sp = stackSave(); try { getWasmTableEntry(index)(); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9) { var sp = stackSave(); try { return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9); } catch(e) { stackRestore(sp); if (e !== e+0) throw e; _setThrew(1, 0); } } // === Auto-generated postamble setup entry stuff === unexportedRuntimeFunction('ccall', false); unexportedRuntimeFunction('cwrap', false); unexportedRuntimeFunction('allocate', false); unexportedRuntimeFunction('UTF8ArrayToString', false); unexportedRuntimeFunction('UTF8ToString', false); unexportedRuntimeFunction('stringToUTF8Array', false); unexportedRuntimeFunction('stringToUTF8', false); unexportedRuntimeFunction('lengthBytesUTF8', false); unexportedRuntimeFunction('addOnPreRun', false); unexportedRuntimeFunction('addOnInit', false); unexportedRuntimeFunction('addOnPreMain', false); unexportedRuntimeFunction('addOnExit', false); unexportedRuntimeFunction('addOnPostRun', false); unexportedRuntimeFunction('addRunDependency', true); unexportedRuntimeFunction('removeRunDependency', true); unexportedRuntimeFunction('FS_createFolder', false); unexportedRuntimeFunction('FS_createPath', true); unexportedRuntimeFunction('FS_createDataFile', true); unexportedRuntimeFunction('FS_createPreloadedFile', true); unexportedRuntimeFunction('FS_createLazyFile', true); unexportedRuntimeFunction('FS_createLink', false); unexportedRuntimeFunction('FS_createDevice', true); unexportedRuntimeFunction('FS_unlink', true); unexportedRuntimeFunction('getLEB', false); unexportedRuntimeFunction('getFunctionTables', false); unexportedRuntimeFunction('alignFunctionTables', false); unexportedRuntimeFunction('registerFunctions', false); unexportedRuntimeFunction('addFunction', false); unexportedRuntimeFunction('removeFunction', false); unexportedRuntimeFunction('prettyPrint', false); unexportedRuntimeFunction('getCompilerSetting', false); unexportedRuntimeFunction('print', false); unexportedRuntimeFunction('printErr', false); unexportedRuntimeFunction('getTempRet0', false); unexportedRuntimeFunction('setTempRet0', false); unexportedRuntimeFunction('callMain', false); unexportedRuntimeFunction('abort', false); unexportedRuntimeFunction('keepRuntimeAlive', false); unexportedRuntimeFunction('wasmMemory', false); unexportedRuntimeFunction('warnOnce', false); unexportedRuntimeFunction('stackSave', false); unexportedRuntimeFunction('stackRestore', false); unexportedRuntimeFunction('stackAlloc', false); unexportedRuntimeFunction('AsciiToString', false); unexportedRuntimeFunction('stringToAscii', false); unexportedRuntimeFunction('UTF16ToString', false); unexportedRuntimeFunction('stringToUTF16', false); unexportedRuntimeFunction('lengthBytesUTF16', false); unexportedRuntimeFunction('UTF32ToString', false); unexportedRuntimeFunction('stringToUTF32', false); unexportedRuntimeFunction('lengthBytesUTF32', false); unexportedRuntimeFunction('allocateUTF8', false); unexportedRuntimeFunction('allocateUTF8OnStack', false); unexportedRuntimeFunction('ExitStatus', false); unexportedRuntimeFunction('intArrayFromString', false); unexportedRuntimeFunction('intArrayToString', false); unexportedRuntimeFunction('writeStringToMemory', false); unexportedRuntimeFunction('writeArrayToMemory', false); unexportedRuntimeFunction('writeAsciiToMemory', false); Module["writeStackCookie"] = writeStackCookie; Module["checkStackCookie"] = checkStackCookie; unexportedRuntimeFunction('ptrToString', false); unexportedRuntimeFunction('zeroMemory', false); unexportedRuntimeFunction('stringToNewUTF8', false); unexportedRuntimeFunction('getHeapMax', false); unexportedRuntimeFunction('emscripten_realloc_buffer', false); unexportedRuntimeFunction('ENV', false); unexportedRuntimeFunction('ERRNO_CODES', false); unexportedRuntimeFunction('ERRNO_MESSAGES', false); unexportedRuntimeFunction('setErrNo', false); unexportedRuntimeFunction('inetPton4', false); unexportedRuntimeFunction('inetNtop4', false); unexportedRuntimeFunction('inetPton6', false); unexportedRuntimeFunction('inetNtop6', false); unexportedRuntimeFunction('readSockaddr', false); unexportedRuntimeFunction('writeSockaddr', false); unexportedRuntimeFunction('DNS', false); unexportedRuntimeFunction('getHostByName', false); unexportedRuntimeFunction('Protocols', false); unexportedRuntimeFunction('Sockets', false); unexportedRuntimeFunction('getRandomDevice', false); unexportedRuntimeFunction('traverseStack', false); unexportedRuntimeFunction('UNWIND_CACHE', false); unexportedRuntimeFunction('convertPCtoSourceLocation', false); unexportedRuntimeFunction('readAsmConstArgsArray', false); unexportedRuntimeFunction('readAsmConstArgs', false); unexportedRuntimeFunction('mainThreadEM_ASM', false); unexportedRuntimeFunction('jstoi_q', false); unexportedRuntimeFunction('jstoi_s', false); unexportedRuntimeFunction('getExecutableName', false); unexportedRuntimeFunction('listenOnce', false); unexportedRuntimeFunction('autoResumeAudioContext', false); unexportedRuntimeFunction('dynCallLegacy', false); unexportedRuntimeFunction('getDynCaller', false); unexportedRuntimeFunction('dynCall', false); unexportedRuntimeFunction('handleException', false); unexportedRuntimeFunction('runtimeKeepalivePush', false); unexportedRuntimeFunction('runtimeKeepalivePop', false); unexportedRuntimeFunction('callUserCallback', false); unexportedRuntimeFunction('maybeExit', false); unexportedRuntimeFunction('safeSetTimeout', false); unexportedRuntimeFunction('asmjsMangle', false); unexportedRuntimeFunction('asyncLoad', false); unexportedRuntimeFunction('alignMemory', false); unexportedRuntimeFunction('mmapAlloc', false); unexportedRuntimeFunction('writeI53ToI64', false); unexportedRuntimeFunction('writeI53ToI64Clamped', false); unexportedRuntimeFunction('writeI53ToI64Signaling', false); unexportedRuntimeFunction('writeI53ToU64Clamped', false); unexportedRuntimeFunction('writeI53ToU64Signaling', false); unexportedRuntimeFunction('readI53FromI64', false); unexportedRuntimeFunction('readI53FromU64', false); unexportedRuntimeFunction('convertI32PairToI53', false); unexportedRuntimeFunction('convertI32PairToI53Checked', false); unexportedRuntimeFunction('convertU32PairToI53', false); unexportedRuntimeFunction('reallyNegative', false); unexportedRuntimeFunction('unSign', false); unexportedRuntimeFunction('strLen', false); unexportedRuntimeFunction('reSign', false); unexportedRuntimeFunction('formatString', false); unexportedRuntimeFunction('setValue', false); unexportedRuntimeFunction('getValue', false); unexportedRuntimeFunction('PATH', false); unexportedRuntimeFunction('PATH_FS', false); unexportedRuntimeFunction('SYSCALLS', false); unexportedRuntimeFunction('getSocketFromFD', false); unexportedRuntimeFunction('getSocketAddress', false); unexportedRuntimeFunction('JSEvents', false); unexportedRuntimeFunction('registerKeyEventCallback', false); unexportedRuntimeFunction('specialHTMLTargets', false); unexportedRuntimeFunction('maybeCStringToJsString', false); unexportedRuntimeFunction('findEventTarget', false); unexportedRuntimeFunction('findCanvasEventTarget', false); unexportedRuntimeFunction('getBoundingClientRect', false); unexportedRuntimeFunction('fillMouseEventData', false); unexportedRuntimeFunction('registerMouseEventCallback', false); unexportedRuntimeFunction('registerWheelEventCallback', false); unexportedRuntimeFunction('registerUiEventCallback', false); unexportedRuntimeFunction('registerFocusEventCallback', false); unexportedRuntimeFunction('fillDeviceOrientationEventData', false); unexportedRuntimeFunction('registerDeviceOrientationEventCallback', false); unexportedRuntimeFunction('fillDeviceMotionEventData', false); unexportedRuntimeFunction('registerDeviceMotionEventCallback', false); unexportedRuntimeFunction('screenOrientation', false); unexportedRuntimeFunction('fillOrientationChangeEventData', false); unexportedRuntimeFunction('registerOrientationChangeEventCallback', false); unexportedRuntimeFunction('fillFullscreenChangeEventData', false); unexportedRuntimeFunction('registerFullscreenChangeEventCallback', false); unexportedRuntimeFunction('JSEvents_requestFullscreen', false); unexportedRuntimeFunction('JSEvents_resizeCanvasForFullscreen', false); unexportedRuntimeFunction('registerRestoreOldStyle', false); unexportedRuntimeFunction('hideEverythingExceptGivenElement', false); unexportedRuntimeFunction('restoreHiddenElements', false); unexportedRuntimeFunction('setLetterbox', false); unexportedRuntimeFunction('currentFullscreenStrategy', false); unexportedRuntimeFunction('restoreOldWindowedStyle', false); unexportedRuntimeFunction('softFullscreenResizeWebGLRenderTarget', false); unexportedRuntimeFunction('doRequestFullscreen', false); unexportedRuntimeFunction('fillPointerlockChangeEventData', false); unexportedRuntimeFunction('registerPointerlockChangeEventCallback', false); unexportedRuntimeFunction('registerPointerlockErrorEventCallback', false); unexportedRuntimeFunction('requestPointerLock', false); unexportedRuntimeFunction('fillVisibilityChangeEventData', false); unexportedRuntimeFunction('registerVisibilityChangeEventCallback', false); unexportedRuntimeFunction('registerTouchEventCallback', false); unexportedRuntimeFunction('fillGamepadEventData', false); unexportedRuntimeFunction('registerGamepadEventCallback', false); unexportedRuntimeFunction('registerBeforeUnloadEventCallback', false); unexportedRuntimeFunction('fillBatteryEventData', false); unexportedRuntimeFunction('battery', false); unexportedRuntimeFunction('registerBatteryEventCallback', false); unexportedRuntimeFunction('setCanvasElementSize', false); unexportedRuntimeFunction('getCanvasElementSize', false); unexportedRuntimeFunction('demangle', false); unexportedRuntimeFunction('demangleAll', false); unexportedRuntimeFunction('jsStackTrace', false); unexportedRuntimeFunction('stackTrace', false); unexportedRuntimeFunction('getEnvStrings', false); unexportedRuntimeFunction('checkWasiClock', false); unexportedRuntimeFunction('flush_NO_FILESYSTEM', false); unexportedRuntimeFunction('setImmediateWrapped', false); unexportedRuntimeFunction('clearImmediateWrapped', false); unexportedRuntimeFunction('polyfillSetImmediate', false); unexportedRuntimeFunction('uncaughtExceptionCount', false); unexportedRuntimeFunction('exceptionLast', false); unexportedRuntimeFunction('exceptionCaught', false); unexportedRuntimeFunction('ExceptionInfo', false); unexportedRuntimeFunction('exception_addRef', false); unexportedRuntimeFunction('exception_decRef', false); unexportedRuntimeFunction('Browser', false); unexportedRuntimeFunction('setMainLoop', false); unexportedRuntimeFunction('wget', false); unexportedRuntimeFunction('tempFixedLengthArray', false); unexportedRuntimeFunction('miniTempWebGLFloatBuffers', false); unexportedRuntimeFunction('heapObjectForWebGLType', false); unexportedRuntimeFunction('heapAccessShiftForWebGLHeap', false); unexportedRuntimeFunction('GL', false); unexportedRuntimeFunction('emscriptenWebGLGet', false); unexportedRuntimeFunction('computeUnpackAlignedImageSize', false); unexportedRuntimeFunction('emscriptenWebGLGetTexPixelData', false); unexportedRuntimeFunction('emscriptenWebGLGetUniform', false); unexportedRuntimeFunction('webglGetUniformLocation', false); unexportedRuntimeFunction('webglPrepareUniformLocationsBeforeFirstUse', false); unexportedRuntimeFunction('webglGetLeftBracePos', false); unexportedRuntimeFunction('emscriptenWebGLGetVertexAttrib', false); unexportedRuntimeFunction('emscriptenWebGLGetIndexed', false); unexportedRuntimeFunction('writeGLArray', false); unexportedRuntimeFunction('InternalError', false); unexportedRuntimeFunction('BindingError', false); unexportedRuntimeFunction('UnboundTypeError', false); unexportedRuntimeFunction('PureVirtualError', false); unexportedRuntimeFunction('init_embind', false); unexportedRuntimeFunction('throwInternalError', false); unexportedRuntimeFunction('throwBindingError', false); unexportedRuntimeFunction('throwUnboundTypeError', false); unexportedRuntimeFunction('ensureOverloadTable', false); unexportedRuntimeFunction('exposePublicSymbol', false); unexportedRuntimeFunction('replacePublicSymbol', false); unexportedRuntimeFunction('extendError', false); unexportedRuntimeFunction('createNamedFunction', false); unexportedRuntimeFunction('embindRepr', false); unexportedRuntimeFunction('registeredInstances', false); unexportedRuntimeFunction('getBasestPointer', false); unexportedRuntimeFunction('registerInheritedInstance', false); unexportedRuntimeFunction('unregisterInheritedInstance', false); unexportedRuntimeFunction('getInheritedInstance', false); unexportedRuntimeFunction('getInheritedInstanceCount', false); unexportedRuntimeFunction('getLiveInheritedInstances', false); unexportedRuntimeFunction('registeredTypes', false); unexportedRuntimeFunction('awaitingDependencies', false); unexportedRuntimeFunction('typeDependencies', false); unexportedRuntimeFunction('registeredPointers', false); unexportedRuntimeFunction('registerType', false); unexportedRuntimeFunction('whenDependentTypesAreResolved', false); unexportedRuntimeFunction('embind_charCodes', false); unexportedRuntimeFunction('embind_init_charCodes', false); unexportedRuntimeFunction('readLatin1String', false); unexportedRuntimeFunction('getTypeName', false); unexportedRuntimeFunction('heap32VectorToArray', false); unexportedRuntimeFunction('requireRegisteredType', false); unexportedRuntimeFunction('getShiftFromSize', false); unexportedRuntimeFunction('integerReadValueFromPointer', false); unexportedRuntimeFunction('enumReadValueFromPointer', false); unexportedRuntimeFunction('floatReadValueFromPointer', false); unexportedRuntimeFunction('simpleReadValueFromPointer', false); unexportedRuntimeFunction('runDestructors', false); unexportedRuntimeFunction('new_', false); unexportedRuntimeFunction('craftInvokerFunction', false); unexportedRuntimeFunction('embind__requireFunction', false); unexportedRuntimeFunction('tupleRegistrations', false); unexportedRuntimeFunction('structRegistrations', false); unexportedRuntimeFunction('genericPointerToWireType', false); unexportedRuntimeFunction('constNoSmartPtrRawPointerToWireType', false); unexportedRuntimeFunction('nonConstNoSmartPtrRawPointerToWireType', false); unexportedRuntimeFunction('init_RegisteredPointer', false); unexportedRuntimeFunction('RegisteredPointer', false); unexportedRuntimeFunction('RegisteredPointer_getPointee', false); unexportedRuntimeFunction('RegisteredPointer_destructor', false); unexportedRuntimeFunction('RegisteredPointer_deleteObject', false); unexportedRuntimeFunction('RegisteredPointer_fromWireType', false); unexportedRuntimeFunction('runDestructor', false); unexportedRuntimeFunction('releaseClassHandle', false); unexportedRuntimeFunction('finalizationRegistry', false); unexportedRuntimeFunction('detachFinalizer_deps', false); unexportedRuntimeFunction('detachFinalizer', false); unexportedRuntimeFunction('attachFinalizer', false); unexportedRuntimeFunction('makeClassHandle', false); unexportedRuntimeFunction('init_ClassHandle', false); unexportedRuntimeFunction('ClassHandle', false); unexportedRuntimeFunction('ClassHandle_isAliasOf', false); unexportedRuntimeFunction('throwInstanceAlreadyDeleted', false); unexportedRuntimeFunction('ClassHandle_clone', false); unexportedRuntimeFunction('ClassHandle_delete', false); unexportedRuntimeFunction('deletionQueue', false); unexportedRuntimeFunction('ClassHandle_isDeleted', false); unexportedRuntimeFunction('ClassHandle_deleteLater', false); unexportedRuntimeFunction('flushPendingDeletes', false); unexportedRuntimeFunction('delayFunction', false); unexportedRuntimeFunction('setDelayFunction', false); unexportedRuntimeFunction('RegisteredClass', false); unexportedRuntimeFunction('shallowCopyInternalPointer', false); unexportedRuntimeFunction('downcastPointer', false); unexportedRuntimeFunction('upcastPointer', false); unexportedRuntimeFunction('validateThis', false); unexportedRuntimeFunction('char_0', false); unexportedRuntimeFunction('char_9', false); unexportedRuntimeFunction('makeLegalFunctionName', false); unexportedRuntimeFunction('emval_handle_array', false); unexportedRuntimeFunction('emval_free_list', false); unexportedRuntimeFunction('emval_symbols', false); unexportedRuntimeFunction('init_emval', false); unexportedRuntimeFunction('count_emval_handles', false); unexportedRuntimeFunction('get_first_emval', false); unexportedRuntimeFunction('getStringOrSymbol', false); unexportedRuntimeFunction('Emval', false); unexportedRuntimeFunction('emval_newers', false); unexportedRuntimeFunction('craftEmvalAllocator', false); unexportedRuntimeFunction('emval_get_global', false); unexportedRuntimeFunction('emval_lookupTypes', false); unexportedRuntimeFunction('emval_allocateDestructors', false); unexportedRuntimeFunction('emval_methodCallers', false); unexportedRuntimeFunction('emval_addMethodCaller', false); unexportedRuntimeFunction('emval_registeredMethods', false); unexportedRuntimeSymbol('ALLOC_NORMAL', false); unexportedRuntimeSymbol('ALLOC_STACK', false); var calledRun; /** * @constructor * @this {ExitStatus} */ function ExitStatus(status) { this.name = "ExitStatus"; this.message = "Program terminated with exit(" + status + ")"; this.status = status; } var calledMain = false; dependenciesFulfilled = function runCaller() { // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) if (!calledRun) run(); if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled }; function stackCheckInit() { // This is normally called automatically during __wasm_call_ctors but need to // get these values before even running any of the ctors so we call it redundantly // here. _emscripten_stack_init(); // TODO(sbc): Move writeStackCookie to native to to avoid this. writeStackCookie(); } /** @type {function(Array=)} */ function run(args) { args = args || arguments_; if (runDependencies > 0) { return; } stackCheckInit(); preRun(); // a preRun added a dependency, run will be called later if (runDependencies > 0) { return; } function doRun() { // run may have just been called through dependencies being fulfilled just in this very frame, // or while the async setStatus time below was happening if (calledRun) return; calledRun = true; Module['calledRun'] = true; if (ABORT) return; initRuntime(); readyPromiseResolve(Module); if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); assert(!Module['_main'], 'compiled without a main, but one is present. if you added it from JS, use Module["onRuntimeInitialized"]'); postRun(); } if (Module['setStatus']) { Module['setStatus']('Running...'); setTimeout(function() { setTimeout(function() { Module['setStatus'](''); }, 1); doRun(); }, 1); } else { doRun(); } checkStackCookie(); } Module['run'] = run; function checkUnflushedContent() { // Compiler settings do not allow exiting the runtime, so flushing // the streams is not possible. but in ASSERTIONS mode we check // if there was something to flush, and if so tell the user they // should request that the runtime be exitable. // Normally we would not even include flush() at all, but in ASSERTIONS // builds we do so just for this check, and here we see if there is any // content to flush, that is, we check if there would have been // something a non-ASSERTIONS build would have not seen. // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0 // mode (which has its own special function for this; otherwise, all // the code is inside libc) var oldOut = out; var oldErr = err; var has = false; out = err = (x) => { has = true; } try { // it doesn't matter if it fails flush_NO_FILESYSTEM(); } catch(e) {} out = oldOut; err = oldErr; if (has) { warnOnce('stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline when you printf etc.'); warnOnce('(this may also be due to not including full filesystem support - try building with -sFORCE_FILESYSTEM)'); } } /** @param {boolean|number=} implicit */ function exit(status, implicit) { EXITSTATUS = status; checkUnflushedContent(); // if exit() was called explicitly, warn the user if the runtime isn't actually being shut down if (keepRuntimeAlive() && !implicit) { var msg = 'program exited (with status: ' + status + '), but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)'; readyPromiseReject(msg); err(msg); } procExit(status); } function procExit(code) { EXITSTATUS = code; if (!keepRuntimeAlive()) { if (Module['onExit']) Module['onExit'](code); ABORT = true; } quit_(code, new ExitStatus(code)); } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; while (Module['preInit'].length > 0) { Module['preInit'].pop()(); } } run(); return CanvasKitInit.ready } ); })(); if (typeof exports === 'object' && typeof module === 'object') module.exports = CanvasKitInit; else if (typeof define === 'function' && define['amd']) define([], function() { return CanvasKitInit; }); else if (typeof exports === 'object') exports["CanvasKitInit"] = CanvasKitInit;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2020-08-14 vs2019 临时下载目录 离线安装 脱机安装
2019-08-14 centos 6.9 安装docker
2018-08-14 nio 阻塞 非阻塞 同步 异步
2015-08-14 jboss7 添加虚拟目录 上传文件路径
2015-08-14 JAVA 取得当前目录的路径/Servlet/class/文件路径/web路径/url地址