Merge pull request #175 from UebelAndre/standardize-on-appveyor

Standardize on App Veyor
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 7106fe5..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-dist: bionic
-
-stages:
-  - build
-
-matrix:
-  include:
-    - name: Static checks (format.sh)
-      stage: build
-      os: linux
-      addons:
-        apt:
-          packages:
-            - dos2unix
-            - recode
-
-script:
-  - bash ./format.sh
diff --git a/README.md b/README.md
index 4ec10ce..eda5f82 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@
 
 FXT1: See the OpenGL extension [GL_3DFX_texture_compression_FXT1](https://www.khronos.org/registry/OpenGL/extensions/3DFX/3DFX_texture_compression_FXT1.txt).
 
-Also see [Intel® Open Source HD Graphics Programmers' Reference Manual (PRM)](https://01.org/sites/default/files/documentation/intel-gfx-bspec-osrc-chv-bsw-vol05-memory-views.pdf). This reference manual details how to encode FXT1, ETC1, ETC2, EAC, DXT/BC1-3, BC4/5/7, and ASTC.
+Also see [Intel(R) Open Source HD Graphics Programmers' Reference Manual (PRM)](https://01.org/sites/default/files/documentation/intel-gfx-bspec-osrc-chv-bsw-vol05-memory-views.pdf). This reference manual details how to encode FXT1, ETC1, ETC2, EAC, DXT/BC1-3, BC4/5/7, and ASTC.
 
 ### Repository Licensing with REUSE
 
diff --git a/appveyor.yml b/appveyor.yml
index b9ba95c..d790c2e 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,12 +1,21 @@
+---
+
 image: 
-  - Visual Studio 2019
-  - Ubuntu
   - macos
+  - Ubuntu2004
+  - Visual Studio 2019
 
 configuration: Release
 
 environment:
   APPVEYOR_YML_DISABLE_PS_LINUX: true
+
+install:
+- sh: |
+    if [ "$(uname)" != "Darwin" ]; then
+      sudo apt-get update -y
+      sudo apt-get install -y dos2unix recode
+    fi
     
 build_script:
   - ps: |
@@ -24,7 +33,16 @@
       cmake --build . --config ${CONFIGURATION}
       cd ../
 
+test_script:
+  - sh: |
+      if [ "$(uname)" != "Darwin" ]; then
+        bash ./format.sh
+      fi
+
 artifacts:
-  - path: bin\$(configuration)\basisu.exe
+  # Linux
   - path: bin/basisu
+  # MacOS
   - path: bin_osx/basisu
+  # Windows
+  - path: bin\$(configuration)\basisu.exe
diff --git a/build_emscripten.sh b/build_emscripten.sh
index 274f539..7b0cfb7 100755
--- a/build_emscripten.sh
+++ b/build_emscripten.sh
@@ -6,4 +6,4 @@
 fi
 
 emcmake cmake .
-emcmake make
\ No newline at end of file
+emcmake make
diff --git a/webgl/README.md b/webgl/README.md
index 333c932..4ad633b 100644
--- a/webgl/README.md
+++ b/webgl/README.md
@@ -35,7 +35,7 @@
 * PVRTC
   * Tested in Chrome and Safari on iOS iPhone 6 Plus.
 
-The glTF model in this demo uses a hypothetical `GOOGLE_texture_basis` extension. That extension is defined for the sake of example only – the glTF format will officially embed Basis files within a KTX2 wrapper, through a new
+The glTF model in this demo uses a hypothetical `GOOGLE_texture_basis` extension. That extension is defined for the sake of example only - the glTF format will officially embed Basis files within a KTX2 wrapper, through a new
 extension that is [currently in development](https://github.com/KhronosGroup/glTF/pull/1612).
 
 ![Screenshot showing a basis texture rendered as the base color texture for a 3D model in a webpage.](gltf/preview.png)
diff --git a/webgl/transcoder/build/basis_loader.js b/webgl/transcoder/build/basis_loader.js
index 8cd4a0e..dcc0179 100644
--- a/webgl/transcoder/build/basis_loader.js
+++ b/webgl/transcoder/build/basis_loader.js
@@ -1,494 +1,494 @@
-/*

- * Basis Loader

- *

- * Usage:

- * // basis_loader.js should be loaded from the same directory as

- * // basis_transcoder.js and basis_transcoder.wasm

- *

- * // Create the texture loader and set the WebGL context it should use. Spawns

- * // a worker which handles all of the transcoding.

- * let basisLoader = new BasisLoader();

- * basisLoader.setWebGLContext(gl);

- *

- * // To allow separate color and alpha textures to be returned in cases where

- * // it would provide higher quality:

- * basisLoader.allowSeparateAlpha = true;

- *

- * // loadFromUrl() returns a promise which resolves to a completed WebGL

- * // texture or rejects if there's an error loading.

- * basisBasics.loadFromUrl(fullPathToTexture).then((result) => {

- *   // WebGL color+alpha texture;

- *   result.texture;

- *

- *   // WebGL alpha texture, only if basisLoader.allowSeparateAlpha is true.

- *   // null if alpha is encoded in result.texture or result.alpha is false.

- *   result.alphaTexture;

- *

- *   // True if the texture contained an alpha channel.

- *   result.alpha;

- *

- *   // Number of mip levels in texture/alphaTexture

- *   result.mipLevels;

- *

- *   // Dimensions of the base mip level.

- *   result.width;

- *   result.height;

- * });

- */

-

-// This file contains the code both for the main thread interface and the

-// worker that does the transcoding.

-const IN_WORKER = typeof importScripts === "function";

-const SCRIPT_PATH = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;

-

-if (!IN_WORKER) {

-  //

-  // Main Thread

-  //

-  class PendingTextureRequest {

-    constructor(gl, url) {

-      this.gl = gl;

-      this.url = url;

-      this.texture = null;

-      this.alphaTexture = null;

-      this.promise = new Promise((resolve, reject) => {

-        this.resolve = resolve;

-        this.reject = reject;

-      });

-    }

-

-    uploadImageData(webglFormat, buffer, mipLevels) {

-      let gl = this.gl;

-      let texture = gl.createTexture();

-      gl.bindTexture(gl.TEXTURE_2D, texture);

-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

-      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, mipLevels.length > 1 || webglFormat.uncompressed ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);

-

-      let levelData = null;

-

-      for (let mipLevel of mipLevels) {

-        if (!webglFormat.uncompressed) {

-          levelData = new Uint8Array(buffer, mipLevel.offset, mipLevel.size);

-          gl.compressedTexImage2D(

-            gl.TEXTURE_2D,

-            mipLevel.level,

-            webglFormat.format,

-            mipLevel.width,

-            mipLevel.height,

-            0,

-            levelData);

-        } else {

-          switch (webglFormat.type) {

-            case WebGLRenderingContext.UNSIGNED_SHORT_4_4_4_4:

-            case WebGLRenderingContext.UNSIGNED_SHORT_5_5_5_1:

-            case WebGLRenderingContext.UNSIGNED_SHORT_5_6_5:

-              levelData = new Uint16Array(buffer, mipLevel.offset, mipLevel.size / 2);

-              break;

-            default:

-              levelData = new Uint8Array(buffer, mipLevel.offset, mipLevel.size);

-              break;

-          }

-          gl.texImage2D(

-            gl.TEXTURE_2D,

-            mipLevel.level,

-            webglFormat.format,

-            mipLevel.width,

-            mipLevel.height,

-            0,

-            webglFormat.format,

-            webglFormat.type,

-            levelData);

-        }

-      }

-

-      if (webglFormat.uncompressed && mipLevels.length == 1) {

-        gl.generateMipmap(gl.TEXTURE_2D);

-      }

-

-      return texture;

-    }

-  };

-

-  class BasisLoader {

-    constructor() {

-      this.gl = null;

-      this.supportedFormats = {};

-      this.pendingTextures = {};

-      this.nextPendingTextureId = 1;

-      this.allowSeparateAlpha = false;

-

-      // Reload the current script as a worker

-      this.worker = new Worker(SCRIPT_PATH);

-      this.worker.onmessage = (msg) => {

-        // Find the pending texture associated with the data we just received

-        // from the worker.

-        let pendingTexture = this.pendingTextures[msg.data.id];

-        if (!pendingTexture) {

-          if (msg.data.error) {

-            console.error(`Basis transcode failed: ${msg.data.error}`);

-          }

-          console.error(`Invalid pending texture ID: ${msg.data.id}`);

-          return;

-        }

-

-        // Remove the pending texture from the waiting list.

-        delete this.pendingTextures[msg.data.id];

-

-        // If the worker indicated an error has occured handle it now.

-        if (msg.data.error) {

-          console.error(`Basis transcode failed: ${msg.data.error}`);

-          pendingTexture.reject(`${msg.data.error}`);

-          return;

-        }

-

-        // Upload the image data returned by the worker.

-        pendingTexture.texture = pendingTexture.uploadImageData(

-            msg.data.webglFormat,

-            msg.data.buffer,

-            msg.data.mipLevels);

-

-        if (msg.data.alphaBuffer) {

-          pendingTexture.alphaTexture = pendingTexture.uploadImageData(

-              msg.data.webglFormat,

-              msg.data.alphaBuffer,

-              msg.data.mipLevels);

-        }

-

-        pendingTexture.resolve({

-          mipLevels: msg.data.mipLevels.length,

-          width: msg.data.mipLevels[0].width,

-          height: msg.data.mipLevels[0].height,

-          alpha: msg.data.hasAlpha,

-          texture: pendingTexture.texture,

-          alphaTexture: pendingTexture.alphaTexture,

-        });

-      };

-    }

-

-    setWebGLContext(gl) {

-      if (this.gl != gl) {

-        this.gl = gl;

-        if (gl) {

-          this.supportedFormats = {

-            s3tc: !!gl.getExtension('WEBGL_compressed_texture_s3tc'),

-            etc1: !!gl.getExtension('WEBGL_compressed_texture_etc1'),

-            etc2: !!gl.getExtension('WEBGL_compressed_texture_etc'),

-            pvrtc: !!gl.getExtension('WEBGL_compressed_texture_pvrtc'),

-            astc: !!gl.getExtension('WEBGL_compressed_texture_astc'),

-            bptc: !!gl.getExtension('EXT_texture_compression_bptc')

-          };

-        } else {

-          this.supportedFormats = {};

-        }

-      }

-    }

-

-    // This method changes the active texture unit's TEXTURE_2D binding

-    // immediately prior to resolving the returned promise.

-    loadFromUrl(url) {

-      let pendingTexture = new PendingTextureRequest(this.gl, url);

-      this.pendingTextures[this.nextPendingTextureId] = pendingTexture;

-

-      this.worker.postMessage({

-        id: this.nextPendingTextureId,

-        url: url,

-        allowSeparateAlpha: this.allowSeparateAlpha,

-        supportedFormats: this.supportedFormats

-      });

-

-      this.nextPendingTextureId++;

-      return pendingTexture.promise;

-    }

-  }

-

-  window.BasisLoader = BasisLoader;

-

-} else {

-  //

-  // Worker

-  //

-  importScripts('basis_transcoder.js');

-

-  let BasisFile = null;

-

-  const BASIS_INITIALIZED = BASIS().then((module) => {

-    BasisFile = module.BasisFile;

-    module.initializeBasis();

-  });

-

-  // Copied from enum class transcoder_texture_format in basisu_transcoder.h with minor javascript-ification

-  const BASIS_FORMAT = {

-    // Compressed formats

-

-    // ETC1-2

-    cTFETC1_RGB: 0,							// Opaque only, returns RGB or alpha data if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified

-    cTFETC2_RGBA: 1,							// Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files

-

-    // BC1-5, BC7 (desktop, some mobile devices)

-    cTFBC1_RGB: 2,							// Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified

-    cTFBC3_RGBA: 3, 							// Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files

-    cTFBC4_R: 4,								// Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified

-    cTFBC5_RG: 5,								// XY: Two BC4 blocks, X=R and Y=Alpha, .basis file should have alpha data (if not Y will be all 255's)

-    cTFBC7_RGBA: 6,							// RGB or RGBA, mode 5 for ETC1S, modes (1,2,3,5,6,7) for UASTC

-

-    // PVRTC1 4bpp (mobile, PowerVR devices)

-    cTFPVRTC1_4_RGB: 8,						// Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.

-    cTFPVRTC1_4_RGBA: 9,					// Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.

-

-    // ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)

-    cTFASTC_4x4_RGBA: 10,					// Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files. Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.

-

-    // Uncompressed (raw pixel) formats

-    cTFRGBA32: 13,							// 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.

-    cTFRGB565: 14,							// 166pp RGB image stored in raster (not block) order in memory, R at bit position 11

-    cTFBGR565: 15,							// 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0

-    cTFRGBA4444: 16,							// 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0

-

-    cTFTotalTextureFormats: 22,

-  };

-

-  // WebGL compressed formats types, from:

-  // http://www.khronos.org/registry/webgl/extensions/

-

-  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/

-  const COMPRESSED_RGB_S3TC_DXT1_EXT  = 0x83F0;

-  const COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;

-  const COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2;

-  const COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3;

-

-  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/

-  const COMPRESSED_RGB_ETC1_WEBGL = 0x8D64;

-

-  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/

-  const COMPRESSED_R11_EAC                        = 0x9270;

-  const COMPRESSED_SIGNED_R11_EAC                 = 0x9271;

-  const COMPRESSED_RG11_EAC                       = 0x9272;

-  const COMPRESSED_SIGNED_RG11_EAC                = 0x9273;

-  const COMPRESSED_RGB8_ETC2                      = 0x9274;

-  const COMPRESSED_SRGB8_ETC2                     = 0x9275;

-  const COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2  = 0x9276;

-  const COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277;

-  const COMPRESSED_RGBA8_ETC2_EAC                 = 0x9278;

-  const COMPRESSED_SRGB8_ALPHA8_ETC2_EAC          = 0x9279;

-

-  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/

-  const COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0;

-

-  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/

-  const COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00;

-  const COMPRESSED_RGB_PVRTC_2BPPV1_IMG  = 0x8C01;

-  const COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02;

-  const COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03;

-

-  // https://www.khronos.org/registry/webgl/extensions/EXT_texture_compression_bptc/

-  const COMPRESSED_RGBA_BPTC_UNORM_EXT = 0x8E8C;

-  const COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT = 0x8E8D;

-  const COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT = 0x8E8E;

-  const COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT = 0x8E8F;

-

-  const BASIS_WEBGL_FORMAT_MAP = {};

-  // Compressed formats

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFBC1_RGB] = { format: COMPRESSED_RGB_S3TC_DXT1_EXT };

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFBC3_RGBA] = { format: COMPRESSED_RGBA_S3TC_DXT5_EXT };

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFBC7_RGBA] = { format: COMPRESSED_RGBA_BPTC_UNORM_EXT };

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFETC1_RGB] = { format: COMPRESSED_RGB_ETC1_WEBGL };

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFETC2_RGBA] = { format: COMPRESSED_RGBA8_ETC2_EAC };

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFASTC_4x4_RGBA] = { format: COMPRESSED_RGBA_ASTC_4x4_KHR };

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFPVRTC1_4_RGB] = { format: COMPRESSED_RGB_PVRTC_4BPPV1_IMG };

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFPVRTC1_4_RGBA] = { format: COMPRESSED_RGBA_PVRTC_4BPPV1_IMG };

-

-  // Uncompressed formats

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFRGBA32] = { uncompressed: true, format: WebGLRenderingContext.RGBA, type: WebGLRenderingContext.UNSIGNED_BYTE };

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFRGB565] = { uncompressed: true, format: WebGLRenderingContext.RGB, type: WebGLRenderingContext.UNSIGNED_SHORT_5_6_5 };

-  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFRGBA4444] = { uncompressed: true, format: WebGLRenderingContext.RGBA, type: WebGLRenderingContext.UNSIGNED_SHORT_4_4_4_4 };

-

-  // Notifies the main thread when a texture has failed to load for any reason.

-  function fail(id, errorMsg) {

-    postMessage({

-      id: id,

-      error: errorMsg

-    });

-  }

-

-  function basisFileFail(id, basisFile, errorMsg) {

-    fail(id, errorMsg);

-    basisFile.close();

-    basisFile.delete();

-  }

-

-  // This utility currently only transcodes the first image in the file.

-  const IMAGE_INDEX = 0;

-  const TOP_LEVEL_MIP = 0;

-

-  function transcode(id, arrayBuffer, supportedFormats, allowSeparateAlpha) {

-    let basisData = new Uint8Array(arrayBuffer);

-

-    let basisFile = new BasisFile(basisData);

-    let images = basisFile.getNumImages();

-    let levels = basisFile.getNumLevels(IMAGE_INDEX);

-    let hasAlpha = basisFile.getHasAlpha();

-    if (!images || !levels) {

-      basisFileFail(id, basisFile, 'Invalid Basis data');

-      return;

-    }

-

-    if (!basisFile.startTranscoding()) {

-      basisFileFail(id, basisFile, 'startTranscoding failed');

-      return;

-    }

-

-    let basisFormat = undefined;

-    let needsSecondaryAlpha = false;

-    if (hasAlpha) {

-      if (supportedFormats.etc2) {

-        basisFormat = BASIS_FORMAT.cTFETC2_RGBA;

-      } else if (supportedFormats.bptc) {

-        basisFormat = BASIS_FORMAT.cTFBC7_RGBA;

-      } else if (supportedFormats.s3tc) {

-        basisFormat = BASIS_FORMAT.cTFBC3_RGBA;

-      } else if (supportedFormats.astc) {

-        basisFormat = BASIS_FORMAT.cTFASTC_4x4_RGBA;

-      } else if (supportedFormats.pvrtc) {

-        if (allowSeparateAlpha) {

-          basisFormat = BASIS_FORMAT.cTFPVRTC1_4_RGB;

-          needsSecondaryAlpha = true;

-        } else {

-          basisFormat = BASIS_FORMAT.cTFPVRTC1_4_RGBA;

-        }

-      } else if (supportedFormats.etc1 && allowSeparateAlpha) {

-        basisFormat = BASIS_FORMAT.cTFETC1_RGB;

-        needsSecondaryAlpha = true;

-      } else {

-        // If we don't support any appropriate compressed formats transcode to

-        // raw pixels. This is something of a last resort, because the GPU

-        // upload will be significantly slower and take a lot more memory, but

-        // at least it prevents you from needing to store a fallback JPG/PNG and

-        // the download size will still likely be smaller.

-        basisFormat = BASIS_FORMAT.RGBA32;

-      }

-    } else {

-      if (supportedFormats.etc1) {

-        // Should be the highest quality, so use when available.

-        // http://richg42.blogspot.com/2018/05/basis-universal-gpu-texture-format.html

-        basisFormat = BASIS_FORMAT.cTFETC1_RGB;

-      } else if (supportedFormats.bptc) {

-        basisFormat = BASIS_FORMAT.cTFBC7_RGBA;

-      } else if (supportedFormats.s3tc) {

-        basisFormat = BASIS_FORMAT.cTFBC1_RGB;

-      } else if (supportedFormats.etc2) {

-        basisFormat = BASIS_FORMAT.cTFETC2_RGBA;

-      } else if (supportedFormats.astc) {

-        basisFormat = BASIS_FORMAT.cTFASTC_4x4_RGBA;

-      } else if (supportedFormats.pvrtc) {

-        basisFormat = BASIS_FORMAT.cTFPVRTC1_4_RGB;

-      } else {

-        // See note on uncompressed transcode above.

-        basisFormat = BASIS_FORMAT.cTFRGB565;

-      }

-    }

-

-    if (basisFormat === undefined) {

-      basisFileFail(id, basisFile, 'No supported transcode formats');

-      return;

-    }

-

-    let webglFormat = BASIS_WEBGL_FORMAT_MAP[basisFormat];

-

-    // If we're not using compressed textures it'll be cheaper to generate

-    // mipmaps on the fly, so only transcode a single level.

-    if (webglFormat.uncompressed) {

-      levels = 1;

-    }

-

-    // Gather information about each mip level to be transcoded.

-    let mipLevels = [];

-    let totalTranscodeSize = 0;

-

-    for (let mipLevel = 0; mipLevel < levels; ++mipLevel) {

-      let transcodeSize = basisFile.getImageTranscodedSizeInBytes(IMAGE_INDEX, mipLevel, basisFormat);

-      mipLevels.push({

-        level: mipLevel,

-        offset: totalTranscodeSize,

-        size: transcodeSize,

-        width: basisFile.getImageWidth(IMAGE_INDEX, mipLevel),

-        height: basisFile.getImageHeight(IMAGE_INDEX, mipLevel),

-      });

-      totalTranscodeSize += transcodeSize;

-    }

-

-    // Allocate a buffer large enough to hold all of the transcoded mip levels at once.

-    let transcodeData = new Uint8Array(totalTranscodeSize);

-    let alphaTranscodeData = needsSecondaryAlpha ? new Uint8Array(totalTranscodeSize) : null;

-

-    // Transcode each mip level into the appropriate section of the overall buffer.

-    for (let mipLevel of mipLevels) {

-      let levelData = new Uint8Array(transcodeData.buffer, mipLevel.offset, mipLevel.size);

-      if (!basisFile.transcodeImage(levelData, IMAGE_INDEX, mipLevel.level, basisFormat, 1, 0)) {

-        basisFileFail(id, basisFile, 'transcodeImage failed');

-        return;

-      }

-      if (needsSecondaryAlpha) {

-        let alphaLevelData = new Uint8Array(alphaTranscodeData.buffer, mipLevel.offset, mipLevel.size);

-        if (!basisFile.transcodeImage(alphaLevelData, IMAGE_INDEX, mipLevel.level, basisFormat, 1, 1)) {

-          basisFileFail(id, basisFile, 'alpha transcodeImage failed');

-          return;

-        }

-      }

-    }

-

-    basisFile.close();

-    basisFile.delete();

-

-    // Post the transcoded results back to the main thread.

-    let transferList = [transcodeData.buffer];

-    if (needsSecondaryAlpha) {

-      transferList.push(alphaTranscodeData.buffer);

-    }

-    postMessage({

-      id: id,

-      buffer: transcodeData.buffer,

-      alphaBuffer: needsSecondaryAlpha ? alphaTranscodeData.buffer : null,

-      webglFormat: webglFormat,

-      mipLevels: mipLevels,

-      hasAlpha: hasAlpha,

-    }, transferList);

-  }

-

-  onmessage = (msg) => {

-    // Each call to the worker must contain:

-    let url = msg.data.url; // The URL of the basis image OR

-    let buffer = msg.data.buffer; // An array buffer with the basis image data

-    let allowSeparateAlpha = msg.data.allowSeparateAlpha;

-    let supportedFormats = msg.data.supportedFormats; // The formats this device supports

-    let id = msg.data.id; // A unique ID for the texture

-

-    if (url) {

-      // Make the call to fetch the basis texture data

-      fetch(url).then(function(response) {

-        if (response.ok) {

-          response.arrayBuffer().then((arrayBuffer) => {

-            if (BasisFile) {

-              transcode(id, arrayBuffer, supportedFormats, allowSeparateAlpha);

-            } else {

-              BASIS_INITIALIZED.then(() => {

-                transcode(id, arrayBuffer, supportedFormats, allowSeparateAlpha);

-              });

-            }

-          });

-        } else {

-          fail(id, `Fetch failed: ${response.status}, ${response.statusText}`);

-        }

-      });

-    } else if (buffer) {

-      if (BasisFile) {

-        transcode(id, buffer, supportedFormats, allowSeparateAlpha);

-      } else {

-        BASIS_INITIALIZED.then(() => {

-          transcode(id, buffer, supportedFormats, allowSeparateAlpha);

-        });

-      }

-    } else {

-      fail(id, `No url or buffer specified`);

-    }

-  };

+/*
+ * Basis Loader
+ *
+ * Usage:
+ * // basis_loader.js should be loaded from the same directory as
+ * // basis_transcoder.js and basis_transcoder.wasm
+ *
+ * // Create the texture loader and set the WebGL context it should use. Spawns
+ * // a worker which handles all of the transcoding.
+ * let basisLoader = new BasisLoader();
+ * basisLoader.setWebGLContext(gl);
+ *
+ * // To allow separate color and alpha textures to be returned in cases where
+ * // it would provide higher quality:
+ * basisLoader.allowSeparateAlpha = true;
+ *
+ * // loadFromUrl() returns a promise which resolves to a completed WebGL
+ * // texture or rejects if there's an error loading.
+ * basisBasics.loadFromUrl(fullPathToTexture).then((result) => {
+ *   // WebGL color+alpha texture;
+ *   result.texture;
+ *
+ *   // WebGL alpha texture, only if basisLoader.allowSeparateAlpha is true.
+ *   // null if alpha is encoded in result.texture or result.alpha is false.
+ *   result.alphaTexture;
+ *
+ *   // True if the texture contained an alpha channel.
+ *   result.alpha;
+ *
+ *   // Number of mip levels in texture/alphaTexture
+ *   result.mipLevels;
+ *
+ *   // Dimensions of the base mip level.
+ *   result.width;
+ *   result.height;
+ * });
+ */
+
+// This file contains the code both for the main thread interface and the
+// worker that does the transcoding.
+const IN_WORKER = typeof importScripts === "function";
+const SCRIPT_PATH = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
+
+if (!IN_WORKER) {
+  //
+  // Main Thread
+  //
+  class PendingTextureRequest {
+    constructor(gl, url) {
+      this.gl = gl;
+      this.url = url;
+      this.texture = null;
+      this.alphaTexture = null;
+      this.promise = new Promise((resolve, reject) => {
+        this.resolve = resolve;
+        this.reject = reject;
+      });
+    }
+
+    uploadImageData(webglFormat, buffer, mipLevels) {
+      let gl = this.gl;
+      let texture = gl.createTexture();
+      gl.bindTexture(gl.TEXTURE_2D, texture);
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, mipLevels.length > 1 || webglFormat.uncompressed ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
+
+      let levelData = null;
+
+      for (let mipLevel of mipLevels) {
+        if (!webglFormat.uncompressed) {
+          levelData = new Uint8Array(buffer, mipLevel.offset, mipLevel.size);
+          gl.compressedTexImage2D(
+            gl.TEXTURE_2D,
+            mipLevel.level,
+            webglFormat.format,
+            mipLevel.width,
+            mipLevel.height,
+            0,
+            levelData);
+        } else {
+          switch (webglFormat.type) {
+            case WebGLRenderingContext.UNSIGNED_SHORT_4_4_4_4:
+            case WebGLRenderingContext.UNSIGNED_SHORT_5_5_5_1:
+            case WebGLRenderingContext.UNSIGNED_SHORT_5_6_5:
+              levelData = new Uint16Array(buffer, mipLevel.offset, mipLevel.size / 2);
+              break;
+            default:
+              levelData = new Uint8Array(buffer, mipLevel.offset, mipLevel.size);
+              break;
+          }
+          gl.texImage2D(
+            gl.TEXTURE_2D,
+            mipLevel.level,
+            webglFormat.format,
+            mipLevel.width,
+            mipLevel.height,
+            0,
+            webglFormat.format,
+            webglFormat.type,
+            levelData);
+        }
+      }
+
+      if (webglFormat.uncompressed && mipLevels.length == 1) {
+        gl.generateMipmap(gl.TEXTURE_2D);
+      }
+
+      return texture;
+    }
+  };
+
+  class BasisLoader {
+    constructor() {
+      this.gl = null;
+      this.supportedFormats = {};
+      this.pendingTextures = {};
+      this.nextPendingTextureId = 1;
+      this.allowSeparateAlpha = false;
+
+      // Reload the current script as a worker
+      this.worker = new Worker(SCRIPT_PATH);
+      this.worker.onmessage = (msg) => {
+        // Find the pending texture associated with the data we just received
+        // from the worker.
+        let pendingTexture = this.pendingTextures[msg.data.id];
+        if (!pendingTexture) {
+          if (msg.data.error) {
+            console.error(`Basis transcode failed: ${msg.data.error}`);
+          }
+          console.error(`Invalid pending texture ID: ${msg.data.id}`);
+          return;
+        }
+
+        // Remove the pending texture from the waiting list.
+        delete this.pendingTextures[msg.data.id];
+
+        // If the worker indicated an error has occured handle it now.
+        if (msg.data.error) {
+          console.error(`Basis transcode failed: ${msg.data.error}`);
+          pendingTexture.reject(`${msg.data.error}`);
+          return;
+        }
+
+        // Upload the image data returned by the worker.
+        pendingTexture.texture = pendingTexture.uploadImageData(
+            msg.data.webglFormat,
+            msg.data.buffer,
+            msg.data.mipLevels);
+
+        if (msg.data.alphaBuffer) {
+          pendingTexture.alphaTexture = pendingTexture.uploadImageData(
+              msg.data.webglFormat,
+              msg.data.alphaBuffer,
+              msg.data.mipLevels);
+        }
+
+        pendingTexture.resolve({
+          mipLevels: msg.data.mipLevels.length,
+          width: msg.data.mipLevels[0].width,
+          height: msg.data.mipLevels[0].height,
+          alpha: msg.data.hasAlpha,
+          texture: pendingTexture.texture,
+          alphaTexture: pendingTexture.alphaTexture,
+        });
+      };
+    }
+
+    setWebGLContext(gl) {
+      if (this.gl != gl) {
+        this.gl = gl;
+        if (gl) {
+          this.supportedFormats = {
+            s3tc: !!gl.getExtension('WEBGL_compressed_texture_s3tc'),
+            etc1: !!gl.getExtension('WEBGL_compressed_texture_etc1'),
+            etc2: !!gl.getExtension('WEBGL_compressed_texture_etc'),
+            pvrtc: !!gl.getExtension('WEBGL_compressed_texture_pvrtc'),
+            astc: !!gl.getExtension('WEBGL_compressed_texture_astc'),
+            bptc: !!gl.getExtension('EXT_texture_compression_bptc')
+          };
+        } else {
+          this.supportedFormats = {};
+        }
+      }
+    }
+
+    // This method changes the active texture unit's TEXTURE_2D binding
+    // immediately prior to resolving the returned promise.
+    loadFromUrl(url) {
+      let pendingTexture = new PendingTextureRequest(this.gl, url);
+      this.pendingTextures[this.nextPendingTextureId] = pendingTexture;
+
+      this.worker.postMessage({
+        id: this.nextPendingTextureId,
+        url: url,
+        allowSeparateAlpha: this.allowSeparateAlpha,
+        supportedFormats: this.supportedFormats
+      });
+
+      this.nextPendingTextureId++;
+      return pendingTexture.promise;
+    }
+  }
+
+  window.BasisLoader = BasisLoader;
+
+} else {
+  //
+  // Worker
+  //
+  importScripts('basis_transcoder.js');
+
+  let BasisFile = null;
+
+  const BASIS_INITIALIZED = BASIS().then((module) => {
+    BasisFile = module.BasisFile;
+    module.initializeBasis();
+  });
+
+  // Copied from enum class transcoder_texture_format in basisu_transcoder.h with minor javascript-ification
+  const BASIS_FORMAT = {
+    // Compressed formats
+
+    // ETC1-2
+    cTFETC1_RGB: 0,							// Opaque only, returns RGB or alpha data if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
+    cTFETC2_RGBA: 1,							// Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files
+
+    // BC1-5, BC7 (desktop, some mobile devices)
+    cTFBC1_RGB: 2,							// Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
+    cTFBC3_RGBA: 3, 							// Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files
+    cTFBC4_R: 4,								// Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
+    cTFBC5_RG: 5,								// XY: Two BC4 blocks, X=R and Y=Alpha, .basis file should have alpha data (if not Y will be all 255's)
+    cTFBC7_RGBA: 6,							// RGB or RGBA, mode 5 for ETC1S, modes (1,2,3,5,6,7) for UASTC
+
+    // PVRTC1 4bpp (mobile, PowerVR devices)
+    cTFPVRTC1_4_RGB: 8,						// Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.
+    cTFPVRTC1_4_RGBA: 9,					// Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.
+
+    // ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)
+    cTFASTC_4x4_RGBA: 10,					// Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files. Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
+
+    // Uncompressed (raw pixel) formats
+    cTFRGBA32: 13,							// 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.
+    cTFRGB565: 14,							// 166pp RGB image stored in raster (not block) order in memory, R at bit position 11
+    cTFBGR565: 15,							// 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0
+    cTFRGBA4444: 16,							// 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
+
+    cTFTotalTextureFormats: 22,
+  };
+
+  // WebGL compressed formats types, from:
+  // http://www.khronos.org/registry/webgl/extensions/
+
+  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
+  const COMPRESSED_RGB_S3TC_DXT1_EXT  = 0x83F0;
+  const COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
+  const COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2;
+  const COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3;
+
+  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/
+  const COMPRESSED_RGB_ETC1_WEBGL = 0x8D64;
+
+  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/
+  const COMPRESSED_R11_EAC                        = 0x9270;
+  const COMPRESSED_SIGNED_R11_EAC                 = 0x9271;
+  const COMPRESSED_RG11_EAC                       = 0x9272;
+  const COMPRESSED_SIGNED_RG11_EAC                = 0x9273;
+  const COMPRESSED_RGB8_ETC2                      = 0x9274;
+  const COMPRESSED_SRGB8_ETC2                     = 0x9275;
+  const COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2  = 0x9276;
+  const COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277;
+  const COMPRESSED_RGBA8_ETC2_EAC                 = 0x9278;
+  const COMPRESSED_SRGB8_ALPHA8_ETC2_EAC          = 0x9279;
+
+  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/
+  const COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0;
+
+  // https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/
+  const COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00;
+  const COMPRESSED_RGB_PVRTC_2BPPV1_IMG  = 0x8C01;
+  const COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02;
+  const COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03;
+
+  // https://www.khronos.org/registry/webgl/extensions/EXT_texture_compression_bptc/
+  const COMPRESSED_RGBA_BPTC_UNORM_EXT = 0x8E8C;
+  const COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT = 0x8E8D;
+  const COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT = 0x8E8E;
+  const COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT = 0x8E8F;
+
+  const BASIS_WEBGL_FORMAT_MAP = {};
+  // Compressed formats
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFBC1_RGB] = { format: COMPRESSED_RGB_S3TC_DXT1_EXT };
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFBC3_RGBA] = { format: COMPRESSED_RGBA_S3TC_DXT5_EXT };
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFBC7_RGBA] = { format: COMPRESSED_RGBA_BPTC_UNORM_EXT };
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFETC1_RGB] = { format: COMPRESSED_RGB_ETC1_WEBGL };
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFETC2_RGBA] = { format: COMPRESSED_RGBA8_ETC2_EAC };
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFASTC_4x4_RGBA] = { format: COMPRESSED_RGBA_ASTC_4x4_KHR };
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFPVRTC1_4_RGB] = { format: COMPRESSED_RGB_PVRTC_4BPPV1_IMG };
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFPVRTC1_4_RGBA] = { format: COMPRESSED_RGBA_PVRTC_4BPPV1_IMG };
+
+  // Uncompressed formats
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFRGBA32] = { uncompressed: true, format: WebGLRenderingContext.RGBA, type: WebGLRenderingContext.UNSIGNED_BYTE };
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFRGB565] = { uncompressed: true, format: WebGLRenderingContext.RGB, type: WebGLRenderingContext.UNSIGNED_SHORT_5_6_5 };
+  BASIS_WEBGL_FORMAT_MAP[BASIS_FORMAT.cTFRGBA4444] = { uncompressed: true, format: WebGLRenderingContext.RGBA, type: WebGLRenderingContext.UNSIGNED_SHORT_4_4_4_4 };
+
+  // Notifies the main thread when a texture has failed to load for any reason.
+  function fail(id, errorMsg) {
+    postMessage({
+      id: id,
+      error: errorMsg
+    });
+  }
+
+  function basisFileFail(id, basisFile, errorMsg) {
+    fail(id, errorMsg);
+    basisFile.close();
+    basisFile.delete();
+  }
+
+  // This utility currently only transcodes the first image in the file.
+  const IMAGE_INDEX = 0;
+  const TOP_LEVEL_MIP = 0;
+
+  function transcode(id, arrayBuffer, supportedFormats, allowSeparateAlpha) {
+    let basisData = new Uint8Array(arrayBuffer);
+
+    let basisFile = new BasisFile(basisData);
+    let images = basisFile.getNumImages();
+    let levels = basisFile.getNumLevels(IMAGE_INDEX);
+    let hasAlpha = basisFile.getHasAlpha();
+    if (!images || !levels) {
+      basisFileFail(id, basisFile, 'Invalid Basis data');
+      return;
+    }
+
+    if (!basisFile.startTranscoding()) {
+      basisFileFail(id, basisFile, 'startTranscoding failed');
+      return;
+    }
+
+    let basisFormat = undefined;
+    let needsSecondaryAlpha = false;
+    if (hasAlpha) {
+      if (supportedFormats.etc2) {
+        basisFormat = BASIS_FORMAT.cTFETC2_RGBA;
+      } else if (supportedFormats.bptc) {
+        basisFormat = BASIS_FORMAT.cTFBC7_RGBA;
+      } else if (supportedFormats.s3tc) {
+        basisFormat = BASIS_FORMAT.cTFBC3_RGBA;
+      } else if (supportedFormats.astc) {
+        basisFormat = BASIS_FORMAT.cTFASTC_4x4_RGBA;
+      } else if (supportedFormats.pvrtc) {
+        if (allowSeparateAlpha) {
+          basisFormat = BASIS_FORMAT.cTFPVRTC1_4_RGB;
+          needsSecondaryAlpha = true;
+        } else {
+          basisFormat = BASIS_FORMAT.cTFPVRTC1_4_RGBA;
+        }
+      } else if (supportedFormats.etc1 && allowSeparateAlpha) {
+        basisFormat = BASIS_FORMAT.cTFETC1_RGB;
+        needsSecondaryAlpha = true;
+      } else {
+        // If we don't support any appropriate compressed formats transcode to
+        // raw pixels. This is something of a last resort, because the GPU
+        // upload will be significantly slower and take a lot more memory, but
+        // at least it prevents you from needing to store a fallback JPG/PNG and
+        // the download size will still likely be smaller.
+        basisFormat = BASIS_FORMAT.RGBA32;
+      }
+    } else {
+      if (supportedFormats.etc1) {
+        // Should be the highest quality, so use when available.
+        // http://richg42.blogspot.com/2018/05/basis-universal-gpu-texture-format.html
+        basisFormat = BASIS_FORMAT.cTFETC1_RGB;
+      } else if (supportedFormats.bptc) {
+        basisFormat = BASIS_FORMAT.cTFBC7_RGBA;
+      } else if (supportedFormats.s3tc) {
+        basisFormat = BASIS_FORMAT.cTFBC1_RGB;
+      } else if (supportedFormats.etc2) {
+        basisFormat = BASIS_FORMAT.cTFETC2_RGBA;
+      } else if (supportedFormats.astc) {
+        basisFormat = BASIS_FORMAT.cTFASTC_4x4_RGBA;
+      } else if (supportedFormats.pvrtc) {
+        basisFormat = BASIS_FORMAT.cTFPVRTC1_4_RGB;
+      } else {
+        // See note on uncompressed transcode above.
+        basisFormat = BASIS_FORMAT.cTFRGB565;
+      }
+    }
+
+    if (basisFormat === undefined) {
+      basisFileFail(id, basisFile, 'No supported transcode formats');
+      return;
+    }
+
+    let webglFormat = BASIS_WEBGL_FORMAT_MAP[basisFormat];
+
+    // If we're not using compressed textures it'll be cheaper to generate
+    // mipmaps on the fly, so only transcode a single level.
+    if (webglFormat.uncompressed) {
+      levels = 1;
+    }
+
+    // Gather information about each mip level to be transcoded.
+    let mipLevels = [];
+    let totalTranscodeSize = 0;
+
+    for (let mipLevel = 0; mipLevel < levels; ++mipLevel) {
+      let transcodeSize = basisFile.getImageTranscodedSizeInBytes(IMAGE_INDEX, mipLevel, basisFormat);
+      mipLevels.push({
+        level: mipLevel,
+        offset: totalTranscodeSize,
+        size: transcodeSize,
+        width: basisFile.getImageWidth(IMAGE_INDEX, mipLevel),
+        height: basisFile.getImageHeight(IMAGE_INDEX, mipLevel),
+      });
+      totalTranscodeSize += transcodeSize;
+    }
+
+    // Allocate a buffer large enough to hold all of the transcoded mip levels at once.
+    let transcodeData = new Uint8Array(totalTranscodeSize);
+    let alphaTranscodeData = needsSecondaryAlpha ? new Uint8Array(totalTranscodeSize) : null;
+
+    // Transcode each mip level into the appropriate section of the overall buffer.
+    for (let mipLevel of mipLevels) {
+      let levelData = new Uint8Array(transcodeData.buffer, mipLevel.offset, mipLevel.size);
+      if (!basisFile.transcodeImage(levelData, IMAGE_INDEX, mipLevel.level, basisFormat, 1, 0)) {
+        basisFileFail(id, basisFile, 'transcodeImage failed');
+        return;
+      }
+      if (needsSecondaryAlpha) {
+        let alphaLevelData = new Uint8Array(alphaTranscodeData.buffer, mipLevel.offset, mipLevel.size);
+        if (!basisFile.transcodeImage(alphaLevelData, IMAGE_INDEX, mipLevel.level, basisFormat, 1, 1)) {
+          basisFileFail(id, basisFile, 'alpha transcodeImage failed');
+          return;
+        }
+      }
+    }
+
+    basisFile.close();
+    basisFile.delete();
+
+    // Post the transcoded results back to the main thread.
+    let transferList = [transcodeData.buffer];
+    if (needsSecondaryAlpha) {
+      transferList.push(alphaTranscodeData.buffer);
+    }
+    postMessage({
+      id: id,
+      buffer: transcodeData.buffer,
+      alphaBuffer: needsSecondaryAlpha ? alphaTranscodeData.buffer : null,
+      webglFormat: webglFormat,
+      mipLevels: mipLevels,
+      hasAlpha: hasAlpha,
+    }, transferList);
+  }
+
+  onmessage = (msg) => {
+    // Each call to the worker must contain:
+    let url = msg.data.url; // The URL of the basis image OR
+    let buffer = msg.data.buffer; // An array buffer with the basis image data
+    let allowSeparateAlpha = msg.data.allowSeparateAlpha;
+    let supportedFormats = msg.data.supportedFormats; // The formats this device supports
+    let id = msg.data.id; // A unique ID for the texture
+
+    if (url) {
+      // Make the call to fetch the basis texture data
+      fetch(url).then(function(response) {
+        if (response.ok) {
+          response.arrayBuffer().then((arrayBuffer) => {
+            if (BasisFile) {
+              transcode(id, arrayBuffer, supportedFormats, allowSeparateAlpha);
+            } else {
+              BASIS_INITIALIZED.then(() => {
+                transcode(id, arrayBuffer, supportedFormats, allowSeparateAlpha);
+              });
+            }
+          });
+        } else {
+          fail(id, `Fetch failed: ${response.status}, ${response.statusText}`);
+        }
+      });
+    } else if (buffer) {
+      if (BasisFile) {
+        transcode(id, buffer, supportedFormats, allowSeparateAlpha);
+      } else {
+        BASIS_INITIALIZED.then(() => {
+          transcode(id, buffer, supportedFormats, allowSeparateAlpha);
+        });
+      }
+    } else {
+      fail(id, `No url or buffer specified`);
+    }
+  };
 }