123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- import { Tools } from "./tools.js";
- /**
- * The worker function that gets converted to a blob url to pass into a worker.
- * To be used if a developer wants to create their own worker instance and inject it instead of using the default worker.
- */
- export function workerFunction() {
- const _BASIS_FORMAT = {
- cTFETC1: 0,
- cTFETC2: 1,
- cTFBC1: 2,
- cTFBC3: 3,
- cTFBC4: 4,
- cTFBC5: 5,
- cTFBC7: 6,
- cTFPVRTC1_4_RGB: 8,
- cTFPVRTC1_4_RGBA: 9,
- cTFASTC_4x4: 10,
- cTFATC_RGB: 11,
- cTFATC_RGBA_INTERPOLATED_ALPHA: 12,
- cTFRGBA32: 13,
- cTFRGB565: 14,
- cTFBGR565: 15,
- cTFRGBA4444: 16,
- cTFFXT1_RGB: 17,
- cTFPVRTC2_4_RGB: 18,
- cTFPVRTC2_4_RGBA: 19,
- cTFETC2_EAC_R11: 20,
- cTFETC2_EAC_RG11: 21,
- };
- let transcoderModulePromise = null;
- onmessage = (event) => {
- if (event.data.action === "init") {
- // Load the transcoder if it hasn't been yet
- if (event.data.url) {
- // make sure we loaded the script correctly
- try {
- importScripts(event.data.url);
- }
- catch (e) {
- postMessage({ action: "error", error: e });
- }
- }
- if (!transcoderModulePromise) {
- transcoderModulePromise = BASIS({
- // Override wasm binary
- wasmBinary: event.data.wasmBinary,
- });
- }
- if (transcoderModulePromise !== null) {
- transcoderModulePromise.then((m) => {
- BASIS = m;
- m.initializeBasis();
- postMessage({ action: "init" });
- });
- }
- }
- else if (event.data.action === "transcode") {
- // Transcode the basis image and return the resulting pixels
- const config = event.data.config;
- const imgData = event.data.imageData;
- const loadedFile = new BASIS.BasisFile(imgData);
- const fileInfo = GetFileInfo(loadedFile);
- let format = event.data.ignoreSupportedFormats ? null : GetSupportedTranscodeFormat(event.data.config, fileInfo);
- let needsConversion = false;
- if (format === null) {
- needsConversion = true;
- format = fileInfo.hasAlpha ? _BASIS_FORMAT.cTFBC3 : _BASIS_FORMAT.cTFBC1;
- }
- // Begin transcode
- let success = true;
- if (!loadedFile.startTranscoding()) {
- success = false;
- }
- const buffers = [];
- for (let imageIndex = 0; imageIndex < fileInfo.images.length; imageIndex++) {
- if (!success) {
- break;
- }
- const image = fileInfo.images[imageIndex];
- if (config.loadSingleImage === undefined || config.loadSingleImage === imageIndex) {
- let mipCount = image.levels.length;
- if (config.loadMipmapLevels === false) {
- mipCount = 1;
- }
- for (let levelIndex = 0; levelIndex < mipCount; levelIndex++) {
- const levelInfo = image.levels[levelIndex];
- const pixels = TranscodeLevel(loadedFile, imageIndex, levelIndex, format, needsConversion);
- if (!pixels) {
- success = false;
- break;
- }
- levelInfo.transcodedPixels = pixels;
- buffers.push(levelInfo.transcodedPixels.buffer);
- }
- }
- }
- // Close file
- loadedFile.close();
- loadedFile.delete();
- if (needsConversion) {
- format = -1;
- }
- if (!success) {
- postMessage({ action: "transcode", success: success, id: event.data.id });
- }
- else {
- postMessage({ action: "transcode", success: success, id: event.data.id, fileInfo: fileInfo, format: format }, buffers);
- }
- }
- };
- /**
- * Detects the supported transcode format for the file
- * @param config transcode config
- * @param fileInfo info about the file
- * @returns the chosed format or null if none are supported
- */
- function GetSupportedTranscodeFormat(config, fileInfo) {
- let format = null;
- if (config.supportedCompressionFormats) {
- if (config.supportedCompressionFormats.astc) {
- format = _BASIS_FORMAT.cTFASTC_4x4;
- }
- else if (config.supportedCompressionFormats.bc7) {
- format = _BASIS_FORMAT.cTFBC7;
- }
- else if (config.supportedCompressionFormats.s3tc) {
- format = fileInfo.hasAlpha ? _BASIS_FORMAT.cTFBC3 : _BASIS_FORMAT.cTFBC1;
- }
- else if (config.supportedCompressionFormats.pvrtc) {
- format = fileInfo.hasAlpha ? _BASIS_FORMAT.cTFPVRTC1_4_RGBA : _BASIS_FORMAT.cTFPVRTC1_4_RGB;
- }
- else if (config.supportedCompressionFormats.etc2) {
- format = _BASIS_FORMAT.cTFETC2;
- }
- else if (config.supportedCompressionFormats.etc1) {
- format = _BASIS_FORMAT.cTFETC1;
- }
- else {
- format = _BASIS_FORMAT.cTFRGB565;
- }
- }
- return format;
- }
- /**
- * Retrieves information about the basis file eg. dimensions
- * @param basisFile the basis file to get the info from
- * @returns information about the basis file
- */
- function GetFileInfo(basisFile) {
- const hasAlpha = basisFile.getHasAlpha();
- const imageCount = basisFile.getNumImages();
- const images = [];
- for (let i = 0; i < imageCount; i++) {
- const imageInfo = {
- levels: [],
- };
- const levelCount = basisFile.getNumLevels(i);
- for (let level = 0; level < levelCount; level++) {
- const levelInfo = {
- width: basisFile.getImageWidth(i, level),
- height: basisFile.getImageHeight(i, level),
- };
- imageInfo.levels.push(levelInfo);
- }
- images.push(imageInfo);
- }
- const info = { hasAlpha, images };
- return info;
- }
- function TranscodeLevel(loadedFile, imageIndex, levelIndex, format, convertToRgb565) {
- const dstSize = loadedFile.getImageTranscodedSizeInBytes(imageIndex, levelIndex, format);
- let dst = new Uint8Array(dstSize);
- if (!loadedFile.transcodeImage(dst, imageIndex, levelIndex, format, 1, 0)) {
- return null;
- }
- // If no supported format is found, load as dxt and convert to rgb565
- if (convertToRgb565) {
- const alignedWidth = (loadedFile.getImageWidth(imageIndex, levelIndex) + 3) & ~3;
- const alignedHeight = (loadedFile.getImageHeight(imageIndex, levelIndex) + 3) & ~3;
- dst = ConvertDxtToRgb565(dst, 0, alignedWidth, alignedHeight);
- }
- return dst;
- }
- /**
- * From https://github.com/BinomialLLC/basis_universal/blob/master/webgl/texture/dxt-to-rgb565.js
- * An unoptimized version of dxtToRgb565. Also, the floating
- * point math used to compute the colors actually results in
- * slightly different colors compared to hardware DXT decoders.
- * @param src dxt src pixels
- * @param srcByteOffset offset for the start of src
- * @param width aligned width of the image
- * @param height aligned height of the image
- * @returns the converted pixels
- */
- function ConvertDxtToRgb565(src, srcByteOffset, width, height) {
- const c = new Uint16Array(4);
- const dst = new Uint16Array(width * height);
- const blockWidth = width / 4;
- const blockHeight = height / 4;
- for (let blockY = 0; blockY < blockHeight; blockY++) {
- for (let blockX = 0; blockX < blockWidth; blockX++) {
- const i = srcByteOffset + 8 * (blockY * blockWidth + blockX);
- c[0] = src[i] | (src[i + 1] << 8);
- c[1] = src[i + 2] | (src[i + 3] << 8);
- c[2] =
- ((2 * (c[0] & 0x1f) + 1 * (c[1] & 0x1f)) / 3) |
- (((2 * (c[0] & 0x7e0) + 1 * (c[1] & 0x7e0)) / 3) & 0x7e0) |
- (((2 * (c[0] & 0xf800) + 1 * (c[1] & 0xf800)) / 3) & 0xf800);
- c[3] =
- ((2 * (c[1] & 0x1f) + 1 * (c[0] & 0x1f)) / 3) |
- (((2 * (c[1] & 0x7e0) + 1 * (c[0] & 0x7e0)) / 3) & 0x7e0) |
- (((2 * (c[1] & 0xf800) + 1 * (c[0] & 0xf800)) / 3) & 0xf800);
- for (let row = 0; row < 4; row++) {
- const m = src[i + 4 + row];
- let dstI = (blockY * 4 + row) * width + blockX * 4;
- dst[dstI++] = c[m & 0x3];
- dst[dstI++] = c[(m >> 2) & 0x3];
- dst[dstI++] = c[(m >> 4) & 0x3];
- dst[dstI++] = c[(m >> 6) & 0x3];
- }
- }
- }
- return dst;
- }
- }
- /**
- * Initialize a web worker with the basis transcoder
- * @param worker the worker to initialize
- * @param wasmBinary the wasm binary to load into the worker
- * @param moduleUrl the url to the basis transcoder module
- * @returns a promise that resolves when the worker is initialized
- */
- export function initializeWebWorker(worker, wasmBinary, moduleUrl) {
- return new Promise((res, reject) => {
- const initHandler = (msg) => {
- if (msg.data.action === "init") {
- worker.removeEventListener("message", initHandler);
- res(worker);
- }
- else if (msg.data.action === "error") {
- reject(msg.data.error || "error initializing worker");
- }
- };
- worker.addEventListener("message", initHandler);
- // we can use transferable objects here because the worker will own the ArrayBuffer
- worker.postMessage({ action: "init", url: moduleUrl ? Tools.GetBabylonScriptURL(moduleUrl) : undefined, wasmBinary }, [wasmBinary]);
- });
- }
- //# sourceMappingURL=basisWorker.js.map
|