Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/basis_universal/transcoder/basisu_transcoder.h
9905 views
1
// basisu_transcoder.h
2
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
3
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
4
//
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
#pragma once
17
18
// 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.
19
// 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.
20
// Also see BASISD_SUPPORT_KTX2_ZSTD in basisu_transcoder.cpp, which individually disables Zstd usage.
21
#ifndef BASISD_SUPPORT_KTX2
22
#define BASISD_SUPPORT_KTX2 1
23
#endif
24
25
// Set BASISD_SUPPORT_KTX2_ZSTD to 0 to disable Zstd usage and KTX2 UASTC Zstd supercompression support
26
#ifndef BASISD_SUPPORT_KTX2_ZSTD
27
#define BASISD_SUPPORT_KTX2_ZSTD 1
28
#endif
29
30
// Set BASISU_FORCE_DEVEL_MESSAGES to 1 to enable debug printf()'s whenever an error occurs, for easier debugging during development.
31
#ifndef BASISU_FORCE_DEVEL_MESSAGES
32
// TODO - disable before checking in
33
#define BASISU_FORCE_DEVEL_MESSAGES 0
34
#endif
35
36
#include "basisu_transcoder_internal.h"
37
#include "basisu_transcoder_uastc.h"
38
#include "basisu_file_headers.h"
39
40
namespace basist
41
{
42
// High-level composite texture formats supported by the transcoder.
43
// Each of these texture formats directly correspond to OpenGL/D3D/Vulkan etc. texture formats.
44
// Notes:
45
// - If you specify a texture format that supports alpha, but the .basis file doesn't have alpha, the transcoder will automatically output a
46
// fully opaque (255) alpha channel.
47
// - The PVRTC1 texture formats only support power of 2 dimension .basis files, but this may be relaxed in a future version.
48
// - The PVRTC1 transcoders are real-time encoders, so don't expect the highest quality. We may add a slower encoder with improved quality.
49
// - These enums must be kept in sync with Javascript code that calls the transcoder.
50
enum class transcoder_texture_format
51
{
52
// Compressed formats
53
54
// ETC1-2
55
cTFETC1_RGB = 0, // Opaque only, returns RGB or alpha data if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
56
cTFETC2_RGBA = 1, // Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files
57
58
// BC1-5, BC7 (desktop, some mobile devices)
59
cTFBC1_RGB = 2, // Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
60
cTFBC3_RGBA = 3, // Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files
61
cTFBC4_R = 4, // Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
62
cTFBC5_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)
63
cTFBC7_RGBA = 6, // RGB or RGBA, mode 5 for ETC1S, modes (1,2,3,5,6,7) for UASTC
64
65
// PVRTC1 4bpp (mobile, PowerVR devices)
66
cTFPVRTC1_4_RGB = 8, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.
67
cTFPVRTC1_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.
68
69
// ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)
70
cTFASTC_4x4_RGBA = 10, // LDR. Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files.
71
// LDR: Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
72
73
// ATC (mobile, Adreno devices, this is a niche format)
74
cTFATC_RGB = 11, // Opaque, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. ATI ATC (GL_ATC_RGB_AMD)
75
cTFATC_RGBA = 12, // Opaque+alpha, alpha channel will be opaque for opaque .basis files. ATI ATC (GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD)
76
77
// FXT1 (desktop, Intel devices, this is a super obscure format)
78
cTFFXT1_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).
79
// 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.
80
// See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h.
81
82
cTFPVRTC2_4_RGB = 18, // Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB).
83
cTFPVRTC2_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.
84
85
cTFETC2_EAC_R11 = 20, // R only (ETC2 EAC R11 unsigned)
86
cTFETC2_EAC_RG11 = 21, // RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps
87
88
cTFBC6H = 22, // HDR, RGB only, unsigned
89
cTFASTC_HDR_4x4_RGBA = 23, // HDR, RGBA (currently UASTC HDR 4x4 encoders are only RGB), unsigned
90
91
// Uncompressed (raw pixel) formats
92
// Note these uncompressed formats (RGBA32, 565, and 4444) can only be transcoded to from LDR input files (ETC1S or UASTC LDR).
93
cTFRGBA32 = 13, // 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.
94
cTFRGB565 = 14, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 11
95
cTFBGR565 = 15, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0
96
cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
97
98
// Note these uncompressed formats (HALF and 9E5) can only be transcoded to from HDR input files (UASTC HDR 4x4 or ASTC HDR 6x6).
99
cTFRGB_HALF = 24, // 48bpp RGB half (16-bits/component, 3 components)
100
cTFRGBA_HALF = 25, // 64bpp RGBA half (16-bits/component, 4 components) (A will always currently 1.0, UASTC_HDR doesn't support alpha)
101
cTFRGB_9E5 = 26, // 32bpp RGB 9E5 (shared exponent, positive only, see GL_EXT_texture_shared_exponent)
102
103
cTFASTC_HDR_6x6_RGBA = 27, // HDR, RGBA (currently our ASTC HDR 6x6 encodes are only RGB), unsigned
104
105
cTFTotalTextureFormats = 28,
106
107
// ----- The following are old/legacy enums for compatibility with code compiled against previous versions
108
cTFETC1 = cTFETC1_RGB,
109
cTFETC2 = cTFETC2_RGBA,
110
cTFBC1 = cTFBC1_RGB,
111
cTFBC3 = cTFBC3_RGBA,
112
cTFBC4 = cTFBC4_R,
113
cTFBC5 = cTFBC5_RG,
114
115
// Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC, which supports numerous modes.
116
cTFBC7_M6_RGB = cTFBC7_RGBA, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats.
117
cTFBC7_M5_RGBA = cTFBC7_RGBA, // Opaque+alpha, alpha channel will be opaque for opaque .basis files
118
cTFBC7_M6_OPAQUE_ONLY = cTFBC7_RGBA,
119
cTFBC7_M5 = cTFBC7_RGBA,
120
cTFBC7_ALT = 7,
121
122
cTFASTC_4x4 = cTFASTC_4x4_RGBA,
123
124
cTFATC_RGBA_INTERPOLATED_ALPHA = cTFATC_RGBA,
125
};
126
127
// For compressed texture formats, this returns the # of bytes per block. For uncompressed, it returns the # of bytes per pixel.
128
// 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.
129
uint32_t basis_get_bytes_per_block_or_pixel(transcoder_texture_format fmt);
130
131
// Returns format's name in ASCII
132
const char* basis_get_format_name(transcoder_texture_format fmt);
133
134
// Returns block format name in ASCII
135
const char* basis_get_block_format_name(block_format fmt);
136
137
// Returns true if the format supports an alpha channel.
138
bool basis_transcoder_format_has_alpha(transcoder_texture_format fmt);
139
140
// Returns true if the format is HDR.
141
bool basis_transcoder_format_is_hdr(transcoder_texture_format fmt);
142
143
// Returns true if the format is LDR.
144
inline bool basis_transcoder_format_is_ldr(transcoder_texture_format fmt) { return !basis_transcoder_format_is_hdr(fmt); }
145
146
// Returns the basisu::texture_format corresponding to the specified transcoder_texture_format.
147
basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt);
148
149
// Returns the texture type's name in ASCII.
150
const char* basis_get_texture_type_name(basis_texture_type tex_type);
151
152
// Returns true if the transcoder texture type is an uncompressed (raw pixel) format.
153
bool basis_transcoder_format_is_uncompressed(transcoder_texture_format tex_type);
154
155
// Returns the # of bytes per pixel for uncompressed formats, or 0 for block texture formats.
156
uint32_t basis_get_uncompressed_bytes_per_pixel(transcoder_texture_format fmt);
157
158
// Returns the block width for the specified texture format, which is currently either 4 or 8 for FXT1.
159
uint32_t basis_get_block_width(transcoder_texture_format tex_type);
160
161
// Returns the block height for the specified texture format, which is currently always 4.
162
uint32_t basis_get_block_height(transcoder_texture_format tex_type);
163
164
// 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).
165
bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt = basis_tex_format::cETC1S);
166
167
// Returns the block width/height for the specified basis texture file format.
168
uint32_t basis_tex_format_get_block_width(basis_tex_format fmt);
169
uint32_t basis_tex_format_get_block_height(basis_tex_format fmt);
170
171
bool basis_tex_format_is_hdr(basis_tex_format fmt);
172
inline bool basis_tex_format_is_ldr(basis_tex_format fmt) { return !basis_tex_format_is_hdr(fmt); }
173
174
// Validates that the output buffer is large enough to hold the entire transcoded texture.
175
// For uncompressed texture formats, most input parameters are in pixels, not blocks. Blocks are 4x4 pixels.
176
bool basis_validate_output_buffer_size(transcoder_texture_format target_format,
177
uint32_t output_blocks_buf_size_in_blocks_or_pixels,
178
uint32_t orig_width, uint32_t orig_height,
179
uint32_t output_row_pitch_in_blocks_or_pixels,
180
uint32_t output_rows_in_pixels);
181
182
// 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.
183
// Note the returned value is not necessarily the # of bytes a transcoder could write to the output buffer due to these minimum PVRTC1 requirements.
184
// (These PVRTC1 requirements are not ours, but OpenGL's.)
185
uint32_t basis_compute_transcoded_image_size_in_bytes(transcoder_texture_format target_format, uint32_t orig_width, uint32_t orig_height);
186
187
class basisu_transcoder;
188
189
// This struct holds all state used during transcoding. For video, it needs to persist between image transcodes (it holds the previous frame).
190
// For threading you can use one state per thread.
191
struct basisu_transcoder_state
192
{
193
struct block_preds
194
{
195
uint16_t m_endpoint_index;
196
uint8_t m_pred_bits;
197
};
198
199
basisu::vector<block_preds> m_block_endpoint_preds[2];
200
201
enum { cMaxPrevFrameLevels = 16 };
202
basisu::vector<uint32_t> m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index]
203
204
void clear()
205
{
206
for (uint32_t i = 0; i < 2; i++)
207
{
208
m_block_endpoint_preds[i].clear();
209
210
for (uint32_t j = 0; j < cMaxPrevFrameLevels; j++)
211
m_prev_frame_indices[i][j].clear();
212
}
213
}
214
};
215
216
// Low-level helper classes that do the actual transcoding.
217
218
// ETC1S
219
class basisu_lowlevel_etc1s_transcoder
220
{
221
friend class basisu_transcoder;
222
223
public:
224
basisu_lowlevel_etc1s_transcoder();
225
226
void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_pGlobal_codebook = pGlobal_codebook; }
227
const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_pGlobal_codebook; }
228
229
bool decode_palettes(
230
uint32_t num_endpoints, const uint8_t* pEndpoints_data, uint32_t endpoints_data_size,
231
uint32_t num_selectors, const uint8_t* pSelectors_data, uint32_t selectors_data_size);
232
233
bool decode_tables(const uint8_t* pTable_data, uint32_t table_data_size);
234
235
bool 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,
236
uint32_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,
237
basisu_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);
238
239
bool 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,
240
uint32_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,
241
basisu_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)
242
{
243
return 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,
244
header.m_tex_type == cBASISTexTypeVideoFrames, (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0, slice_desc.m_level_index,
245
slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels, pState,
246
astc_transcode_alpha,
247
pAlpha_blocks,
248
output_rows_in_pixels, decode_flags);
249
}
250
251
// Container independent transcoding
252
bool transcode_image(
253
transcoder_texture_format target_format,
254
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
255
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
256
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
257
uint32_t rgb_offset, uint32_t rgb_length, uint32_t alpha_offset, uint32_t alpha_length,
258
uint32_t decode_flags = 0,
259
bool basis_file_has_alpha_slices = false,
260
bool is_video = false,
261
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
262
basisu_transcoder_state* pState = nullptr,
263
uint32_t output_rows_in_pixels = 0);
264
265
void clear()
266
{
267
m_local_endpoints.clear();
268
m_local_selectors.clear();
269
m_endpoint_pred_model.clear();
270
m_delta_endpoint_model.clear();
271
m_selector_model.clear();
272
m_selector_history_buf_rle_model.clear();
273
m_selector_history_buf_size = 0;
274
}
275
276
// Low-level methods
277
typedef basisu::vector<endpoint> endpoint_vec;
278
const endpoint_vec& get_endpoints() const { return m_local_endpoints; }
279
280
typedef basisu::vector<selector> selector_vec;
281
const selector_vec& get_selectors() const { return m_local_selectors; }
282
283
private:
284
const basisu_lowlevel_etc1s_transcoder* m_pGlobal_codebook;
285
286
endpoint_vec m_local_endpoints;
287
selector_vec m_local_selectors;
288
289
huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model;
290
291
uint32_t m_selector_history_buf_size;
292
293
basisu_transcoder_state m_def_state;
294
};
295
296
enum basisu_decode_flags
297
{
298
// 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.
299
cDecodeFlagsPVRTCDecodeToNextPow2 = 2,
300
301
// 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.
302
// This is primarily to allow decoding of textures with alpha to multiple ETC1 textures (one for color, another for alpha).
303
cDecodeFlagsTranscodeAlphaDataToOpaqueFormats = 4,
304
305
// Forbid usage of BC1 3 color blocks (we don't support BC1 punchthrough alpha yet).
306
// This flag is used internally when decoding to BC3.
307
cDecodeFlagsBC1ForbidThreeColorBlocks = 8,
308
309
// The output buffer contains alpha endpoint/selector indices.
310
// Used internally when decoding formats like ASTC that require both color and alpha data to be available when transcoding to the output format.
311
cDecodeFlagsOutputHasAlphaIndices = 16,
312
313
cDecodeFlagsHighQuality = 32,
314
315
cDecodeFlagsNoETC1SChromaFiltering = 64
316
};
317
318
// UASTC LDR 4x4
319
class basisu_lowlevel_uastc_ldr_4x4_transcoder
320
{
321
friend class basisu_transcoder;
322
323
public:
324
basisu_lowlevel_uastc_ldr_4x4_transcoder();
325
326
bool 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,
327
uint32_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,
328
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
329
330
bool 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,
331
uint32_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,
332
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
333
{
334
return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,
335
output_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,
336
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
337
}
338
339
// Container independent transcoding
340
bool transcode_image(
341
transcoder_texture_format target_format,
342
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
343
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
344
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
345
uint32_t slice_offset, uint32_t slice_length,
346
uint32_t decode_flags = 0,
347
bool has_alpha = false,
348
bool is_video = false,
349
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
350
basisu_transcoder_state* pState = nullptr,
351
uint32_t output_rows_in_pixels = 0,
352
int channel0 = -1, int channel1 = -1);
353
};
354
355
// UASTC HDR 4x4
356
class basisu_lowlevel_uastc_hdr_4x4_transcoder
357
{
358
friend class basisu_transcoder;
359
360
public:
361
basisu_lowlevel_uastc_hdr_4x4_transcoder();
362
363
bool 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,
364
uint32_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,
365
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
366
367
bool 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,
368
uint32_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,
369
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
370
{
371
return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,
372
output_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,
373
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
374
}
375
376
// Container independent transcoding
377
bool transcode_image(
378
transcoder_texture_format target_format,
379
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
380
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
381
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
382
uint32_t slice_offset, uint32_t slice_length,
383
uint32_t decode_flags = 0,
384
bool has_alpha = false,
385
bool is_video = false,
386
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
387
basisu_transcoder_state* pState = nullptr,
388
uint32_t output_rows_in_pixels = 0,
389
int channel0 = -1, int channel1 = -1);
390
};
391
392
// ASTC HDR 6x6
393
class basisu_lowlevel_astc_hdr_6x6_transcoder
394
{
395
friend class basisu_transcoder;
396
397
public:
398
basisu_lowlevel_astc_hdr_6x6_transcoder();
399
400
bool 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,
401
uint32_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,
402
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
403
404
bool 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,
405
uint32_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,
406
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
407
{
408
return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,
409
output_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,
410
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
411
}
412
413
// Container independent transcoding
414
bool transcode_image(
415
transcoder_texture_format target_format,
416
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
417
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
418
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
419
uint32_t slice_offset, uint32_t slice_length,
420
uint32_t decode_flags = 0,
421
bool has_alpha = false,
422
bool is_video = false,
423
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
424
basisu_transcoder_state* pState = nullptr,
425
uint32_t output_rows_in_pixels = 0,
426
int channel0 = -1, int channel1 = -1);
427
};
428
429
// ASTC HDR 6x6 intermediate
430
class basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder
431
{
432
friend class basisu_transcoder;
433
434
public:
435
basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder();
436
437
bool 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,
438
uint32_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,
439
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
440
441
bool 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,
442
uint32_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,
443
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
444
{
445
return transcode_slice(pDst_blocks, num_blocks_x, num_blocks_y, pImage_data, image_data_size, fmt,
446
output_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,
447
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
448
}
449
450
// Container independent transcoding
451
bool transcode_image(
452
transcoder_texture_format target_format,
453
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
454
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
455
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
456
uint32_t slice_offset, uint32_t slice_length,
457
uint32_t decode_flags = 0,
458
bool has_alpha = false,
459
bool is_video = false,
460
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
461
basisu_transcoder_state* pState = nullptr,
462
uint32_t output_rows_in_pixels = 0,
463
int channel0 = -1, int channel1 = -1);
464
};
465
466
struct basisu_slice_info
467
{
468
uint32_t m_orig_width;
469
uint32_t m_orig_height;
470
471
uint32_t m_width;
472
uint32_t m_height;
473
474
uint32_t m_num_blocks_x;
475
uint32_t m_num_blocks_y;
476
uint32_t m_total_blocks;
477
478
uint32_t m_block_width;
479
uint32_t m_block_height;
480
481
uint32_t m_compressed_size;
482
483
uint32_t m_slice_index; // the slice index in the .basis file
484
uint32_t m_image_index; // the source image index originally provided to the encoder
485
uint32_t m_level_index; // the mipmap level within this image
486
487
uint32_t m_unpacked_slice_crc16;
488
489
bool m_alpha_flag; // true if the slice has alpha data
490
bool m_iframe_flag; // true if the slice is an I-Frame
491
};
492
493
typedef basisu::vector<basisu_slice_info> basisu_slice_info_vec;
494
495
struct basisu_image_info
496
{
497
uint32_t m_image_index;
498
uint32_t m_total_levels;
499
500
uint32_t m_orig_width;
501
uint32_t m_orig_height;
502
503
uint32_t m_width;
504
uint32_t m_height;
505
506
uint32_t m_block_width;
507
uint32_t m_block_height;
508
509
uint32_t m_num_blocks_x;
510
uint32_t m_num_blocks_y;
511
uint32_t m_total_blocks;
512
513
uint32_t m_first_slice_index;
514
515
bool m_alpha_flag; // true if the image has alpha data
516
bool m_iframe_flag; // true if the image is an I-Frame
517
};
518
519
struct basisu_image_level_info
520
{
521
uint32_t m_image_index;
522
uint32_t m_level_index;
523
524
uint32_t m_orig_width;
525
uint32_t m_orig_height;
526
527
uint32_t m_width;
528
uint32_t m_height;
529
530
uint32_t m_block_width;
531
uint32_t m_block_height;
532
533
uint32_t m_num_blocks_x;
534
uint32_t m_num_blocks_y;
535
uint32_t m_total_blocks;
536
537
uint32_t m_first_slice_index;
538
539
uint32_t m_rgb_file_ofs;
540
uint32_t m_rgb_file_len;
541
uint32_t m_alpha_file_ofs;
542
uint32_t m_alpha_file_len;
543
544
bool m_alpha_flag; // true if the image has alpha data
545
bool m_iframe_flag; // true if the image is an I-Frame
546
};
547
548
struct basisu_file_info
549
{
550
uint32_t m_version;
551
uint32_t m_total_header_size;
552
553
uint32_t m_total_selectors;
554
// will be 0 for UASTC or if the file uses global codebooks
555
uint32_t m_selector_codebook_ofs;
556
uint32_t m_selector_codebook_size;
557
558
uint32_t m_total_endpoints;
559
// will be 0 for UASTC or if the file uses global codebooks
560
uint32_t m_endpoint_codebook_ofs;
561
uint32_t m_endpoint_codebook_size;
562
563
uint32_t m_tables_ofs;
564
uint32_t m_tables_size;
565
566
uint32_t m_slices_size;
567
568
basis_texture_type m_tex_type;
569
uint32_t m_us_per_frame;
570
571
// Low-level slice information (1 slice per image for color-only basis files, 2 for alpha basis files)
572
basisu_slice_info_vec m_slice_info;
573
574
uint32_t m_total_images; // total # of images
575
basisu::vector<uint32_t> m_image_mipmap_levels; // the # of mipmap levels for each image
576
577
uint32_t m_userdata0;
578
uint32_t m_userdata1;
579
580
basis_tex_format m_tex_format; // ETC1S, UASTC, etc.
581
582
uint32_t m_block_width;
583
uint32_t m_block_height;
584
585
bool m_y_flipped; // true if the image was Y flipped
586
bool m_etc1s; // true if the file is ETC1S
587
bool m_has_alpha_slices; // true if the texture has alpha slices (for ETC1S: even slices RGB, odd slices alpha)
588
};
589
590
// 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.
591
// If you're just starting out this is the class you care about.
592
class basisu_transcoder
593
{
594
basisu_transcoder(basisu_transcoder&);
595
basisu_transcoder& operator= (const basisu_transcoder&);
596
597
public:
598
basisu_transcoder();
599
600
// Validates the .basis file. This computes a crc16 over the entire file, so it's slow.
601
bool validate_file_checksums(const void* pData, uint32_t data_size, bool full_validation) const;
602
603
// Quick header validation - no crc16 checks.
604
bool validate_header(const void* pData, uint32_t data_size) const;
605
606
basis_texture_type get_texture_type(const void* pData, uint32_t data_size) const;
607
bool get_userdata(const void* pData, uint32_t data_size, uint32_t& userdata0, uint32_t& userdata1) const;
608
609
// Returns the total number of images in the basis file (always 1 or more).
610
// Note that the number of mipmap levels for each image may differ, and that images may have different resolutions.
611
uint32_t get_total_images(const void* pData, uint32_t data_size) const;
612
613
basis_tex_format get_basis_tex_format(const void* pData, uint32_t data_size) const;
614
615
// Returns the number of mipmap levels in an image.
616
uint32_t get_total_image_levels(const void* pData, uint32_t data_size, uint32_t image_index) const;
617
618
// Returns basic information about an image. Note that orig_width/orig_height may not be a multiple of 4.
619
bool 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;
620
621
// Returns information about the specified image.
622
bool get_image_info(const void* pData, uint32_t data_size, basisu_image_info& image_info, uint32_t image_index) const;
623
624
// Returns information about the specified image's mipmap level.
625
bool 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;
626
627
// Get a description of the basis file and low-level information about each slice.
628
bool get_file_info(const void* pData, uint32_t data_size, basisu_file_info& file_info) const;
629
630
// start_transcoding() must be called before calling transcode_slice() or transcode_image_level().
631
// 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).
632
bool start_transcoding(const void* pData, uint32_t data_size);
633
634
bool stop_transcoding();
635
636
// Returns true if start_transcoding() has been called.
637
bool get_ready_to_transcode() const { return m_ready_to_transcode; }
638
639
// transcode_image_level() decodes a single mipmap level from the .basis file to any of the supported output texture formats.
640
// 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).
641
// If the .basis file doesn't have alpha slices, the output alpha blocks will be set to fully opaque (all 255's).
642
// Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2, due to PVRTC1 format requirements.
643
// 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.
644
// 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).
645
// 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).
646
// Notes:
647
// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.
648
// - 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 in
649
// a first pass, which will be read in a second pass.
650
bool transcode_image_level(
651
const void* pData, uint32_t data_size,
652
uint32_t image_index, uint32_t level_index,
653
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
654
transcoder_texture_format fmt,
655
uint32_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;
656
657
// Finds the basis slice corresponding to the specified image/level/alpha params, or -1 if the slice can't be found.
658
int find_slice(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const;
659
660
// 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().
661
// 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).
662
// output_blocks_buf_size_in_blocks_or_pixels is just used for verification to make sure the output buffer is large enough.
663
// 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.
664
// output_block_stride_in_bytes: Number of bytes between each output block.
665
// 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).
666
// 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).
667
// Notes:
668
// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.
669
bool transcode_slice(const void* pData, uint32_t data_size, uint32_t slice_index,
670
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
671
block_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,
672
uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1) const;
673
674
static void write_opaque_alpha_blocks(
675
uint32_t num_blocks_x, uint32_t num_blocks_y,
676
void* pOutput_blocks, block_format fmt,
677
uint32_t block_stride_in_bytes, uint32_t output_row_pitch_in_blocks_or_pixels);
678
679
void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_lowlevel_etc1s_decoder.set_global_codebooks(pGlobal_codebook); }
680
const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_lowlevel_etc1s_decoder.get_global_codebooks(); }
681
682
const basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() const { return m_lowlevel_etc1s_decoder; }
683
basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() { return m_lowlevel_etc1s_decoder; }
684
685
const basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_decoder; }
686
basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_decoder; }
687
688
private:
689
mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;
690
mutable basisu_lowlevel_uastc_ldr_4x4_transcoder m_lowlevel_uastc_decoder;
691
mutable basisu_lowlevel_uastc_hdr_4x4_transcoder m_lowlevel_uastc_4x4_hdr_decoder;
692
mutable basisu_lowlevel_astc_hdr_6x6_transcoder m_lowlevel_astc_6x6_hdr_decoder;
693
mutable basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder m_lowlevel_astc_6x6_hdr_intermediate_decoder;
694
695
bool m_ready_to_transcode;
696
697
int find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const;
698
699
bool validate_header_quick(const void* pData, uint32_t data_size) const;
700
};
701
702
// basisu_transcoder_init() MUST be called before a .basis file can be transcoded.
703
void basisu_transcoder_init();
704
705
enum debug_flags_t
706
{
707
cDebugFlagVisCRs = 1,
708
cDebugFlagVisBC1Sels = 2,
709
cDebugFlagVisBC1Endpoints = 4
710
};
711
uint32_t get_debug_flags();
712
void set_debug_flags(uint32_t f);
713
714
// ------------------------------------------------------------------------------------------------------
715
// Optional .KTX2 file format support
716
// KTX2 reading optionally requires miniz or Zstd decompressors for supercompressed UASTC files.
717
// ------------------------------------------------------------------------------------------------------
718
#if BASISD_SUPPORT_KTX2
719
#pragma pack(push)
720
#pragma pack(1)
721
struct ktx2_header
722
{
723
uint8_t m_identifier[12];
724
basisu::packed_uint<4> m_vk_format;
725
basisu::packed_uint<4> m_type_size;
726
basisu::packed_uint<4> m_pixel_width;
727
basisu::packed_uint<4> m_pixel_height;
728
basisu::packed_uint<4> m_pixel_depth;
729
basisu::packed_uint<4> m_layer_count;
730
basisu::packed_uint<4> m_face_count;
731
basisu::packed_uint<4> m_level_count;
732
basisu::packed_uint<4> m_supercompression_scheme;
733
basisu::packed_uint<4> m_dfd_byte_offset;
734
basisu::packed_uint<4> m_dfd_byte_length;
735
basisu::packed_uint<4> m_kvd_byte_offset;
736
basisu::packed_uint<4> m_kvd_byte_length;
737
basisu::packed_uint<8> m_sgd_byte_offset;
738
basisu::packed_uint<8> m_sgd_byte_length;
739
};
740
741
struct ktx2_level_index
742
{
743
basisu::packed_uint<8> m_byte_offset;
744
basisu::packed_uint<8> m_byte_length;
745
basisu::packed_uint<8> m_uncompressed_byte_length;
746
};
747
748
struct ktx2_etc1s_global_data_header
749
{
750
basisu::packed_uint<2> m_endpoint_count;
751
basisu::packed_uint<2> m_selector_count;
752
basisu::packed_uint<4> m_endpoints_byte_length;
753
basisu::packed_uint<4> m_selectors_byte_length;
754
basisu::packed_uint<4> m_tables_byte_length;
755
basisu::packed_uint<4> m_extended_byte_length;
756
};
757
758
struct ktx2_etc1s_image_desc
759
{
760
basisu::packed_uint<4> m_image_flags;
761
basisu::packed_uint<4> m_rgb_slice_byte_offset;
762
basisu::packed_uint<4> m_rgb_slice_byte_length;
763
basisu::packed_uint<4> m_alpha_slice_byte_offset;
764
basisu::packed_uint<4> m_alpha_slice_byte_length;
765
};
766
767
struct ktx2_astc_hdr_6x6_intermediate_image_desc
768
{
769
basisu::packed_uint<4> m_rgb_slice_byte_offset;
770
basisu::packed_uint<4> m_rgb_slice_byte_length;
771
};
772
773
struct ktx2_animdata
774
{
775
basisu::packed_uint<4> m_duration;
776
basisu::packed_uint<4> m_timescale;
777
basisu::packed_uint<4> m_loopcount;
778
};
779
#pragma pack(pop)
780
781
const uint32_t KTX2_VK_FORMAT_UNDEFINED = 0;
782
783
// These are standard Vulkan texture VkFormat ID's, see https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFormat.html
784
const uint32_t KTX2_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000;
785
const uint32_t KTX2_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001;
786
const uint32_t KTX2_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002;
787
const uint32_t KTX2_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003;
788
const uint32_t KTX2_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004;
789
const uint32_t KTX2_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005;
790
const uint32_t KTX2_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006;
791
792
const uint32_t KTX2_KDF_DF_MODEL_ASTC = 162; // 0xA2
793
const uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163; // 0xA3
794
const uint32_t KTX2_KDF_DF_MODEL_UASTC_LDR_4X4 = 166; // 0xA6
795
const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_4X4 = 167; // 0xA7
796
const uint32_t KTX2_KDF_DF_MODEL_ASTC_HDR_6X6_INTERMEDIATE = 168; // 0xA8, TODO - coordinate with Khronos on this
797
798
const uint32_t KTX2_IMAGE_IS_P_FRAME = 2;
799
const uint32_t KTX2_UASTC_BLOCK_SIZE = 16; // also the block size for UASTC_HDR
800
const uint32_t KTX2_MAX_SUPPORTED_LEVEL_COUNT = 16; // this is an implementation specific constraint and can be increased
801
802
// The KTX2 transfer functions supported by KTX2
803
const uint32_t KTX2_KHR_DF_TRANSFER_LINEAR = 1;
804
const uint32_t KTX2_KHR_DF_TRANSFER_SRGB = 2;
805
806
enum ktx2_supercompression
807
{
808
KTX2_SS_NONE = 0,
809
KTX2_SS_BASISLZ = 1,
810
KTX2_SS_ZSTANDARD = 2,
811
KTX2_SS_BASIS
812
};
813
814
extern const uint8_t g_ktx2_file_identifier[12];
815
816
enum ktx2_df_channel_id
817
{
818
KTX2_DF_CHANNEL_ETC1S_RGB = 0U,
819
KTX2_DF_CHANNEL_ETC1S_RRR = 3U,
820
KTX2_DF_CHANNEL_ETC1S_GGG = 4U,
821
KTX2_DF_CHANNEL_ETC1S_AAA = 15U,
822
823
KTX2_DF_CHANNEL_UASTC_DATA = 0U,
824
KTX2_DF_CHANNEL_UASTC_RGB = 0U,
825
KTX2_DF_CHANNEL_UASTC_RGBA = 3U,
826
KTX2_DF_CHANNEL_UASTC_RRR = 4U,
827
KTX2_DF_CHANNEL_UASTC_RRRG = 5U,
828
KTX2_DF_CHANNEL_UASTC_RG = 6U,
829
};
830
831
inline const char* ktx2_get_etc1s_df_channel_id_str(ktx2_df_channel_id id)
832
{
833
switch (id)
834
{
835
case KTX2_DF_CHANNEL_ETC1S_RGB: return "RGB";
836
case KTX2_DF_CHANNEL_ETC1S_RRR: return "RRR";
837
case KTX2_DF_CHANNEL_ETC1S_GGG: return "GGG";
838
case KTX2_DF_CHANNEL_ETC1S_AAA: return "AAA";
839
default: break;
840
}
841
return "?";
842
}
843
844
inline const char* ktx2_get_uastc_df_channel_id_str(ktx2_df_channel_id id)
845
{
846
switch (id)
847
{
848
case KTX2_DF_CHANNEL_UASTC_RGB: return "RGB";
849
case KTX2_DF_CHANNEL_UASTC_RGBA: return "RGBA";
850
case KTX2_DF_CHANNEL_UASTC_RRR: return "RRR";
851
case KTX2_DF_CHANNEL_UASTC_RRRG: return "RRRG";
852
case KTX2_DF_CHANNEL_UASTC_RG: return "RG";
853
default: break;
854
}
855
return "?";
856
}
857
858
enum ktx2_df_color_primaries
859
{
860
KTX2_DF_PRIMARIES_UNSPECIFIED = 0,
861
KTX2_DF_PRIMARIES_BT709 = 1,
862
KTX2_DF_PRIMARIES_SRGB = 1,
863
KTX2_DF_PRIMARIES_BT601_EBU = 2,
864
KTX2_DF_PRIMARIES_BT601_SMPTE = 3,
865
KTX2_DF_PRIMARIES_BT2020 = 4,
866
KTX2_DF_PRIMARIES_CIEXYZ = 5,
867
KTX2_DF_PRIMARIES_ACES = 6,
868
KTX2_DF_PRIMARIES_ACESCC = 7,
869
KTX2_DF_PRIMARIES_NTSC1953 = 8,
870
KTX2_DF_PRIMARIES_PAL525 = 9,
871
KTX2_DF_PRIMARIES_DISPLAYP3 = 10,
872
KTX2_DF_PRIMARIES_ADOBERGB = 11
873
};
874
875
inline const char* ktx2_get_df_color_primaries_str(ktx2_df_color_primaries p)
876
{
877
switch (p)
878
{
879
case KTX2_DF_PRIMARIES_UNSPECIFIED: return "UNSPECIFIED";
880
case KTX2_DF_PRIMARIES_BT709: return "BT709";
881
case KTX2_DF_PRIMARIES_BT601_EBU: return "EBU";
882
case KTX2_DF_PRIMARIES_BT601_SMPTE: return "SMPTE";
883
case KTX2_DF_PRIMARIES_BT2020: return "BT2020";
884
case KTX2_DF_PRIMARIES_CIEXYZ: return "CIEXYZ";
885
case KTX2_DF_PRIMARIES_ACES: return "ACES";
886
case KTX2_DF_PRIMARIES_ACESCC: return "ACESCC";
887
case KTX2_DF_PRIMARIES_NTSC1953: return "NTSC1953";
888
case KTX2_DF_PRIMARIES_PAL525: return "PAL525";
889
case KTX2_DF_PRIMARIES_DISPLAYP3: return "DISPLAYP3";
890
case KTX2_DF_PRIMARIES_ADOBERGB: return "ADOBERGB";
891
default: break;
892
}
893
return "?";
894
}
895
896
// Information about a single 2D texture "image" in a KTX2 file.
897
struct ktx2_image_level_info
898
{
899
// The mipmap level index (0=largest), texture array layer index, and cubemap face index of the image.
900
uint32_t m_level_index;
901
uint32_t m_layer_index;
902
uint32_t m_face_index;
903
904
// The image's actual (or the original source image's) width/height in pixels, which may not be divisible by 4 pixels.
905
uint32_t m_orig_width;
906
uint32_t m_orig_height;
907
908
// The image's physical width/height, which will always be divisible by 4 pixels.
909
uint32_t m_width;
910
uint32_t m_height;
911
912
// The texture's dimensions in 4x4 or 6x6 texel blocks.
913
uint32_t m_num_blocks_x;
914
uint32_t m_num_blocks_y;
915
916
// The format's block width/height (currently either 4 or 6).
917
uint32_t m_block_width;
918
uint32_t m_block_height;
919
920
// The total number of blocks
921
uint32_t m_total_blocks;
922
923
// true if the image has alpha data
924
bool m_alpha_flag;
925
926
// 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.
927
bool m_iframe_flag;
928
};
929
930
// Thread-specific ETC1S/supercompressed UASTC transcoder state. (If you're not doing multithreading transcoding you can ignore this.)
931
struct ktx2_transcoder_state
932
{
933
basist::basisu_transcoder_state m_transcoder_state;
934
basisu::uint8_vec m_level_uncomp_data;
935
int m_uncomp_data_level_index;
936
937
void clear()
938
{
939
m_transcoder_state.clear();
940
m_level_uncomp_data.clear();
941
m_uncomp_data_level_index = -1;
942
}
943
};
944
945
// This class is quite similar to basisu_transcoder. It treats KTX2 files as a simple container for ETC1S/UASTC texture data.
946
// It does not support 1D or 3D textures.
947
// It only supports 2D and cubemap textures, with or without mipmaps, texture arrays of 2D/cubemap textures, and texture video files.
948
// It only supports raw non-supercompressed UASTC, ETC1S, UASTC+Zstd, or UASTC+zlib compressed files.
949
// DFD (Data Format Descriptor) parsing is purposely as simple as possible.
950
// If you need to know how to interpret the texture channels you'll need to parse the DFD yourself after calling get_dfd().
951
class ktx2_transcoder
952
{
953
public:
954
ktx2_transcoder();
955
956
// Frees all allocations, resets object.
957
void clear();
958
959
// init() parses the KTX2 header, level index array, DFD, and key values, but nothing else.
960
// 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.
961
// This method holds a pointer to the file data until clear() is called.
962
bool init(const void* pData, uint32_t data_size);
963
964
// Returns the data/size passed to init().
965
const uint8_t* get_data() const { return m_pData; }
966
uint32_t get_data_size() const { return m_data_size; }
967
968
// Returns the KTX2 header. Valid after init().
969
const ktx2_header& get_header() const { return m_header; }
970
971
// Returns the KTX2 level index array. There will be one entry for each mipmap level. Valid after init().
972
const basisu::vector<ktx2_level_index>& get_level_index() const { return m_levels; }
973
974
// Returns the texture's width in texels. Always non-zero, might not be divisible by 4. Valid after init().
975
uint32_t get_width() const { return m_header.m_pixel_width; }
976
977
// Returns the texture's height in texels. Always non-zero, might not be divisible by 4. Valid after init().
978
uint32_t get_height() const { return m_header.m_pixel_height; }
979
980
// Returns the texture's number of mipmap levels. Always returns 1 or higher. Valid after init().
981
uint32_t get_levels() const { return m_header.m_level_count; }
982
983
// Returns the number of faces. Returns 1 for 2D textures and or 6 for cubemaps. Valid after init().
984
uint32_t get_faces() const { return m_header.m_face_count; }
985
986
// Returns 0 or the number of layers in the texture array or texture video. Valid after init().
987
uint32_t get_layers() const { return m_header.m_layer_count; }
988
989
// Returns cETC1S, cUASTC4x4, cUASTC_HDR_4x4, cASTC_HDR_6x6, cASTC_HDR_6x6_INTERMEDIATE. Valid after init().
990
basist::basis_tex_format get_basis_tex_format() const { return m_format; }
991
992
// ETC1S LDR 4x4
993
bool is_etc1s() const { return get_basis_tex_format() == basist::basis_tex_format::cETC1S; }
994
995
// UASTC LDR 4x4 (only)
996
bool is_uastc() const { return get_basis_tex_format() == basist::basis_tex_format::cUASTC4x4; }
997
998
// Is ASTC HDR 4x4 or 6x6
999
bool is_hdr() const
1000
{
1001
return basis_tex_format_is_hdr(get_basis_tex_format());
1002
}
1003
1004
bool is_ldr() const
1005
{
1006
return !is_hdr();
1007
}
1008
1009
bool is_hdr_4x4() const
1010
{
1011
return (get_basis_tex_format() == basist::basis_tex_format::cUASTC_HDR_4x4);
1012
}
1013
1014
bool is_hdr_6x6() const
1015
{
1016
return (get_basis_tex_format() == basist::basis_tex_format::cASTC_HDR_6x6) || (get_basis_tex_format() == basist::basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE);
1017
}
1018
1019
uint32_t get_block_width() const { return basis_tex_format_get_block_width(get_basis_tex_format()); }
1020
uint32_t get_block_height() const { return basis_tex_format_get_block_height(get_basis_tex_format()); }
1021
1022
// 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().
1023
uint32_t get_has_alpha() const { return m_has_alpha; }
1024
1025
// Returns the entire Data Format Descriptor (DFD) from the KTX2 file. Valid after init().
1026
// See https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#_the_khronos_data_format_descriptor_overview
1027
const basisu::uint8_vec& get_dfd() const { return m_dfd; }
1028
1029
// Some basic DFD accessors. Valid after init().
1030
uint32_t get_dfd_color_model() const { return m_dfd_color_model; }
1031
1032
// Returns the DFD color primary.
1033
// We do not validate the color primaries, so the returned value may not be in the ktx2_df_color_primaries enum.
1034
ktx2_df_color_primaries get_dfd_color_primaries() const { return m_dfd_color_prims; }
1035
1036
// Returns KTX2_KHR_DF_TRANSFER_LINEAR or KTX2_KHR_DF_TRANSFER_SRGB.
1037
uint32_t get_dfd_transfer_func() const { return m_dfd_transfer_func; }
1038
1039
uint32_t get_dfd_flags() const { return m_dfd_flags; }
1040
1041
// Returns 1 (ETC1S/UASTC) or 2 (ETC1S with an internal alpha channel).
1042
uint32_t get_dfd_total_samples() const { return m_dfd_samples; }
1043
1044
// Returns the channel mapping for each DFD "sample". UASTC always has 1 sample, ETC1S can have one or two.
1045
// Note the returned value SHOULD be one of the ktx2_df_channel_id enums, but we don't validate that.
1046
// It's up to the caller to decide what to do if the value isn't in the enum.
1047
ktx2_df_channel_id get_dfd_channel_id0() const { return m_dfd_chan0; }
1048
ktx2_df_channel_id get_dfd_channel_id1() const { return m_dfd_chan1; }
1049
1050
// Key value field data.
1051
struct key_value
1052
{
1053
// The key field is UTF8 and always zero terminated.
1054
// In memory we always append a zero terminator to the key.
1055
basisu::uint8_vec m_key;
1056
1057
// The value may be empty. In the KTX2 file it consists of raw bytes which may or may not be zero terminated.
1058
// In memory we always append a zero terminator to the value.
1059
basisu::uint8_vec m_value;
1060
1061
bool operator< (const key_value& rhs) const { return strcmp((const char*)m_key.data(), (const char *)rhs.m_key.data()) < 0; }
1062
};
1063
typedef basisu::vector<key_value> key_value_vec;
1064
1065
// Returns the array of key-value entries. This may be empty. Valid after init().
1066
// 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.
1067
const key_value_vec& get_key_values() const { return m_key_values; }
1068
1069
const basisu::uint8_vec *find_key(const std::string& key_name) const;
1070
1071
// Low-level ETC1S specific accessors
1072
1073
// Returns the ETC1S global supercompression data header, which is only valid after start_transcoding() is called.
1074
const ktx2_etc1s_global_data_header& get_etc1s_header() const { return m_etc1s_header; }
1075
1076
// Returns the array of ETC1S image descriptors, which is only valid after get_etc1s_image_descs() is called.
1077
const basisu::vector<ktx2_etc1s_image_desc>& get_etc1s_image_descs() const { return m_etc1s_image_descs; }
1078
1079
const basisu::vector<ktx2_astc_hdr_6x6_intermediate_image_desc>& get_astc_hdr_6x6_intermediate_image_descs() const { return m_astc_6x6_intermediate_image_descs; }
1080
1081
// Must have called startTranscoding() first
1082
uint32_t get_etc1s_image_descs_image_flags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) const;
1083
1084
// is_video() is only valid after start_transcoding() is called.
1085
// For ETC1S data, if this returns true you must currently transcode the file from first to last frame, in order, without skipping any frames.
1086
bool is_video() const { return m_is_video; }
1087
1088
// Defaults to 0, only non-zero if the key existed in the source KTX2 file.
1089
float get_ldr_hdr_upconversion_nit_multiplier() const { return m_ldr_hdr_upconversion_nit_multiplier; }
1090
1091
// start_transcoding() MUST be called before calling transcode_image().
1092
// This method decompresses the ETC1S global endpoint/selector codebooks, which is not free, so try to avoid calling it excessively.
1093
bool start_transcoding();
1094
1095
// get_image_level_info() be called after init(), but the m_iframe_flag's won't be valid until start_transcoding() is called.
1096
// You can call this method before calling transcode_image_level() to retrieve basic information about the mipmap level's dimensions, etc.
1097
bool get_image_level_info(ktx2_image_level_info& level_info, uint32_t level_index, uint32_t layer_index, uint32_t face_index) const;
1098
1099
// transcode_image_level() transcodes a single 2D texture or cubemap face from the KTX2 file.
1100
// Internally it uses the same low-level transcode API's as basisu_transcoder::transcode_image_level().
1101
// 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 is
1102
// 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.
1103
// Currently ETC1S videos must always be transcoded from first to last frame (or KTX2 "layer"), in order, with no skipping of frames.
1104
// By default this method is not thread safe unless you specify a pointer to a user allocated thread-specific transcoder_state struct.
1105
bool transcode_image_level(
1106
uint32_t level_index, uint32_t layer_index, uint32_t face_index,
1107
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
1108
basist::transcoder_texture_format fmt,
1109
uint32_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,
1110
ktx2_transcoder_state *pState = nullptr);
1111
1112
private:
1113
const uint8_t* m_pData;
1114
uint32_t m_data_size;
1115
1116
ktx2_header m_header;
1117
basisu::vector<ktx2_level_index> m_levels;
1118
basisu::uint8_vec m_dfd;
1119
key_value_vec m_key_values;
1120
1121
ktx2_etc1s_global_data_header m_etc1s_header;
1122
basisu::vector<ktx2_etc1s_image_desc> m_etc1s_image_descs;
1123
basisu::vector<ktx2_astc_hdr_6x6_intermediate_image_desc> m_astc_6x6_intermediate_image_descs;
1124
1125
basist::basis_tex_format m_format;
1126
1127
uint32_t m_dfd_color_model;
1128
ktx2_df_color_primaries m_dfd_color_prims;
1129
uint32_t m_dfd_transfer_func;
1130
uint32_t m_dfd_flags;
1131
uint32_t m_dfd_samples;
1132
ktx2_df_channel_id m_dfd_chan0, m_dfd_chan1;
1133
1134
basist::basisu_lowlevel_etc1s_transcoder m_etc1s_transcoder;
1135
basist::basisu_lowlevel_uastc_ldr_4x4_transcoder m_uastc_transcoder;
1136
basist::basisu_lowlevel_uastc_hdr_4x4_transcoder m_uastc_hdr_transcoder;
1137
basist::basisu_lowlevel_astc_hdr_6x6_transcoder m_astc_hdr_6x6_transcoder;
1138
basist::basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder m_astc_hdr_6x6_intermediate_transcoder;
1139
1140
ktx2_transcoder_state m_def_transcoder_state;
1141
1142
bool m_has_alpha;
1143
bool m_is_video;
1144
float m_ldr_hdr_upconversion_nit_multiplier;
1145
1146
bool decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data);
1147
bool read_astc_6x6_hdr_intermediate_global_data();
1148
bool decompress_etc1s_global_data();
1149
bool read_key_values();
1150
};
1151
1152
// Replaces if the key already exists
1153
inline void ktx2_add_key_value(ktx2_transcoder::key_value_vec& key_values, const std::string& key, const std::string& val)
1154
{
1155
assert(key.size());
1156
1157
basist::ktx2_transcoder::key_value* p = nullptr;
1158
1159
// Try to find an existing key
1160
for (size_t i = 0; i < key_values.size(); i++)
1161
{
1162
if (strcmp((const char*)key_values[i].m_key.data(), key.c_str()) == 0)
1163
{
1164
p = &key_values[i];
1165
break;
1166
}
1167
}
1168
1169
if (!p)
1170
p = key_values.enlarge(1);
1171
1172
p->m_key.resize(0);
1173
p->m_value.resize(0);
1174
1175
p->m_key.resize(key.size() + 1);
1176
memcpy(p->m_key.data(), key.c_str(), key.size());
1177
1178
p->m_value.resize(val.size() + 1);
1179
if (val.size())
1180
memcpy(p->m_value.data(), val.c_str(), val.size());
1181
}
1182
1183
#endif // BASISD_SUPPORT_KTX2
1184
1185
// Returns true if the transcoder was compiled with KTX2 support.
1186
bool basisu_transcoder_supports_ktx2();
1187
1188
// Returns true if the transcoder was compiled with Zstandard support.
1189
bool basisu_transcoder_supports_ktx2_zstd();
1190
1191
} // namespace basisu
1192
1193
1194