Path: blob/master/thirdparty/basis_universal/transcoder/basisu_transcoder.h
9905 views
// basisu_transcoder.h1// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.2// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing3//4// Licensed under the Apache License, Version 2.0 (the "License");5// you may not use this file except in compliance with the License.6// You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing, software11// distributed under the License is distributed on an "AS IS" BASIS,12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13// See the License for the specific language governing permissions and14// limitations under the License.15#pragma once1617// By default KTX2 support is enabled to simplify compilation. This implies the need for the Zstandard library (which we distribute as a single source file in the "zstd" directory) by default.18// Set BASISD_SUPPORT_KTX2 to 0 to completely disable KTX2 support as well as Zstd/miniz usage which is only required for UASTC supercompression in KTX2 files.19// Also see BASISD_SUPPORT_KTX2_ZSTD in basisu_transcoder.cpp, which individually disables Zstd usage.20#ifndef BASISD_SUPPORT_KTX221#define BASISD_SUPPORT_KTX2 122#endif2324// Set BASISD_SUPPORT_KTX2_ZSTD to 0 to disable Zstd usage and KTX2 UASTC Zstd supercompression support25#ifndef BASISD_SUPPORT_KTX2_ZSTD26#define BASISD_SUPPORT_KTX2_ZSTD 127#endif2829// Set BASISU_FORCE_DEVEL_MESSAGES to 1 to enable debug printf()'s whenever an error occurs, for easier debugging during development.30#ifndef BASISU_FORCE_DEVEL_MESSAGES31// TODO - disable before checking in32#define BASISU_FORCE_DEVEL_MESSAGES 033#endif3435#include "basisu_transcoder_internal.h"36#include "basisu_transcoder_uastc.h"37#include "basisu_file_headers.h"3839namespace basist40{41// High-level composite texture formats supported by the transcoder.42// Each of these texture formats directly correspond to OpenGL/D3D/Vulkan etc. texture formats.43// Notes:44// - If you specify a texture format that supports alpha, but the .basis file doesn't have alpha, the transcoder will automatically output a45// fully opaque (255) alpha channel.46// - The PVRTC1 texture formats only support power of 2 dimension .basis files, but this may be relaxed in a future version.47// - The PVRTC1 transcoders are real-time encoders, so don't expect the highest quality. We may add a slower encoder with improved quality.48// - These enums must be kept in sync with Javascript code that calls the transcoder.49enum class transcoder_texture_format50{51// Compressed formats5253// ETC1-254cTFETC1_RGB = 0, // Opaque only, returns RGB or alpha data if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified55cTFETC2_RGBA = 1, // Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files5657// BC1-5, BC7 (desktop, some mobile devices)58cTFBC1_RGB = 2, // Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified59cTFBC3_RGBA = 3, // Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files60cTFBC4_R = 4, // Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified61cTFBC5_RG = 5, // XY: Two BC4 blocks, X=R and Y=Alpha, .basis file should have alpha data (if not Y will be all 255's)62cTFBC7_RGBA = 6, // RGB or RGBA, mode 5 for ETC1S, modes (1,2,3,5,6,7) for UASTC6364// PVRTC1 4bpp (mobile, PowerVR devices)65cTFPVRTC1_4_RGB = 8, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.66cTFPVRTC1_4_RGBA = 9, // Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.6768// ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)69cTFASTC_4x4_RGBA = 10, // LDR. Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files.70// LDR: Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.7172// ATC (mobile, Adreno devices, this is a niche format)73cTFATC_RGB = 11, // Opaque, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. ATI ATC (GL_ATC_RGB_AMD)74cTFATC_RGBA = 12, // Opaque+alpha, alpha channel will be opaque for opaque .basis files. ATI ATC (GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD)7576// FXT1 (desktop, Intel devices, this is a super obscure format)77cTFFXT1_RGB = 17, // Opaque only, uses exclusively CC_MIXED blocks. Notable for having a 8x4 block size. GL_3DFX_texture_compression_FXT1 is supported on Intel integrated GPU's (such as HD 630).78// Punch-through alpha is relatively easy to support, but full alpha is harder. This format is only here for completeness so opaque-only is fine for now.79// See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h.8081cTFPVRTC2_4_RGB = 18, // Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB).82cTFPVRTC2_4_RGBA = 19, // Opaque+alpha, slower to encode than cTFPVRTC2_4_RGB. Premultiplied alpha is highly recommended, otherwise the color channel can leak into the alpha channel on transparent blocks.8384cTFETC2_EAC_R11 = 20, // R only (ETC2 EAC R11 unsigned)85cTFETC2_EAC_RG11 = 21, // RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps8687cTFBC6H = 22, // HDR, RGB only, unsigned88cTFASTC_HDR_4x4_RGBA = 23, // HDR, RGBA (currently UASTC HDR 4x4 encoders are only RGB), unsigned8990// Uncompressed (raw pixel) formats91// Note these uncompressed formats (RGBA32, 565, and 4444) can only be transcoded to from LDR input files (ETC1S or UASTC LDR).92cTFRGBA32 = 13, // 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.93cTFRGB565 = 14, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 1194cTFBGR565 = 15, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 095cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 09697// Note these uncompressed formats (HALF and 9E5) can only be transcoded to from HDR input files (UASTC HDR 4x4 or ASTC HDR 6x6).98cTFRGB_HALF = 24, // 48bpp RGB half (16-bits/component, 3 components)99cTFRGBA_HALF = 25, // 64bpp RGBA half (16-bits/component, 4 components) (A will always currently 1.0, UASTC_HDR doesn't support alpha)100cTFRGB_9E5 = 26, // 32bpp RGB 9E5 (shared exponent, positive only, see GL_EXT_texture_shared_exponent)101102cTFASTC_HDR_6x6_RGBA = 27, // HDR, RGBA (currently our ASTC HDR 6x6 encodes are only RGB), unsigned103104cTFTotalTextureFormats = 28,105106// ----- The following are old/legacy enums for compatibility with code compiled against previous versions107cTFETC1 = cTFETC1_RGB,108cTFETC2 = cTFETC2_RGBA,109cTFBC1 = cTFBC1_RGB,110cTFBC3 = cTFBC3_RGBA,111cTFBC4 = cTFBC4_R,112cTFBC5 = cTFBC5_RG,113114// Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC, which supports numerous modes.115cTFBC7_M6_RGB = cTFBC7_RGBA, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats.116cTFBC7_M5_RGBA = cTFBC7_RGBA, // Opaque+alpha, alpha channel will be opaque for opaque .basis files117cTFBC7_M6_OPAQUE_ONLY = cTFBC7_RGBA,118cTFBC7_M5 = cTFBC7_RGBA,119cTFBC7_ALT = 7,120121cTFASTC_4x4 = cTFASTC_4x4_RGBA,122123cTFATC_RGBA_INTERPOLATED_ALPHA = cTFATC_RGBA,124};125126// For compressed texture formats, this returns the # of bytes per block. For uncompressed, it returns the # of bytes per pixel.127// NOTE: Previously, this function was called basis_get_bytes_per_block(), and it always returned 16*bytes_per_pixel for uncompressed formats which was confusing.128uint32_t basis_get_bytes_per_block_or_pixel(transcoder_texture_format fmt);129130// Returns format's name in ASCII131const char* basis_get_format_name(transcoder_texture_format fmt);132133// Returns block format name in ASCII134const char* basis_get_block_format_name(block_format fmt);135136// Returns true if the format supports an alpha channel.137bool basis_transcoder_format_has_alpha(transcoder_texture_format fmt);138139// Returns true if the format is HDR.140bool basis_transcoder_format_is_hdr(transcoder_texture_format fmt);141142// Returns true if the format is LDR.143inline bool basis_transcoder_format_is_ldr(transcoder_texture_format fmt) { return !basis_transcoder_format_is_hdr(fmt); }144145// Returns the basisu::texture_format corresponding to the specified transcoder_texture_format.146basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt);147148// Returns the texture type's name in ASCII.149const char* basis_get_texture_type_name(basis_texture_type tex_type);150151// Returns true if the transcoder texture type is an uncompressed (raw pixel) format.152bool basis_transcoder_format_is_uncompressed(transcoder_texture_format tex_type);153154// Returns the # of bytes per pixel for uncompressed formats, or 0 for block texture formats.155uint32_t basis_get_uncompressed_bytes_per_pixel(transcoder_texture_format fmt);156157// Returns the block width for the specified texture format, which is currently either 4 or 8 for FXT1.158uint32_t basis_get_block_width(transcoder_texture_format tex_type);159160// Returns the block height for the specified texture format, which is currently always 4.161uint32_t basis_get_block_height(transcoder_texture_format tex_type);162163// Returns true if the specified format was enabled at compile time, and is supported for the specific basis/ktx2 texture format (ETC1S, UASTC, or UASTC HDR).164bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt = basis_tex_format::cETC1S);165166// Returns the block width/height for the specified basis texture file format.167uint32_t basis_tex_format_get_block_width(basis_tex_format fmt);168uint32_t basis_tex_format_get_block_height(basis_tex_format fmt);169170bool basis_tex_format_is_hdr(basis_tex_format fmt);171inline bool basis_tex_format_is_ldr(basis_tex_format fmt) { return !basis_tex_format_is_hdr(fmt); }172173// Validates that the output buffer is large enough to hold the entire transcoded texture.174// For uncompressed texture formats, most input parameters are in pixels, not blocks. Blocks are 4x4 pixels.175bool basis_validate_output_buffer_size(transcoder_texture_format target_format,176uint32_t output_blocks_buf_size_in_blocks_or_pixels,177uint32_t orig_width, uint32_t orig_height,178uint32_t output_row_pitch_in_blocks_or_pixels,179uint32_t output_rows_in_pixels);180181// Computes the size in bytes of a transcoded image or texture, taking into account the format's block width/height and any minimum size PVRTC1 requirements required by OpenGL.182// Note the returned value is not necessarily the # of bytes a transcoder could write to the output buffer due to these minimum PVRTC1 requirements.183// (These PVRTC1 requirements are not ours, but OpenGL's.)184uint32_t basis_compute_transcoded_image_size_in_bytes(transcoder_texture_format target_format, uint32_t orig_width, uint32_t orig_height);185186class basisu_transcoder;187188// This struct holds all state used during transcoding. For video, it needs to persist between image transcodes (it holds the previous frame).189// For threading you can use one state per thread.190struct basisu_transcoder_state191{192struct block_preds193{194uint16_t m_endpoint_index;195uint8_t m_pred_bits;196};197198basisu::vector<block_preds> m_block_endpoint_preds[2];199200enum { cMaxPrevFrameLevels = 16 };201basisu::vector<uint32_t> m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index]202203void clear()204{205for (uint32_t i = 0; i < 2; i++)206{207m_block_endpoint_preds[i].clear();208209for (uint32_t j = 0; j < cMaxPrevFrameLevels; j++)210m_prev_frame_indices[i][j].clear();211}212}213};214215// Low-level helper classes that do the actual transcoding.216217// ETC1S218class basisu_lowlevel_etc1s_transcoder219{220friend class basisu_transcoder;221222public:223basisu_lowlevel_etc1s_transcoder();224225void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_pGlobal_codebook = pGlobal_codebook; }226const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_pGlobal_codebook; }227228bool decode_palettes(229uint32_t num_endpoints, const uint8_t* pEndpoints_data, uint32_t endpoints_data_size,230uint32_t num_selectors, const uint8_t* pSelectors_data, uint32_t selectors_data_size);231232bool decode_tables(const uint8_t* pTable_data, uint32_t table_data_size);233234bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,235uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const bool is_video, const bool is_alpha_slice, const uint32_t level_index, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,236basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0, uint32_t decode_flags = 0);237238bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,239uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,240basisu_transcoder_state* pState = nullptr, bool astc_transcode_alpha = false, void* pAlpha_blocks = nullptr, uint32_t output_rows_in_pixels = 0, uint32_t decode_flags = 0)241{242return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt, output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks,243header.m_tex_type == cBASISTexTypeVideoFrames, (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0, slice_desc.m_level_index,244slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels, pState,245astc_transcode_alpha,246pAlpha_blocks,247output_rows_in_pixels, decode_flags);248}249250// Container independent transcoding251bool transcode_image(252transcoder_texture_format target_format,253void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,254const uint8_t* pCompressed_data, uint32_t compressed_data_length,255uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,256uint32_t rgb_offset, uint32_t rgb_length, uint32_t alpha_offset, uint32_t alpha_length,257uint32_t decode_flags = 0,258bool basis_file_has_alpha_slices = false,259bool is_video = false,260uint32_t output_row_pitch_in_blocks_or_pixels = 0,261basisu_transcoder_state* pState = nullptr,262uint32_t output_rows_in_pixels = 0);263264void clear()265{266m_local_endpoints.clear();267m_local_selectors.clear();268m_endpoint_pred_model.clear();269m_delta_endpoint_model.clear();270m_selector_model.clear();271m_selector_history_buf_rle_model.clear();272m_selector_history_buf_size = 0;273}274275// Low-level methods276typedef basisu::vector<endpoint> endpoint_vec;277const endpoint_vec& get_endpoints() const { return m_local_endpoints; }278279typedef basisu::vector<selector> selector_vec;280const selector_vec& get_selectors() const { return m_local_selectors; }281282private:283const basisu_lowlevel_etc1s_transcoder* m_pGlobal_codebook;284285endpoint_vec m_local_endpoints;286selector_vec m_local_selectors;287288huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model;289290uint32_t m_selector_history_buf_size;291292basisu_transcoder_state m_def_state;293};294295enum basisu_decode_flags296{297// PVRTC1: decode non-pow2 ETC1S texture level to the next larger power of 2 (not implemented yet, but we're going to support it). Ignored if the slice's dimensions are already a power of 2.298cDecodeFlagsPVRTCDecodeToNextPow2 = 2,299300// When decoding to an opaque texture format, if the basis file has alpha, decode the alpha slice instead of the color slice to the output texture format.301// This is primarily to allow decoding of textures with alpha to multiple ETC1 textures (one for color, another for alpha).302cDecodeFlagsTranscodeAlphaDataToOpaqueFormats = 4,303304// Forbid usage of BC1 3 color blocks (we don't support BC1 punchthrough alpha yet).305// This flag is used internally when decoding to BC3.306cDecodeFlagsBC1ForbidThreeColorBlocks = 8,307308// The output buffer contains alpha endpoint/selector indices.309// Used internally when decoding formats like ASTC that require both color and alpha data to be available when transcoding to the output format.310cDecodeFlagsOutputHasAlphaIndices = 16,311312cDecodeFlagsHighQuality = 32,313314cDecodeFlagsNoETC1SChromaFiltering = 64315};316317// UASTC LDR 4x4318class basisu_lowlevel_uastc_ldr_4x4_transcoder319{320friend class basisu_transcoder;321322public:323basisu_lowlevel_uastc_ldr_4x4_transcoder();324325bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,326uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,327basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);328329bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,330uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,331basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)332{333return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,334output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,335pState, output_rows_in_pixels, channel0, channel1, decode_flags);336}337338// Container independent transcoding339bool transcode_image(340transcoder_texture_format target_format,341void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,342const uint8_t* pCompressed_data, uint32_t compressed_data_length,343uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,344uint32_t slice_offset, uint32_t slice_length,345uint32_t decode_flags = 0,346bool has_alpha = false,347bool is_video = false,348uint32_t output_row_pitch_in_blocks_or_pixels = 0,349basisu_transcoder_state* pState = nullptr,350uint32_t output_rows_in_pixels = 0,351int channel0 = -1, int channel1 = -1);352};353354// UASTC HDR 4x4355class basisu_lowlevel_uastc_hdr_4x4_transcoder356{357friend class basisu_transcoder;358359public:360basisu_lowlevel_uastc_hdr_4x4_transcoder();361362bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,363uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,364basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);365366bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,367uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,368basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)369{370return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,371output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,372pState, output_rows_in_pixels, channel0, channel1, decode_flags);373}374375// Container independent transcoding376bool transcode_image(377transcoder_texture_format target_format,378void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,379const uint8_t* pCompressed_data, uint32_t compressed_data_length,380uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,381uint32_t slice_offset, uint32_t slice_length,382uint32_t decode_flags = 0,383bool has_alpha = false,384bool is_video = false,385uint32_t output_row_pitch_in_blocks_or_pixels = 0,386basisu_transcoder_state* pState = nullptr,387uint32_t output_rows_in_pixels = 0,388int channel0 = -1, int channel1 = -1);389};390391// ASTC HDR 6x6392class basisu_lowlevel_astc_hdr_6x6_transcoder393{394friend class basisu_transcoder;395396public:397basisu_lowlevel_astc_hdr_6x6_transcoder();398399bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,400uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,401basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);402403bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,404uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,405basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)406{407return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,408output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,409pState, output_rows_in_pixels, channel0, channel1, decode_flags);410}411412// Container independent transcoding413bool transcode_image(414transcoder_texture_format target_format,415void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,416const uint8_t* pCompressed_data, uint32_t compressed_data_length,417uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,418uint32_t slice_offset, uint32_t slice_length,419uint32_t decode_flags = 0,420bool has_alpha = false,421bool is_video = false,422uint32_t output_row_pitch_in_blocks_or_pixels = 0,423basisu_transcoder_state* pState = nullptr,424uint32_t output_rows_in_pixels = 0,425int channel0 = -1, int channel1 = -1);426};427428// ASTC HDR 6x6 intermediate429class basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder430{431friend class basisu_transcoder;432433public:434basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder();435436bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,437uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,438basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);439440bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,441uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,442basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)443{444return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,445output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,446pState, output_rows_in_pixels, channel0, channel1, decode_flags);447}448449// Container independent transcoding450bool transcode_image(451transcoder_texture_format target_format,452void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,453const uint8_t* pCompressed_data, uint32_t compressed_data_length,454uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,455uint32_t slice_offset, uint32_t slice_length,456uint32_t decode_flags = 0,457bool has_alpha = false,458bool is_video = false,459uint32_t output_row_pitch_in_blocks_or_pixels = 0,460basisu_transcoder_state* pState = nullptr,461uint32_t output_rows_in_pixels = 0,462int channel0 = -1, int channel1 = -1);463};464465struct basisu_slice_info466{467uint32_t m_orig_width;468uint32_t m_orig_height;469470uint32_t m_width;471uint32_t m_height;472473uint32_t m_num_blocks_x;474uint32_t m_num_blocks_y;475uint32_t m_total_blocks;476477uint32_t m_block_width;478uint32_t m_block_height;479480uint32_t m_compressed_size;481482uint32_t m_slice_index; // the slice index in the .basis file483uint32_t m_image_index; // the source image index originally provided to the encoder484uint32_t m_level_index; // the mipmap level within this image485486uint32_t m_unpacked_slice_crc16;487488bool m_alpha_flag; // true if the slice has alpha data489bool m_iframe_flag; // true if the slice is an I-Frame490};491492typedef basisu::vector<basisu_slice_info> basisu_slice_info_vec;493494struct basisu_image_info495{496uint32_t m_image_index;497uint32_t m_total_levels;498499uint32_t m_orig_width;500uint32_t m_orig_height;501502uint32_t m_width;503uint32_t m_height;504505uint32_t m_block_width;506uint32_t m_block_height;507508uint32_t m_num_blocks_x;509uint32_t m_num_blocks_y;510uint32_t m_total_blocks;511512uint32_t m_first_slice_index;513514bool m_alpha_flag; // true if the image has alpha data515bool m_iframe_flag; // true if the image is an I-Frame516};517518struct basisu_image_level_info519{520uint32_t m_image_index;521uint32_t m_level_index;522523uint32_t m_orig_width;524uint32_t m_orig_height;525526uint32_t m_width;527uint32_t m_height;528529uint32_t m_block_width;530uint32_t m_block_height;531532uint32_t m_num_blocks_x;533uint32_t m_num_blocks_y;534uint32_t m_total_blocks;535536uint32_t m_first_slice_index;537538uint32_t m_rgb_file_ofs;539uint32_t m_rgb_file_len;540uint32_t m_alpha_file_ofs;541uint32_t m_alpha_file_len;542543bool m_alpha_flag; // true if the image has alpha data544bool m_iframe_flag; // true if the image is an I-Frame545};546547struct basisu_file_info548{549uint32_t m_version;550uint32_t m_total_header_size;551552uint32_t m_total_selectors;553// will be 0 for UASTC or if the file uses global codebooks554uint32_t m_selector_codebook_ofs;555uint32_t m_selector_codebook_size;556557uint32_t m_total_endpoints;558// will be 0 for UASTC or if the file uses global codebooks559uint32_t m_endpoint_codebook_ofs;560uint32_t m_endpoint_codebook_size;561562uint32_t m_tables_ofs;563uint32_t m_tables_size;564565uint32_t m_slices_size;566567basis_texture_type m_tex_type;568uint32_t m_us_per_frame;569570// Low-level slice information (1 slice per image for color-only basis files, 2 for alpha basis files)571basisu_slice_info_vec m_slice_info;572573uint32_t m_total_images; // total # of images574basisu::vector<uint32_t> m_image_mipmap_levels; // the # of mipmap levels for each image575576uint32_t m_userdata0;577uint32_t m_userdata1;578579basis_tex_format m_tex_format; // ETC1S, UASTC, etc.580581uint32_t m_block_width;582uint32_t m_block_height;583584bool m_y_flipped; // true if the image was Y flipped585bool m_etc1s; // true if the file is ETC1S586bool m_has_alpha_slices; // true if the texture has alpha slices (for ETC1S: even slices RGB, odd slices alpha)587};588589// High-level transcoder class which accepts .basis file data and allows the caller to query information about the file and transcode image levels to various texture formats.590// If you're just starting out this is the class you care about.591class basisu_transcoder592{593basisu_transcoder(basisu_transcoder&);594basisu_transcoder& operator= (const basisu_transcoder&);595596public:597basisu_transcoder();598599// Validates the .basis file. This computes a crc16 over the entire file, so it's slow.600bool validate_file_checksums(const void* pData, uint32_t data_size, bool full_validation) const;601602// Quick header validation - no crc16 checks.603bool validate_header(const void* pData, uint32_t data_size) const;604605basis_texture_type get_texture_type(const void* pData, uint32_t data_size) const;606bool get_userdata(const void* pData, uint32_t data_size, uint32_t& userdata0, uint32_t& userdata1) const;607608// Returns the total number of images in the basis file (always 1 or more).609// Note that the number of mipmap levels for each image may differ, and that images may have different resolutions.610uint32_t get_total_images(const void* pData, uint32_t data_size) const;611612basis_tex_format get_basis_tex_format(const void* pData, uint32_t data_size) const;613614// Returns the number of mipmap levels in an image.615uint32_t get_total_image_levels(const void* pData, uint32_t data_size, uint32_t image_index) const;616617// Returns basic information about an image. Note that orig_width/orig_height may not be a multiple of 4.618bool get_image_level_desc(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, uint32_t& orig_width, uint32_t& orig_height, uint32_t& total_blocks) const;619620// Returns information about the specified image.621bool get_image_info(const void* pData, uint32_t data_size, basisu_image_info& image_info, uint32_t image_index) const;622623// Returns information about the specified image's mipmap level.624bool get_image_level_info(const void* pData, uint32_t data_size, basisu_image_level_info& level_info, uint32_t image_index, uint32_t level_index) const;625626// Get a description of the basis file and low-level information about each slice.627bool get_file_info(const void* pData, uint32_t data_size, basisu_file_info& file_info) const;628629// start_transcoding() must be called before calling transcode_slice() or transcode_image_level().630// For ETC1S files, this call decompresses the selector/endpoint codebooks, so ideally you would only call this once per .basis file (not each image/mipmap level).631bool start_transcoding(const void* pData, uint32_t data_size);632633bool stop_transcoding();634635// Returns true if start_transcoding() has been called.636bool get_ready_to_transcode() const { return m_ready_to_transcode; }637638// transcode_image_level() decodes a single mipmap level from the .basis file to any of the supported output texture formats.639// It'll first find the slice(s) to transcode, then call transcode_slice() one or two times to decode both the color and alpha texture data (or RG texture data from two slices for BC5).640// If the .basis file doesn't have alpha slices, the output alpha blocks will be set to fully opaque (all 255's).641// Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2, due to PVRTC1 format requirements.642// output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32 etc.643// output_row_pitch_in_blocks_or_pixels: Number of blocks or pixels per row. If 0, the transcoder uses the slice's num_blocks_x or orig_width (NOT num_blocks_x * 4). Ignored for PVRTC1 (due to texture swizzling).644// output_rows_in_pixels: Ignored unless fmt is uncompressed (cRGBA32, etc.). The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4).645// Notes:646// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.647// - This method assumes the output texture buffer is readable. In some cases to handle alpha, the transcoder will write temporary data to the output texture in648// a first pass, which will be read in a second pass.649bool transcode_image_level(650const void* pData, uint32_t data_size,651uint32_t image_index, uint32_t level_index,652void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,653transcoder_texture_format fmt,654uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0) const;655656// Finds the basis slice corresponding to the specified image/level/alpha params, or -1 if the slice can't be found.657int find_slice(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const;658659// transcode_slice() decodes a single slice from the .basis file. It's a low-level API - most likely you want to use transcode_image_level().660// This is a low-level API, and will be needed to be called multiple times to decode some texture formats (like BC3, BC5, or ETC2).661// output_blocks_buf_size_in_blocks_or_pixels is just used for verification to make sure the output buffer is large enough.662// output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32.663// output_block_stride_in_bytes: Number of bytes between each output block.664// output_row_pitch_in_blocks_or_pixels: Number of blocks or pixels per row. If 0, the transcoder uses the slice's num_blocks_x or orig_width (NOT num_blocks_x * 4). Ignored for PVRTC1 (due to texture swizzling).665// output_rows_in_pixels: Ignored unless fmt is cRGBA32. The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4).666// Notes:667// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.668bool transcode_slice(const void* pData, uint32_t data_size, uint32_t slice_index,669void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,670block_format fmt, uint32_t output_block_stride_in_bytes, uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, basisu_transcoder_state* pState = nullptr, void* pAlpha_blocks = nullptr,671uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1) const;672673static void write_opaque_alpha_blocks(674uint32_t num_blocks_x, uint32_t num_blocks_y,675void* pOutput_blocks, block_format fmt,676uint32_t block_stride_in_bytes, uint32_t output_row_pitch_in_blocks_or_pixels);677678void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_lowlevel_etc1s_decoder.set_global_codebooks(pGlobal_codebook); }679const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_lowlevel_etc1s_decoder.get_global_codebooks(); }680681const basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() const { return m_lowlevel_etc1s_decoder; }682basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() { return m_lowlevel_etc1s_decoder; }683684const basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_decoder; }685basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_decoder; }686687private:688mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;689mutable basisu_lowlevel_uastc_ldr_4x4_transcoder m_lowlevel_uastc_decoder;690mutable basisu_lowlevel_uastc_hdr_4x4_transcoder m_lowlevel_uastc_4x4_hdr_decoder;691mutable basisu_lowlevel_astc_hdr_6x6_transcoder m_lowlevel_astc_6x6_hdr_decoder;692mutable basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder m_lowlevel_astc_6x6_hdr_intermediate_decoder;693694bool m_ready_to_transcode;695696int find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const;697698bool validate_header_quick(const void* pData, uint32_t data_size) const;699};700701// basisu_transcoder_init() MUST be called before a .basis file can be transcoded.702void basisu_transcoder_init();703704enum debug_flags_t705{706cDebugFlagVisCRs = 1,707cDebugFlagVisBC1Sels = 2,708cDebugFlagVisBC1Endpoints = 4709};710uint32_t get_debug_flags();711void set_debug_flags(uint32_t f);712713// ------------------------------------------------------------------------------------------------------714// Optional .KTX2 file format support715// KTX2 reading optionally requires miniz or Zstd decompressors for supercompressed UASTC files.716// ------------------------------------------------------------------------------------------------------717#if BASISD_SUPPORT_KTX2718#pragma pack(push)719#pragma pack(1)720struct ktx2_header721{722uint8_t m_identifier[12];723basisu::packed_uint<4> m_vk_format;724basisu::packed_uint<4> m_type_size;725basisu::packed_uint<4> m_pixel_width;726basisu::packed_uint<4> m_pixel_height;727basisu::packed_uint<4> m_pixel_depth;728basisu::packed_uint<4> m_layer_count;729basisu::packed_uint<4> m_face_count;730basisu::packed_uint<4> m_level_count;731basisu::packed_uint<4> m_supercompression_scheme;732basisu::packed_uint<4> m_dfd_byte_offset;733basisu::packed_uint<4> m_dfd_byte_length;734basisu::packed_uint<4> m_kvd_byte_offset;735basisu::packed_uint<4> m_kvd_byte_length;736basisu::packed_uint<8> m_sgd_byte_offset;737basisu::packed_uint<8> m_sgd_byte_length;738};739740struct ktx2_level_index741{742basisu::packed_uint<8> m_byte_offset;743basisu::packed_uint<8> m_byte_length;744basisu::packed_uint<8> m_uncompressed_byte_length;745};746747struct ktx2_etc1s_global_data_header748{749basisu::packed_uint<2> m_endpoint_count;750basisu::packed_uint<2> m_selector_count;751basisu::packed_uint<4> m_endpoints_byte_length;752basisu::packed_uint<4> m_selectors_byte_length;753basisu::packed_uint<4> m_tables_byte_length;754basisu::packed_uint<4> m_extended_byte_length;755};756757struct ktx2_etc1s_image_desc758{759basisu::packed_uint<4> m_image_flags;760basisu::packed_uint<4> m_rgb_slice_byte_offset;761basisu::packed_uint<4> m_rgb_slice_byte_length;762basisu::packed_uint<4> m_alpha_slice_byte_offset;763basisu::packed_uint<4> m_alpha_slice_byte_length;764};765766struct ktx2_astc_hdr_6x6_intermediate_image_desc767{768basisu::packed_uint<4> m_rgb_slice_byte_offset;769basisu::packed_uint<4> m_rgb_slice_byte_length;770};771772struct ktx2_animdata773{774basisu::packed_uint<4> m_duration;775basisu::packed_uint<4> m_timescale;776basisu::packed_uint<4> m_loopcount;777};778#pragma pack(pop)779780const uint32_t KTX2_VK_FORMAT_UNDEFINED = 0;781782// These are standard Vulkan texture VkFormat ID's, see https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFormat.html783const uint32_t KTX2_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000;784const uint32_t KTX2_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001;785const uint32_t KTX2_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002;786const uint32_t KTX2_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003;787const uint32_t KTX2_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004;788const uint32_t KTX2_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005;789const uint32_t KTX2_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006;790791const uint32_t KTX2_KDF_DF_MODEL_ASTC = 162; // 0xA2792const uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163; // 0xA3793const uint32_t KTX2_KDF_DF_MODEL_UASTC_LDR_4X4 = 166; // 0xA6794const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_4X4 = 167; // 0xA7795const uint32_t KTX2_KDF_DF_MODEL_ASTC_HDR_6X6_INTERMEDIATE = 168; // 0xA8, TODO - coordinate with Khronos on this796797const uint32_t KTX2_IMAGE_IS_P_FRAME = 2;798const uint32_t KTX2_UASTC_BLOCK_SIZE = 16; // also the block size for UASTC_HDR799const uint32_t KTX2_MAX_SUPPORTED_LEVEL_COUNT = 16; // this is an implementation specific constraint and can be increased800801// The KTX2 transfer functions supported by KTX2802const uint32_t KTX2_KHR_DF_TRANSFER_LINEAR = 1;803const uint32_t KTX2_KHR_DF_TRANSFER_SRGB = 2;804805enum ktx2_supercompression806{807KTX2_SS_NONE = 0,808KTX2_SS_BASISLZ = 1,809KTX2_SS_ZSTANDARD = 2,810KTX2_SS_BASIS811};812813extern const uint8_t g_ktx2_file_identifier[12];814815enum ktx2_df_channel_id816{817KTX2_DF_CHANNEL_ETC1S_RGB = 0U,818KTX2_DF_CHANNEL_ETC1S_RRR = 3U,819KTX2_DF_CHANNEL_ETC1S_GGG = 4U,820KTX2_DF_CHANNEL_ETC1S_AAA = 15U,821822KTX2_DF_CHANNEL_UASTC_DATA = 0U,823KTX2_DF_CHANNEL_UASTC_RGB = 0U,824KTX2_DF_CHANNEL_UASTC_RGBA = 3U,825KTX2_DF_CHANNEL_UASTC_RRR = 4U,826KTX2_DF_CHANNEL_UASTC_RRRG = 5U,827KTX2_DF_CHANNEL_UASTC_RG = 6U,828};829830inline const char* ktx2_get_etc1s_df_channel_id_str(ktx2_df_channel_id id)831{832switch (id)833{834case KTX2_DF_CHANNEL_ETC1S_RGB: return "RGB";835case KTX2_DF_CHANNEL_ETC1S_RRR: return "RRR";836case KTX2_DF_CHANNEL_ETC1S_GGG: return "GGG";837case KTX2_DF_CHANNEL_ETC1S_AAA: return "AAA";838default: break;839}840return "?";841}842843inline const char* ktx2_get_uastc_df_channel_id_str(ktx2_df_channel_id id)844{845switch (id)846{847case KTX2_DF_CHANNEL_UASTC_RGB: return "RGB";848case KTX2_DF_CHANNEL_UASTC_RGBA: return "RGBA";849case KTX2_DF_CHANNEL_UASTC_RRR: return "RRR";850case KTX2_DF_CHANNEL_UASTC_RRRG: return "RRRG";851case KTX2_DF_CHANNEL_UASTC_RG: return "RG";852default: break;853}854return "?";855}856857enum ktx2_df_color_primaries858{859KTX2_DF_PRIMARIES_UNSPECIFIED = 0,860KTX2_DF_PRIMARIES_BT709 = 1,861KTX2_DF_PRIMARIES_SRGB = 1,862KTX2_DF_PRIMARIES_BT601_EBU = 2,863KTX2_DF_PRIMARIES_BT601_SMPTE = 3,864KTX2_DF_PRIMARIES_BT2020 = 4,865KTX2_DF_PRIMARIES_CIEXYZ = 5,866KTX2_DF_PRIMARIES_ACES = 6,867KTX2_DF_PRIMARIES_ACESCC = 7,868KTX2_DF_PRIMARIES_NTSC1953 = 8,869KTX2_DF_PRIMARIES_PAL525 = 9,870KTX2_DF_PRIMARIES_DISPLAYP3 = 10,871KTX2_DF_PRIMARIES_ADOBERGB = 11872};873874inline const char* ktx2_get_df_color_primaries_str(ktx2_df_color_primaries p)875{876switch (p)877{878case KTX2_DF_PRIMARIES_UNSPECIFIED: return "UNSPECIFIED";879case KTX2_DF_PRIMARIES_BT709: return "BT709";880case KTX2_DF_PRIMARIES_BT601_EBU: return "EBU";881case KTX2_DF_PRIMARIES_BT601_SMPTE: return "SMPTE";882case KTX2_DF_PRIMARIES_BT2020: return "BT2020";883case KTX2_DF_PRIMARIES_CIEXYZ: return "CIEXYZ";884case KTX2_DF_PRIMARIES_ACES: return "ACES";885case KTX2_DF_PRIMARIES_ACESCC: return "ACESCC";886case KTX2_DF_PRIMARIES_NTSC1953: return "NTSC1953";887case KTX2_DF_PRIMARIES_PAL525: return "PAL525";888case KTX2_DF_PRIMARIES_DISPLAYP3: return "DISPLAYP3";889case KTX2_DF_PRIMARIES_ADOBERGB: return "ADOBERGB";890default: break;891}892return "?";893}894895// Information about a single 2D texture "image" in a KTX2 file.896struct ktx2_image_level_info897{898// The mipmap level index (0=largest), texture array layer index, and cubemap face index of the image.899uint32_t m_level_index;900uint32_t m_layer_index;901uint32_t m_face_index;902903// The image's actual (or the original source image's) width/height in pixels, which may not be divisible by 4 pixels.904uint32_t m_orig_width;905uint32_t m_orig_height;906907// The image's physical width/height, which will always be divisible by 4 pixels.908uint32_t m_width;909uint32_t m_height;910911// The texture's dimensions in 4x4 or 6x6 texel blocks.912uint32_t m_num_blocks_x;913uint32_t m_num_blocks_y;914915// The format's block width/height (currently either 4 or 6).916uint32_t m_block_width;917uint32_t m_block_height;918919// The total number of blocks920uint32_t m_total_blocks;921922// true if the image has alpha data923bool m_alpha_flag;924925// true if the image is an I-Frame. Currently, for ETC1S textures, the first frame will always be an I-Frame, and subsequent frames will always be P-Frames.926bool m_iframe_flag;927};928929// Thread-specific ETC1S/supercompressed UASTC transcoder state. (If you're not doing multithreading transcoding you can ignore this.)930struct ktx2_transcoder_state931{932basist::basisu_transcoder_state m_transcoder_state;933basisu::uint8_vec m_level_uncomp_data;934int m_uncomp_data_level_index;935936void clear()937{938m_transcoder_state.clear();939m_level_uncomp_data.clear();940m_uncomp_data_level_index = -1;941}942};943944// This class is quite similar to basisu_transcoder. It treats KTX2 files as a simple container for ETC1S/UASTC texture data.945// It does not support 1D or 3D textures.946// It only supports 2D and cubemap textures, with or without mipmaps, texture arrays of 2D/cubemap textures, and texture video files.947// It only supports raw non-supercompressed UASTC, ETC1S, UASTC+Zstd, or UASTC+zlib compressed files.948// DFD (Data Format Descriptor) parsing is purposely as simple as possible.949// If you need to know how to interpret the texture channels you'll need to parse the DFD yourself after calling get_dfd().950class ktx2_transcoder951{952public:953ktx2_transcoder();954955// Frees all allocations, resets object.956void clear();957958// init() parses the KTX2 header, level index array, DFD, and key values, but nothing else.959// Importantly, it does not parse or decompress the ETC1S global supercompressed data, so some things (like which frames are I/P-Frames) won't be available until start_transcoding() is called.960// This method holds a pointer to the file data until clear() is called.961bool init(const void* pData, uint32_t data_size);962963// Returns the data/size passed to init().964const uint8_t* get_data() const { return m_pData; }965uint32_t get_data_size() const { return m_data_size; }966967// Returns the KTX2 header. Valid after init().968const ktx2_header& get_header() const { return m_header; }969970// Returns the KTX2 level index array. There will be one entry for each mipmap level. Valid after init().971const basisu::vector<ktx2_level_index>& get_level_index() const { return m_levels; }972973// Returns the texture's width in texels. Always non-zero, might not be divisible by 4. Valid after init().974uint32_t get_width() const { return m_header.m_pixel_width; }975976// Returns the texture's height in texels. Always non-zero, might not be divisible by 4. Valid after init().977uint32_t get_height() const { return m_header.m_pixel_height; }978979// Returns the texture's number of mipmap levels. Always returns 1 or higher. Valid after init().980uint32_t get_levels() const { return m_header.m_level_count; }981982// Returns the number of faces. Returns 1 for 2D textures and or 6 for cubemaps. Valid after init().983uint32_t get_faces() const { return m_header.m_face_count; }984985// Returns 0 or the number of layers in the texture array or texture video. Valid after init().986uint32_t get_layers() const { return m_header.m_layer_count; }987988// Returns cETC1S, cUASTC4x4, cUASTC_HDR_4x4, cASTC_HDR_6x6, cASTC_HDR_6x6_INTERMEDIATE. Valid after init().989basist::basis_tex_format get_basis_tex_format() const { return m_format; }990991// ETC1S LDR 4x4992bool is_etc1s() const { return get_basis_tex_format() == basist::basis_tex_format::cETC1S; }993994// UASTC LDR 4x4 (only)995bool is_uastc() const { return get_basis_tex_format() == basist::basis_tex_format::cUASTC4x4; }996997// Is ASTC HDR 4x4 or 6x6998bool is_hdr() const999{1000return basis_tex_format_is_hdr(get_basis_tex_format());1001}10021003bool is_ldr() const1004{1005return !is_hdr();1006}10071008bool is_hdr_4x4() const1009{1010return (get_basis_tex_format() == basist::basis_tex_format::cUASTC_HDR_4x4);1011}10121013bool is_hdr_6x6() const1014{1015return (get_basis_tex_format() == basist::basis_tex_format::cASTC_HDR_6x6) || (get_basis_tex_format() == basist::basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE);1016}10171018uint32_t get_block_width() const { return basis_tex_format_get_block_width(get_basis_tex_format()); }1019uint32_t get_block_height() const { return basis_tex_format_get_block_height(get_basis_tex_format()); }10201021// Returns true if the ETC1S file has two planes (typically RGBA, or RRRG), or true if the UASTC file has alpha data. Valid after init().1022uint32_t get_has_alpha() const { return m_has_alpha; }10231024// Returns the entire Data Format Descriptor (DFD) from the KTX2 file. Valid after init().1025// See https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#_the_khronos_data_format_descriptor_overview1026const basisu::uint8_vec& get_dfd() const { return m_dfd; }10271028// Some basic DFD accessors. Valid after init().1029uint32_t get_dfd_color_model() const { return m_dfd_color_model; }10301031// Returns the DFD color primary.1032// We do not validate the color primaries, so the returned value may not be in the ktx2_df_color_primaries enum.1033ktx2_df_color_primaries get_dfd_color_primaries() const { return m_dfd_color_prims; }10341035// Returns KTX2_KHR_DF_TRANSFER_LINEAR or KTX2_KHR_DF_TRANSFER_SRGB.1036uint32_t get_dfd_transfer_func() const { return m_dfd_transfer_func; }10371038uint32_t get_dfd_flags() const { return m_dfd_flags; }10391040// Returns 1 (ETC1S/UASTC) or 2 (ETC1S with an internal alpha channel).1041uint32_t get_dfd_total_samples() const { return m_dfd_samples; }10421043// Returns the channel mapping for each DFD "sample". UASTC always has 1 sample, ETC1S can have one or two.1044// Note the returned value SHOULD be one of the ktx2_df_channel_id enums, but we don't validate that.1045// It's up to the caller to decide what to do if the value isn't in the enum.1046ktx2_df_channel_id get_dfd_channel_id0() const { return m_dfd_chan0; }1047ktx2_df_channel_id get_dfd_channel_id1() const { return m_dfd_chan1; }10481049// Key value field data.1050struct key_value1051{1052// The key field is UTF8 and always zero terminated.1053// In memory we always append a zero terminator to the key.1054basisu::uint8_vec m_key;10551056// The value may be empty. In the KTX2 file it consists of raw bytes which may or may not be zero terminated.1057// In memory we always append a zero terminator to the value.1058basisu::uint8_vec m_value;10591060bool operator< (const key_value& rhs) const { return strcmp((const char*)m_key.data(), (const char *)rhs.m_key.data()) < 0; }1061};1062typedef basisu::vector<key_value> key_value_vec;10631064// Returns the array of key-value entries. This may be empty. Valid after init().1065// The order of key values fields in this array exactly matches the order they were stored in the file. The keys are supposed to be sorted by their Unicode code points.1066const key_value_vec& get_key_values() const { return m_key_values; }10671068const basisu::uint8_vec *find_key(const std::string& key_name) const;10691070// Low-level ETC1S specific accessors10711072// Returns the ETC1S global supercompression data header, which is only valid after start_transcoding() is called.1073const ktx2_etc1s_global_data_header& get_etc1s_header() const { return m_etc1s_header; }10741075// Returns the array of ETC1S image descriptors, which is only valid after get_etc1s_image_descs() is called.1076const basisu::vector<ktx2_etc1s_image_desc>& get_etc1s_image_descs() const { return m_etc1s_image_descs; }10771078const basisu::vector<ktx2_astc_hdr_6x6_intermediate_image_desc>& get_astc_hdr_6x6_intermediate_image_descs() const { return m_astc_6x6_intermediate_image_descs; }10791080// Must have called startTranscoding() first1081uint32_t get_etc1s_image_descs_image_flags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) const;10821083// is_video() is only valid after start_transcoding() is called.1084// For ETC1S data, if this returns true you must currently transcode the file from first to last frame, in order, without skipping any frames.1085bool is_video() const { return m_is_video; }10861087// Defaults to 0, only non-zero if the key existed in the source KTX2 file.1088float get_ldr_hdr_upconversion_nit_multiplier() const { return m_ldr_hdr_upconversion_nit_multiplier; }10891090// start_transcoding() MUST be called before calling transcode_image().1091// This method decompresses the ETC1S global endpoint/selector codebooks, which is not free, so try to avoid calling it excessively.1092bool start_transcoding();10931094// get_image_level_info() be called after init(), but the m_iframe_flag's won't be valid until start_transcoding() is called.1095// You can call this method before calling transcode_image_level() to retrieve basic information about the mipmap level's dimensions, etc.1096bool get_image_level_info(ktx2_image_level_info& level_info, uint32_t level_index, uint32_t layer_index, uint32_t face_index) const;10971098// transcode_image_level() transcodes a single 2D texture or cubemap face from the KTX2 file.1099// Internally it uses the same low-level transcode API's as basisu_transcoder::transcode_image_level().1100// If the file is UASTC and is supercompressed with Zstandard, and the file is a texture array or cubemap, it's highly recommended that each mipmap level is1101// completely transcoded before switching to another level. Every time the mipmap level is changed all supercompressed level data must be decompressed using Zstandard as a single unit.1102// Currently ETC1S videos must always be transcoded from first to last frame (or KTX2 "layer"), in order, with no skipping of frames.1103// By default this method is not thread safe unless you specify a pointer to a user allocated thread-specific transcoder_state struct.1104bool transcode_image_level(1105uint32_t level_index, uint32_t layer_index, uint32_t face_index,1106void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,1107basist::transcoder_texture_format fmt,1108uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1,1109ktx2_transcoder_state *pState = nullptr);11101111private:1112const uint8_t* m_pData;1113uint32_t m_data_size;11141115ktx2_header m_header;1116basisu::vector<ktx2_level_index> m_levels;1117basisu::uint8_vec m_dfd;1118key_value_vec m_key_values;11191120ktx2_etc1s_global_data_header m_etc1s_header;1121basisu::vector<ktx2_etc1s_image_desc> m_etc1s_image_descs;1122basisu::vector<ktx2_astc_hdr_6x6_intermediate_image_desc> m_astc_6x6_intermediate_image_descs;11231124basist::basis_tex_format m_format;11251126uint32_t m_dfd_color_model;1127ktx2_df_color_primaries m_dfd_color_prims;1128uint32_t m_dfd_transfer_func;1129uint32_t m_dfd_flags;1130uint32_t m_dfd_samples;1131ktx2_df_channel_id m_dfd_chan0, m_dfd_chan1;11321133basist::basisu_lowlevel_etc1s_transcoder m_etc1s_transcoder;1134basist::basisu_lowlevel_uastc_ldr_4x4_transcoder m_uastc_transcoder;1135basist::basisu_lowlevel_uastc_hdr_4x4_transcoder m_uastc_hdr_transcoder;1136basist::basisu_lowlevel_astc_hdr_6x6_transcoder m_astc_hdr_6x6_transcoder;1137basist::basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder m_astc_hdr_6x6_intermediate_transcoder;11381139ktx2_transcoder_state m_def_transcoder_state;11401141bool m_has_alpha;1142bool m_is_video;1143float m_ldr_hdr_upconversion_nit_multiplier;11441145bool decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data);1146bool read_astc_6x6_hdr_intermediate_global_data();1147bool decompress_etc1s_global_data();1148bool read_key_values();1149};11501151// Replaces if the key already exists1152inline void ktx2_add_key_value(ktx2_transcoder::key_value_vec& key_values, const std::string& key, const std::string& val)1153{1154assert(key.size());11551156basist::ktx2_transcoder::key_value* p = nullptr;11571158// Try to find an existing key1159for (size_t i = 0; i < key_values.size(); i++)1160{1161if (strcmp((const char*)key_values[i].m_key.data(), key.c_str()) == 0)1162{1163p = &key_values[i];1164break;1165}1166}11671168if (!p)1169p = key_values.enlarge(1);11701171p->m_key.resize(0);1172p->m_value.resize(0);11731174p->m_key.resize(key.size() + 1);1175memcpy(p->m_key.data(), key.c_str(), key.size());11761177p->m_value.resize(val.size() + 1);1178if (val.size())1179memcpy(p->m_value.data(), val.c_str(), val.size());1180}11811182#endif // BASISD_SUPPORT_KTX211831184// Returns true if the transcoder was compiled with KTX2 support.1185bool basisu_transcoder_supports_ktx2();11861187// Returns true if the transcoder was compiled with Zstandard support.1188bool basisu_transcoder_supports_ktx2_zstd();11891190} // namespace basisu1191119211931194