mirror of
https://github.com/KhronosGroup/KTX-Software.git
synced 2026-01-18 17:41:19 +01:00
- Rework navigation among the multiple Doxygen projects for much easier use. - Rename new ktx tool man pages from `ktxtools_*` to `ktx_*` - Add `ktx` tool mainpage based on RELEASE_NOTES info. - Make minor formatting fix in `ktx` man page. - Update acknowledgements. - Remove outdated TODO.md. - Add script to do `$Date$` keyword smudging. Use it in CI and reference it from README.md to avoid repetition of list of files needing smudging. - Add `$Date$` keywords to some docs. - Remove `$Date$` and #ident keywords that are no longer needed or used. - Document the parts of `khr_df.h` relevant to the libktx API.
803 lines
33 KiB
C++
803 lines
33 KiB
C++
/* -*- tab-width: 4; -*- */
|
|
/* vi: set sw=2 ts=4 expandtab textwidth=80: */
|
|
|
|
/*
|
|
* Copyright 2019-2020 Khronos Group, Inc.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/*
|
|
* This transcoder is DEPRECATED. Use the container independent transcoder from
|
|
* the Binomial LLC repo instead: https://github.com/BinomialLLC/basis_universal.
|
|
*/
|
|
|
|
#include <emscripten/bind.h>
|
|
#include "basisu/transcoder/basisu_transcoder.h"
|
|
|
|
using namespace emscripten;
|
|
using namespace basist;
|
|
|
|
namespace msc {
|
|
// Container independent image description. This was originally in a PR
|
|
// submitted to the BasisU repo to add container independent transcoders.
|
|
// However the code that was eventually added favored explicit parameters
|
|
// over collecting them in a struct. To maintain backward compatibility for
|
|
// msc_basisu_trancoder users, recreate the struct here.
|
|
struct basisu_image_desc {
|
|
uint32_t m_flags;
|
|
uint32_t m_rgb_byte_offset;
|
|
uint32_t m_rgb_byte_length;
|
|
uint32_t m_alpha_byte_offset;
|
|
uint32_t m_alpha_byte_length;
|
|
uint32_t m_orig_width;
|
|
uint32_t m_orig_height;
|
|
uint32_t m_num_blocks_x;
|
|
uint32_t m_num_blocks_y;
|
|
uint32_t m_level;
|
|
|
|
basisu_image_desc() {
|
|
memset(this, 0, sizeof(*this));
|
|
}
|
|
|
|
basisu_image_desc(basis_tex_format, uint32_t width, uint32_t height,
|
|
uint32_t level)
|
|
{
|
|
memset(this, 0, sizeof(*this));
|
|
m_orig_width = width;
|
|
m_orig_height = height;
|
|
// Current formats are all 4 x 4 so ignore tex format param.
|
|
const uint32_t bw = 4, bh = 4;
|
|
m_num_blocks_x = (m_orig_width + (bw - 1)) / bw;
|
|
m_num_blocks_y = (m_orig_height + (bh - 1)) / bh;
|
|
m_level = level;
|
|
}
|
|
|
|
basisu_image_desc(const basis_slice_desc* pSlice_desc,
|
|
const bool hasAlphaSlice,
|
|
uint32_t level = 0) : m_level(level)
|
|
{
|
|
m_flags = pSlice_desc->m_flags & cSliceDescFlagsFrameIsIFrame;
|
|
m_rgb_byte_offset = pSlice_desc->m_file_ofs;
|
|
m_rgb_byte_length = pSlice_desc->m_file_size;
|
|
m_orig_width = pSlice_desc->m_orig_width;
|
|
m_orig_height = pSlice_desc->m_orig_height;
|
|
m_num_blocks_x = pSlice_desc->m_num_blocks_x;
|
|
m_num_blocks_y = pSlice_desc->m_num_blocks_y;
|
|
if (hasAlphaSlice) {
|
|
++pSlice_desc;
|
|
m_alpha_byte_offset = pSlice_desc->m_file_ofs;
|
|
m_alpha_byte_length = pSlice_desc->m_file_size;
|
|
} else {
|
|
m_alpha_byte_offset = 0;
|
|
m_alpha_byte_length = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
// This is needed because the enum defining CDecode* is anonymous.
|
|
enum TranscodeFlagBits {
|
|
TranscodeAlphaDataToOpaqueFormats =
|
|
cDecodeFlagsTranscodeAlphaDataToOpaqueFormats,
|
|
HighQuality = cDecodeFlagsHighQuality
|
|
};
|
|
|
|
class BasisTranscoderState: public basisu_transcoder_state {
|
|
};
|
|
|
|
class TranscodedImage {
|
|
public:
|
|
TranscodedImage(size_t size) : image(size) { }
|
|
|
|
uint8_t* data() { return image.data(); }
|
|
size_t size() { return image.size(); }
|
|
|
|
val get_typed_memory_view() {
|
|
return val(typed_memory_view(image.size(), image.data()));
|
|
}
|
|
|
|
protected:
|
|
std::vector<uint8_t> image;
|
|
};
|
|
|
|
class ImageTranscoderHelper {
|
|
// block size calculations
|
|
static inline uint32_t getWidthInBlocks(uint32_t w, uint32_t bw)
|
|
{
|
|
return (w + (bw - 1)) / bw;
|
|
}
|
|
|
|
static inline uint32_t getHeightInBlocks(uint32_t h, uint32_t bh)
|
|
{
|
|
return (h + (bh - 1)) / bh;
|
|
} //
|
|
|
|
public:
|
|
static size_t getTranscodedImageByteLength(transcoder_texture_format format,
|
|
uint32_t width, uint32_t height)
|
|
{
|
|
uint32_t blockByteLength =
|
|
basis_get_bytes_per_block_or_pixel(format);
|
|
if (basis_transcoder_format_is_uncompressed(format)) {
|
|
return width * height * blockByteLength;
|
|
} else if (format == transcoder_texture_format::cTFPVRTC1_4_RGB
|
|
|| format == transcoder_texture_format::cTFPVRTC1_4_RGBA) {
|
|
// For PVRTC1, Basis only writes (or requires)
|
|
// blockWidth * blockHeight * blockByteLength. But GL requires
|
|
// extra padding for very small textures:
|
|
// https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
|
|
const uint32_t paddedWidth = (width + 3) & ~3;
|
|
const uint32_t paddedHeight = (height + 3) & ~3;
|
|
return (std::max(8U, paddedWidth)
|
|
* std::max(8U, paddedHeight) * 4 + 7) / 8;
|
|
} else {
|
|
uint32_t blockWidth = getWidthInBlocks(width, basis_get_block_width(format));
|
|
uint32_t blockHeight = getHeightInBlocks(height, basis_get_block_height(format));
|
|
return blockWidth * blockHeight * blockByteLength;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class BasisLzEtc1sImageTranscoder : public basisu_lowlevel_etc1s_transcoder {
|
|
public:
|
|
BasisLzEtc1sImageTranscoder()
|
|
: basisu_lowlevel_etc1s_transcoder() { }
|
|
|
|
// Yes, code in the following functions handling data coming in from
|
|
// ArrayBuffers IS copying the data. Sigh! According to Alon Zakai:
|
|
//
|
|
// "There isn't a way to let compiled code access a new ArrayBuffer.
|
|
// The compiled code has hardcoded access to the wasm Memory it was
|
|
// instantiated with - all the pointers it can understand are
|
|
// indexes into that Memory. It can't refer to anything else,
|
|
// I'm afraid."
|
|
//
|
|
// "In the future using different address spaces or techniques with
|
|
// reference types may open up some possibilities here."
|
|
|
|
bool decode_palettes(uint32_t num_endpoints, const val& jsEndpoints,
|
|
uint32_t num_selectors, const val& jsSelectors)
|
|
{
|
|
std::vector<uint8_t> cEndpoints{}, cSelectors{};
|
|
cEndpoints.resize(jsEndpoints["byteLength"].as<size_t>());
|
|
val memory = val::module_property("HEAP8")["buffer"];
|
|
val endpointsView = jsEndpoints["constructor"].new_(memory,
|
|
reinterpret_cast<uintptr_t>(cEndpoints.data()),
|
|
jsEndpoints["length"].as<uint32_t>());
|
|
endpointsView.call<void>("set", jsEndpoints);
|
|
|
|
cSelectors.resize(jsSelectors["byteLength"].as<size_t>());
|
|
// In case the resize caused HEAP8 to grow.
|
|
memory = val::module_property("HEAP8")["buffer"];
|
|
val selectorsView = jsSelectors["constructor"].new_(memory,
|
|
reinterpret_cast<uintptr_t>(cSelectors.data()),
|
|
jsSelectors["length"].as<uint32_t>());
|
|
selectorsView.call<void>("set", jsSelectors);
|
|
|
|
return basisu_lowlevel_etc1s_transcoder::decode_palettes(num_endpoints,
|
|
cEndpoints.data(),
|
|
cEndpoints.size(),
|
|
num_selectors,
|
|
cSelectors.data(),
|
|
cSelectors.size());
|
|
}
|
|
|
|
bool decode_tables(const val& jsTableData)
|
|
{
|
|
std::vector<uint8_t> cTableData{};
|
|
|
|
cTableData.resize(jsTableData["byteLength"].as<size_t>());
|
|
val memory = val::module_property("HEAP8")["buffer"];
|
|
val TableDataView = jsTableData["constructor"].new_(memory,
|
|
reinterpret_cast<uintptr_t>(cTableData.data()),
|
|
jsTableData["length"].as<uint32_t>());
|
|
TableDataView.call<void>("set", jsTableData);
|
|
|
|
return basisu_lowlevel_etc1s_transcoder::decode_tables(
|
|
cTableData.data(),
|
|
cTableData.size());
|
|
}
|
|
|
|
// @~English
|
|
// @brief Transcode a single BasisLZ supercompressed ETC1S image.
|
|
//
|
|
// @param[in] targetFormat the format to which to transcode the image.
|
|
// This enum comes from Basis Universal.
|
|
// @param[in] jsInSlices emscripten::val of a .subarray of the
|
|
// ArrayBuffer holding the file data that
|
|
// points to the first slice for this image.
|
|
// An alpha slice, if it exists, always
|
|
// immediately follows the rgb slice.
|
|
// @param[in] imageDesc reference to a struct basisu_image_desc
|
|
// giving information about the image.
|
|
// @param[in] decodeFlags
|
|
// an OR of basisu_decode_flags bits setting decode
|
|
// options. The only one of general interest is
|
|
// @c cDecodeFlagsTranscodeAlphaDataToOpaqueFormats.
|
|
// This can be used when @p targetFormat lacks an
|
|
// alpha component. When set the alpha slice is
|
|
// transcoded into the RGB components of the target.
|
|
//
|
|
// @return An emscripten::val with 1 entries, @c transcodedImage. If
|
|
// the transcode failed, @c transcodedImage will be undefined.
|
|
//
|
|
emscripten::val transcode_image(
|
|
transcoder_texture_format targetFormat,
|
|
const val& jsInSlices,
|
|
basisu_image_desc& imageDesc,
|
|
uint32_t decodeFlags = 0,
|
|
bool isVideo = false)
|
|
{
|
|
// First of all copy in the deflated data.
|
|
std::vector <uint8_t> deflatedSlices;
|
|
uint32_t deflatedSlicesByteLength
|
|
= jsInSlices["byteLength"].as<uint32_t>();
|
|
deflatedSlices.resize(deflatedSlicesByteLength);
|
|
val memory = val::module_property("HEAP8")["buffer"];
|
|
|
|
val memoryView = jsInSlices["constructor"].new_(memory,
|
|
reinterpret_cast<uintptr_t>(deflatedSlices.data()),
|
|
deflatedSlices.size());
|
|
memoryView.call<void>("set", jsInSlices);
|
|
|
|
size_t tiByteLength =
|
|
ImageTranscoderHelper::getTranscodedImageByteLength(targetFormat,
|
|
imageDesc.m_orig_width,
|
|
imageDesc.m_orig_height);
|
|
TranscodedImage* dst = new TranscodedImage(tiByteLength);
|
|
// ETC1S texel block dimensions
|
|
const uint32_t bw = 4, bh = 4;
|
|
uint32_t numBlocksX =( imageDesc.m_orig_width + (bw - 1)) / bw;
|
|
uint32_t numBlocksY = (imageDesc.m_orig_height + (bh - 1)) / bh;
|
|
|
|
bool status = basisu_lowlevel_etc1s_transcoder::transcode_image(
|
|
targetFormat,
|
|
dst->data(),
|
|
dst->size(),
|
|
deflatedSlices.data(),
|
|
deflatedSlices.size(),
|
|
numBlocksX,
|
|
numBlocksY,
|
|
imageDesc.m_orig_width,
|
|
imageDesc.m_orig_height,
|
|
imageDesc.m_level,
|
|
imageDesc.m_rgb_byte_offset,
|
|
imageDesc.m_rgb_byte_length,
|
|
imageDesc.m_alpha_byte_offset,
|
|
imageDesc.m_alpha_byte_length,
|
|
decodeFlags,
|
|
imageDesc.m_alpha_byte_length != 0,
|
|
isVideo
|
|
// API currently doesn't have any
|
|
// way indicate if this is an
|
|
// iFrame or pFrame.
|
|
);
|
|
val ret = val::object();
|
|
if (status) {
|
|
ret.set("transcodedImage", dst);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
protected:
|
|
};
|
|
|
|
class UastcImageTranscoder : public basisu_lowlevel_uastc_transcoder {
|
|
public:
|
|
UastcImageTranscoder() : basisu_lowlevel_uastc_transcoder() { }
|
|
|
|
// @~English
|
|
// @brief Transcode a single UASTC encoded image.
|
|
//
|
|
// @param[in] targetFormat the format to which to transcode the image.
|
|
// This enum comes from Basis Universal.
|
|
// @param[in] jsInSlices emscripten::val of a .subarray of the
|
|
// ArrayBuffer holding the file data that
|
|
// points to the the image to transcode.
|
|
// @param[in] imageDesc reference to a struct basisu_image_desc
|
|
// giving information about the image.
|
|
// @param[in] decodeFlags
|
|
// an OR of basisu_decode_flags bits setting decode
|
|
// options. The only one of general interest is
|
|
// @c cDecodeFlagsTranscodeAlphaDataToOpaqueFormats.
|
|
// This can be used when @p targetFormat lacks an
|
|
// alpha component. When set, the alpha components
|
|
// are decoded into the RGB components of the target.
|
|
//
|
|
// @return An emscripten::val with 1 entries, @c transcodedImage. If
|
|
// the transcode failed, @c transcodedImage will be undefined.
|
|
//
|
|
emscripten::val transcode_image(
|
|
transcoder_texture_format targetFormat,
|
|
const val& jsInImage,
|
|
basisu_image_desc& imageDesc,
|
|
uint32_t decodeFlags = 0,
|
|
bool hasAlpha = false,
|
|
bool isVideo = false)
|
|
{
|
|
// Copy in the deflated image.
|
|
std::vector <uint8_t> deflatedImage;
|
|
size_t deflatedImageByteLength
|
|
= jsInImage["byteLength"].as<size_t>();
|
|
deflatedImage.resize(deflatedImageByteLength);
|
|
val memory = val::module_property("HEAP8")["buffer"];
|
|
val memoryView = jsInImage["constructor"].new_(memory,
|
|
reinterpret_cast<uintptr_t>(deflatedImage.data()),
|
|
deflatedImageByteLength);
|
|
memoryView.call<void>("set", jsInImage);
|
|
|
|
size_t tiByteLength =
|
|
ImageTranscoderHelper::getTranscodedImageByteLength(targetFormat,
|
|
imageDesc.m_orig_width,
|
|
imageDesc.m_orig_height);
|
|
TranscodedImage* dst = new TranscodedImage(tiByteLength);
|
|
// UASTC texel block dimensions
|
|
const uint32_t bw = 4, bh = 4;
|
|
uint32_t numBlocksX =( imageDesc.m_orig_width + (bw - 1)) / bw;
|
|
uint32_t numBlocksY = (imageDesc.m_orig_height + (bh - 1)) / bh;
|
|
|
|
bool status =
|
|
basisu_lowlevel_uastc_transcoder::transcode_image(
|
|
targetFormat,
|
|
dst->data(),
|
|
dst->size(),
|
|
deflatedImage.data(),
|
|
deflatedImage.size(),
|
|
numBlocksX,
|
|
numBlocksY,
|
|
imageDesc.m_orig_width,
|
|
imageDesc.m_orig_height,
|
|
imageDesc.m_level,
|
|
imageDesc.m_rgb_byte_offset,
|
|
imageDesc.m_rgb_byte_length,
|
|
decodeFlags,
|
|
hasAlpha,
|
|
isVideo
|
|
// API currently doesn't have any
|
|
// way indicate if this is an
|
|
// iFrame or pFrame.
|
|
);
|
|
val ret = val::object();
|
|
if (status) {
|
|
ret.set("transcodedImage", dst);
|
|
}
|
|
return ret;
|
|
}
|
|
};
|
|
}
|
|
|
|
/** @page msc_basis_transcoder Basis Universal Image Transcoder binding
|
|
|
|
@warning 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.
|
|
|
|
## WebIDL for the binding
|
|
|
|
@code{.unparsed}
|
|
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"
|
|
};
|
|
|
|
@endcode
|
|
|
|
## 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 @c src 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.
|
|
|
|
### 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.
|
|
@code{.unparsed}
|
|
<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>
|
|
@endcode
|
|
|
|
@e After the module is initialized, invoke code that will directly or indirectly cause
|
|
a function with code like the following to be executed.
|
|
|
|
## Somewhere in the loader/transcoder
|
|
|
|
Assume a KTX file is fetched via an XMLHttpRequest which deposits the data into
|
|
a Uint8Array, "buData"...
|
|
|
|
@note 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.
|
|
|
|
@code{.unparsed}
|
|
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.' );
|
|
}
|
|
@endcode
|
|
|
|
This is the function for transcoding etc1s.
|
|
|
|
@code{.unparsed}
|
|
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.
|
|
@endcode
|
|
|
|
This is the function for transcoding Uastc.
|
|
|
|
@code{.unparsed}
|
|
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.
|
|
}
|
|
@endcode
|
|
|
|
*/
|
|
|
|
EMSCRIPTEN_BINDINGS(ktx_wrappers)
|
|
{
|
|
enum_<transcoder_texture_format>("TranscodeTarget")
|
|
.value("ETC1_RGB", transcoder_texture_format::cTFETC1_RGB)
|
|
.value("BC1_RGB", transcoder_texture_format::cTFBC1_RGB)
|
|
.value("BC4_R", transcoder_texture_format::cTFBC4_R)
|
|
.value("BC5_RG", transcoder_texture_format::cTFBC5_RG)
|
|
.value("BC3_RGBA", transcoder_texture_format::cTFBC3_RGBA)
|
|
.value("PVRTC1_4_RGB", transcoder_texture_format::cTFPVRTC1_4_RGB)
|
|
.value("PVRTC1_4_RGBA", transcoder_texture_format::cTFPVRTC1_4_RGBA)
|
|
.value("BC7_RGBA", transcoder_texture_format::cTFBC7_RGBA)
|
|
// Deprecated. Use BC7_RGBA.
|
|
.value("BC7_M6_RGB", transcoder_texture_format::cTFBC7_M6_RGB)
|
|
// Deprecated. Use BC7_RGBA.
|
|
.value("BC7_M5_RGBA", transcoder_texture_format::cTFBC7_M5_RGBA)
|
|
.value("ETC2_RGBA", transcoder_texture_format::cTFETC2_RGBA)
|
|
.value("ASTC_4x4_RGBA", transcoder_texture_format::cTFASTC_4x4_RGBA)
|
|
.value("RGBA32", transcoder_texture_format::cTFRGBA32)
|
|
.value("RGB565", transcoder_texture_format::cTFRGB565)
|
|
.value("BGR565", transcoder_texture_format::cTFBGR565)
|
|
.value("RGBA4444", transcoder_texture_format::cTFRGBA4444)
|
|
.value("PVRTC2_4_RGB", transcoder_texture_format::cTFPVRTC2_4_RGB)
|
|
.value("PVRTC2_4_RGBA", transcoder_texture_format::cTFPVRTC2_4_RGBA)
|
|
.value("EAC_R11", transcoder_texture_format::cTFETC2_EAC_R11)
|
|
.value("EAC_RG11", transcoder_texture_format::cTFETC2_EAC_RG11)
|
|
;
|
|
|
|
enum_<basis_tex_format>("TextureFormat")
|
|
.value("ETC1S", basis_tex_format::cETC1S)
|
|
.value("UASTC4x4", basis_tex_format::cUASTC4x4)
|
|
;
|
|
|
|
enum_<msc::TranscodeFlagBits>("TranscodeFlagBits")
|
|
.value("TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS",
|
|
msc::TranscodeAlphaDataToOpaqueFormats)
|
|
.value("HIGH_QUALITY", msc::HighQuality)
|
|
;
|
|
|
|
function("initTranscoders", basisu_transcoder_init);
|
|
function("isFormatSupported", basis_is_format_supported);
|
|
|
|
class_<msc::basisu_image_desc>("ImageInfo")
|
|
.constructor<basis_tex_format,uint32_t,uint32_t,uint32_t>()
|
|
.property("flags", &msc::basisu_image_desc::m_flags)
|
|
.property("rgbByteOffset", &msc::basisu_image_desc::m_rgb_byte_offset)
|
|
.property("rgbByteLength", &msc::basisu_image_desc::m_rgb_byte_length)
|
|
.property("alphaByteOffset", &msc::basisu_image_desc::m_alpha_byte_offset)
|
|
.property("alphaByteLength", &msc::basisu_image_desc::m_alpha_byte_length)
|
|
.property("width", &msc::basisu_image_desc::m_orig_width)
|
|
.property("height", &msc::basisu_image_desc::m_orig_height)
|
|
.property("numBlocksX", &msc::basisu_image_desc::m_num_blocks_x)
|
|
.property("numBlocksY", &msc::basisu_image_desc::m_num_blocks_y)
|
|
.property("level", &msc::basisu_image_desc::m_level)
|
|
;
|
|
|
|
class_<msc::BasisLzEtc1sImageTranscoder>("BasisLzEtc1sImageTranscoder")
|
|
.constructor()
|
|
.class_function("getBytesPerBlock", basis_get_bytes_per_block_or_pixel)
|
|
.function("decodePalettes",
|
|
&msc::BasisLzEtc1sImageTranscoder::decode_palettes)
|
|
.function("decodeTables",
|
|
&msc::BasisLzEtc1sImageTranscoder::decode_tables)
|
|
.function("transcodeImage",
|
|
&msc::BasisLzEtc1sImageTranscoder::transcode_image)
|
|
;
|
|
|
|
class_<msc::UastcImageTranscoder>("UastcImageTranscoder")
|
|
.constructor()
|
|
.class_function("getBytesPerBlock", basis_get_bytes_per_block_or_pixel)
|
|
.function("transcodeImage",
|
|
&msc::UastcImageTranscoder::transcode_image)
|
|
;
|
|
|
|
class_<basisu_transcoder_state>("BasisTranscoderState")
|
|
.constructor()
|
|
;
|
|
|
|
class_<msc::TranscodedImage>("TranscodedImage")
|
|
.function( "get_typed_memory_view",
|
|
&msc::TranscodedImage::get_typed_memory_view )
|
|
;
|
|
|
|
}
|