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;

 

posted @ 2023-08-14 17:30  Bigben  阅读(604)  评论(0编辑  收藏  举报