Files
how-to-vulkan/ktx/build/docs/html/ktxjswrappers/msc_basis_transcoder.html
T
2026-06-14 19:09:18 +01:00

422 lines
24 KiB
HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.16.1"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>KTX Javascript Wrappers Reference: Basis Universal Image Transcoder binding</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<script type="text/javascript" src="clipboard.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="navtreedata.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<script type="text/javascript" src="cookie.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr id="projectrow">
<td id="projectlogo"><img alt="Logo" src="ktx_logo_200.png"/></td>
<td id="projectalign">
<div id="projectname">KTX Javascript Wrappers Reference<span id="projectnumber">&#160;0.0.0</span>
</div>
<div id="projectbrief">Libraries and tools to create and read KTX image texture files.</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.16.1 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search/",'.html');
</script>
<script type="text/javascript">
$(function() { codefold.init(); });
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
$(function() {
initMenu('',true,false,'search.php','Search',true);
$(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
<div id="nav-tree">
<div id="nav-tree-contents">
<div id="nav-sync" class="sync"></div>
</div>
</div>
<div id="splitbar" style="-moz-user-select:none;"
class="ui-resizable-handle">
</div>
</div>
<script type="text/javascript">
$(function(){initNavTree('msc_basis_transcoder.html','',''); });
</script>
<div id="container">
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<div id="MSearchResults">
<div class="SRPage">
<div id="SRIndex">
<div id="SRResults"></div>
<div class="SRStatus" id="Loading">Loading...</div>
<div class="SRStatus" id="Searching">Searching...</div>
<div class="SRStatus" id="NoMatches">No Matches</div>
</div>
</div>
</div>
</div>
<div><div class="header">
<div class="headertitle"><div class="title">Basis Universal Image Transcoder binding </div></div>
</div><!--header-->
<div class="contents">
<div class="textblock"><dl class="section warning"><dt>Warning</dt><dd>Deprecated. Use the container independent transcoder from the Binomial LLC repo instead: <a href="https://github.com/BinomialLLC/basis_universal">https://github.com/BinomialLLC/basis_universal</a>. 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.</dd></dl>
<h2 class="doxsection"><a class="anchor" id="autotoc_md6"></a>
WebIDL for the binding</h2>
<div class="fragment"><div class="line">void initTranscoders();</div>
<div class="line"> </div>
<div class="line">bool isFormatSupported(TranscodeTarget targetFormat, TextureFormat texFormat);</div>
<div class="line"> </div>
<div class="line">interface BasisTranscoderState {</div>
<div class="line"> void BasisTranscoderState();</div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line">interface TranscodedImage {</div>
<div class="line"> ArrayBufferView get_typed_memory_view();</div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line">interface TranscodeResult {</div>
<div class="line"> TranscodedImage transcodedImage;</div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line">interface BasisLzEtc1sImageTranscoder {</div>
<div class="line"> void BasisLzEtc1sImageTranscoder();</div>
<div class="line"> uint32_t getBytesPerBlock(TranscodeTarget format);</div>
<div class="line"> bool decode_palettes(uint32_t num_endpoints,</div>
<div class="line"> const Uint8Array endpoints,</div>
<div class="line"> uint32_t num_selectors,</div>
<div class="line"> const Uint8Array selectors);</div>
<div class="line"> bool decode_tables(const Uint8Array tableData);</div>
<div class="line"> TranscodeResult transcode_image(</div>
<div class="line"> TranscodeTarget targetFormat,</div>
<div class="line"> const Uint8Array jsInSlices,</div>
<div class="line"> ImageInfo imageInfo,</div>
<div class="line"> uint32_t decodeFlags = 0,</div>
<div class="line"> bool isVideo = false);</div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line">interface BasisUastcImageTranscoder {</div>
<div class="line"> void BasisUastcImageTranscoder();</div>
<div class="line"> uint32_t getBytesPerBlock(const TranscodeTarget format);</div>
<div class="line"> TranscodeResult transcode_image(</div>
<div class="line"> TranscodeTarget targetFormat,</div>
<div class="line"> const Uint8Array jsInImage,</div>
<div class="line"> basisu_image_desc&amp; imageDesc,</div>
<div class="line"> uint32_t decodeFlags = 0,</div>
<div class="line"> bool hasAlpha = false,</div>
<div class="line"> bool isVideo = false);</div>
<div class="line"> </div>
<div class="line">interface ImageInfo = {</div>
<div class="line"> ImageInfo(TextureFormat texFormat, uint32_t width, uint32_t height,</div>
<div class="line"> uint32_t level);</div>
<div class="line"> attribute uint32_t flags;</div>
<div class="line"> attribute long rgbByteOffset;</div>
<div class="line"> attribute long rgbByteLength;</div>
<div class="line"> attribute long alphaByteOffset;</div>
<div class="line"> attribute long alphaByteLength;</div>
<div class="line"> attribute uint32_t width;</div>
<div class="line"> attribute uint32_t height;</div>
<div class="line"> attribute uint32_t numBlocksX;</div>
<div class="line"> attribute uint32_t numBlocksY;</div>
<div class="line"> attribute uint32_t level;</div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line">// Some targets may not be available depending on options used when compiling</div>
<div class="line">// the web assembly.</div>
<div class="line">enum TranscodeTarget = {</div>
<div class="line"> &quot;ETC1_RGB&quot;,</div>
<div class="line"> &quot;BC1_RGB&quot;,</div>
<div class="line"> &quot;BC4_R&quot;,</div>
<div class="line"> &quot;BC5_RG&quot;,</div>
<div class="line"> &quot;BC3_RGBA&quot;,</div>
<div class="line"> &quot;PVRTC1_4_RGB&quot;,</div>
<div class="line"> &quot;PVRTC1_4_RGBA&quot;,</div>
<div class="line"> &quot;BC7_RGBA&quot;,</div>
<div class="line"> &quot;BC7_M6_RGB&quot;, //Deprecated. Use BC7_RGBA.</div>
<div class="line"> &quot;BC7_M5_RGBA&quot;, //Deprecated. Use BC7_RGBA.</div>
<div class="line"> &quot;ETC2_RGBA&quot;,</div>
<div class="line"> &quot;ASTC_4x4_RGBA&quot;,</div>
<div class="line"> &quot;RGBA32&quot;,</div>
<div class="line"> &quot;RGB565&quot;,</div>
<div class="line"> &quot;BGR565&quot;,</div>
<div class="line"> &quot;RGBA4444&quot;,</div>
<div class="line"> &quot;PVRTC2_4_RGB&quot;,</div>
<div class="line"> &quot;PVRTC2_4_RGBA&quot;,</div>
<div class="line"> &quot;EAC_R11&quot;,</div>
<div class="line"> &quot;EAC_RG11&quot;</div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line">enum TextureFormat = {</div>
<div class="line"> &quot;ETC1S&quot;,</div>
<div class="line"> &quot;UASTC4x4&quot;,</div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line">enum TranscodeFlagBits = </div>
<div class="line"> &quot;TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS&quot;,</div>
<div class="line"> &quot;HIGH_QUALITY&quot;,</div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line">enum TranscodeFlagBits = {</div>
<div class="line"> &quot;TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS&quot;,</div>
<div class="line"> &quot;HIGH_QUALITY&quot;</div>
<div class="line">};</div>
</div><!-- fragment --><h2 class="doxsection"><a class="anchor" id="autotoc_md7"></a>
How to use</h2>
<p>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 <code>src</code> 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.</p>
<h3 class="doxsection"><a class="anchor" id="autotoc_md8"></a>
Create an instance of the MSC_TRANSCODER module</h3>
<p>For example, add this to the .html file to initialize the transcoder and make it available on the main window. </p><div class="fragment"><div class="line">&amp;lt;script src=&quot;msc_transcoder_wrapper.js&quot;&gt;&amp;lt;/script&gt;</div>
<div class="line">&amp;lt;script type=&quot;text/javascript&quot;&gt;</div>
<div class="line"> MSC_TRANSCODER().then(module =&gt; {</div>
<div class="line"> window.MSC_TRANSCODER = module;</div>
<div class="line"> module.initTranscoders();</div>
<div class="line"> // Call a function to begin loading or transcoding..</div>
<div class="line">&amp;lt;/script&gt;</div>
</div><!-- fragment --><p><em>After</em> the module is initialized, invoke code that will directly or indirectly cause a function with code like the following to be executed.</p>
<h2 class="doxsection"><a class="anchor" id="autotoc_md9"></a>
Somewhere in the loader/transcoder</h2>
<p>Assume a KTX file is fetched via an XMLHttpRequest which deposits the data into a Uint8Array, "buData"...</p>
<dl class="section note"><dt>Note</dt><dd>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.</dd></dl>
<div class="fragment"><div class="line">const {</div>
<div class="line"> BasisLzEtc1sImageTranscoder,</div>
<div class="line"> BasisUastcImageTranscoder,</div>
<div class="line"> TranscodeTarget</div>
<div class="line">} = MSC_TRANSCODER;</div>
<div class="line"> </div>
<div class="line">// Determine from the KTX2 header information in buData if</div>
<div class="line">// the data format is BasisU or Uastc.</div>
<div class="line">// supercompressionScheme value == 1, it&#39;s TextureFormat.ETC1S.</div>
<div class="line">// DFD colorModel == 166, it&#39;s TextureFormat.UASTC4x4.</div>
<div class="line">const texFormat = ...</div>
<div class="line"> </div>
<div class="line">// Determine appropriate transcode format from available targets,</div>
<div class="line">// info about the texture, e.g. texture.numComponents, and</div>
<div class="line">// expected use. Use values from TranscodeTarget.</div>
<div class="line">const targetFormat = ...</div>
<div class="line">if ( !MSC_TRANSCODER.isFormatSupported(targetFormat, texFormat) {</div>
<div class="line"> throw new Error( ... );</div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line">if (TextureFormat.UASTC4x4) {</div>
<div class="line"> var result = transcodeUastc(targetFormat);</div>
<div class="line">} else {</div>
<div class="line"> var result = transcodeEtc1s(targetFormat);</div>
<div class="line">}</div>
<div class="line">if ( result.transcodedImage === undefined ) {</div>
<div class="line"> throw new Error( &#39;Unable to transcode image.&#39; );</div>
<div class="line">}</div>
</div><!-- fragment --><p>This is the function for transcoding etc1s.</p>
<div class="fragment"><div class="line">transcodeEtc1s(targetFormat) {</div>
<div class="line"> // Locate the supercompression global data and compresssed</div>
<div class="line"> // mip level data within buData.</div>
<div class="line"> </div>
<div class="line"> var bit = new BasisLzEtc1sImageTranscoder();</div>
<div class="line"> </div>
<div class="line"> // Find the index of the starts of the endpoints, selectors and tables</div>
<div class="line"> // data within buData...</div>
<div class="line"> var endpointsStart = ...</div>
<div class="line"> var selectorsStart = ...</div>
<div class="line"> var tablesStart = ...</div>
<div class="line"> // The numbers of endpoints &amp; selectors and their byteLengths are items</div>
<div class="line"> // within buData. They are in the header of a .ktx2 file&#39;s</div>
<div class="line"> // supercompressionGlobalData and in the header of a .basis file.</div>
<div class="line"> </div>
<div class="line"> var endpoints = new Uint8Array(buData, endpointsStart,</div>
<div class="line"> endpointsByteLength);</div>
<div class="line"> var selectors = new Uint8Array(buData, selectorsStart,</div>
<div class="line"> selectorsByteLength);</div>
<div class="line"> </div>
<div class="line"> bit.decodePalettes(numEndpoints, endpoints,</div>
<div class="line"> numSelectors, selectors);</div>
<div class="line"> </div>
<div class="line"> var tables = new UInt8Array(buData, tablesStart, tablesByteLength);</div>
<div class="line"> bit.decodeTables(tables);</div>
<div class="line"> </div>
<div class="line"> // Determine if the file contains a video sequence...</div>
<div class="line"> var isVideo = ...</div>
<div class="line"> </div>
<div class="line"> // Calculate the total number of images in the data</div>
<div class="line"> var numImages = ...</div>
<div class="line"> </div>
<div class="line"> // Set up a subarray pointing at the deflated image descriptions</div>
<div class="line"> // in buData. This is for .ktx2 containers. The image descriptions</div>
<div class="line"> // are located in supercompressionGlobalData. .basis containers will</div>
<div class="line"> // require different code to locate the slice descriptions within</div>
<div class="line"> // the file.</div>
<div class="line"> var imageDescsStart = ...:</div>
<div class="line"> // An imageDesc has 5 uint32 values.</div>
<div class="line"> var imageDescs = new Uint32Data(buData, imageDescsStart,</div>
<div class="line"> numImages * 5 * 4);</div>
<div class="line"> var curImageIndex = 0;</div>
<div class="line"> </div>
<div class="line"> // Pseudo code for processing the levels of a .ktx2 container...</div>
<div class="line"> foreach level {</div>
<div class="line"> var leveWidth = width of image at this level</div>
<div class="line"> var levelHeight = height of image at this level</div>
<div class="line"> imageInfo = new ImageInfo(TextureFormat::ETC1S, levelWidth, levelHeight,</div>
<div class="line"> level);</div>
<div class="line"> foreach image in level {</div>
<div class="line"> // In KTX2 container locate the imageDesc for this image.</div>
<div class="line"> var imageDesc = imageDescs[curImageIndex++];</div>
<div class="line"> imageInfo.flags = imageDesc.imageFlags;</div>
<div class="line"> imageInfo.rgbByteOffset = 0;</div>
<div class="line"> imageInfo.rgbByteLength = imageDesc.rgbSliceByteLength;</div>
<div class="line"> imageInfo.alphaByteOffset = imageDesc.alphaSliceByteOffset &gt; 0 ? imageDesc.rgbSliceByteLength : 0;</div>
<div class="line"> imageInfo.alphaByteLength = imageDesc.alphaSliceByteLength;</div>
<div class="line"> // Determine the location in the ArrayBuffer of the start</div>
<div class="line"> // of the deflated data for level.</div>
<div class="line"> var levelOffset = ...</div>
<div class="line"> // Make a .subarray of the rgb slice data.</div>
<div class="line"> var levelData = new Uint8Array(</div>
<div class="line"> buData,</div>
<div class="line"> levelOffset + imageDesc.rgbSliceByteOffset,</div>
<div class="line"> imageDesc.rgbSliceByteLength + imageDesc.alphaByteLength</div>
<div class="line"> );</div>
<div class="line"> var result = bit.transcodeImage(</div>
<div class="line"> targetFormat,</div>
<div class="line"> levelData,</div>
<div class="line"> imageInfo,</div>
<div class="line"> 0,</div>
<div class="line"> isVideo);</div>
<div class="line"> if ( result.transcodedImage === undefined ) {</div>
<div class="line"> throw new Error( ... );</div>
<div class="line"> }</div>
<div class="line"> let imgData = transcodedImage.get_typed_memory_view();</div>
<div class="line"> </div>
<div class="line"> // Upload data in imgData to WebGL...</div>
<div class="line"> </div>
<div class="line"> // Do not call delete() until data has been uploaded</div>
<div class="line"> // or otherwise copied.</div>
<div class="line"> transcodedImage.delete();</div>
<div class="line"> }</div>
<div class="line"> }</div>
<div class="line"> </div>
<div class="line"> // For .basis containers, it is necessary to locate the slice</div>
<div class="line"> // description(s) for the image and set the values in imageInfo</div>
<div class="line"> // from them. Use of the .basis-specific transcoder is recommended.</div>
<div class="line"> // The definition of the basis_slice_desc struct makes it difficult</div>
<div class="line"> // to create JS interface for it with embind.</div>
</div><!-- fragment --><p>This is the function for transcoding Uastc.</p>
<div class="fragment"><div class="line">transcodeUastc(targetFormat) {</div>
<div class="line"> var uit = new UastcImageTranscoder();</div>
<div class="line"> </div>
<div class="line"> // Determine if the data is supercompressed.</div>
<div class="line"> var zstd = (supercompressionScheme == 2);</div>
<div class="line"> </div>
<div class="line"> // Determine if the data has alpha.</div>
<div class="line"> var hasAlpha = (Channel ID of sample[0] in DFD == 1);</div>
<div class="line"> </div>
<div class="line"> var dctx;</div>
<div class="line"> if (zstd) {</div>
<div class="line"> // Initialize the zstd decoder. Zstd JS wrapper + wasm is</div>
<div class="line"> // a separate package.</div>
<div class="line"> dctx = ZSTD_createDCtx();</div>
<div class="line"> }</div>
<div class="line"> </div>
<div class="line"> // Pseudo code for processing the levels of a .ktx2 container...</div>
<div class="line"> foreach level {</div>
<div class="line"> // Determine the location in the ArrayBuffer buData of the</div>
<div class="line"> // start of the deflated data for the level.</div>
<div class="line"> var levelData = ...</div>
<div class="line"> if (zstd) {</div>
<div class="line"> // Inflate the level data</div>
<div class="line"> levelData = ZSTD_decompressDCtx(dctx, levelData, ... );</div>
<div class="line"> }</div>
<div class="line"> </div>
<div class="line"> var levelWidth = width of image at this level</div>
<div class="line"> var levelHeight = height of image at this level</div>
<div class="line"> var depth = depth of texture at this level</div>
<div class="line"> var levelImageCount = number of layers * number of faces * depth;</div>
<div class="line"> var imageOffsetInLevel = 0;</div>
<div class="line"> </div>
<div class="line"> var imageInfo = new ImageInfo(TextureFormat::UASTC4x4,</div>
<div class="line"> levelWidth, levelHeight, level);</div>
<div class="line"> var levelImageByteLength = imageInfo.numBlocksX * imageInfo.numBlocksY * DFD bytesPlane0;</div>
<div class="line"> </div>
<div class="line"> foreach image in level {</div>
<div class="line"> inImage = Uint8Array(levelData, imageOffsetInLevel, levelImageByteLength);</div>
<div class="line"> imageInfo.flags = 0;</div>
<div class="line"> imageInfo.rgbByteOffset = 0;</div>
<div class="line"> imageInfo.rgbByteLength = levelImageByteLength;</div>
<div class="line"> imageInfo.alphaByteOffset = 0;</div>
<div class="line"> imageInfo.alphaByteLength = 0;</div>
<div class="line"> </div>
<div class="line"> const {transcodedImage} = uit.transcodeImage(</div>
<div class="line"> targetFormat,</div>
<div class="line"> inImage,</div>
<div class="line"> imageInfo,</div>
<div class="line"> 0,</div>
<div class="line"> hasAlpha,</div>
<div class="line"> isVideo);</div>
<div class="line"> if ( result.transcodedImage === undefined ) {</div>
<div class="line"> throw new Error( ... );</div>
<div class="line"> }</div>
<div class="line"> let imgData = transcodedImage.get_typed_memory_view();</div>
<div class="line"> </div>
<div class="line"> // Upload data in imgData to WebGL...</div>
<div class="line"> </div>
<div class="line"> // Do not call delete() until data has been uploaded</div>
<div class="line"> // or otherwise copied.</div>
<div class="line"> transcodedImage.delete();</div>
<div class="line"> </div>
<div class="line"> imageOffsetInLevel += levelImageByteLength;</div>
<div class="line"> }</div>
<div class="line"> }</div>
<div class="line"> // For .basis containers, as with ETC1S, it is necessary to locate</div>
<div class="line"> // the slice description for the image and set the values in imageInfo</div>
<div class="line"> // from it.</div>
<div class="line">}</div>
</div><!-- fragment --> </div></div><!-- contents -->
</div><!-- PageDoc -->
</div><!-- doc-content -->
<div id="page-nav" class="page-nav-panel">
<div id="page-nav-resize-handle"></div>
<div id="page-nav-tree">
<div id="page-nav-contents">
</div><!-- page-nav-contents -->
</div><!-- page-nav-tree -->
</div><!-- page-nav -->
</div><!-- container -->
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
<li class="navelem"><a href="index.html">KTX Javascript Wrappers Reference</a></li>
<li class="footer">Generated on <span class="timestamp"></span> for KTX Javascript Wrappers Reference by <a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.16.1 </li>
</ul>
</div>
</body>
</html>