/* -*- 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 #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 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(buildSelectorCodebook()) { } // 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 cEndpoints{}, cSelectors{}; cEndpoints.resize(jsEndpoints["byteLength"].as()); val memory = val::module_property("HEAP8")["buffer"]; val endpointsView = jsEndpoints["constructor"].new_(memory, reinterpret_cast(cEndpoints.data()), jsEndpoints["length"].as()); endpointsView.call("set", jsEndpoints); cSelectors.resize(jsSelectors["byteLength"].as()); // In case the resize caused HEAP8 to grow. memory = val::module_property("HEAP8")["buffer"]; val selectorsView = jsSelectors["constructor"].new_(memory, reinterpret_cast(cSelectors.data()), jsSelectors["length"].as()); selectorsView.call("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 cTableData{}; cTableData.resize(jsTableData["byteLength"].as()); val memory = val::module_property("HEAP8")["buffer"]; val TableDataView = jsTableData["constructor"].new_(memory, reinterpret_cast(cTableData.data()), jsTableData["length"].as()); TableDataView.call("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 deflatedSlices; uint32_t deflatedSlicesByteLength = jsInSlices["byteLength"].as(); deflatedSlices.resize(deflatedSlicesByteLength); val memory = val::module_property("HEAP8")["buffer"]; val memoryView = jsInSlices["constructor"].new_(memory, reinterpret_cast(deflatedSlices.data()), deflatedSlices.size()); memoryView.call("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: static basist::etc1_global_selector_codebook* pGlobal_codebook; static basist::etc1_global_selector_codebook* buildSelectorCodebook() { if (!pGlobal_codebook) { pGlobal_codebook = new basist::etc1_global_selector_codebook( g_global_selector_cb_size, g_global_selector_cb); } return pGlobal_codebook; } }; 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 deflatedImage; size_t deflatedImageByteLength = jsInImage["byteLength"].as(); deflatedImage.resize(deflatedImageByteLength); val memory = val::module_property("HEAP8")["buffer"]; val memoryView = jsInImage["constructor"].new_(memory, reinterpret_cast(deflatedImage.data()), deflatedImageByteLength); memoryView.call("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; } }; basist::etc1_global_selector_codebook* BasisLzEtc1sImageTranscoder::pGlobal_codebook; } /** @page msc_basis_transcoder Basis 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_("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_("TextureFormat") .value("ETC1S", basis_tex_format::cETC1S) .value("UASTC4x4", basis_tex_format::cUASTC4x4) ; enum_("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_("ImageInfo") .constructor() .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_("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_("UastcImageTranscoder") .constructor() .class_function("getBytesPerBlock", basis_get_bytes_per_block_or_pixel) .function("transcodeImage", &msc::UastcImageTranscoder::transcode_image) ; class_("BasisTranscoderState") .constructor() ; class_("TranscodedImage") .function( "get_typed_memory_view", &msc::TranscodedImage::get_typed_memory_view ) ; }