| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 | /* global require */import defined from "../Core/defined.js";import Check from "../Core/Check.js";import PixelFormat from "../Core/PixelFormat.js";import RuntimeError from "../Core/RuntimeError.js";import VulkanConstants from "../Core//VulkanConstants.js";import PixelDatatype from "../Renderer/PixelDatatype.js";import createTaskProcessorWorker from "./createTaskProcessorWorker.js";import ktx_parse from "../ThirdParty/ktx-parse.js";const faceOrder = [  "positiveX",  "negativeX",  "positiveY",  "negativeY",  "positiveZ",  "negativeZ",];// Flagsconst colorModelETC1S = 163;const colorModelUASTC = 166;let transcoderModule;function transcode(parameters, transferableObjects) {  //>>includeStart('debug', pragmas.debug);  Check.typeOf.object("transcoderModule", transcoderModule);  //>>includeEnd('debug');  const data = parameters.ktx2Buffer;  const supportedTargetFormats = parameters.supportedTargetFormats;  let header;  try {    header = ktx_parse(data);  } catch (e) {    throw new RuntimeError("Invalid KTX2 file.");  }  if (header.layerCount !== 0) {    throw new RuntimeError("KTX2 texture arrays are not supported.");  }  if (header.pixelDepth !== 0) {    throw new RuntimeError("KTX2 3D textures are unsupported.");  }  const dfd = header.dataFormatDescriptor[0];  const result = new Array(header.levelCount);  if (    header.vkFormat === 0x0 &&    (dfd.colorModel === colorModelETC1S || dfd.colorModel === colorModelUASTC)  ) {    // Compressed, initialize transcoder module    transcodeCompressed(      data,      header,      supportedTargetFormats,      transcoderModule,      transferableObjects,      result    );  } else {    transferableObjects.push(data.buffer);    parseUncompressed(header, result);  }  return result;}// Parser for uncompressedfunction parseUncompressed(header, result) {  const internalFormat =    header.vkFormat === VulkanConstants.VK_FORMAT_R8G8B8_SRGB      ? PixelFormat.RGB      : PixelFormat.RGBA;  let datatype;  if (header.vkFormat === VulkanConstants.VK_FORMAT_R8G8B8A8_UNORM) {    datatype = PixelDatatype.UNSIGNED_BYTE;  } else if (    header.vkFormat === VulkanConstants.VK_FORMAT_R16G16B16A16_SFLOAT  ) {    datatype = PixelDatatype.HALF_FLOAT;  } else if (    header.vkFormat === VulkanConstants.VK_FORMAT_R32G32B32A32_SFLOAT  ) {    datatype = PixelDatatype.FLOAT;  }  for (let i = 0; i < header.levels.length; ++i) {    const level = {};    result[i] = level;    const levelBuffer = header.levels[i].levelData;    const width = header.pixelWidth >> i;    const height = header.pixelHeight >> i;    const faceLength =      width * height * PixelFormat.componentsLength(internalFormat);    for (let j = 0; j < header.faceCount; ++j) {      // multiply levelBuffer.byteOffset by the size in bytes of the pixel data type      const faceByteOffset =        levelBuffer.byteOffset + faceLength * header.typeSize * j;      let faceView;      if (!defined(datatype) || PixelDatatype.sizeInBytes(datatype) === 1) {        faceView = new Uint8Array(          levelBuffer.buffer,          faceByteOffset,          faceLength        );      } else if (PixelDatatype.sizeInBytes(datatype) === 2) {        faceView = new Uint16Array(          levelBuffer.buffer,          faceByteOffset,          faceLength        );      } else {        faceView = new Float32Array(          levelBuffer.buffer,          faceByteOffset,          faceLength        );      }      level[faceOrder[j]] = {        internalFormat: internalFormat,        datatype: datatype,        width: width,        height: height,        levelBuffer: faceView,      };    }  }}function transcodeCompressed(  data,  header,  supportedTargetFormats,  transcoderModule,  transferableObjects,  result) {  const ktx2File = new transcoderModule.KTX2File(data);  let width = ktx2File.getWidth();  let height = ktx2File.getHeight();  const levels = ktx2File.getLevels();  const hasAlpha = ktx2File.getHasAlpha();  if (!(width > 0) || !(height > 0) || !(levels > 0)) {    ktx2File.close();    ktx2File.delete();    throw new RuntimeError("Invalid KTX2 file");  }  let internalFormat, transcoderFormat;  const dfd = header.dataFormatDescriptor[0];  const BasisFormat = transcoderModule.transcoder_texture_format;  // Determine target format based on platform support  if (dfd.colorModel === colorModelETC1S) {    if (supportedTargetFormats.etc) {      internalFormat = hasAlpha        ? PixelFormat.RGBA8_ETC2_EAC        : PixelFormat.RGB8_ETC2;      transcoderFormat = hasAlpha        ? BasisFormat.cTFETC2_RGBA        : BasisFormat.cTFETC1_RGB;    } else if (supportedTargetFormats.etc1 && !hasAlpha) {      internalFormat = PixelFormat.RGB_ETC1;      transcoderFormat = BasisFormat.cTFETC1_RGB;    } else if (supportedTargetFormats.s3tc) {      internalFormat = hasAlpha ? PixelFormat.RGBA_DXT5 : PixelFormat.RGB_DXT1;      transcoderFormat = hasAlpha        ? BasisFormat.cTFBC3_RGBA        : BasisFormat.cTFBC1_RGB;    } else if (supportedTargetFormats.pvrtc) {      internalFormat = hasAlpha        ? PixelFormat.RGBA_PVRTC_4BPPV1        : PixelFormat.RGB_PVRTC_4BPPV1;      transcoderFormat = hasAlpha        ? BasisFormat.cTFPVRTC1_4_RGBA        : BasisFormat.cTFPVRTC1_4_RGB;    } else if (supportedTargetFormats.astc) {      internalFormat = PixelFormat.RGBA_ASTC;      transcoderFormat = BasisFormat.cTFASTC_4x4_RGBA;    } else if (supportedTargetFormats.bc7) {      internalFormat = PixelFormat.RGBA_BC7;      transcoderFormat = BasisFormat.cTFBC7_RGBA;    } else {      throw new RuntimeError(        "No transcoding format target available for ETC1S compressed ktx2."      );    }  } else if (dfd.colorModel === colorModelUASTC) {    if (supportedTargetFormats.astc) {      internalFormat = PixelFormat.RGBA_ASTC;      transcoderFormat = BasisFormat.cTFASTC_4x4_RGBA;    } else if (supportedTargetFormats.bc7) {      internalFormat = PixelFormat.RGBA_BC7;      transcoderFormat = BasisFormat.cTFBC7_RGBA;    } else if (supportedTargetFormats.s3tc) {      internalFormat = hasAlpha ? PixelFormat.RGBA_DXT5 : PixelFormat.RGB_DXT1;      transcoderFormat = hasAlpha        ? BasisFormat.cTFBC3_RGBA        : BasisFormat.cTFBC1_RGB;    } else if (supportedTargetFormats.etc) {      internalFormat = hasAlpha        ? PixelFormat.RGBA8_ETC2_EAC        : PixelFormat.RGB8_ETC2;      transcoderFormat = hasAlpha        ? BasisFormat.cTFETC2_RGBA        : BasisFormat.cTFETC1_RGB;    } else if (supportedTargetFormats.etc1 && !hasAlpha) {      internalFormat = PixelFormat.RGB_ETC1;      transcoderFormat = BasisFormat.cTFETC1_RGB;    } else if (supportedTargetFormats.pvrtc) {      internalFormat = hasAlpha        ? PixelFormat.RGBA_PVRTC_4BPPV1        : PixelFormat.RGB_PVRTC_4BPPV1;      transcoderFormat = hasAlpha        ? BasisFormat.cTFPVRTC1_4_RGBA        : BasisFormat.cTFPVRTC1_4_RGB;    } else {      throw new RuntimeError(        "No transcoding format target available for UASTC compressed ktx2."      );    }  }  if (!ktx2File.startTranscoding()) {    ktx2File.close();    ktx2File.delete();    throw new RuntimeError("startTranscoding() failed");  }  for (let i = 0; i < header.levels.length; ++i) {    const level = {};    result[i] = level;    width = header.pixelWidth >> i;    height = header.pixelHeight >> i;    // Since supercompressed cubemaps are unsupported, this function    // does not iterate over KTX2 faces and assumes faceCount = 1.    const dstSize = ktx2File.getImageTranscodedSizeInBytes(      i, // level index      0, // layer index      0, // face index      transcoderFormat.value    );    const dst = new Uint8Array(dstSize);    const transcoded = ktx2File.transcodeImage(      dst,      i, // level index      0, // layer index      0, // face index      transcoderFormat.value,      0, // get_alpha_for_opaque_formats      -1, // channel0      -1 // channel1    );    if (!defined(transcoded)) {      throw new RuntimeError("transcodeImage() failed.");    }    transferableObjects.push(dst.buffer);    level[faceOrder[0]] = {      internalFormat: internalFormat,      width: width,      height: height,      levelBuffer: dst,    };  }  ktx2File.close();  ktx2File.delete();  return result;}function initWorker(compiledModule) {  transcoderModule = compiledModule;  transcoderModule.initializeBasis();  self.onmessage = createTaskProcessorWorker(transcode);  self.postMessage(true);}function transcodeKTX2(event) {  const data = event.data;  // Expect the first message to be to load a web assembly module  const wasmConfig = data.webAssemblyConfig;  if (defined(wasmConfig)) {    // Require and compile WebAssembly module, or use fallback if not supported    return require([wasmConfig.modulePath], function (mscBasisTranscoder) {      if (defined(wasmConfig.wasmBinaryFile)) {        if (!defined(mscBasisTranscoder)) {          mscBasisTranscoder = self.MSC_TRANSCODER;        }        mscBasisTranscoder(wasmConfig).then(function (compiledModule) {          initWorker(compiledModule);        });      } else {        return mscBasisTranscoder().then(function (transcoder) {          initWorker(transcoder);        });      }    });  }}export default transcodeKTX2;
 |