UNPKG

@loaders.gl/textures

Version:

Framework-independent loaders for compressed and super compressed (basis) textures

981 lines (964 loc) 31.5 kB
"use strict"; (() => { // ../loader-utils/src/lib/module-utils/js-module-utils.ts function registerJSModules(modules) { globalThis.loaders ||= {}; globalThis.loaders.modules ||= {}; Object.assign(globalThis.loaders.modules, modules); } function getJSModuleOrNull(name) { const module = globalThis.loaders?.modules?.[name]; return module || null; } // ../worker-utils/src/lib/env-utils/version.ts function getVersion() { if (!globalThis._loadersgl_?.version) { globalThis._loadersgl_ = globalThis._loadersgl_ || {}; if (false) { console.warn( "loaders.gl: The __VERSION__ variable is not injected using babel plugin. Latest unstable workers would be fetched from the CDN." ); globalThis._loadersgl_.version = NPM_TAG; warningIssued = true; } else { globalThis._loadersgl_.version = "4.4.2"; } } return globalThis._loadersgl_.version; } var VERSION = getVersion(); // ../worker-utils/src/lib/env-utils/assert.ts function assert(condition, message) { if (!condition) { throw new Error(message || "loaders.gl assertion failed."); } } // ../worker-utils/src/lib/env-utils/globals.ts var globals = { self: typeof self !== "undefined" && self, window: typeof window !== "undefined" && window, global: typeof global !== "undefined" && global, document: typeof document !== "undefined" && document }; var self_ = globals.self || globals.window || globals.global || {}; var window_ = globals.window || globals.self || globals.global || {}; var global_ = globals.global || globals.self || globals.window || {}; var document_ = globals.document || {}; var isBrowser = ( // @ts-ignore process.browser typeof process !== "object" || String(process) !== "[object process]" || process.browser ); var isWorker = typeof importScripts === "function"; var isMobile = typeof window !== "undefined" && typeof window.orientation !== "undefined"; var matches = typeof process !== "undefined" && process.version && /v([0-9]*)/.exec(process.version); var nodeVersion = matches && parseFloat(matches[1]) || 0; // ../worker-utils/src/lib/node/worker_threads-browser.ts var parentPort = null; // ../worker-utils/src/lib/worker-utils/get-transfer-list.ts function getTransferList(object, recursive = true, transfers) { const transfersSet = transfers || /* @__PURE__ */ new Set(); if (!object) { } else if (isTransferable(object)) { transfersSet.add(object); } else if (isTransferable(object.buffer)) { transfersSet.add(object.buffer); } else if (ArrayBuffer.isView(object)) { } else if (recursive && typeof object === "object") { for (const key in object) { getTransferList(object[key], recursive, transfersSet); } } return transfers === void 0 ? Array.from(transfersSet) : []; } function isTransferable(object) { if (!object) { return false; } if (object instanceof ArrayBuffer) { return true; } if (typeof MessagePort !== "undefined" && object instanceof MessagePort) { return true; } if (typeof ImageBitmap !== "undefined" && object instanceof ImageBitmap) { return true; } if (typeof OffscreenCanvas !== "undefined" && object instanceof OffscreenCanvas) { return true; } return false; } // ../worker-utils/src/lib/worker-farm/worker-body.ts async function getParentPort() { return parentPort; } var onMessageWrapperMap = /* @__PURE__ */ new Map(); var WorkerBody = class { /** Check that we are actually in a worker thread */ static async inWorkerThread() { return typeof self !== "undefined" || Boolean(await getParentPort()); } /* * (type: WorkerMessageType, payload: WorkerMessagePayload) => any */ static set onmessage(onMessage) { async function handleMessage(message) { const parentPort2 = await getParentPort(); const { type, payload } = parentPort2 ? message : message.data; onMessage(type, payload); } getParentPort().then((parentPort2) => { if (parentPort2) { parentPort2.on("message", (message) => { handleMessage(message); }); parentPort2.on("exit", () => console.debug("Node worker closing")); } else { globalThis.onmessage = handleMessage; } }); } static async addEventListener(onMessage) { let onMessageWrapper = onMessageWrapperMap.get(onMessage); if (!onMessageWrapper) { onMessageWrapper = async (message) => { if (!isKnownMessage(message)) { return; } const parentPort3 = await getParentPort(); const { type, payload } = parentPort3 ? message : message.data; onMessage(type, payload); }; } const parentPort2 = await getParentPort(); if (parentPort2) { console.error("not implemented"); } else { globalThis.addEventListener("message", onMessageWrapper); } } static async removeEventListener(onMessage) { const onMessageWrapper = onMessageWrapperMap.get(onMessage); onMessageWrapperMap.delete(onMessage); const parentPort2 = await getParentPort(); if (parentPort2) { console.error("not implemented"); } else { globalThis.removeEventListener("message", onMessageWrapper); } } /** * Send a message from a worker to creating thread (main thread) * @param type * @param payload */ static async postMessage(type, payload) { const data = { source: "loaders.gl", type, payload }; const transferList = getTransferList(payload); const parentPort2 = await getParentPort(); if (parentPort2) { parentPort2.postMessage(data, transferList); } else { globalThis.postMessage(data, transferList); } } }; function isKnownMessage(message) { const { type, data } = message; return type === "message" && data && typeof data.source === "string" && data.source.startsWith("loaders.gl"); } // ../worker-utils/src/lib/library-utils/library-utils.ts var loadLibraryPromises = {}; function extractLoadLibraryOptions(options = {}) { const useLocalLibraries = options.useLocalLibraries ?? options.core?.useLocalLibraries; const CDN = options.CDN ?? options.core?.CDN; const modules = options.modules; return { ...useLocalLibraries !== void 0 ? { useLocalLibraries } : {}, ...CDN !== void 0 ? { CDN } : {}, ...modules !== void 0 ? { modules } : {} }; } async function loadLibrary(libraryUrl, moduleName = null, options = {}, libraryName = null) { if (moduleName) { libraryUrl = getLibraryUrl(libraryUrl, moduleName, options, libraryName); } loadLibraryPromises[libraryUrl] = // eslint-disable-next-line @typescript-eslint/no-misused-promises loadLibraryPromises[libraryUrl] || loadLibraryFromFile(libraryUrl); return await loadLibraryPromises[libraryUrl]; } function getLibraryUrl(library, moduleName, options = {}, libraryName = null) { if (options?.core) { throw new Error("loadLibrary: options.core must be pre-normalized"); } if (!options.useLocalLibraries && library.startsWith("http")) { return library; } libraryName = libraryName || library; const modules = options.modules || {}; if (modules[libraryName]) { return modules[libraryName]; } if (!isBrowser) { return `modules/${moduleName}/dist/libs/${libraryName}`; } if (options.CDN) { assert(options.CDN.startsWith("http")); return `${options.CDN}/${moduleName}@${VERSION}/dist/libs/${libraryName}`; } if (isWorker) { return `../src/libs/${libraryName}`; } return `modules/${moduleName}/src/libs/${libraryName}`; } async function loadLibraryFromFile(libraryUrl) { if (libraryUrl.endsWith("wasm")) { return await loadAsArrayBuffer(libraryUrl); } if (!isBrowser) { const { requireFromFile } = globalThis.loaders || {}; try { const result = await requireFromFile?.(libraryUrl); if (result || !libraryUrl.includes("/dist/libs/")) { return result; } return await requireFromFile?.(libraryUrl.replace("/dist/libs/", "/src/libs/")); } catch (error) { if (libraryUrl.includes("/dist/libs/")) { try { return await requireFromFile?.(libraryUrl.replace("/dist/libs/", "/src/libs/")); } catch { } } console.error(error); return null; } } if (isWorker) { return importScripts(libraryUrl); } const scriptSource = await loadAsText(libraryUrl); return loadLibraryFromString(scriptSource, libraryUrl); } function loadLibraryFromString(scriptSource, id) { if (!isBrowser) { const { requireFromString } = globalThis.loaders || {}; return requireFromString?.(scriptSource, id); } if (isWorker) { eval.call(globalThis, scriptSource); return null; } const script = document.createElement("script"); script.id = id; try { script.appendChild(document.createTextNode(scriptSource)); } catch (e) { script.text = scriptSource; } document.body.appendChild(script); return null; } async function loadAsArrayBuffer(url) { const { readFileAsArrayBuffer } = globalThis.loaders || {}; if (isBrowser || !readFileAsArrayBuffer || url.startsWith("http")) { const response = await fetch(url); return await response.arrayBuffer(); } try { return await readFileAsArrayBuffer(url); } catch { if (url.includes("/dist/libs/")) { return await readFileAsArrayBuffer(url.replace("/dist/libs/", "/src/libs/")); } throw new Error(`Failed to load ArrayBuffer from ${url}`); } } async function loadAsText(url) { const { readFileAsText } = globalThis.loaders || {}; if (isBrowser || !readFileAsText || url.startsWith("http")) { const response = await fetch(url); return await response.text(); } try { return await readFileAsText(url); } catch { if (url.includes("/dist/libs/")) { return await readFileAsText(url.replace("/dist/libs/", "/src/libs/")); } throw new Error(`Failed to load text from ${url}`); } } // ../loader-utils/src/lib/worker-loader-utils/create-loader-worker.ts var requestId = 0; async function createLoaderWorker(loader) { if (!await WorkerBody.inWorkerThread()) { return; } WorkerBody.onmessage = async (type, payload) => { switch (type) { case "process": try { const { input, options = {}, context = {} } = payload; const result = await parseData({ loader, arrayBuffer: input, options, // @ts-expect-error fetch missing context: { ...context, _parse: parseOnMainThread } }); WorkerBody.postMessage("done", { result }); } catch (error) { const message = error instanceof Error ? error.message : ""; WorkerBody.postMessage("error", { error: message }); } break; default: } }; } function parseOnMainThread(arrayBuffer, loader, options, context) { return new Promise((resolve, reject) => { const id = requestId++; const onMessage = (type, payload2) => { if (payload2.id !== id) { return; } switch (type) { case "done": WorkerBody.removeEventListener(onMessage); resolve(payload2.result); break; case "error": WorkerBody.removeEventListener(onMessage); reject(payload2.error); break; default: } }; WorkerBody.addEventListener(onMessage); const payload = { id, input: arrayBuffer, options }; WorkerBody.postMessage("process", payload); }); } async function parseData({ loader, arrayBuffer, options, context }) { let data; let parser; if (loader.parseSync || loader.parse) { data = arrayBuffer; parser = loader.parseSync || loader.parse; } else if (loader.parseTextSync) { const textDecoder = new TextDecoder(); data = textDecoder.decode(arrayBuffer); parser = loader.parseTextSync; } else { throw new Error(`Could not load data with ${loader.name} loader`); } options = { ...options, modules: loader && loader.options && loader.options.modules || {}, core: { ...options.core, worker: false } }; return await parser(data, { ...options }, context, loader); } // src/lib/utils/version.ts var VERSION2 = true ? "4.4.2" : "latest"; // src/lib/parsers/basis-module-loader.ts var BASIS_EXTERNAL_LIBRARIES = { /** Basis transcoder, javascript wrapper part */ TRANSCODER: "basis_transcoder.js", /** Basis transcoder, compiled web assembly part */ TRANSCODER_WASM: "basis_transcoder.wasm", /** Basis encoder, javascript wrapper part */ ENCODER: "basis_encoder.js", /** Basis encoder, compiled web assembly part */ ENCODER_WASM: "basis_encoder.wasm" }; var loadBasisTranscoderPromise; async function loadBasisTranscoderModule(options) { registerJSModules(options.modules); const basis = getJSModuleOrNull("basis"); if (basis) { return basis; } loadBasisTranscoderPromise ||= loadBasisTranscoder(options); return await loadBasisTranscoderPromise; } async function loadBasisTranscoder(options) { let BASIS = null; let wasmBinary = null; [BASIS, wasmBinary] = await Promise.all([ await loadLibrary(BASIS_EXTERNAL_LIBRARIES.TRANSCODER, "textures", options), await loadLibrary(BASIS_EXTERNAL_LIBRARIES.TRANSCODER_WASM, "textures", options) ]); BASIS = BASIS || globalThis.BASIS; return await initializeBasisTranscoderModule(BASIS, wasmBinary); } function initializeBasisTranscoderModule(BasisModule, wasmBinary) { const options = {}; if (wasmBinary) { options.wasmBinary = wasmBinary; } return new Promise((resolve) => { BasisModule(options).then((module) => { const { BasisFile, initializeBasis } = module; initializeBasis(); resolve({ BasisFile }); }); }); } var loadBasisEncoderPromise; async function loadBasisEncoderModule(options) { const modules = options.modules || {}; if (modules.basisEncoder) { return modules.basisEncoder; } loadBasisEncoderPromise = loadBasisEncoderPromise || loadBasisEncoder(options); return await loadBasisEncoderPromise; } async function loadBasisEncoder(options) { let BASIS_ENCODER = null; let wasmBinary = null; [BASIS_ENCODER, wasmBinary] = await Promise.all([ await loadLibrary(BASIS_EXTERNAL_LIBRARIES.ENCODER, "textures", options), await loadLibrary(BASIS_EXTERNAL_LIBRARIES.ENCODER_WASM, "textures", options) ]); BASIS_ENCODER = BASIS_ENCODER || globalThis.BASIS; return await initializeBasisEncoderModule(BASIS_ENCODER, wasmBinary); } function initializeBasisEncoderModule(BasisEncoderModule, wasmBinary) { const options = {}; if (wasmBinary) { options.wasmBinary = wasmBinary; } return new Promise((resolve) => { BasisEncoderModule(options).then((module) => { const { BasisFile, KTX2File, initializeBasis, BasisEncoder } = module; initializeBasis(); resolve({ BasisFile, KTX2File, BasisEncoder }); }); }); } // src/lib/gl-extensions.ts var GL_RGBA4 = 32854; var GL_RGBA8 = 32856; var GL_RGB565 = 36194; var GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 33776; var GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 33779; var GL_COMPRESSED_RGBA8_ETC2_EAC = 37493; var GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 35840; var GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 35842; var GL_COMPRESSED_RGB_ETC1_WEBGL = 36196; var GL_COMPRESSED_RGB_ATC_WEBGL = 35986; var GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL = 34798; var GL_COMPRESSED_RGBA_ASTC_4x4_KHR = 37808; var GL_COMPRESSED_RED_RGTC1_EXT = 36283; var GL_COMPRESSED_RED_GREEN_RGTC2_EXT = 36285; var GL_COMPRESSED_RGBA_BPTC_UNORM_EXT = 36492; // src/lib/utils/detect-supported-texture-formats.ts var BROWSER_PREFIXES = ["", "WEBKIT_", "MOZ_"]; var WEBGL_TEXTURE_FORMATS = { /* eslint-disable camelcase */ WEBGL_compressed_texture_s3tc: [ "bc1-rgb-unorm-webgl", "bc1-rgba-unorm", "bc2-rgba-unorm", "bc3-rgba-unorm" ], WEBGL_compressed_texture_s3tc_srgb: [ "bc1-rgb-unorm-srgb-webgl", "bc1-rgba-unorm-srgb", "bc2-rgba-unorm-srgb", "bc3-rgba-unorm-srgb" ], EXT_texture_compression_rgtc: ["bc4-r-unorm", "bc4-r-snorm", "bc5-rg-unorm", "bc5-rg-snorm"], EXT_texture_compression_bptc: [ "bc6h-rgb-ufloat", "bc6h-rgb-float", "bc7-rgba-unorm", "bc7-rgba-unorm-srgb" ], WEBGL_compressed_texture_etc1: ["etc1-rgb-unorm-webgl"], WEBGL_compressed_texture_etc: [ "etc2-rgb8unorm", "etc2-rgb8unorm-srgb", "etc2-rgb8a1unorm", "etc2-rgb8a1unorm-srgb", "etc2-rgba8unorm", "etc2-rgba8unorm-srgb", "eac-r11unorm", "eac-r11snorm", "eac-rg11unorm", "eac-rg11snorm" ], WEBGL_compressed_texture_pvrtc: [ "pvrtc-rgb4unorm-webgl", "pvrtc-rgba4unorm-webgl", "pvrtc-rgb2unorm-webgl", "pvrtc-rgba2unorm-webgl" ], WEBGL_compressed_texture_atc: [ "atc-rgb-unorm-webgl", "atc-rgba-unorm-webgl", "atc-rgbai-unorm-webgl" ], WEBGL_compressed_texture_astc: [ "astc-4x4-unorm", "astc-4x4-unorm-srgb", "astc-5x4-unorm", "astc-5x4-unorm-srgb", "astc-5x5-unorm", "astc-5x5-unorm-srgb", "astc-6x5-unorm", "astc-6x5-unorm-srgb", "astc-6x6-unorm", "astc-6x6-unorm-srgb", "astc-8x5-unorm", "astc-8x5-unorm-srgb", "astc-8x6-unorm", "astc-8x6-unorm-srgb", "astc-8x8-unorm", "astc-8x8-unorm-srgb", "astc-10x5-unorm", "astc-10x5-unorm-srgb", "astc-10x6-unorm", "astc-10x6-unorm-srgb", "astc-10x8-unorm", "astc-10x8-unorm-srgb", "astc-10x10-unorm", "astc-10x10-unorm-srgb", "astc-12x10-unorm", "astc-12x10-unorm-srgb", "astc-12x12-unorm", "astc-12x12-unorm-srgb" ] /* eslint-enable camelcase */ }; var textureFormats = null; function detectSupportedTextureFormats(gl) { if (!textureFormats) { gl = gl || getWebGLContext() || void 0; textureFormats = /* @__PURE__ */ new Set(); for (const prefix of BROWSER_PREFIXES) { for (const extension in WEBGL_TEXTURE_FORMATS) { if (gl && gl.getExtension(`${prefix}${extension}`)) { for (const textureFormat of WEBGL_TEXTURE_FORMATS[extension]) { textureFormats.add(textureFormat); } } } } } return textureFormats; } function getWebGLContext() { try { const canvas = document.createElement("canvas"); return canvas.getContext("webgl"); } catch (error) { return null; } } // src/lib/parsers/parse-ktx.ts var KTX2_ID = [ // '´', 'K', 'T', 'X', '2', '0', 'ª', '\r', '\n', '\x1A', '\n' 171, 75, 84, 88, 32, 50, 48, 187, 13, 10, 26, 10 ]; function isKTX(data) { const id = new Uint8Array(data); const notKTX = id.byteLength < KTX2_ID.length || id[0] !== KTX2_ID[0] || // '´' id[1] !== KTX2_ID[1] || // 'K' id[2] !== KTX2_ID[2] || // 'T' id[3] !== KTX2_ID[3] || // 'X' id[4] !== KTX2_ID[4] || // ' ' id[5] !== KTX2_ID[5] || // '2' id[6] !== KTX2_ID[6] || // '0' id[7] !== KTX2_ID[7] || // 'ª' id[8] !== KTX2_ID[8] || // '\r' id[9] !== KTX2_ID[9] || // '\n' id[10] !== KTX2_ID[10] || // '\x1A' id[11] !== KTX2_ID[11]; return !notKTX; } // src/lib/parsers/parse-basis.ts var basisTranscodingLock = Promise.resolve(); var BASIS_FORMAT_TO_OUTPUT_OPTIONS = { etc1: { basisFormat: 0, compressed: true, format: GL_COMPRESSED_RGB_ETC1_WEBGL, textureFormat: "etc1-rgb-unorm-webgl" }, etc2: { basisFormat: 1, compressed: true, format: GL_COMPRESSED_RGBA8_ETC2_EAC, textureFormat: "etc2-rgba8unorm" }, bc1: { basisFormat: 2, compressed: true, format: GL_COMPRESSED_RGB_S3TC_DXT1_EXT, textureFormat: "bc1-rgb-unorm-webgl" }, bc3: { basisFormat: 3, compressed: true, format: GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, textureFormat: "bc3-rgba-unorm" }, bc4: { basisFormat: 4, compressed: true, format: GL_COMPRESSED_RED_RGTC1_EXT, textureFormat: "bc4-r-unorm" }, bc5: { basisFormat: 5, compressed: true, format: GL_COMPRESSED_RED_GREEN_RGTC2_EXT, textureFormat: "bc5-rg-unorm" }, "bc7-m6-opaque-only": { basisFormat: 6, compressed: true, format: GL_COMPRESSED_RGBA_BPTC_UNORM_EXT, textureFormat: "bc7-rgba-unorm" }, "bc7-m5": { basisFormat: 7, compressed: true, format: GL_COMPRESSED_RGBA_BPTC_UNORM_EXT, textureFormat: "bc7-rgba-unorm" }, "pvrtc1-4-rgb": { basisFormat: 8, compressed: true, format: GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, textureFormat: "pvrtc-rgb4unorm-webgl" }, "pvrtc1-4-rgba": { basisFormat: 9, compressed: true, format: GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, textureFormat: "pvrtc-rgba4unorm-webgl" }, "astc-4x4": { basisFormat: 10, compressed: true, format: GL_COMPRESSED_RGBA_ASTC_4x4_KHR, textureFormat: "astc-4x4-unorm" }, "atc-rgb": { basisFormat: 11, compressed: true, format: GL_COMPRESSED_RGB_ATC_WEBGL, textureFormat: "atc-rgb-unorm-webgl" }, "atc-rgba-interpolated-alpha": { basisFormat: 12, compressed: true, format: GL_COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL, textureFormat: "atc-rgbai-unorm-webgl" }, rgba32: { basisFormat: 13, compressed: false, format: GL_RGBA8, textureFormat: "rgba8unorm" }, rgb565: { basisFormat: 14, compressed: false, format: GL_RGB565, textureFormat: "rgb565unorm-webgl" }, bgr565: { basisFormat: 15, compressed: false, format: GL_RGB565, textureFormat: "rgb565unorm-webgl" }, rgba4444: { basisFormat: 16, compressed: false, format: GL_RGBA4, textureFormat: "rgba4unorm-webgl" } }; var BASIS_FORMATS = Object.freeze( Object.keys(BASIS_FORMAT_TO_OUTPUT_OPTIONS) ); async function withBasisTranscodingLock(transcode) { const previousLock = basisTranscodingLock; let releaseLock; basisTranscodingLock = new Promise((resolve) => { releaseLock = resolve; }); await previousLock; try { return await transcode(); } finally { releaseLock(); } } async function parseBasis(data, options = {}) { const loadLibraryOptions = extractLoadLibraryOptions(options); return await withBasisTranscodingLock(async () => { if (!options.basis?.containerFormat || options.basis.containerFormat === "auto") { if (isKTX(data)) { const fileConstructors = await loadBasisEncoderModule(loadLibraryOptions); return parseKTX2File(fileConstructors.KTX2File, data, options); } const { BasisFile } = await loadBasisTranscoderModule(loadLibraryOptions); return parseBasisFile(BasisFile, data, options); } switch (options.basis.module) { case "encoder": const fileConstructors = await loadBasisEncoderModule(loadLibraryOptions); switch (options.basis.containerFormat) { case "ktx2": return parseKTX2File(fileConstructors.KTX2File, data, options); case "basis": default: return parseBasisFile(fileConstructors.BasisFile, data, options); } case "transcoder": default: const { BasisFile } = await loadBasisTranscoderModule(loadLibraryOptions); return parseBasisFile(BasisFile, data, options); } }); } function parseBasisFile(BasisFile, data, options) { const basisFile = new BasisFile(new Uint8Array(data)); try { if (!basisFile.startTranscoding()) { throw new Error("Failed to start basis transcoding"); } const imageCount = basisFile.getNumImages(); const images = []; for (let imageIndex = 0; imageIndex < imageCount; imageIndex++) { const levelsCount = basisFile.getNumLevels(imageIndex); const levels = []; for (let levelIndex = 0; levelIndex < levelsCount; levelIndex++) { levels.push(transcodeImage(basisFile, imageIndex, levelIndex, options)); } images.push(levels); } return images; } finally { basisFile.close(); basisFile.delete(); } } function transcodeImage(basisFile, imageIndex, levelIndex, options) { const width = basisFile.getImageWidth(imageIndex, levelIndex); const height = basisFile.getImageHeight(imageIndex, levelIndex); const hasAlpha = basisFile.getHasAlpha( /* imageIndex, levelIndex */ ); const { compressed, format, basisFormat, textureFormat } = getBasisOptions(options, hasAlpha); const decodedSize = basisFile.getImageTranscodedSizeInBytes(imageIndex, levelIndex, basisFormat); const decodedData = new Uint8Array(decodedSize); if (!basisFile.transcodeImage(decodedData, imageIndex, levelIndex, basisFormat, 0, 0)) { throw new Error("failed to start Basis transcoding"); } return { // standard loaders.gl image category payload shape: "texture-level", width, height, data: decodedData, compressed, ...format !== void 0 ? { format } : {}, ...textureFormat !== void 0 ? { textureFormat } : {}, // Additional fields // Add levelSize field. hasAlpha }; } function parseKTX2File(KTX2File, data, options) { const ktx2File = new KTX2File(new Uint8Array(data)); try { if (!ktx2File.startTranscoding()) { throw new Error("failed to start KTX2 transcoding"); } const levelsCount = ktx2File.getLevels(); const levels = []; for (let levelIndex = 0; levelIndex < levelsCount; levelIndex++) { levels.push(transcodeKTX2Image(ktx2File, levelIndex, options)); } return [levels]; } finally { ktx2File.close(); ktx2File.delete(); } } function transcodeKTX2Image(ktx2File, levelIndex, options) { const { alphaFlag, height, width } = ktx2File.getImageLevelInfo(levelIndex, 0, 0); const { compressed, format, basisFormat, textureFormat } = getBasisOptions(options, alphaFlag); const decodedSize = ktx2File.getImageTranscodedSizeInBytes( levelIndex, 0, 0, basisFormat ); const decodedData = new Uint8Array(decodedSize); if (!ktx2File.transcodeImage( decodedData, levelIndex, 0, 0, basisFormat, 0, -1, -1 /* channel1 */ )) { throw new Error("Failed to transcode KTX2 image"); } return { // standard loaders.gl image category payload shape: "texture-level", width, height, data: decodedData, compressed, ...format !== void 0 ? { format } : {}, ...textureFormat !== void 0 ? { textureFormat } : {}, // Additional fields levelSize: decodedSize, hasAlpha: alphaFlag }; } function getBasisOptions(options, hasAlpha) { let format = options.basis?.format || "auto"; if (format === "auto") { format = options.basis?.supportedTextureFormats ? selectSupportedBasisFormat(options.basis.supportedTextureFormats) : selectSupportedBasisFormat(); } if (typeof format === "object") { format = hasAlpha ? format.alpha : format.noAlpha; } const normalizedFormat = format.toLowerCase(); const basisOutputOptions = BASIS_FORMAT_TO_OUTPUT_OPTIONS[normalizedFormat]; if (!basisOutputOptions) { throw new Error(`Unknown Basis format ${format}`); } return basisOutputOptions; } function selectSupportedBasisFormat(supportedTextureFormats = detectSupportedTextureFormats()) { const textureFormats2 = new Set(supportedTextureFormats); if (hasSupportedTextureFormat(textureFormats2, ["astc-4x4-unorm", "astc-4x4-unorm-srgb"])) { return "astc-4x4"; } else if (hasSupportedTextureFormat(textureFormats2, ["bc7-rgba-unorm", "bc7-rgba-unorm-srgb"])) { return { alpha: "bc7-m5", noAlpha: "bc7-m6-opaque-only" }; } else if (hasSupportedTextureFormat(textureFormats2, [ "bc1-rgb-unorm-webgl", "bc1-rgb-unorm-srgb-webgl", "bc1-rgba-unorm", "bc1-rgba-unorm-srgb", "bc2-rgba-unorm", "bc2-rgba-unorm-srgb", "bc3-rgba-unorm", "bc3-rgba-unorm-srgb" ])) { return { alpha: "bc3", noAlpha: "bc1" }; } else if (hasSupportedTextureFormat(textureFormats2, [ "pvrtc-rgb4unorm-webgl", "pvrtc-rgba4unorm-webgl", "pvrtc-rgb2unorm-webgl", "pvrtc-rgba2unorm-webgl" ])) { return { alpha: "pvrtc1-4-rgba", noAlpha: "pvrtc1-4-rgb" }; } else if (hasSupportedTextureFormat(textureFormats2, [ "etc2-rgb8unorm", "etc2-rgb8unorm-srgb", "etc2-rgb8a1unorm", "etc2-rgb8a1unorm-srgb", "etc2-rgba8unorm", "etc2-rgba8unorm-srgb", "eac-r11unorm", "eac-r11snorm", "eac-rg11unorm", "eac-rg11snorm" ])) { return "etc2"; } else if (textureFormats2.has("etc1-rgb-unorm-webgl")) { return "etc1"; } else if (hasSupportedTextureFormat(textureFormats2, [ "atc-rgb-unorm-webgl", "atc-rgba-unorm-webgl", "atc-rgbai-unorm-webgl" ])) { return { alpha: "atc-rgba-interpolated-alpha", noAlpha: "atc-rgb" }; } return "rgb565"; } function hasSupportedTextureFormat(supportedTextureFormats, candidateTextureFormats) { return candidateTextureFormats.some( (textureFormat) => supportedTextureFormats.has(textureFormat) ); } // src/basis-loader.ts var BasisWorkerLoader = { dataType: null, batchType: null, name: "Basis", id: "basis", module: "textures", version: VERSION2, worker: true, extensions: ["basis", "ktx2"], mimeTypes: ["application/octet-stream", "image/ktx2"], tests: ["sB"], binary: true, options: { basis: { format: "auto", containerFormat: "auto", module: "transcoder" } } }; var BasisLoader = { ...BasisWorkerLoader, parse: parseBasis }; // src/workers/basis-worker.ts createLoaderWorker(BasisLoader); })();