ktx2-encoder
Version:
KTX2(.ktx2) encoder for browser applications
77 lines (76 loc) • 3.54 kB
JavaScript
import { read, write } from "ktx-parse";
import { applyInputOptions } from "../applyInputOptions.js";
import { BasisTextureType, HDRSourceType, SourceType } from "../enum.js";
let promise = null;
const DEFAULT_WASM_URL = "https://mdn.alipayobjects.com/rms/afts/file/A*r7D4SKbksYcAAAAAAAAAAAAAARQnAQ/basis_encoder.wasm";
class BrowserBasisEncoder {
async init(options) {
if (!promise) {
function _init() {
const wasmUrl = options?.wasmUrl ?? DEFAULT_WASM_URL;
const jsUrl = options?.jsUrl ?? "../basis/basis_encoder.js";
return new Promise((resolve, reject) => {
Promise.all([
import(/* @vite-ignore */ jsUrl),
wasmUrl ? fetch(wasmUrl).then((res) => res.arrayBuffer()) : undefined
])
.then(([{ default: BASIS }, wasmBinary]) => {
return BASIS({ wasmBinary }).then((Module) => {
Module.initializeBasis();
resolve(Module);
});
})
.catch(reject);
});
}
promise = _init();
}
return promise;
}
/**
* encode image data to ktx2 file data
* @param bufferOrBufferArray - image data, can be a single image or an array of images
* if it's an array, the images will be encoded as a cube map, the order of the images is:
* 0: Positive X face
* 1: Negative X face
* 2: Positive Y face
* 3: Negative Y face
* 4: Positive Z face
* 5: Negative Z face
* @param options - encode options, see {@link IEncodeOptions}
* @returns ktx2 file data
*/
async encode(bufferOrBufferArray, options = {}) {
const basisModule = await this.init(options);
const encoder = new basisModule.BasisEncoder();
applyInputOptions(options, encoder);
const isCube = Array.isArray(bufferOrBufferArray) && bufferOrBufferArray.length === 6;
encoder.setTexType(isCube ? BasisTextureType.cBASISTexTypeCubemapArray : BasisTextureType.cBASISTexType2D);
const bufferArray = Array.isArray(bufferOrBufferArray) ? bufferOrBufferArray : [bufferOrBufferArray];
for (let i = 0; i < bufferArray.length; i++) {
const buffer = bufferArray[i];
if (options.isHDR) {
encoder.setSliceSourceImageHDR(i, buffer, 0, 0, options.imageType === "hdr" ? HDRSourceType.HDR : HDRSourceType.EXR, true);
}
else {
const imageData = await options.imageDecoder(buffer);
encoder.setSliceSourceImage(i, new Uint8Array(imageData.data), imageData.width, imageData.height, SourceType.RAW);
}
}
const ktx2FileData = new Uint8Array(1024 * 1024 * (options.isHDR ? 24 : 10));
const byteLength = encoder.encode(ktx2FileData);
if (byteLength === 0) {
throw new Error("Encode failed");
}
let actualKTX2FileData = new Uint8Array(ktx2FileData.buffer, 0, byteLength);
if (options.kvData) {
const container = read(ktx2FileData);
for (let k in options.kvData) {
container.keyValue[k] = options.kvData[k];
}
actualKTX2FileData = write(container, { keepWriter: true });
}
return actualKTX2FileData;
}
}
export const browserEncoder = new BrowserBasisEncoder();