-
Notifications
You must be signed in to change notification settings - Fork 2
/
tc_texture_load.h
1642 lines (1568 loc) · 69.5 KB
/
tc_texture_load.h
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
/*
* tc_texture_load.h: Texture image format loading.
*
* DEPENDS:
* VERSION: 0.0.2 (2018-06-30)
* LICENSE: CC0 & Boost (dual-licensed)
* AUTHOR: Tim Cas
* URL: https://github.com/darkuranium/tclib
*
* VERSION HISTORY:
* 0.0.2 fixed a crash when image height is set to 0 (thanks, American Fuzzy Lop!)
* 0.0.1 initial public release (DDS files only)
*
* TODOs:
* - Test, test, test, and then test some more!
* - Handle a lack of provided pitch or linear size.
* - More FourCC codes, more legacy DDS formats, et cetera.
* - KTX (Khronos Texture) support
* - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
* - ASTC support
* - https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_texture_compression_astc_hdr.txt
* - https://msdn.microsoft.com/en-us/library/windows/desktop/dn903790(v=vs.85).aspx
*
*
*
* A library for loading texture image formats, such as DDS.
*
* A single file should contain the following `#define` before including the header:
*
* #define TC_TEXTURE_LOAD_IMPLEMENTATION
* #include "tc_texture_load.h"
*
* The following `#define`s are optional, and should be used to enable the
* relevant conveniency functions:
*
* #define TC_TEXTURE_LOAD_VULKAN_FORMATS
* #define TC_TEXTURE_LOAD_OPENGL_FORMATS
* #define TC_TEXTURE_LOAD_DIRECT3D_FORMATS
*/
#ifndef TC_TEXTURE_LOAD_H_
#define TC_TEXTURE_LOAD_H_
#include <stdint.h>
#ifndef TC_TEXTURE_LOAD_NO_STDIO
#include <stdio.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef enum TCTex_InternalFormat
{
/* these map 1:1 to DDS in values (but might not always, so we keep a separate enum!) */
TCTEX_IFORMAT_UNDEFINED = 0,
TCTEX_IFORMAT_R32G32B32A32_TYPELESS = 1,
TCTEX_IFORMAT_R32G32B32A32_SFLOAT = 2,
TCTEX_IFORMAT_R32G32B32A32_UINT = 3,
TCTEX_IFORMAT_R32G32B32A32_SINT = 4,
TCTEX_IFORMAT_R32G32B32_TYPELESS = 5,
TCTEX_IFORMAT_R32G32B32_SFLOAT = 6,
TCTEX_IFORMAT_R32G32B32_UINT = 7,
TCTEX_IFORMAT_R32G32B32_SINT = 8,
TCTEX_IFORMAT_R16G16B16A16_TYPELESS = 9,
TCTEX_IFORMAT_R16G16B16A16_SFLOAT = 10,
TCTEX_IFORMAT_R16G16B16A16_UNORM = 11,
TCTEX_IFORMAT_R16G16B16A16_UINT = 12,
TCTEX_IFORMAT_R16G16B16A16_SNORM = 13,
TCTEX_IFORMAT_R16G16B16A16_SINT = 14,
TCTEX_IFORMAT_R32G32_TYPELESS = 15,
TCTEX_IFORMAT_R32G32_SFLOAT = 16,
TCTEX_IFORMAT_R32G32_UINT = 17,
TCTEX_IFORMAT_R32G32_SINT = 18,
TCTEX_IFORMAT_R32G8X24_TYPELESS = 19,
TCTEX_IFORMAT_D32_SFLOAT_S8X24_UINT = 20,
TCTEX_IFORMAT_R32_SFLOAT_X8X24_TYPELESS = 21,
TCTEX_IFORMAT_X32_TYPELESS_G8X24_UINT = 22,
TCTEX_IFORMAT_R10G10B10A2_TYPELESS = 23,
TCTEX_IFORMAT_R10G10B10A2_UNORM = 24,
TCTEX_IFORMAT_R10G10B10A2_UINT = 25,
TCTEX_IFORMAT_R11G11B10_SFLOAT = 26,
TCTEX_IFORMAT_R8G8B8A8_TYPELESS = 27,
TCTEX_IFORMAT_R8G8B8A8_UNORM = 28,
TCTEX_IFORMAT_R8G8B8A8_SRGB = 29,
TCTEX_IFORMAT_R8G8B8A8_UINT = 30,
TCTEX_IFORMAT_R8G8B8A8_SNORM = 31,
TCTEX_IFORMAT_R8G8B8A8_SINT = 32,
TCTEX_IFORMAT_R16G16_TYPELESS = 33,
TCTEX_IFORMAT_R16G16_SFLOAT = 34,
TCTEX_IFORMAT_R16G16_UNORM = 35,
TCTEX_IFORMAT_R16G16_UINT = 36,
TCTEX_IFORMAT_R16G16_SNORM = 37,
TCTEX_IFORMAT_R16G16_SINT = 38,
TCTEX_IFORMAT_R32_TYPELESS = 39,
TCTEX_IFORMAT_D32_SFLOAT = 40,
TCTEX_IFORMAT_R32_SFLOAT = 41,
TCTEX_IFORMAT_R32_UINT = 42,
TCTEX_IFORMAT_R32_SINT = 43,
TCTEX_IFORMAT_R24G8_TYPELESS = 44,
TCTEX_IFORMAT_D24_UNORM_S8_UINT = 45,
TCTEX_IFORMAT_R24_UNORM_X8_TYPELESS = 46,
TCTEX_IFORMAT_X24_TYPELESS_G8_UINT = 47,
TCTEX_IFORMAT_R8G8_TYPELESS = 48,
TCTEX_IFORMAT_R8G8_UNORM = 49,
TCTEX_IFORMAT_R8G8_UINT = 50,
TCTEX_IFORMAT_R8G8_SNORM = 51,
TCTEX_IFORMAT_R8G8_SINT = 52,
TCTEX_IFORMAT_R16_TYPELESS = 53,
TCTEX_IFORMAT_R16_SFLOAT = 54,
TCTEX_IFORMAT_D16_UNORM = 55,
TCTEX_IFORMAT_R16_UNORM = 56,
TCTEX_IFORMAT_R16_UINT = 57,
TCTEX_IFORMAT_R16_SNORM = 58,
TCTEX_IFORMAT_R16_SINT = 59,
TCTEX_IFORMAT_R8_TYPELESS = 60,
TCTEX_IFORMAT_R8_UNORM = 61,
TCTEX_IFORMAT_R8_UINT = 62,
TCTEX_IFORMAT_R8_SNORM = 63,
TCTEX_IFORMAT_R8_SINT = 64,
TCTEX_IFORMAT_A8_UNORM = 65,
TCTEX_IFORMAT_R1_UNORM = 66,
TCTEX_IFORMAT_R9G9B9E5_UFLOAT = 67,
TCTEX_IFORMAT_R8G8_B8G8_UNORM = 68,
TCTEX_IFORMAT_G8R8_G8B8_UNORM = 69,
TCTEX_IFORMAT_COMPRESSED_BC1_TYPELESS = 70,
TCTEX_IFORMAT_COMPRESSED_BC1_UNORM = 71,
TCTEX_IFORMAT_COMPRESSED_BC1_SRGB = 72,
TCTEX_IFORMAT_COMPRESSED_BC2_TYPELESS = 73,
TCTEX_IFORMAT_COMPRESSED_BC2_UNORM = 74,
TCTEX_IFORMAT_COMPRESSED_BC2_SRGB = 75,
TCTEX_IFORMAT_COMPRESSED_BC3_TYPELESS = 76,
TCTEX_IFORMAT_COMPRESSED_BC3_UNORM = 77,
TCTEX_IFORMAT_COMPRESSED_BC3_SRGB = 78,
TCTEX_IFORMAT_COMPRESSED_BC4_TYPELESS = 79,
TCTEX_IFORMAT_COMPRESSED_BC4_UNORM = 80,
TCTEX_IFORMAT_COMPRESSED_BC4_SNORM = 81,
TCTEX_IFORMAT_COMPRESSED_BC5_TYPELESS = 82,
TCTEX_IFORMAT_COMPRESSED_BC5_UNORM = 83,
TCTEX_IFORMAT_COMPRESSED_BC5_SNORM = 84,
TCTEX_IFORMAT_B5G6R5_UNORM = 85,
TCTEX_IFORMAT_B5G5R5A1_UNORM = 86,
TCTEX_IFORMAT_B8G8R8A8_UNORM = 87,
TCTEX_IFORMAT_B8G8R8X8_UNORM = 88,
TCTEX_IFORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
TCTEX_IFORMAT_B8G8R8A8_TYPELESS = 90,
TCTEX_IFORMAT_B8G8R8A8_SRGB = 91,
TCTEX_IFORMAT_B8G8R8X8_TYPELESS = 92,
TCTEX_IFORMAT_B8G8R8X8_SRGB = 93,
TCTEX_IFORMAT_COMPRESSED_BC6H_TYPELESS = 94,
TCTEX_IFORMAT_COMPRESSED_BC6H_UFLOAT = 95,
TCTEX_IFORMAT_COMPRESSED_BC6H_SFLOAT = 96,
TCTEX_IFORMAT_COMPRESSED_BC7_TYPELESS = 97,
TCTEX_IFORMAT_COMPRESSED_BC7_UNORM = 98,
TCTEX_IFORMAT_COMPRESSED_BC7_SRGB = 99,
TCTEX_IFORMAT_AYUV = 100,
TCTEX_IFORMAT_Y410 = 101,
TCTEX_IFORMAT_Y416 = 102,
TCTEX_IFORMAT_NV12 = 103,
TCTEX_IFORMAT_P010 = 104,
TCTEX_IFORMAT_P016 = 105,
TCTEX_IFORMAT_420_OPAQUE = 106,
TCTEX_IFORMAT_YUY2 = 107,
TCTEX_IFORMAT_Y210 = 108,
TCTEX_IFORMAT_Y216 = 109,
TCTEX_IFORMAT_NV11 = 110,
TCTEX_IFORMAT_AI44 = 111,
TCTEX_IFORMAT_IA44 = 112,
TCTEX_IFORMAT_P8 = 113,
TCTEX_IFORMAT_A8P8 = 114,
TCTEX_IFORMAT_B4G4R4A4_UNORM = 115,
TCTEX_IFORMAT_P208 = 130,
TCTEX_IFORMAT_V208 = 131,
TCTEX_IFORMAT_V408 = 132,
} TCTex_InternalFormat;
// TODO: rename these to descriptive once I figure out which is which (UP/DOWN/LEFT/RIGHT/...)
#define TCTEX_CUBE_FACE_POSX 0x01
#define TCTEX_CUBE_FACE_NEGX 0x02
#define TCTEX_CUBE_FACE_POSY 0x04
#define TCTEX_CUBE_FACE_NEGY 0x08
#define TCTEX_CUBE_FACE_POSZ 0x10
#define TCTEX_CUBE_FACE_NEGZ 0x20
// use it as: `(cubefaces.mask & TCTEX_CUBE_FACE_ALL) == TCTEX_CUBE_FACE_ALL`
#define TCTEX_CUBE_FACE_ALL (TCTEX_CUBE_FACE_POSX|TCTEX_CUBE_FACE_NEGX|TCTEX_CUBE_FACE_POSY|TCTEX_CUBE_FACE_NEGY|TCTEX_CUBE_FACE_POSZ|TCTEX_CUBE_FACE_NEGZ)
typedef enum TCTex_AlphaMode
{
/* these map 1:1 to DDS */
TCTEX_ALPHA_MODE_UNKNOWN = 0,
TCTEX_ALPHA_MODE_STRAIGHT = 1,
TCTEX_ALPHA_MODE_PREMULTIPLIED = 2,
TCTEX_ALPHA_MODE_OPAQUE = 3,
TCTEX_ALPHA_MODE_CUSTOM = 4,
} TCTex_AlphaMode;
typedef struct TCTex_Texture
{
const uint8_t* memory;
uint32_t offset0;
uint32_t nbytes;
struct { uint32_t x, y, z; } size;
struct { uint32_t y, z; } pitch;
uint32_t arraylen;
uint32_t nmiplevels;
uint8_t dimension; // 1-3 (for TEXTURE_nD)
struct
{
uint8_t num;
uint8_t mask;
} cubefaces;
uint8_t alphamode;
uint8_t isvolume: 1;
uint8_t :7;
TCTex_InternalFormat iformat;
const char* errmsg;
void* imem_; // internal!
} TCTex_Texture;
TCTex_Texture* tctex_load_mem(TCTex_Texture* tex, const void* data, size_t datalen);
#ifndef TC_TEXTURE_LOAD_NO_STDIO
TCTex_Texture* tctex_load_file(TCTex_Texture* tex, FILE* file);
TCTex_Texture* tctex_load_fname(TCTex_Texture* tex, const char* fname);
#endif
typedef struct TCTex_MipMapInfo
{
uint64_t offset;
uint32_t nbytes;
struct { uint32_t x, y, z; } size;
struct { uint32_t y, z; } pitch;
} TCTex_MipMapInfo;
// Get all the mipmaps for a texture at a specific array index.
// Cubemap order is: +X, -X, +Y, -Y, +Z, -Z.
uint32_t tctex_get_mipmaps(const TCTex_Texture* tex, TCTex_MipMapInfo* mipmaps, uint32_t maxmipmaps, uint32_t textureidx);
void tctex_close(TCTex_Texture* tex);
#ifdef TC_TEXTURE_LOAD_VULKAN_FORMATS
typedef struct TCTex_VK_FormatInfo
{
uint32_t format;
uint8_t is_approx: 1;
uint8_t padding0_: 7;
} TCTex_VK_FormatInfo;
TCTex_VK_FormatInfo tctex_vk_get_formatinfo(const TCTex_Texture* tex);
#endif
#ifdef TC_TEXTURE_LOAD_OPENGL_FORMATS
typedef struct TCTex_GL_FormatInfo
{
uint32_t baseInternalFormat;
uint32_t internalFormat;
uint32_t format;
uint32_t type;
uint16_t extensions;
uint8_t is_approx: 1;
uint8_t padding0_: 7;
} TCTex_GL_FormatInfo;
TCTex_GL_FormatInfo tctex_gl_get_formatinfo(const TCTex_Texture* tex);
#endif
#ifdef TC_TEXTURE_LOAD_DIRECT3D_FORMATS
typedef struct TCTex_D3D_FormatInfo
{
uint32_t dxgiFormat;
} TCTex_D3D_FormatInfo;
TCTex_D3D_FormatInfo tctex_d3d_get_formatinfo(const TCTex_Texture* tex);
#endif
#ifdef __cplusplus
}
#endif
#endif /* TC_TEXTURE_LOAD_H_ */
#ifdef TC_TEXTURE_LOAD_IMPLEMENTATION
#include <stdlib.h> // for malloc, free
#include <stdbool.h>
#ifndef TC_TEXTURE_LOAD_NO_STDIO
#include <string.h> // for strerror(int)
#include <errno.h> // for int errno
#define TC_FTELL(stream) ftell(stream)
#define TC_FSEEK(stream,offset,origin) fseek(stream,offset,origin)
typedef long int TC_foffset;
#endif
#ifndef TC__STATIC_OR_RUNTIME_ASSERT
#if __STDC_VERSION__ >= 201112L
#define TC__STATIC_OR_RUNTIME_ASSERT(x,msg) _Static_assert(x,msg)
#elif __cplusplus >= 201103L
#define TC__STATIC_OR_RUNTIME_ASSERT(x,msg) static_assert(x,msg)
#else /* oh well ... fallback to runtime! */
#include <assert.h>
#define TC__STATIC_OR_RUNTIME_ASSERT(x,msg) assert((x) && (msg))
#endif
#endif
#ifndef TC__STATIC_CAST
#ifdef __cplusplus
#define TC__STATIC_CAST(T,v) static_cast<T>(v)
#else
#define TC__STATIC_CAST(T,v) ((T)(v))
#endif
#endif /* TC__STATIC_CAST */
/* no cast done to preserve undefined function warnings in C */
#ifndef TC__VOID_CAST
#ifdef __cplusplus
#define TC__VOID_CAST(T,v) TC__STATIC_CAST(T,v)
#else
#define TC__VOID_CAST(T,v) (v)
#endif
#endif /* TC__VOID_CAST */
// TODO: clean this up.
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#ifndef TCTEX__FROM_LE16
static uint16_t tctex_i_from_le16(uint16_t x)
{
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return x;
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return (x >> 8) | (x << 8);
#else
#error "Undefined byte order!"
#endif
}
#define TCTEX__FROM_LE16(x) tctex_i_from_le16(x)
#endif
#endif
#ifndef TCTEX__FROM_LE32
static uint32_t tctex_i_from_le32(uint32_t x)
{
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return x;
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return ((uint32_t)TCTEX__FROM_LE16(x) << 16) | TCTEX__FROM_LE16(x >> 16);
#else
#error "Undefined byte order!"
#endif
}
#define TCTEX__FROM_LE32(x) tctex_i_from_le32(x)
#endif
typedef enum TCTex_DXGI_FORMAT {
TCTEX_DXGI_FORMAT_UNKNOWN = 0,
TCTEX_DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
TCTEX_DXGI_FORMAT_R32G32B32A32_FLOAT = 2,
TCTEX_DXGI_FORMAT_R32G32B32A32_UINT = 3,
TCTEX_DXGI_FORMAT_R32G32B32A32_SINT = 4,
TCTEX_DXGI_FORMAT_R32G32B32_TYPELESS = 5,
TCTEX_DXGI_FORMAT_R32G32B32_FLOAT = 6,
TCTEX_DXGI_FORMAT_R32G32B32_UINT = 7,
TCTEX_DXGI_FORMAT_R32G32B32_SINT = 8,
TCTEX_DXGI_FORMAT_R16G16B16A16_TYPELESS = 9,
TCTEX_DXGI_FORMAT_R16G16B16A16_FLOAT = 10,
TCTEX_DXGI_FORMAT_R16G16B16A16_UNORM = 11,
TCTEX_DXGI_FORMAT_R16G16B16A16_UINT = 12,
TCTEX_DXGI_FORMAT_R16G16B16A16_SNORM = 13,
TCTEX_DXGI_FORMAT_R16G16B16A16_SINT = 14,
TCTEX_DXGI_FORMAT_R32G32_TYPELESS = 15,
TCTEX_DXGI_FORMAT_R32G32_FLOAT = 16,
TCTEX_DXGI_FORMAT_R32G32_UINT = 17,
TCTEX_DXGI_FORMAT_R32G32_SINT = 18,
TCTEX_DXGI_FORMAT_R32G8X24_TYPELESS = 19,
TCTEX_DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20,
TCTEX_DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
TCTEX_DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22,
TCTEX_DXGI_FORMAT_R10G10B10A2_TYPELESS = 23,
TCTEX_DXGI_FORMAT_R10G10B10A2_UNORM = 24,
TCTEX_DXGI_FORMAT_R10G10B10A2_UINT = 25,
TCTEX_DXGI_FORMAT_R11G11B10_FLOAT = 26,
TCTEX_DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
TCTEX_DXGI_FORMAT_R8G8B8A8_UNORM = 28,
TCTEX_DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
TCTEX_DXGI_FORMAT_R8G8B8A8_UINT = 30,
TCTEX_DXGI_FORMAT_R8G8B8A8_SNORM = 31,
TCTEX_DXGI_FORMAT_R8G8B8A8_SINT = 32,
TCTEX_DXGI_FORMAT_R16G16_TYPELESS = 33,
TCTEX_DXGI_FORMAT_R16G16_FLOAT = 34,
TCTEX_DXGI_FORMAT_R16G16_UNORM = 35,
TCTEX_DXGI_FORMAT_R16G16_UINT = 36,
TCTEX_DXGI_FORMAT_R16G16_SNORM = 37,
TCTEX_DXGI_FORMAT_R16G16_SINT = 38,
TCTEX_DXGI_FORMAT_R32_TYPELESS = 39,
TCTEX_DXGI_FORMAT_D32_FLOAT = 40,
TCTEX_DXGI_FORMAT_R32_FLOAT = 41,
TCTEX_DXGI_FORMAT_R32_UINT = 42,
TCTEX_DXGI_FORMAT_R32_SINT = 43,
TCTEX_DXGI_FORMAT_R24G8_TYPELESS = 44,
TCTEX_DXGI_FORMAT_D24_UNORM_S8_UINT = 45,
TCTEX_DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46,
TCTEX_DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47,
TCTEX_DXGI_FORMAT_R8G8_TYPELESS = 48,
TCTEX_DXGI_FORMAT_R8G8_UNORM = 49,
TCTEX_DXGI_FORMAT_R8G8_UINT = 50,
TCTEX_DXGI_FORMAT_R8G8_SNORM = 51,
TCTEX_DXGI_FORMAT_R8G8_SINT = 52,
TCTEX_DXGI_FORMAT_R16_TYPELESS = 53,
TCTEX_DXGI_FORMAT_R16_FLOAT = 54,
TCTEX_DXGI_FORMAT_D16_UNORM = 55,
TCTEX_DXGI_FORMAT_R16_UNORM = 56,
TCTEX_DXGI_FORMAT_R16_UINT = 57,
TCTEX_DXGI_FORMAT_R16_SNORM = 58,
TCTEX_DXGI_FORMAT_R16_SINT = 59,
TCTEX_DXGI_FORMAT_R8_TYPELESS = 60,
TCTEX_DXGI_FORMAT_R8_UNORM = 61,
TCTEX_DXGI_FORMAT_R8_UINT = 62,
TCTEX_DXGI_FORMAT_R8_SNORM = 63,
TCTEX_DXGI_FORMAT_R8_SINT = 64,
TCTEX_DXGI_FORMAT_A8_UNORM = 65,
TCTEX_DXGI_FORMAT_R1_UNORM = 66,
TCTEX_DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67,
TCTEX_DXGI_FORMAT_R8G8_B8G8_UNORM = 68,
TCTEX_DXGI_FORMAT_G8R8_G8B8_UNORM = 69,
TCTEX_DXGI_FORMAT_BC1_TYPELESS = 70,
TCTEX_DXGI_FORMAT_BC1_UNORM = 71,
TCTEX_DXGI_FORMAT_BC1_UNORM_SRGB = 72,
TCTEX_DXGI_FORMAT_BC2_TYPELESS = 73,
TCTEX_DXGI_FORMAT_BC2_UNORM = 74,
TCTEX_DXGI_FORMAT_BC2_UNORM_SRGB = 75,
TCTEX_DXGI_FORMAT_BC3_TYPELESS = 76,
TCTEX_DXGI_FORMAT_BC3_UNORM = 77,
TCTEX_DXGI_FORMAT_BC3_UNORM_SRGB = 78,
TCTEX_DXGI_FORMAT_BC4_TYPELESS = 79,
TCTEX_DXGI_FORMAT_BC4_UNORM = 80,
TCTEX_DXGI_FORMAT_BC4_SNORM = 81,
TCTEX_DXGI_FORMAT_BC5_TYPELESS = 82,
TCTEX_DXGI_FORMAT_BC5_UNORM = 83,
TCTEX_DXGI_FORMAT_BC5_SNORM = 84,
TCTEX_DXGI_FORMAT_B5G6R5_UNORM = 85,
TCTEX_DXGI_FORMAT_B5G5R5A1_UNORM = 86,
TCTEX_DXGI_FORMAT_B8G8R8A8_UNORM = 87,
TCTEX_DXGI_FORMAT_B8G8R8X8_UNORM = 88,
TCTEX_DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
TCTEX_DXGI_FORMAT_B8G8R8A8_TYPELESS = 90,
TCTEX_DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91,
TCTEX_DXGI_FORMAT_B8G8R8X8_TYPELESS = 92,
TCTEX_DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93,
TCTEX_DXGI_FORMAT_BC6H_TYPELESS = 94,
TCTEX_DXGI_FORMAT_BC6H_UF16 = 95,
TCTEX_DXGI_FORMAT_BC6H_SF16 = 96,
TCTEX_DXGI_FORMAT_BC7_TYPELESS = 97,
TCTEX_DXGI_FORMAT_BC7_UNORM = 98,
TCTEX_DXGI_FORMAT_BC7_UNORM_SRGB = 99,
TCTEX_DXGI_FORMAT_AYUV = 100,
TCTEX_DXGI_FORMAT_Y410 = 101,
TCTEX_DXGI_FORMAT_Y416 = 102,
TCTEX_DXGI_FORMAT_NV12 = 103,
TCTEX_DXGI_FORMAT_P010 = 104,
TCTEX_DXGI_FORMAT_P016 = 105,
TCTEX_DXGI_FORMAT_420_OPAQUE = 106,
TCTEX_DXGI_FORMAT_YUY2 = 107,
TCTEX_DXGI_FORMAT_Y210 = 108,
TCTEX_DXGI_FORMAT_Y216 = 109,
TCTEX_DXGI_FORMAT_NV11 = 110,
TCTEX_DXGI_FORMAT_AI44 = 111,
TCTEX_DXGI_FORMAT_IA44 = 112,
TCTEX_DXGI_FORMAT_P8 = 113,
TCTEX_DXGI_FORMAT_A8P8 = 114,
TCTEX_DXGI_FORMAT_B4G4R4A4_UNORM = 115,
TCTEX_DXGI_FORMAT_P208 = 130,
TCTEX_DXGI_FORMAT_V208 = 131,
TCTEX_DXGI_FORMAT_V408 = 132,
TCTEX_DXGI_FORMAT_MAX_ = 132,
} TCTex_DXGI_FORMAT;
// TC_DDS_PIXELFORMAT.dwFlags
#define TCTEX_DDPF_ALPHAPIXELS 0x1 // contains alpha data; dwRGBAlphaBitMask contains valid data
#define TCTEX_DDPF_ALPHA 0x2 // (older DDS) alpha channel only uncompressed; dwRGBBitCount contains bitcount, dwABitMask contains valid data
#define TCTEX_DDPF_FOURCC 0x4 // compressed RGB; dwFourCC contains valid data
#define TCTEX_DDPF_RGB 0x40 // uncompressed RGB; dwRGBBitCount and the RGB masks (dwRBitMask, dwGBitMask, dwBBitMask) contain valid data.
#define TCTEX_DDPF_YUV 0x200 // (older DDS) YUV uncompressed; dwRGBBitCount=>bit count, dwRBitMask=>Y mask, dwGBitMask=>U mask, dwBBitMask=>V mask
#define TCTEX_DDPF_LUMINANCE 0x20000 // (older DDS) single channel uncompressed; dwRGBBitCount=>bit count; dwRBitMask=>mask; can be combined with DDPF_ALPHAPIXELS for 2 channels
// TC_DDS_HEADER.dwFlags | NOTE: Do not rely on TC_DDSD_CAPS / TC_DDSD_PIXELFORMAT / TC_DDSD_MIPMAPCOUNT when reading!
#define TCTEX_DDSD_CAPS 0x1 // required
#define TCTEX_DDSD_HEIGHT 0x2 // required
#define TCTEX_DDSD_WIDTH 0x4 // required
#define TCTEX_DDSD_PITCH 0x8 // required when pitch provided for uncompressed
#define TCTEX_DDSD_PIXELFORMAT 0x1000 // required
#define TCTEX_DDSD_MIPMAPCOUNT 0x20000 // required when mipmapped texture
#define TCTEX_DDSD_LINEARSIZE 0x80000 // required when pitch provided for compressed
#define TCTEX_DDSD_DEPTH 0x800000 // required when depth texture
// TC_DDS_HEADER.dwCaps | NOTE: Do not rely on TC_DDSCAPS_COMPLEX / TC_DDSCAPS_TEXTURE when reading!
#define TCTEX_DDSCAPS_COMPLEX 0x8 // required when file contains more than 1 surface (mipmap, cubemap, or mipmapped volume texture)
#define TCTEX_DDSCAPS_MIPMAP 0x400000 // required when mipmap
#define TCTEX_DDSCAPS_TEXTURE 0x1000 // required
// TC_DDS_HEADER.dwCaps2
#define TCTEX_DDSCAPS2_CUBEMAP 0x200 // required when cubemap
#define TCTEX_DDSCAPS2_CUBEMAP_POSITIVEX 0x400 // required when stored in cubemap
#define TCTEX_DDSCAPS2_CUBEMAP_NEGATIVEX 0x800
#define TCTEX_DDSCAPS2_CUBEMAP_POSITIVEY 0x1000
#define TCTEX_DDSCAPS2_CUBEMAP_NEGATIVEY 0x2000
#define TCTEX_DDSCAPS2_CUBEMAP_POSITIVEZ 0x4000
#define TCTEX_DDSCAPS2_CUBEMAP_NEGATIVEZ 0x8000
#define TCTEX_DDSCAPS2_VOLUME 0x200000 // required when volume texture
// undocumented stuff
#define TCTEX_DDPF_BUMPDUDV 0x80000 //
// TC_DDS_HEADER_DXT10.resourceDimension
typedef enum TCTex_D3D10_RESOURCE_DIMENSION {
TCTEX_D3D10_RESOURCE_DIMENSION_UNKNOWN = 0,
TCTEX_D3D10_RESOURCE_DIMENSION_BUFFER = 1,
TCTEX_D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2,
TCTEX_D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3,
TCTEX_D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4
} TCTex_D3D10_RESOURCE_DIMENSION;
#define TCTEX_DDS_DIMENSION_TEXTURE1D TCTEX_D3D10_RESOURCE_DIMENSION_TEXTURE1D
#define TCTEX_DDS_DIMENSION_TEXTURE2D TCTEX_D3D10_RESOURCE_DIMENSION_TEXTURE2D
#define TCTEX_DDS_DIMENSION_TEXTURE3D TCTEX_D3D10_RESOURCE_DIMENSION_TEXTURE3D
// TC_DDS_HEADER_DXT10.miscFlag
#define TCTEX_DDS_RESOURCE_MISC_TEXTURECUBE 0x4
// TC_DDS_HEADER_DXT10.miscFlags2
#define TCTEX_DDS_ALPHA_MODE_UNKNOWN 0x0
#define TCTEX_DDS_ALPHA_MODE_STRAIGHT 0x1
#define TCTEX_DDS_ALPHA_MODE_PREMULTIPLIED 0x2
#define TCTEX_DDS_ALPHA_MODE_OPAQUE 0x3
#define TCTEX_DDS_ALPHA_MODE_CUSTOM 0x4
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb943982(v=vs.85).aspx
typedef struct TCTex_DDS_PIXELFORMAT
{
uint32_t dwSize;
uint32_t dwFlags;
uint32_t dwFourCC; // basic: DXT1, DXT2, DXT3, DXT4, DXT5; extended: DX10
uint32_t dwRGBBitCount;
uint32_t dwRBitMask;
uint32_t dwGBitMask;
uint32_t dwBBitMask;
uint32_t dwABitMask;
} TCTex_DDS_PIXELFORMAT;
typedef struct TCTex_DDS_HEADER
{
uint32_t dwSize;
uint32_t dwFlags;
uint32_t dwHeight;
uint32_t dwWidth;
uint32_t dwPitchOrLinearSize;
uint32_t dwDepth;
uint32_t dwMipMapCount;
uint32_t dwReserved1[11];
TCTex_DDS_PIXELFORMAT ddspf;
uint32_t dwCaps;
uint32_t dwCaps2;
uint32_t dwCaps3;
uint32_t dwCaps4;
uint32_t dwReserved2;
} TCTex_DDS_HEADER;
typedef struct TCTex_DDS_HEADER_DXT10
{
uint32_t dxgiFormat;
uint32_t resourceDimension;
uint32_t miscFlag;
uint32_t arraySize;
uint32_t miscFlags2;
} TCTex_DDS_HEADER_DXT10;
static uint32_t tctex_i_max32u(uint32_t a, uint32_t b)
{
return a > b ? a : b;
}
static uint32_t tctex_i_min32u(uint32_t a, uint32_t b)
{
return a < b ? a : b;
}
#define TCTEX_I_RETERROR(tex,msg) do { (tex)->errmsg = msg; return NULL; } while(0)
// source: bit twiddling hacks (https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightParallel)
static uint8_t tctex_i_count_trailing0(uint32_t v)
{
uint8_t c = 32;
v &= -v;
if(v) c--;
if(v & 0x0000FFFF) c -= 16;
if(v & 0x00FF00FF) c -= 8;
if(v & 0x0F0F0F0F) c -= 4;
if(v & 0x33333333) c -= 2;
if(v & 0x55555555) c -= 1;
return c;
}
static uint8_t tctex_i_count_trailing1(uint32_t v)
{
return tctex_i_count_trailing0(~v);
}
static void tctex_i_dds_get_cmask_info(uint8_t* shift, uint8_t* nbits, uint32_t mask)
{
uint8_t cshift = tctex_i_count_trailing0(mask);
if(cshift == 32)
{
*shift = *nbits = 0;
return;
}
mask >>= cshift;
uint8_t cnbits = tctex_i_count_trailing1(mask);
// this tests if the bits are consecutive (no holes!)
if((1U << cnbits) - 1U != mask)
{
*shift = 32;
*nbits = 0;
return;
}
*shift = cshift;
*nbits = cnbits;
}
#define TCTEX_I_DDS_CHECKMASK_BGRA(shift,nbits,B,G,R,A) ( \
(nbits)[0]==(R) && (!(R)||(shift)[0]==(B)+(G)) \
&& (nbits)[1]==(G) && (!(G)||(shift)[1]==(B)) \
&& (nbits)[2]==(B) && (!(B)||(shift)[2]==0) \
&& (nbits)[3]==(A) && (!(A)||(shift)[3]==(B)+(G)+(R)) \
)
#define TCTEX_I_DDS_CHECKMASK_RGBA(shift,nbits,R,G,B,A) ( \
(nbits)[0]==(R) && (!(R)||(shift)[0]==0) \
&& (nbits)[1]==(G) && (!(G)||(shift)[1]==(R)) \
&& (nbits)[2]==(B) && (!(B)||(shift)[2]==(R)+(G)) \
&& (nbits)[3]==(A) && (!(A)||(shift)[3]==(R)+(G)+(B)) \
)
static TCTex_Texture* tctex_i_dds_handle_pixel_format(TCTex_Texture* tex, const TCTex_DDS_PIXELFORMAT* pformat, bool* hasDX10)
{
TC__STATIC_OR_RUNTIME_ASSERT(sizeof(TCTex_DDS_PIXELFORMAT) == 32, "Invalid size of DDS_PIXELFORMAT structure");
if(TCTEX__FROM_LE32(pformat->dwSize) != sizeof(TCTex_DDS_PIXELFORMAT)) TCTEX_I_RETERROR(tex, "Invalid DDS pixel format");
uint32_t dwFlags = TCTEX__FROM_LE32(pformat->dwFlags);
uint32_t dwFourCC;
uint32_t dwRGBBitCount;
uint8_t shift[4], nbits[4];
size_t i;
*hasDX10 = false;
switch(dwFlags & (TCTEX_DDPF_ALPHA | TCTEX_DDPF_FOURCC | TCTEX_DDPF_RGB | TCTEX_DDPF_YUV | TCTEX_DDPF_LUMINANCE | TCTEX_DDPF_BUMPDUDV))
{
case TCTEX_DDPF_ALPHA:
tex->alphamode = TCTEX_ALPHA_MODE_STRAIGHT;
switch(TCTEX__FROM_LE32(pformat->dwRGBBitCount))
{
case 8:
if(TCTEX__FROM_LE32(pformat->dwABitMask) != 0xFF) TCTEX_I_RETERROR(tex, "[TODO] UNHANDLED: Unknown alpha bit mask");
tex->iformat = TCTEX_IFORMAT_A8_UNORM;
break;
default: TCTEX_I_RETERROR(tex, "[TODO] UNHANDLED: Unknown alpha bit count");
}
break;
case TCTEX_DDPF_FOURCC:
dwFourCC = TCTEX__FROM_LE32(pformat->dwFourCC);
switch(dwFourCC)
{
case 0x30315844U: // "DX10"
*hasDX10 = true;
break;
case 0x32495441U: // "ATI2"
case 0x55354342U: // "BC5U"
tex->iformat = TCTEX_IFORMAT_COMPRESSED_BC5_UNORM;
break;
case 0x53354342U: // "BC5S"
tex->iformat = TCTEX_IFORMAT_COMPRESSED_BC5_SNORM;
break;
case 0x31495441U: // "ATI1"
case 0x55344342U: // "BC4U"
tex->iformat = TCTEX_IFORMAT_COMPRESSED_BC4_UNORM;
break;
case 0x53344342U: // "BC4S"
tex->iformat = TCTEX_IFORMAT_COMPRESSED_BC4_SNORM;
break;
case 0x35545844U: // "DXT5"
tex->iformat = TCTEX_IFORMAT_COMPRESSED_BC3_UNORM;
tex->alphamode = TCTEX_ALPHA_MODE_STRAIGHT;
break;
case 0x34545844U: // "DXT4"
tex->iformat = TCTEX_IFORMAT_COMPRESSED_BC3_UNORM;
tex->alphamode = TCTEX_ALPHA_MODE_PREMULTIPLIED;
break;
case 0x33545844U: // "DXT3"
tex->iformat = TCTEX_IFORMAT_COMPRESSED_BC2_UNORM;
tex->alphamode = TCTEX_ALPHA_MODE_STRAIGHT;
break;
case 0x32545844U: // "DXT2"
tex->iformat = TCTEX_IFORMAT_COMPRESSED_BC2_UNORM;
tex->alphamode = TCTEX_ALPHA_MODE_PREMULTIPLIED;
break;
case 0x31545844U: // "DXT1"
tex->iformat = TCTEX_IFORMAT_COMPRESSED_BC1_UNORM;
tex->alphamode = TCTEX_ALPHA_MODE_PREMULTIPLIED;
break;
case 0x47424752U: // "RGBG"
tex->iformat = TCTEX_IFORMAT_R8G8_B8G8_UNORM;
break;
case 0x42475247U: // "GRGB"
tex->iformat = TCTEX_IFORMAT_G8R8_G8B8_UNORM;
break;
case 0x32595559: // "YUY2"
tex->iformat = TCTEX_IFORMAT_YUY2;
break;
case 0x00000074U: // "t\0\0\0"
tex->iformat = TCTEX_IFORMAT_R32G32B32A32_SFLOAT;
break;
case 0x00000073U: // "s\0\0\0"
tex->iformat = TCTEX_IFORMAT_R32G32_SFLOAT;
break;
case 0x00000072U: // "r\0\0\0"
tex->iformat = TCTEX_IFORMAT_R32_SFLOAT;
break;
case 0x00000071U: // "q\0\0\0"
tex->iformat = TCTEX_IFORMAT_R16G16B16A16_SFLOAT;
break;
case 0x00000070U: // "p\0\0\0"
tex->iformat = TCTEX_IFORMAT_R16G16_SFLOAT;
break;
case 0x0000006FU: // "o\0\0\0"
tex->iformat = TCTEX_IFORMAT_R16_SFLOAT;
break;
case 0x0000006EU: // "N\0\0\0"
tex->iformat = TCTEX_IFORMAT_R16G16B16A16_SNORM;
break;
case 0x00000024U: // "$\0\0\0"
tex->iformat = TCTEX_IFORMAT_R16G16B16A16_UNORM;
break;
default: printf("FourCC %.4s 0x%.8X\n", (const char*)&dwFourCC, dwFourCC); TCTEX_I_RETERROR(tex, "Unknown DDS FourCC code");
}
break;
case TCTEX_DDPF_RGB:
dwRGBBitCount = TCTEX__FROM_LE32(pformat->dwRGBBitCount);
tctex_i_dds_get_cmask_info(&shift[0], &nbits[0], TCTEX__FROM_LE32(pformat->dwRBitMask));
tctex_i_dds_get_cmask_info(&shift[1], &nbits[1], TCTEX__FROM_LE32(pformat->dwGBitMask));
tctex_i_dds_get_cmask_info(&shift[2], &nbits[2], TCTEX__FROM_LE32(pformat->dwBBitMask));
if(dwFlags & TCTEX_DDPF_ALPHAPIXELS)
tctex_i_dds_get_cmask_info(&shift[3], &nbits[3], TCTEX__FROM_LE32(pformat->dwABitMask));
else
shift[3] = nbits[3] = 0;
for(i = 0; i < 4; i++)
if(shift[i] == 32) TCTEX_I_RETERROR(tex, "Invalid DDS channel masks");
switch(dwRGBBitCount)
{
case 0: TCTEX_I_RETERROR(tex, "Invalid DDS pixel bit count");
case 16:
if(TCTEX_I_DDS_CHECKMASK_BGRA(shift,nbits,5,6,5,0)) tex->iformat = TCTEX_IFORMAT_B5G6R5_UNORM;
else if(TCTEX_I_DDS_CHECKMASK_BGRA(shift,nbits,5,5,5,1)) tex->iformat = TCTEX_IFORMAT_B5G5R5A1_UNORM;
else if(TCTEX_I_DDS_CHECKMASK_BGRA(shift,nbits,4,4,4,4)) tex->iformat = TCTEX_IFORMAT_B4G4R4A4_UNORM;
break;
case 32:
if(TCTEX_I_DDS_CHECKMASK_RGBA(shift,nbits,8,8,8,8)) tex->iformat = TCTEX_IFORMAT_R8G8B8A8_UNORM;
else if(TCTEX_I_DDS_CHECKMASK_RGBA(shift,nbits,16,16,0,0)) tex->iformat = TCTEX_IFORMAT_R16G16_UNORM;
else if(TCTEX_I_DDS_CHECKMASK_BGRA(shift,nbits,8,8,8,8)) tex->iformat = TCTEX_IFORMAT_B8G8R8A8_UNORM;
else if(TCTEX_I_DDS_CHECKMASK_BGRA(shift,nbits,8,8,8,0)) tex->iformat = TCTEX_IFORMAT_B8G8R8X8_UNORM;
break;
}
if(!tex->iformat) TCTEX_I_RETERROR(tex, "Unknown DDS channel masks (cannot convert to enum)");
break;
case TCTEX_DDPF_YUV: TCTEX_I_RETERROR(tex, "[TODO] UNHANDLED: YUV pixel format");
case TCTEX_DDPF_LUMINANCE:
dwRGBBitCount = TCTEX__FROM_LE32(pformat->dwRGBBitCount);
tctex_i_dds_get_cmask_info(&shift[0], &nbits[0], TCTEX__FROM_LE32(pformat->dwRBitMask));
if(dwFlags & TCTEX_DDPF_ALPHAPIXELS)
tctex_i_dds_get_cmask_info(&shift[1], &nbits[1], TCTEX__FROM_LE32(pformat->dwABitMask));
else
shift[1] = nbits[1] = 0;
for(i = 0; i < 4; i++)
if(shift[i] == 32) TCTEX_I_RETERROR(tex, "Invalid DDS channel masks");
switch(dwRGBBitCount)
{
case 0: TCTEX_I_RETERROR(tex, "Invalid DDS pixel bit count");
case 8:
if(TCTEX_I_DDS_CHECKMASK_RGBA(shift,nbits,8,0,0,0)) tex->iformat = TCTEX_IFORMAT_R8_UNORM;
break;
case 16:
if(TCTEX_I_DDS_CHECKMASK_RGBA(shift,nbits,8,8,0,0)) tex->iformat = TCTEX_IFORMAT_R8G8_UNORM;
else if(TCTEX_I_DDS_CHECKMASK_RGBA(shift,nbits,16,0,0,0)) tex->iformat = TCTEX_IFORMAT_R16_UNORM;
break;
}
if(!tex->iformat) TCTEX_I_RETERROR(tex, "Unknown DDS channel masks (cannot convert to enum)");
break;
case TCTEX_DDPF_BUMPDUDV:
dwRGBBitCount = TCTEX__FROM_LE32(pformat->dwRGBBitCount);
tctex_i_dds_get_cmask_info(&shift[0], &nbits[0], TCTEX__FROM_LE32(pformat->dwRBitMask));
tctex_i_dds_get_cmask_info(&shift[1], &nbits[1], TCTEX__FROM_LE32(pformat->dwGBitMask));
tctex_i_dds_get_cmask_info(&shift[2], &nbits[2], TCTEX__FROM_LE32(pformat->dwBBitMask));
tctex_i_dds_get_cmask_info(&shift[3], &nbits[3], TCTEX__FROM_LE32(pformat->dwABitMask));
for(i = 0; i < 4; i++)
if(shift[i] == 32) TCTEX_I_RETERROR(tex, "Invalid DDS channel masks");
switch(dwRGBBitCount)
{
case 0: TCTEX_I_RETERROR(tex, "Invalid DDS pixel bit count");
case 16:
if(TCTEX_I_DDS_CHECKMASK_RGBA(shift,nbits,8,8,0,0)) tex->iformat = TCTEX_IFORMAT_R8G8_SNORM;
break;
case 32:
if(TCTEX_I_DDS_CHECKMASK_RGBA(shift,nbits,8,8,8,8)) tex->iformat = TCTEX_IFORMAT_R8G8B8A8_SNORM;
else if(TCTEX_I_DDS_CHECKMASK_RGBA(shift,nbits,16,16,0,0)) tex->iformat = TCTEX_IFORMAT_R16G16_SNORM;
break;
}
if(!tex->iformat) TCTEX_I_RETERROR(tex, "Unknown DDS channel masks (cannot convert to enum)");
break;
default:
TCTEX_I_RETERROR(tex, "Invalid DDS pixel format (conflicting flags)");
}
return tex;
}
static TCTex_Texture* tctex_i_dds_handle_header10(TCTex_Texture* tex, const TCTex_DDS_HEADER_DXT10* header10)
{
uint32_t dxgiFormat = TCTEX__FROM_LE32(header10->dxgiFormat);
if(dxgiFormat > TCTEX_DXGI_FORMAT_MAX_) TCTEX_I_RETERROR(tex, "Invalid DDS file (unknown internal format)");
tex->iformat = dxgiFormat;
switch(TCTEX__FROM_LE32(header10->resourceDimension))
{
case TCTEX_DDS_DIMENSION_TEXTURE1D: tex->dimension = 1; break;
case TCTEX_DDS_DIMENSION_TEXTURE2D: tex->dimension = 2; break;
case TCTEX_DDS_DIMENSION_TEXTURE3D: tex->dimension = 3; break;
default: TCTEX_I_RETERROR(tex, "Invalid DDS file (unknown or missing resource dimension)");
}
uint32_t miscFlag = TCTEX__FROM_LE32(header10->miscFlag);
if(miscFlag & TCTEX_DDS_RESOURCE_MISC_TEXTURECUBE)
{
if(!tex->cubefaces.num) TCTEX_I_RETERROR(tex, "Invalid DDS file (cubemap with no defined cubemap faces)");
if(tex->dimension != 2) TCTEX_I_RETERROR(tex, "Invalid DDS file (cubemap with dimension != 2)");
}
tex->arraylen = TCTEX__FROM_LE32(header10->arraySize);
if(tex->dimension == 3 && tex->arraylen != 1) TCTEX_I_RETERROR(tex, "Invalid DDS file (arrays of 3D textures are not permitted)");
uint32_t miscFlags2 = TCTEX__FROM_LE32(header10->miscFlags2);
uint32_t alphaMode = miscFlags2 & 0x7;
switch(alphaMode)
{
case TCTEX_DDS_ALPHA_MODE_UNKNOWN:
case TCTEX_DDS_ALPHA_MODE_STRAIGHT:
case TCTEX_DDS_ALPHA_MODE_PREMULTIPLIED:
case TCTEX_DDS_ALPHA_MODE_OPAQUE:
case TCTEX_DDS_ALPHA_MODE_CUSTOM:
tex->alphamode = alphaMode;
break;
default: TCTEX_I_RETERROR(tex, "Invalid DDS file (invalid alpha mode)");
}
return tex;
}
static TCTex_Texture* tctex_i_dds_load(TCTex_Texture* tex, const uint8_t* udata, size_t udataoffset, size_t udatalen)
{
TC__STATIC_OR_RUNTIME_ASSERT(sizeof(TCTex_DDS_HEADER) == 124, "Invalid size of DDS_HEADER structure");
udata += udataoffset;
udatalen -= udataoffset;
const TCTex_DDS_HEADER* header = TC__STATIC_CAST(const TCTex_DDS_HEADER*,udata);
uint32_t dwSize = TCTEX__FROM_LE32(header->dwSize);
if(dwSize < sizeof(TCTex_DDS_HEADER)) TCTEX_I_RETERROR(tex, "Invalid DDS file (header truncated)");
/* I don't like Hungarian notation either, but for consistency with DDS_HEADER, I'll maintain the names ... */
uint32_t dwFlags = TCTEX__FROM_LE32(header->dwFlags);
if((dwFlags & (TCTEX_DDSD_HEIGHT | TCTEX_DDSD_WIDTH)) != (TCTEX_DDSD_HEIGHT | TCTEX_DDSD_WIDTH)) TCTEX_I_RETERROR(tex, "Invallid DDS file (invalid file flags)");
tex->size.x = TCTEX__FROM_LE32(header->dwWidth);
tex->size.y = TCTEX__FROM_LE32(header->dwHeight);
tex->size.z = (dwFlags & TCTEX_DDSD_DEPTH) ? TCTEX__FROM_LE32(header->dwDepth) : 1;
uint32_t dwPitchOrLinearSize = TCTEX__FROM_LE32(header->dwPitchOrLinearSize);
switch(dwFlags & (TCTEX_DDSD_PITCH | TCTEX_DDSD_LINEARSIZE))
{
case 0: TCTEX_I_RETERROR(tex, "[TODO] UNHANDLED: Compute pitch *and* linear size");
case TCTEX_DDSD_PITCH:
tex->pitch.y = TCTEX__FROM_LE32(dwPitchOrLinearSize);
tex->pitch.z = tex->size.y * tex->pitch.y;
tex->nbytes = tex->size.z * tex->pitch.z;
break;
case TCTEX_DDSD_LINEARSIZE:
tex->nbytes = TCTEX__FROM_LE32(header->dwPitchOrLinearSize);
tex->pitch.y = tex->size.y ? tex->nbytes / tex->size.y : 0;//TODO: verify
tex->pitch.z = tex->nbytes;
tex->nbytes *= tex->size.z;
break;
default: TCTEX_I_RETERROR(tex, "Invalid DDS file (conflicting file flags)");
}
tex->nmiplevels = TCTEX__FROM_LE32(header->dwMipMapCount);
bool hasDX10;
if(!tctex_i_dds_handle_pixel_format(tex, &header->ddspf, &hasDX10)) return NULL;
uint32_t dwCaps = TCTEX__FROM_LE32(header->dwCaps);
if(!(dwCaps & TCTEX_DDSCAPS_MIPMAP))
{
if(tex->nmiplevels > 1) TCTEX_I_RETERROR(tex, "Conflicting DDS flags (has no mipmaps, but defines more than 1 anyway)");
else tex->nmiplevels = 1;
}
uint32_t dwCaps2 = TCTEX__FROM_LE32(header->dwCaps2);
if(dwCaps2 & TCTEX_DDSCAPS2_CUBEMAP)
{
if(dwCaps2 & TCTEX_DDSCAPS2_CUBEMAP_POSITIVEX) { tex->cubefaces.mask |= TCTEX_CUBE_FACE_POSX; tex->cubefaces.num++; }
if(dwCaps2 & TCTEX_DDSCAPS2_CUBEMAP_NEGATIVEX) { tex->cubefaces.mask |= TCTEX_CUBE_FACE_NEGX; tex->cubefaces.num++; }
if(dwCaps2 & TCTEX_DDSCAPS2_CUBEMAP_POSITIVEY) { tex->cubefaces.mask |= TCTEX_CUBE_FACE_POSY; tex->cubefaces.num++; }
if(dwCaps2 & TCTEX_DDSCAPS2_CUBEMAP_NEGATIVEY) { tex->cubefaces.mask |= TCTEX_CUBE_FACE_NEGY; tex->cubefaces.num++; }
if(dwCaps2 & TCTEX_DDSCAPS2_CUBEMAP_POSITIVEZ) { tex->cubefaces.mask |= TCTEX_CUBE_FACE_POSZ; tex->cubefaces.num++; }
if(dwCaps2 & TCTEX_DDSCAPS2_CUBEMAP_NEGATIVEZ) { tex->cubefaces.mask |= TCTEX_CUBE_FACE_NEGZ; tex->cubefaces.num++; }
if(!tex->cubefaces.num) TCTEX_I_RETERROR(tex, "Conflicting DDS flags (is cubemap, but no faces defined)");
}
tex->isvolume = !!(dwCaps2 & TCTEX_DDSCAPS2_VOLUME);// TODO: do we really need this? we have ->dimension and ->size.z
tex->offset0 = udataoffset + dwSize;
if(hasDX10)
{
TC__STATIC_OR_RUNTIME_ASSERT(sizeof(TCTex_DDS_HEADER_DXT10) == 20, "Invalid size of DDS_HEADER_DXT10 structure");
if(udatalen < dwSize + sizeof(TCTex_DDS_HEADER_DXT10)) TCTEX_I_RETERROR(tex, "Invalid DDS file (file truncated)");
if(!tctex_i_dds_handle_header10(tex, TC__STATIC_CAST(const TCTex_DDS_HEADER_DXT10*,udata + dwSize))) return NULL;
tex->offset0 += sizeof(TCTex_DDS_HEADER_DXT10);
}
else
{
tex->arraylen = 1;
tex->dimension = 2;
}
return tex;
}
TCTex_Texture* tctex_load_mem(TCTex_Texture* tex, const void* data, size_t datalen)
{
static const TCTex_Texture Initial = {};
if(!tex) return NULL;
*tex = Initial;
if(datalen < 4 + sizeof(TCTex_DDS_HEADER)) TCTEX_I_RETERROR(tex, "Invalid file (not a DDS file or file truncated)");
tex->memory = TC__VOID_CAST(const uint8_t*,data);
if(TCTEX__FROM_LE32(*TC__STATIC_CAST(const uint32_t*,data)) == 0x20534444U /* "DDS " */) return tctex_i_dds_load(tex, tex->memory, 4, datalen);
tex->errmsg = "Invalid filetype (not a DDS file)";
return NULL;
}
#ifndef TC_TEXTURE_LOAD_NO_STDIO
TCTex_Texture* tctex_load_file(TCTex_Texture* tex, FILE* file)
{
if(!tex) return NULL;
TC_foffset head, tail;
if((head = TC_FTELL(file)) < 0) return NULL;
if(TC_FSEEK(file, 0, SEEK_END)) return NULL;
if((tail = TC_FTELL(file)) < 0) return NULL;
if(TC_FSEEK(file, head, SEEK_SET)) return NULL;
TC_foffset len = tail - head;
tex->imem_ = malloc(len);
if(!tex->imem_) { tex->errmsg = "Unable to allocate memory"; return NULL; }
if(fread(tex->imem_, 1, len, file) != len) { tex->errmsg = strerror(errno); goto err_freemem; }
if(!tctex_load_mem(tex, tex->imem_, len)) goto err_freemem;
return tex;
err_freemem:
free(tex->imem_);
return NULL;
}
TCTex_Texture* tctex_load_fname(TCTex_Texture* tex, const char* fname)
{
if(!tex) return NULL;
FILE* file = fopen(fname, "rb");
if(!file)
{
tex->errmsg = strerror(errno);
return NULL;
}
tex = tctex_load_file(tex, file);
fclose(file);
return tex;
}
#endif
uint32_t tctex_get_mipmaps(const TCTex_Texture* tex, TCTex_MipMapInfo* mipmaps, uint32_t maxmipmaps, uint32_t textureidx)
{
uint32_t nmipmaps = tctex_i_min32u(tex->nmiplevels, maxmipmaps);
TCTex_MipMapInfo mipmap;
mipmap.offset = 0;
mipmap.size.x = tex->size.x;
mipmap.size.y = tex->size.y;
mipmap.size.z = tex->size.z;
mipmap.pitch.y = tex->pitch.y;
mipmap.pitch.z = tex->pitch.z;
mipmap.nbytes = tex->nbytes;
uint32_t i;
// first, compute the base info
for(i = 0; i < nmipmaps; i++)
{
mipmaps[i] = mipmap;
mipmap.offset += mipmap.nbytes;
mipmap.size.x = tctex_i_max32u(mipmap.size.x >> 1, 1U);
mipmap.size.y = tctex_i_max32u(mipmap.size.y >> 1, 1U);
mipmap.size.z = tctex_i_max32u(mipmap.size.z >> 1, 1U);
//mipmap.pitch.y = tctex_i_max32u(((mipmap.pitch.y >> 1) + 3U) & ~3U, 4U);