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`);
+ }
+ };
}