354 lines
12 KiB
Plaintext
354 lines
12 KiB
Plaintext
.TH "msc_basis_transcoder" 3 "Sun Jun 14 2026 18:00:57" "Version 0.0.0" "KTX Javascript Wrappers Reference" \" -*- nroff -*-
|
|
.ad l
|
|
.nh
|
|
.SH NAME
|
|
msc_basis_transcoder \- Basis Universal Image Transcoder binding
|
|
.PP
|
|
|
|
.PP
|
|
\fBWarning\fP
|
|
.RS 4
|
|
Deprecated\&. Use the container independent transcoder from the Binomial LLC repo instead: https://github.com/BinomialLLC/basis_universal\&. This JS wrapper was designed to use an underlying C++ API that accepted image info in a structure\&. The API actually added to basis_universal uses explicit parameters so users of this transcoder will be packing the info into a struct from which it will be immediately unpacked before the underlying transcoder is called\&.
|
|
.RE
|
|
.PP
|
|
.SS "WebIDL for the binding"
|
|
.PP
|
|
.nf
|
|
void initTranscoders();
|
|
|
|
bool isFormatSupported(TranscodeTarget targetFormat, TextureFormat texFormat);
|
|
|
|
interface BasisTranscoderState {
|
|
void BasisTranscoderState();
|
|
};
|
|
|
|
interface TranscodedImage {
|
|
ArrayBufferView get_typed_memory_view();
|
|
};
|
|
|
|
interface TranscodeResult {
|
|
TranscodedImage transcodedImage;
|
|
};
|
|
|
|
interface BasisLzEtc1sImageTranscoder {
|
|
void BasisLzEtc1sImageTranscoder();
|
|
uint32_t getBytesPerBlock(TranscodeTarget format);
|
|
bool decode_palettes(uint32_t num_endpoints,
|
|
const Uint8Array endpoints,
|
|
uint32_t num_selectors,
|
|
const Uint8Array selectors);
|
|
bool decode_tables(const Uint8Array tableData);
|
|
TranscodeResult transcode_image(
|
|
TranscodeTarget targetFormat,
|
|
const Uint8Array jsInSlices,
|
|
ImageInfo imageInfo,
|
|
uint32_t decodeFlags = 0,
|
|
bool isVideo = false);
|
|
};
|
|
|
|
interface BasisUastcImageTranscoder {
|
|
void BasisUastcImageTranscoder();
|
|
uint32_t getBytesPerBlock(const TranscodeTarget format);
|
|
TranscodeResult transcode_image(
|
|
TranscodeTarget targetFormat,
|
|
const Uint8Array jsInImage,
|
|
basisu_image_desc& imageDesc,
|
|
uint32_t decodeFlags = 0,
|
|
bool hasAlpha = false,
|
|
bool isVideo = false);
|
|
|
|
interface ImageInfo = {
|
|
ImageInfo(TextureFormat texFormat, uint32_t width, uint32_t height,
|
|
uint32_t level);
|
|
attribute uint32_t flags;
|
|
attribute long rgbByteOffset;
|
|
attribute long rgbByteLength;
|
|
attribute long alphaByteOffset;
|
|
attribute long alphaByteLength;
|
|
attribute uint32_t width;
|
|
attribute uint32_t height;
|
|
attribute uint32_t numBlocksX;
|
|
attribute uint32_t numBlocksY;
|
|
attribute uint32_t level;
|
|
};
|
|
|
|
// Some targets may not be available depending on options used when compiling
|
|
// the web assembly\&.
|
|
enum TranscodeTarget = {
|
|
"ETC1_RGB",
|
|
"BC1_RGB",
|
|
"BC4_R",
|
|
"BC5_RG",
|
|
"BC3_RGBA",
|
|
"PVRTC1_4_RGB",
|
|
"PVRTC1_4_RGBA",
|
|
"BC7_RGBA",
|
|
"BC7_M6_RGB", //Deprecated\&. Use BC7_RGBA\&.
|
|
"BC7_M5_RGBA", //Deprecated\&. Use BC7_RGBA\&.
|
|
"ETC2_RGBA",
|
|
"ASTC_4x4_RGBA",
|
|
"RGBA32",
|
|
"RGB565",
|
|
"BGR565",
|
|
"RGBA4444",
|
|
"PVRTC2_4_RGB",
|
|
"PVRTC2_4_RGBA",
|
|
"EAC_R11",
|
|
"EAC_RG11"
|
|
};
|
|
|
|
enum TextureFormat = {
|
|
"ETC1S",
|
|
"UASTC4x4",
|
|
};
|
|
|
|
enum TranscodeFlagBits =
|
|
"TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS",
|
|
"HIGH_QUALITY",
|
|
};
|
|
|
|
enum TranscodeFlagBits = {
|
|
"TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS",
|
|
"HIGH_QUALITY"
|
|
};
|
|
.fi
|
|
.PP
|
|
.SS "How to use"
|
|
Put msc_basis_transcoder\&.js and msc_basis_transcoder\&.wasm in a directory on your server\&. Create a script tag with msc_basis_tranacoder\&.js as the \fRsrc\fP as shown below, changing the path as necessary for the relative locations of your \&.html file and the script source\&. msc_basis_transcoder\&.js will automatically load msc_basis_transcoder\&.wasm\&.
|
|
.SS "Create an instance of the MSC_TRANSCODER module"
|
|
For example, add this to the \&.html file to initialize the transcoder and make it available on the main window\&.
|
|
.PP
|
|
.nf
|
|
<script src="msc_transcoder_wrapper\&.js"></script>
|
|
<script type="text/javascript">
|
|
MSC_TRANSCODER()\&.then(module => {
|
|
window\&.MSC_TRANSCODER = module;
|
|
module\&.initTranscoders();
|
|
// Call a function to begin loading or transcoding\&.\&.
|
|
</script>
|
|
|
|
.fi
|
|
.PP
|
|
|
|
.PP
|
|
\fIAfter\fP the module is initialized, invoke code that will directly or indirectly cause a function with code like the following to be executed\&.
|
|
.SS "Somewhere in the loader/transcoder"
|
|
Assume a KTX file is fetched via an XMLHttpRequest which deposits the data into a Uint8Array, "buData"\&.\&.\&.
|
|
|
|
.PP
|
|
\fBNote\fP
|
|
.RS 4
|
|
The names of the data items used in the following code are those from the KTX2 specification but the actual data is not specific to that container format\&.
|
|
.RE
|
|
.PP
|
|
.PP
|
|
.nf
|
|
const {
|
|
BasisLzEtc1sImageTranscoder,
|
|
BasisUastcImageTranscoder,
|
|
TranscodeTarget
|
|
} = MSC_TRANSCODER;
|
|
|
|
// Determine from the KTX2 header information in buData if
|
|
// the data format is BasisU or Uastc\&.
|
|
// supercompressionScheme value == 1, it's TextureFormat\&.ETC1S\&.
|
|
// DFD colorModel == 166, it's TextureFormat\&.UASTC4x4\&.
|
|
const texFormat = \&.\&.\&.
|
|
|
|
// Determine appropriate transcode format from available targets,
|
|
// info about the texture, e\&.g\&. texture\&.numComponents, and
|
|
// expected use\&. Use values from TranscodeTarget\&.
|
|
const targetFormat = \&.\&.\&.
|
|
if ( !MSC_TRANSCODER\&.isFormatSupported(targetFormat, texFormat) {
|
|
throw new Error( \&.\&.\&. );
|
|
}
|
|
|
|
if (TextureFormat\&.UASTC4x4) {
|
|
var result = transcodeUastc(targetFormat);
|
|
} else {
|
|
var result = transcodeEtc1s(targetFormat);
|
|
}
|
|
if ( result\&.transcodedImage === undefined ) {
|
|
throw new Error( 'Unable to transcode image\&.' );
|
|
}
|
|
.fi
|
|
.PP
|
|
|
|
.PP
|
|
This is the function for transcoding etc1s\&.
|
|
|
|
.PP
|
|
.PP
|
|
.nf
|
|
transcodeEtc1s(targetFormat) {
|
|
// Locate the supercompression global data and compresssed
|
|
// mip level data within buData\&.
|
|
|
|
var bit = new BasisLzEtc1sImageTranscoder();
|
|
|
|
// Find the index of the starts of the endpoints, selectors and tables
|
|
// data within buData\&.\&.\&.
|
|
var endpointsStart = \&.\&.\&.
|
|
var selectorsStart = \&.\&.\&.
|
|
var tablesStart = \&.\&.\&.
|
|
// The numbers of endpoints & selectors and their byteLengths are items
|
|
// within buData\&. They are in the header of a \&.ktx2 file's
|
|
// supercompressionGlobalData and in the header of a \&.basis file\&.
|
|
|
|
var endpoints = new Uint8Array(buData, endpointsStart,
|
|
endpointsByteLength);
|
|
var selectors = new Uint8Array(buData, selectorsStart,
|
|
selectorsByteLength);
|
|
|
|
bit\&.decodePalettes(numEndpoints, endpoints,
|
|
numSelectors, selectors);
|
|
|
|
var tables = new UInt8Array(buData, tablesStart, tablesByteLength);
|
|
bit\&.decodeTables(tables);
|
|
|
|
// Determine if the file contains a video sequence\&.\&.\&.
|
|
var isVideo = \&.\&.\&.
|
|
|
|
// Calculate the total number of images in the data
|
|
var numImages = \&.\&.\&.
|
|
|
|
// Set up a subarray pointing at the deflated image descriptions
|
|
// in buData\&. This is for \&.ktx2 containers\&. The image descriptions
|
|
// are located in supercompressionGlobalData\&. \&.basis containers will
|
|
// require different code to locate the slice descriptions within
|
|
// the file\&.
|
|
var imageDescsStart = \&.\&.\&.:
|
|
// An imageDesc has 5 uint32 values\&.
|
|
var imageDescs = new Uint32Data(buData, imageDescsStart,
|
|
numImages * 5 * 4);
|
|
var curImageIndex = 0;
|
|
|
|
// Pseudo code for processing the levels of a \&.ktx2 container\&.\&.\&.
|
|
foreach level {
|
|
var leveWidth = width of image at this level
|
|
var levelHeight = height of image at this level
|
|
imageInfo = new ImageInfo(TextureFormat::ETC1S, levelWidth, levelHeight,
|
|
level);
|
|
foreach image in level {
|
|
// In KTX2 container locate the imageDesc for this image\&.
|
|
var imageDesc = imageDescs[curImageIndex++];
|
|
imageInfo\&.flags = imageDesc\&.imageFlags;
|
|
imageInfo\&.rgbByteOffset = 0;
|
|
imageInfo\&.rgbByteLength = imageDesc\&.rgbSliceByteLength;
|
|
imageInfo\&.alphaByteOffset = imageDesc\&.alphaSliceByteOffset > 0 ? imageDesc\&.rgbSliceByteLength : 0;
|
|
imageInfo\&.alphaByteLength = imageDesc\&.alphaSliceByteLength;
|
|
// Determine the location in the ArrayBuffer of the start
|
|
// of the deflated data for level\&.
|
|
var levelOffset = \&.\&.\&.
|
|
// Make a \&.subarray of the rgb slice data\&.
|
|
var levelData = new Uint8Array(
|
|
buData,
|
|
levelOffset + imageDesc\&.rgbSliceByteOffset,
|
|
imageDesc\&.rgbSliceByteLength + imageDesc\&.alphaByteLength
|
|
);
|
|
var result = bit\&.transcodeImage(
|
|
targetFormat,
|
|
levelData,
|
|
imageInfo,
|
|
0,
|
|
isVideo);
|
|
if ( result\&.transcodedImage === undefined ) {
|
|
throw new Error( \&.\&.\&. );
|
|
}
|
|
let imgData = transcodedImage\&.get_typed_memory_view();
|
|
|
|
// Upload data in imgData to WebGL\&.\&.\&.
|
|
|
|
// Do not call delete() until data has been uploaded
|
|
// or otherwise copied\&.
|
|
transcodedImage\&.delete();
|
|
}
|
|
}
|
|
|
|
// For \&.basis containers, it is necessary to locate the slice
|
|
// description(s) for the image and set the values in imageInfo
|
|
// from them\&. Use of the \&.basis\-specific transcoder is recommended\&.
|
|
// The definition of the basis_slice_desc struct makes it difficult
|
|
// to create JS interface for it with embind\&.
|
|
.fi
|
|
.PP
|
|
|
|
.PP
|
|
This is the function for transcoding Uastc\&.
|
|
|
|
.PP
|
|
.PP
|
|
.nf
|
|
transcodeUastc(targetFormat) {
|
|
var uit = new UastcImageTranscoder();
|
|
|
|
// Determine if the data is supercompressed\&.
|
|
var zstd = (supercompressionScheme == 2);
|
|
|
|
// Determine if the data has alpha\&.
|
|
var hasAlpha = (Channel ID of sample[0] in DFD == 1);
|
|
|
|
var dctx;
|
|
if (zstd) {
|
|
// Initialize the zstd decoder\&. Zstd JS wrapper + wasm is
|
|
// a separate package\&.
|
|
dctx = ZSTD_createDCtx();
|
|
}
|
|
|
|
// Pseudo code for processing the levels of a \&.ktx2 container\&.\&.\&.
|
|
foreach level {
|
|
// Determine the location in the ArrayBuffer buData of the
|
|
// start of the deflated data for the level\&.
|
|
var levelData = \&.\&.\&.
|
|
if (zstd) {
|
|
// Inflate the level data
|
|
levelData = ZSTD_decompressDCtx(dctx, levelData, \&.\&.\&. );
|
|
}
|
|
|
|
var levelWidth = width of image at this level
|
|
var levelHeight = height of image at this level
|
|
var depth = depth of texture at this level
|
|
var levelImageCount = number of layers * number of faces * depth;
|
|
var imageOffsetInLevel = 0;
|
|
|
|
var imageInfo = new ImageInfo(TextureFormat::UASTC4x4,
|
|
levelWidth, levelHeight, level);
|
|
var levelImageByteLength = imageInfo\&.numBlocksX * imageInfo\&.numBlocksY * DFD bytesPlane0;
|
|
|
|
foreach image in level {
|
|
inImage = Uint8Array(levelData, imageOffsetInLevel, levelImageByteLength);
|
|
imageInfo\&.flags = 0;
|
|
imageInfo\&.rgbByteOffset = 0;
|
|
imageInfo\&.rgbByteLength = levelImageByteLength;
|
|
imageInfo\&.alphaByteOffset = 0;
|
|
imageInfo\&.alphaByteLength = 0;
|
|
|
|
const {transcodedImage} = uit\&.transcodeImage(
|
|
targetFormat,
|
|
inImage,
|
|
imageInfo,
|
|
0,
|
|
hasAlpha,
|
|
isVideo);
|
|
if ( result\&.transcodedImage === undefined ) {
|
|
throw new Error( \&.\&.\&. );
|
|
}
|
|
let imgData = transcodedImage\&.get_typed_memory_view();
|
|
|
|
// Upload data in imgData to WebGL\&.\&.\&.
|
|
|
|
// Do not call delete() until data has been uploaded
|
|
// or otherwise copied\&.
|
|
transcodedImage\&.delete();
|
|
|
|
imageOffsetInLevel += levelImageByteLength;
|
|
}
|
|
}
|
|
// For \&.basis containers, as with ETC1S, it is necessary to locate
|
|
// the slice description for the image and set the values in imageInfo
|
|
// from it\&.
|
|
}
|
|
.fi
|
|
.PP
|
|
|