-
Notifications
You must be signed in to change notification settings - Fork 329
Expand file tree
/
Copy pathbasisu_transcoder.h
More file actions
1358 lines (1088 loc) · 66.1 KB
/
basisu_transcoder.h
File metadata and controls
1358 lines (1088 loc) · 66.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// basisu_transcoder.h
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also see basis_tex_format in basisu_file_headers.h (TODO: Perhaps move key definitions into here.)
#pragma once
// 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.
// 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.
// Also see BASISD_SUPPORT_KTX2_ZSTD in basisu_transcoder.cpp, which individually disables Zstd usage.
#ifndef BASISD_SUPPORT_KTX2
#define BASISD_SUPPORT_KTX2 1
#endif
// Set BASISD_SUPPORT_KTX2_ZSTD to 0 to disable Zstd usage and KTX2 UASTC Zstd supercompression support
#ifndef BASISD_SUPPORT_KTX2_ZSTD
#define BASISD_SUPPORT_KTX2_ZSTD 1
#endif
#include "basisu_transcoder_internal.h"
#include "basisu_transcoder_uastc.h"
#include "basisu_file_headers.h"
namespace basist
{
const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384;
// High-level composite texture formats supported by the transcoder.
// Each of these texture formats directly correspond to OpenGL/D3D/Vulkan etc. texture formats.
// Notes:
// - If you specify a texture format that supports alpha, but the .basis file doesn't have alpha, the transcoder will automatically output a
// fully opaque (255) alpha channel.
// - The PVRTC1 texture formats only support power of 2 dimension .basis files, but this may be relaxed in a future version.
// - The PVRTC1 transcoders are real-time encoders, so don't expect the highest quality. We may add a slower encoder with improved quality.
// - These enums must be kept in sync with Javascript code that calls the transcoder.
enum class transcoder_texture_format
{
// Compressed formats
// ETC1-2
cTFETC1_RGB = 0, // Opaque only, returns RGB or alpha data if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFETC2_RGBA = 1, // Opaque+alpha, ETC2_EAC_A8 block followed by a ETC1 block, alpha channel will be opaque for opaque .basis files
// BC1-5, BC7 (desktop, some mobile devices)
cTFBC1_RGB = 2, // Opaque only, no punchthrough alpha support yet, transcodes alpha slice if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
cTFBC3_RGBA = 3, // Opaque+alpha, BC4 followed by a BC1 block, alpha channel will be opaque for opaque .basis files
cTFBC4_R = 4, // Red only, alpha slice is transcoded to output if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified
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)
cTFBC7_RGBA = 6, // RGB or RGBA, mode 5 for ETC1S, modes (1,2,3,5,6,7) for UASTC
// PVRTC1 4bpp (mobile, PowerVR devices)
cTFPVRTC1_4_RGB = 8, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.
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.
// ASTC (mobile, some Intel CPU's, hopefully all desktop GPU's one day)
cTFASTC_LDR_4x4_RGBA = 10, // LDR. Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files.
// LDR: Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
// ATC (mobile, Adreno devices, this is a niche format)
cTFATC_RGB = 11, // Opaque, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. ATI ATC (GL_ATC_RGB_AMD)
cTFATC_RGBA = 12, // Opaque+alpha, alpha channel will be opaque for opaque .basis files. ATI ATC (GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD)
// FXT1 (desktop, Intel devices, this is a super obscure format)
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).
// 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.
// See the BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING macro in basisu_transcoder_internal.h.
cTFPVRTC2_4_RGB = 18, // Opaque-only, almost BC1 quality, much faster to transcode and supports arbitrary texture dimensions (unlike PVRTC1 RGB).
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.
cTFETC2_EAC_R11 = 20, // R only (ETC2 EAC R11 unsigned)
cTFETC2_EAC_RG11 = 21, // RG only (ETC2 EAC RG11 unsigned), R=opaque.r, G=alpha - for tangent space normal maps
cTFBC6H = 22, // HDR, RGB only, unsigned
cTFASTC_HDR_4x4_RGBA = 23, // HDR, RGBA (currently UASTC HDR 4x4 encoders are only RGB), unsigned
// Uncompressed (raw pixel) formats
// Note these uncompressed formats (RGBA32, 565, and 4444) can only be transcoded to from LDR input files (ETC1S or UASTC LDR).
cTFRGBA32 = 13, // 32bpp RGBA image stored in raster (not block) order in memory, R is first byte, A is last byte.
cTFRGB565 = 14, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 11
cTFBGR565 = 15, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0
cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
// Note these uncompressed formats (HALF and 9E5) can only be transcoded to from HDR input files (UASTC HDR 4x4 or ASTC HDR 6x6).
cTFRGB_HALF = 24, // 48bpp RGB half (16-bits/component, 3 components)
cTFRGBA_HALF = 25, // 64bpp RGBA half (16-bits/component, 4 components) (A will always currently 1.0, UASTC_HDR doesn't support alpha)
cTFRGB_9E5 = 26, // 32bpp RGB 9E5 (shared exponent, positive only, see GL_EXT_texture_shared_exponent)
cTFASTC_HDR_6x6_RGBA = 27, // HDR, RGBA (currently our ASTC HDR 6x6 encodes are only RGB), unsigned
// The remaining LDR ASTC block sizes, excluding 4x4 (which is above). There are 14 total valid ASTC LDR/HDR block sizes.
cTFASTC_LDR_5x4_RGBA = 28,
cTFASTC_LDR_5x5_RGBA = 29,
cTFASTC_LDR_6x5_RGBA = 30,
cTFASTC_LDR_6x6_RGBA = 31,
cTFASTC_LDR_8x5_RGBA = 32,
cTFASTC_LDR_8x6_RGBA = 33,
cTFASTC_LDR_10x5_RGBA = 34,
cTFASTC_LDR_10x6_RGBA = 35,
cTFASTC_LDR_8x8_RGBA = 36,
cTFASTC_LDR_10x8_RGBA = 37,
cTFASTC_LDR_10x10_RGBA = 38,
cTFASTC_LDR_12x10_RGBA = 39,
cTFASTC_LDR_12x12_RGBA = 40,
cTFTotalTextureFormats = 41,
// ----- The following are old/legacy enums for compatibility with code compiled against previous versions
cTFETC1 = cTFETC1_RGB,
cTFETC2 = cTFETC2_RGBA,
cTFBC1 = cTFBC1_RGB,
cTFBC3 = cTFBC3_RGBA,
cTFBC4 = cTFBC4_R,
cTFBC5 = cTFBC5_RG,
// Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC LDR 4x4, which supports numerous modes.
cTFBC7_M6_RGB = cTFBC7_RGBA, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats.
cTFBC7_M5_RGBA = cTFBC7_RGBA, // Opaque+alpha, alpha channel will be opaque for opaque .basis files
cTFBC7_M6_OPAQUE_ONLY = cTFBC7_RGBA,
cTFBC7_M5 = cTFBC7_RGBA,
cTFBC7_ALT = 7,
cTFASTC_4x4 = cTFASTC_LDR_4x4_RGBA,
cTFATC_RGBA_INTERPOLATED_ALPHA = cTFATC_RGBA,
cTFASTC_4x4_RGBA = cTFASTC_LDR_4x4_RGBA
};
// For compressed texture formats, this returns the # of bytes per block. For uncompressed, it returns the # of bytes per pixel.
// 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.
uint32_t basis_get_bytes_per_block_or_pixel(transcoder_texture_format fmt);
// Returns the transcoder texture format's name in ASCII
const char* basis_get_format_name(transcoder_texture_format fmt);
// Returns basis texture format name in ASCII
const char* basis_get_tex_format_name(basis_tex_format fmt);
// Returns block format name in ASCII
const char* basis_get_block_format_name(block_format fmt);
// Returns true if the format supports an alpha channel.
bool basis_transcoder_format_has_alpha(transcoder_texture_format fmt);
// Returns true if the format is HDR.
bool basis_transcoder_format_is_hdr(transcoder_texture_format fmt);
// Returns true if the format is LDR.
inline bool basis_transcoder_format_is_ldr(transcoder_texture_format fmt) { return !basis_transcoder_format_is_hdr(fmt); }
// Returns true if the format is an LDR or HDR ASTC format.
bool basis_is_transcoder_texture_format_astc(transcoder_texture_format fmt);
// Returns the basisu::texture_format corresponding to the specified transcoder_texture_format.
basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt);
// Returns the texture type's name in ASCII.
const char* basis_get_texture_type_name(basis_texture_type tex_type);
// Returns true if the transcoder texture type is an uncompressed (raw pixel) format.
bool basis_transcoder_format_is_uncompressed(transcoder_texture_format tex_type);
// Returns the # of bytes per pixel for uncompressed formats, or 0 for block texture formats.
uint32_t basis_get_uncompressed_bytes_per_pixel(transcoder_texture_format fmt);
// Returns the block width for the specified texture format, which is currently either 4 or 8 for FXT1.
uint32_t basis_get_block_width(transcoder_texture_format fmt);
// Returns the block height for the specified texture format, which is currently always 4.
uint32_t basis_get_block_height(transcoder_texture_format fmt);
// ASTC/XUASTC LDR formats only: Given a basis_tex_format (mode or codec), return the corresponding ASTC basisu::texture_format with the proper block size from 4x4-12x12.
basisu::texture_format basis_get_texture_format_from_xuastc_or_astc_ldr_basis_tex_format(basis_tex_format fmt);
// For any given basis_tex_format (mode or codec), return the LDR/HDR ASTC transcoder texture format with the proper block size.
transcoder_texture_format basis_get_transcoder_texture_format_from_basis_tex_format(basis_tex_format fmt);
// basis_get_transcoder_texture_format_from_xuastc_or_astc_ldr_basis_tex_format: same as basis_get_transcoder_texture_format_from_basis_tex_format (TODO: remove)
transcoder_texture_format basis_get_transcoder_texture_format_from_xuastc_or_astc_ldr_basis_tex_format(basis_tex_format fmt);
// 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, or XUASTC LDR 4x4-12x12).
// For XUASTC the ASTC block size must match the transcoder_texture_format's ASTC block size.
bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt = basis_tex_format::cETC1S);
// Returns the block width/height for the specified basis texture file format.
uint32_t basis_tex_format_get_block_width(basis_tex_format fmt);
uint32_t basis_tex_format_get_block_height(basis_tex_format fmt);
bool basis_tex_format_is_hdr(basis_tex_format fmt);
inline bool basis_tex_format_is_ldr(basis_tex_format fmt) { return !basis_tex_format_is_hdr(fmt); }
// Validates that the output buffer is large enough to hold the entire transcoded texture.
// For uncompressed texture formats, most input parameters are in pixels, not blocks.
bool basis_validate_output_buffer_size(transcoder_texture_format target_format,
uint32_t output_blocks_buf_size_in_blocks_or_pixels,
uint32_t orig_width, uint32_t orig_height,
uint32_t output_row_pitch_in_blocks_or_pixels,
uint32_t output_rows_in_pixels);
// 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.
// Note the returned value is not necessarily the # of bytes a transcoder could write to the output buffer due to these minimum PVRTC1 requirements.
// (These PVRTC1 requirements are not ours, but OpenGL's.)
uint32_t basis_compute_transcoded_image_size_in_bytes(transcoder_texture_format target_format, uint32_t orig_width, uint32_t orig_height);
class basisu_transcoder;
// This struct holds all state used during transcoding. For video, it needs to persist between image transcodes (it holds the previous frame).
// For threading you can use one state per thread.
struct basisu_transcoder_state
{
struct block_preds
{
uint16_t m_endpoint_index;
uint8_t m_pred_bits;
};
basisu::vector<block_preds> m_block_endpoint_preds[2];
enum { cMaxPrevFrameLevels = 16 };
basisu::vector<uint32_t> m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index]
void clear()
{
for (uint32_t i = 0; i < 2; i++)
{
m_block_endpoint_preds[i].clear();
for (uint32_t j = 0; j < cMaxPrevFrameLevels; j++)
m_prev_frame_indices[i][j].clear();
}
}
};
// Low-level helper classes that do the actual transcoding.
enum basisu_decode_flags
{
// 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.
cDecodeFlagsPVRTCDecodeToNextPow2 = 2,
// 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.
// This is primarily to allow decoding of textures with alpha to multiple ETC1 textures (one for color, another for alpha).
cDecodeFlagsTranscodeAlphaDataToOpaqueFormats = 4,
// Forbid usage of BC1 3 color blocks (we don't support BC1 punchthrough alpha yet).
// This flag is used internally when decoding to BC3.
cDecodeFlagsBC1ForbidThreeColorBlocks = 8,
// The output buffer contains alpha endpoint/selector indices.
// Used internally when decoding formats like ASTC that require both color and alpha data to be available when transcoding to the output format.
cDecodeFlagsOutputHasAlphaIndices = 16,
// Enable slower, but higher quality transcoding for some formats.
// For ASTC/XUASTC->BC7, this enables partially analytical encoding vs. fully analytical.
cDecodeFlagsHighQuality = 32,
// Disable ETC1S->BC7 adaptive chroma filtering, for much faster transcoding to BC7.
cDecodeFlagsNoETC1SChromaFiltering = 64,
// Disable deblock filtering for XUASTC LDR transcoding to non-ASTC formats.
// For ASTC 8x6 or smaller block sizes, deblocking is always disabled unless you force it on using cDecodeFlagsForceDeblockFiltering.
cDecodeFlagsNoDeblockFiltering = 128,
// More aggressive deblock filtering (only used when it's enabled)
cDecodeFlagsStrongerDeblockFiltering = 256,
// Always apply deblocking, even for smaller ASTC block sizes (4x4-8x6).
cDecodeFlagsForceDeblockFiltering = 512,
// By default XUASTC LDR 4x4, 6x6 and 8x6 are directly transcoded to BC7 without always requiring a full ASTC block unpack and analytical BC7 encode. This is 1.4x up to 3x faster in WASM.
// This trade offs some quality. The largest transcoding speed gain is achieved when the source XUASTC data isn't dual plane and only uses 1 subset. Otherwise the actual perf. gain is variable.
// To disable this optimization for all XUASTC block sizes and always use the fallback encoder, specify cDecodeFlagXUASTCLDRDisableFastBC7Transcoding.
cDecodeFlagXUASTCLDRDisableFastBC7Transcoding = 1024
};
// ETC1S
class basisu_lowlevel_etc1s_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_etc1s_transcoder();
void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_pGlobal_codebook = pGlobal_codebook; }
const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_pGlobal_codebook; }
bool decode_palettes(
uint32_t num_endpoints, const uint8_t* pEndpoints_data, uint32_t endpoints_data_size,
uint32_t num_selectors, const uint8_t* pSelectors_data, uint32_t selectors_data_size);
bool decode_tables(const uint8_t* pTable_data, uint32_t table_data_size);
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,
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,
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);
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,
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,
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)
{
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,
header.m_tex_type == cBASISTexTypeVideoFrames, (slice_desc.m_flags & cSliceDescFlagsHasAlpha) != 0, slice_desc.m_level_index,
slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels, pState,
astc_transcode_alpha,
pAlpha_blocks,
output_rows_in_pixels, decode_flags);
}
// Container independent transcoding
bool transcode_image(
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t rgb_offset, uint32_t rgb_length, uint64_t alpha_offset, uint32_t alpha_length,
uint32_t decode_flags = 0,
bool basis_file_has_alpha_slices = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0);
void clear()
{
m_local_endpoints.clear();
m_local_selectors.clear();
m_endpoint_pred_model.clear();
m_delta_endpoint_model.clear();
m_selector_model.clear();
m_selector_history_buf_rle_model.clear();
m_selector_history_buf_size = 0;
}
// Low-level methods
typedef basisu::vector<endpoint> endpoint_vec;
const endpoint_vec& get_endpoints() const { return m_local_endpoints; }
typedef basisu::vector<selector> selector_vec;
const selector_vec& get_selectors() const { return m_local_selectors; }
private:
const basisu_lowlevel_etc1s_transcoder* m_pGlobal_codebook;
endpoint_vec m_local_endpoints;
selector_vec m_local_selectors;
huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model;
uint32_t m_selector_history_buf_size;
basisu_transcoder_state m_def_state;
};
// UASTC LDR 4x4
class basisu_lowlevel_uastc_ldr_4x4_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_uastc_ldr_4x4_transcoder();
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,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
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,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
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, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
#if BASISD_SUPPORT_XUASTC
// XUASTC LDR 4x4-12x12 or ASTC LDR 4x4-12x12
struct xuastc_decoded_image
{
uint32_t m_actual_block_width = 0, m_actual_block_height = 0, m_actual_width = 0, m_actual_height = 0;
bool m_actual_has_alpha = false, m_uses_srgb_astc_decode_mode = false;
bool decode(const uint8_t* pImage_data, uint32_t image_data_size,
astc_ldr_t::xuastc_decomp_image_init_callback_ptr pInit_callback, void* pInit_callback_data,
astc_ldr_t::xuastc_decomp_image_block_callback_ptr pBlock_callback, void* pBlock_callback_data)
{
const bool decomp_flag = astc_ldr_t::xuastc_ldr_decompress_image(pImage_data, image_data_size,
m_actual_block_width, m_actual_block_height,
m_actual_width, m_actual_height,
m_actual_has_alpha, m_uses_srgb_astc_decode_mode, basisu::g_debug_printf,
pInit_callback, pInit_callback_data,
pBlock_callback, pBlock_callback_data);
return decomp_flag;
}
void clear()
{
m_actual_block_width = 0;
m_actual_block_height = 0;
m_actual_width = 0;
m_actual_height = 0;
m_actual_has_alpha = false;
m_uses_srgb_astc_decode_mode = false;
}
};
#endif
// This is both ASTC LDR 4x4-12x12 and XUASTC LDR 4x4-12x12.
class basisu_lowlevel_xuastc_ldr_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_xuastc_ldr_transcoder();
bool transcode_slice(basis_tex_format src_format, bool use_astc_srgb_decode_profile, void* pDst_blocks, uint32_t src_num_blocks_x, uint32_t src_num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
bool transcode_slice(basis_tex_format src_format, bool use_astc_srgb_decode_profile, void* pDst_blocks, uint32_t src_num_blocks_x, uint32_t src_num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
return transcode_slice(src_format, use_astc_srgb_decode_profile, pDst_blocks, src_num_blocks_x, src_num_blocks_y, pImage_data, image_data_size, fmt,
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,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
basis_tex_format src_format, bool use_astc_srgb_decode_profile,
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t src_num_blocks_x, uint32_t src_num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
// UASTC HDR 4x4
class basisu_lowlevel_uastc_hdr_4x4_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_uastc_hdr_4x4_transcoder();
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,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
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,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
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, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
// ASTC HDR 6x6
class basisu_lowlevel_astc_hdr_6x6_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_astc_hdr_6x6_transcoder();
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,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
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,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
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, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
// UASTC HDR 6x6 intermediate
class basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder();
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,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
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,
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,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
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, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t num_blocks_x, uint32_t num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
struct basisu_slice_info
{
// The image's ACTUAL dimensions in texels.
uint32_t m_orig_width;
uint32_t m_orig_height;
// The texture's dimensions in texels - always a multiple of the texture's underlying block size (4x4-12x12).
uint32_t m_width;
uint32_t m_height;
uint32_t m_num_blocks_x;
uint32_t m_num_blocks_y;
uint32_t m_total_blocks;
uint32_t m_block_width;
uint32_t m_block_height;
uint32_t m_compressed_size;
uint32_t m_slice_index; // the slice index in the .basis file
uint32_t m_image_index; // the source image index originally provided to the encoder
uint32_t m_level_index; // the mipmap level within this image
uint32_t m_unpacked_slice_crc16;
bool m_alpha_flag; // true if the slice has alpha data
bool m_iframe_flag; // true if the slice is an I-Frame
};
typedef basisu::vector<basisu_slice_info> basisu_slice_info_vec;
struct basisu_image_info
{
uint32_t m_image_index;
uint32_t m_total_levels;
// The image's ACTUAL dimensions in texels.
uint32_t m_orig_width;
uint32_t m_orig_height;
// The texture's dimensions in texels - always a multiple of the texture's underlying block size (4x4-12x12).
uint32_t m_width;
uint32_t m_height;
uint32_t m_block_width;
uint32_t m_block_height;
uint32_t m_num_blocks_x;
uint32_t m_num_blocks_y;
uint32_t m_total_blocks;
uint32_t m_first_slice_index;
bool m_alpha_flag; // true if the image has alpha data
bool m_iframe_flag; // true if the image is an I-Frame
};
struct basisu_image_level_info
{
uint32_t m_image_index;
uint32_t m_level_index;
uint32_t m_orig_width;
uint32_t m_orig_height;
uint32_t m_width;
uint32_t m_height;
uint32_t m_block_width;
uint32_t m_block_height;
uint32_t m_num_blocks_x;
uint32_t m_num_blocks_y;
uint32_t m_total_blocks;
uint32_t m_first_slice_index;
uint32_t m_rgb_file_ofs;
uint32_t m_rgb_file_len;
uint32_t m_alpha_file_ofs;
uint32_t m_alpha_file_len;
bool m_alpha_flag; // true if the image has alpha data
bool m_iframe_flag; // true if the image is an I-Frame
};
struct basisu_file_info
{
uint32_t m_version;
uint32_t m_total_header_size;
uint32_t m_total_selectors;
// will be 0 for UASTC or if the file uses global codebooks
uint32_t m_selector_codebook_ofs;
uint32_t m_selector_codebook_size;
uint32_t m_total_endpoints;
// will be 0 for UASTC or if the file uses global codebooks
uint32_t m_endpoint_codebook_ofs;
uint32_t m_endpoint_codebook_size;
uint32_t m_tables_ofs;
uint32_t m_tables_size;
uint32_t m_slices_size;
basis_texture_type m_tex_type;
uint32_t m_us_per_frame;
// Low-level slice information (1 slice per image for color-only basis files, 2 for alpha basis files)
basisu_slice_info_vec m_slice_info;
uint32_t m_total_images; // total # of images
basisu::vector<uint32_t> m_image_mipmap_levels; // the # of mipmap levels for each image
uint32_t m_userdata0;
uint32_t m_userdata1;
basis_tex_format m_tex_format; // ETC1S, UASTC, etc.
uint32_t m_block_width;
uint32_t m_block_height;
bool m_y_flipped; // true if the image was Y flipped
bool m_srgb; // true if the image is sRGB, false if linear
bool m_etc1s; // true if the file is ETC1S
bool m_has_alpha_slices; // true if the texture has alpha slices (for ETC1S: even slices RGB, odd slices alpha)
};
// 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.
// If you're just starting out this is the class you care about (or see the KTX2 transcoder below).
class basisu_transcoder
{
basisu_transcoder(basisu_transcoder&);
basisu_transcoder& operator= (const basisu_transcoder&);
public:
basisu_transcoder();
// Validates the .basis file. This computes a crc16 over the entire file, so it's slow.
bool validate_file_checksums(const void* pData, uint32_t data_size, bool full_validation) const;
// Quick header validation - no crc16 checks.
bool validate_header(const void* pData, uint32_t data_size) const;
basis_texture_type get_texture_type(const void* pData, uint32_t data_size) const;
bool get_userdata(const void* pData, uint32_t data_size, uint32_t& userdata0, uint32_t& userdata1) const;
// Returns the total number of images in the basis file (always 1 or more).
// Note that the number of mipmap levels for each image may differ, and that images may have different resolutions.
uint32_t get_total_images(const void* pData, uint32_t data_size) const;
basis_tex_format get_basis_tex_format(const void* pData, uint32_t data_size) const;
// Returns the number of mipmap levels in an image.
uint32_t get_total_image_levels(const void* pData, uint32_t data_size, uint32_t image_index) const;
// Returns basic information about an image. Note that orig_width/orig_height may not be a multiple of 4.
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;
// Returns information about the specified image.
bool get_image_info(const void* pData, uint32_t data_size, basisu_image_info& image_info, uint32_t image_index) const;
// Returns information about the specified image's mipmap level.
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;
// Get a description of the basis file and low-level information about each slice.
bool get_file_info(const void* pData, uint32_t data_size, basisu_file_info& file_info) const;
// start_transcoding() must be called before calling transcode_slice() or transcode_image_level().
// 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).
bool start_transcoding(const void* pData, uint32_t data_size);
bool stop_transcoding();
// Returns true if start_transcoding() has been called.
bool get_ready_to_transcode() const { return m_ready_to_transcode; }
// transcode_image_level() decodes a single mipmap level from the .basis file to any of the supported output texture formats.
// 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).
// If the .basis file doesn't have alpha slices, the output alpha blocks will be set to fully opaque (all 255's).
// Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2, due to PVRTC1 format requirements.
// 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.
// 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).
// 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).
// Notes:
// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.
// - 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
// a first pass, which will be read in a second pass.
bool transcode_image_level(
const void* pData, uint32_t data_size,
uint32_t image_index, uint32_t level_index,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
transcoder_texture_format fmt,
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;
// Finds the basis slice corresponding to the specified image/level/alpha params, or -1 if the slice can't be found.
int find_slice(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index, bool alpha_data) const;
// 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().
// 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).
// output_blocks_buf_size_in_blocks_or_pixels is just used for verification to make sure the output buffer is large enough.
// 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.
// output_block_stride_in_bytes: Number of bytes between each output block.
// 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).
// 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).
// Notes:
// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.
bool transcode_slice(const void* pData, uint32_t data_size, uint32_t slice_index,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
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,
uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1) const;
static void write_opaque_alpha_blocks(
uint32_t num_blocks_x, uint32_t num_blocks_y,
void* pOutput_blocks, block_format fmt,
uint32_t block_stride_in_bytes, uint32_t output_row_pitch_in_blocks_or_pixels);
void set_global_codebooks(const basisu_lowlevel_etc1s_transcoder* pGlobal_codebook) { m_lowlevel_etc1s_decoder.set_global_codebooks(pGlobal_codebook); }
const basisu_lowlevel_etc1s_transcoder* get_global_codebooks() const { return m_lowlevel_etc1s_decoder.get_global_codebooks(); }
const basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() const { return m_lowlevel_etc1s_decoder; }
basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() { return m_lowlevel_etc1s_decoder; }
const basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_ldr_4x4_decoder; }
basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_ldr_4x4_decoder; }
private:
mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;
mutable basisu_lowlevel_uastc_ldr_4x4_transcoder m_lowlevel_uastc_ldr_4x4_decoder;
mutable basisu_lowlevel_xuastc_ldr_transcoder m_lowlevel_xuastc_ldr_decoder;
mutable basisu_lowlevel_uastc_hdr_4x4_transcoder m_lowlevel_uastc_4x4_hdr_decoder;
mutable basisu_lowlevel_astc_hdr_6x6_transcoder m_lowlevel_astc_6x6_hdr_decoder;
mutable basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder m_lowlevel_astc_6x6_hdr_intermediate_decoder;
bool m_ready_to_transcode;
int find_first_slice_index(const void* pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const;
bool validate_header_quick(const void* pData, uint32_t data_size) const;
};
// basisu_transcoder_init() MUST be called before a .basis file can be transcoded.
void basisu_transcoder_init();
enum debug_flags_t
{
cDebugFlagVisCRs = 1,
cDebugFlagVisBC1Sels = 2,
cDebugFlagVisBC1Endpoints = 4
};
uint32_t get_debug_flags();
void set_debug_flags(uint32_t f);
// ------------------------------------------------------------------------------------------------------
// Optional .KTX2 file format support
// KTX2 reading optionally requires miniz or Zstd decompressors for supercompressed UASTC files.
// ------------------------------------------------------------------------------------------------------
#if BASISD_SUPPORT_KTX2
#pragma pack(push)
#pragma pack(1)
struct ktx2_header
{
uint8_t m_identifier[12];
basisu::packed_uint<4> m_vk_format;
basisu::packed_uint<4> m_type_size;
basisu::packed_uint<4> m_pixel_width;
basisu::packed_uint<4> m_pixel_height;
basisu::packed_uint<4> m_pixel_depth;
basisu::packed_uint<4> m_layer_count;
basisu::packed_uint<4> m_face_count;
basisu::packed_uint<4> m_level_count;
basisu::packed_uint<4> m_supercompression_scheme;
basisu::packed_uint<4> m_dfd_byte_offset;
basisu::packed_uint<4> m_dfd_byte_length;
basisu::packed_uint<4> m_kvd_byte_offset;
basisu::packed_uint<4> m_kvd_byte_length;
basisu::packed_uint<8> m_sgd_byte_offset;
basisu::packed_uint<8> m_sgd_byte_length;
};
struct ktx2_level_index
{
basisu::packed_uint<8> m_byte_offset;
basisu::packed_uint<8> m_byte_length;
basisu::packed_uint<8> m_uncompressed_byte_length;
};
struct ktx2_etc1s_global_data_header
{
basisu::packed_uint<2> m_endpoint_count;
basisu::packed_uint<2> m_selector_count;
basisu::packed_uint<4> m_endpoints_byte_length;
basisu::packed_uint<4> m_selectors_byte_length;
basisu::packed_uint<4> m_tables_byte_length;
basisu::packed_uint<4> m_extended_byte_length;
};
struct ktx2_etc1s_image_desc
{
basisu::packed_uint<4> m_image_flags;
basisu::packed_uint<4> m_rgb_slice_byte_offset;
basisu::packed_uint<4> m_rgb_slice_byte_length;
basisu::packed_uint<4> m_alpha_slice_byte_offset;
basisu::packed_uint<4> m_alpha_slice_byte_length;
};
// The initial v1.6 release (for backwards compatibility only with our older .KTX2 files)
struct ktx2_slice_offset_len_desc_orig
{
basisu::packed_uint<4> m_slice_byte_offset; // byte offset relative to the KTX2 mipmap level
basisu::packed_uint<4> m_slice_byte_length;
};
// The Khronos KTX2 spec standard
struct ktx2_slice_offset_len_desc_std
{
basisu::packed_uint<4> m_slice_byte_offset; // byte offset relative to the KTX2 mipmap level
basisu::packed_uint<4> m_slice_byte_length;
basisu::packed_uint<4> m_profile;
};
struct ktx2_animdata
{
basisu::packed_uint<4> m_duration;
basisu::packed_uint<4> m_timescale;
basisu::packed_uint<4> m_loopcount;
};
#pragma pack(pop)
const uint32_t KTX2_VK_FORMAT_UNDEFINED = 0;
// These are standard Vulkan texture VkFormat ID's, see https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFormat.html
const uint32_t KTX2_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000;
const uint32_t KTX2_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001;
const uint32_t KTX2_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002;
const uint32_t KTX2_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003;
const uint32_t KTX2_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004;
const uint32_t KTX2_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005;
const uint32_t KTX2_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006;
const uint32_t KTX2_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, KTX2_FORMAT_ASTC_4x4_SRGB_BLOCK = 158;
const uint32_t KTX2_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, KTX2_FORMAT_ASTC_5x4_SRGB_BLOCK = 160;
const uint32_t KTX2_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, KTX2_FORMAT_ASTC_5x5_SRGB_BLOCK = 162;
const uint32_t KTX2_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, KTX2_FORMAT_ASTC_6x5_SRGB_BLOCK = 164;
const uint32_t KTX2_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, KTX2_FORMAT_ASTC_6x6_SRGB_BLOCK = 166;
const uint32_t KTX2_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, KTX2_FORMAT_ASTC_8x5_SRGB_BLOCK = 168;
const uint32_t KTX2_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, KTX2_FORMAT_ASTC_8x6_SRGB_BLOCK = 170;
const uint32_t KTX2_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, KTX2_FORMAT_ASTC_10x5_SRGB_BLOCK = 174;
const uint32_t KTX2_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, KTX2_FORMAT_ASTC_10x6_SRGB_BLOCK = 176;
const uint32_t KTX2_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, KTX2_FORMAT_ASTC_8x8_SRGB_BLOCK = 172; // note the ASTC block size order is off in the vkFormat definitions
const uint32_t KTX2_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, KTX2_FORMAT_ASTC_10x8_SRGB_BLOCK = 178;
const uint32_t KTX2_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, KTX2_FORMAT_ASTC_10x10_SRGB_BLOCK = 180;
const uint32_t KTX2_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, KTX2_FORMAT_ASTC_12x10_SRGB_BLOCK = 182;
const uint32_t KTX2_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, KTX2_FORMAT_ASTC_12x12_SRGB_BLOCK = 184;
const uint32_t KTX2_KDF_DF_MODEL_ASTC = 162; // 0xA2
const uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163; // 0xA3
const uint32_t KTX2_KDF_DF_MODEL_UASTC_LDR_4X4 = 166; // 0xA6
const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_4X4 = 167; // 0xA7
const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_6X6_INTERMEDIATE = 168; // 0xA8, TODO - coordinate with Khronos on this
const uint32_t KTX2_KDF_DF_MODEL_XUASTC_LDR_INTERMEDIATE = 169; // 0xA9, TODO - coordinate with Khronos on this
const uint32_t KTX2_IMAGE_IS_P_FRAME = 2;
const uint32_t KTX2_UASTC_BLOCK_SIZE = 16; // also the block size for UASTC_HDR
const uint32_t KTX2_MAX_SUPPORTED_LEVEL_COUNT = 16; // this is an implementation specific constraint and can be increased
// The KTX2 transfer functions supported by KTX2
const uint32_t KTX2_KHR_DF_TRANSFER_LINEAR = 1;
const uint32_t KTX2_KHR_DF_TRANSFER_SRGB = 2;
enum ktx2_supercompression
{
KTX2_SS_NONE = 0,
KTX2_SS_BASISLZ = 1, // actually ETC1S
KTX2_SS_ZSTANDARD = 2,
KTX2_SS_DEFLATE = 3, // currently unsupported by us
KTX2_SS_UASTC_HDR_6x6I = 4, // UASTC HDR 6x6i (picked by Khronos, in KTX-Software as of 2/19/2026)
KTX2_SS_XUASTC_LDR = 5 // XUASTC LDR 4x4-12x12 (coordinate with Khronos, not in KTX-Software yet as of 2/19/2026)
};
extern const uint8_t g_ktx2_file_identifier[12];
enum ktx2_df_channel_id
{
KTX2_DF_CHANNEL_ETC1S_RGB = 0U,
KTX2_DF_CHANNEL_ETC1S_RRR = 3U,
KTX2_DF_CHANNEL_ETC1S_GGG = 4U,
KTX2_DF_CHANNEL_ETC1S_AAA = 15U,
KTX2_DF_CHANNEL_UASTC_DATA = 0U,
KTX2_DF_CHANNEL_UASTC_RGB = 0U,
KTX2_DF_CHANNEL_UASTC_RGBA = 3U,
KTX2_DF_CHANNEL_UASTC_RRR = 4U,
KTX2_DF_CHANNEL_UASTC_RRRG = 5U,
KTX2_DF_CHANNEL_UASTC_RG = 6U,
};
inline const char* ktx2_get_etc1s_df_channel_id_str(ktx2_df_channel_id id)
{
switch (id)
{
case KTX2_DF_CHANNEL_ETC1S_RGB: return "RGB";
case KTX2_DF_CHANNEL_ETC1S_RRR: return "RRR";
case KTX2_DF_CHANNEL_ETC1S_GGG: return "GGG";
case KTX2_DF_CHANNEL_ETC1S_AAA: return "AAA";
default: break;
}
return "?";
}
inline const char* ktx2_get_uastc_df_channel_id_str(ktx2_df_channel_id id)
{
switch (id)
{
case KTX2_DF_CHANNEL_UASTC_RGB: return "RGB";