-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathddspp.h
1057 lines (962 loc) · 36.5 KB
/
ddspp.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
#pragma once
// Sources
// https://learn.microsoft.com/en-us/windows/uwp/gaming/complete-code-for-ddstextureloader
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-header-dxt10
// https://learn.microsoft.com/en-us/windows/win32/direct3ddds/dds-pixelformat
#if (__cpp_constexpr >= 201304) || (_MSC_VER > 1900)
#define ddspp_constexpr constexpr
#else
#define ddspp_constexpr const
#endif
namespace ddspp
{
namespace internal
{
static ddspp_constexpr unsigned int DDS_MAGIC = 0x20534444;
static ddspp_constexpr unsigned int DDS_ALPHAPIXELS = 0x00000001;
static ddspp_constexpr unsigned int DDS_ALPHA = 0x00000002; // DDPF_ALPHA
static ddspp_constexpr unsigned int DDS_FOURCC = 0x00000004; // DDPF_FOURCC
static ddspp_constexpr unsigned int DDS_RGB = 0x00000040; // DDPF_RGB
static ddspp_constexpr unsigned int DDS_RGBA = 0x00000041; // DDPF_RGB | DDPF_ALPHAPIXELS
static ddspp_constexpr unsigned int DDS_YUV = 0x00000200; // DDPF_YUV
static ddspp_constexpr unsigned int DDS_LUMINANCE = 0x00020000; // DDPF_LUMINANCE
static ddspp_constexpr unsigned int DDS_LUMINANCEA = 0x00020001; // DDPF_LUMINANCE | DDPF_ALPHAPIXELS
static ddspp_constexpr unsigned int DDS_PAL8 = 0x00000020; // DDPF_PALETTEINDEXED8
static ddspp_constexpr unsigned int DDS_BUMPDUDV = 0x00080000; // DDPF_BUMPDUDV
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_CAPS = 0x00000001; // DDSD_CAPS
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_HEIGHT = 0x00000002; // DDSD_HEIGHT
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_WIDTH = 0x00000004; // DDSD_WIDTH
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_PITCH = 0x00000008; // DDSD_PITCH
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_PIXELFORMAT = 0x00001000; // DDSD_PIXELFORMAT
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_MIPMAP = 0x00020000; // DDSD_MIPMAPCOUNT
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_LINEARSIZE = 0x00080000; // DDSD_LINEARSIZE
static ddspp_constexpr unsigned int DDS_HEADER_FLAGS_VOLUME = 0x00800000; // DDSD_DEPTH
static ddspp_constexpr unsigned int DDS_HEADER_CAPS_COMPLEX = 0x00000008; // DDSCAPS_COMPLEX
static ddspp_constexpr unsigned int DDS_HEADER_CAPS_MIPMAP = 0x00400000; // DDSCAPS_MIPMAP
static ddspp_constexpr unsigned int DDS_HEADER_CAPS_TEXTURE = 0x00001000; // DDSCAPS_TEXTURE
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP = 0x00000200; // DDSCAPS2_CUBEMAP
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_POSITIVEX = 0x00000600; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEX = 0x00000a00; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_POSITIVEY = 0x00001200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEY = 0x00002200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_POSITIVEZ = 0x00004200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEZ = 0x00008200; // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_VOLUME = 0x00200000; // DDSCAPS2_VOLUME
static ddspp_constexpr unsigned int DDS_HEADER_CAPS2_CUBEMAP_ALLFACES =
DDS_HEADER_CAPS2_CUBEMAP_POSITIVEX | DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEX | DDS_HEADER_CAPS2_CUBEMAP_POSITIVEY |
DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEY | DDS_HEADER_CAPS2_CUBEMAP_POSITIVEZ | DDS_HEADER_CAPS2_CUBEMAP_NEGATIVEZ;
static ddspp_constexpr unsigned int DXGI_MISC_FLAG_CUBEMAP = 0x2; // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_resource_misc_flag
static ddspp_constexpr unsigned int DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x7;
#define ddspp_make_fourcc(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
// FOURCC constants
static ddspp_constexpr unsigned int FOURCC_DXT1 = ddspp_make_fourcc('D', 'X', 'T', '1'); // BC1_UNORM
static ddspp_constexpr unsigned int FOURCC_DXT2 = ddspp_make_fourcc('D', 'X', 'T', '2'); // BC2_UNORM
static ddspp_constexpr unsigned int FOURCC_DXT3 = ddspp_make_fourcc('D', 'X', 'T', '3'); // BC2_UNORM
static ddspp_constexpr unsigned int FOURCC_DXT4 = ddspp_make_fourcc('D', 'X', 'T', '4'); // BC3_UNORM
static ddspp_constexpr unsigned int FOURCC_DXT5 = ddspp_make_fourcc('D', 'X', 'T', '5'); // BC3_UNORM
static ddspp_constexpr unsigned int FOURCC_ATI1 = ddspp_make_fourcc('A', 'T', 'I', '1'); // BC4_UNORM
static ddspp_constexpr unsigned int FOURCC_BC4U = ddspp_make_fourcc('B', 'C', '4', 'U'); // BC4_UNORM
static ddspp_constexpr unsigned int FOURCC_BC4S = ddspp_make_fourcc('B', 'C', '4', 'S'); // BC4_SNORM
static ddspp_constexpr unsigned int FOURCC_ATI2 = ddspp_make_fourcc('A', 'T', 'I', '2'); // BC5_UNORM
static ddspp_constexpr unsigned int FOURCC_BC5U = ddspp_make_fourcc('B', 'C', '5', 'U'); // BC5_UNORM
static ddspp_constexpr unsigned int FOURCC_BC5S = ddspp_make_fourcc('B', 'C', '5', 'S'); // BC5_SNORM
static ddspp_constexpr unsigned int FOURCC_RGBG = ddspp_make_fourcc('R', 'G', 'B', 'G'); // R8G8_B8G8_UNORM
static ddspp_constexpr unsigned int FOURCC_GRBG = ddspp_make_fourcc('G', 'R', 'G', 'B'); // G8R8_G8B8_UNORM
static ddspp_constexpr unsigned int FOURCC_YUY2 = ddspp_make_fourcc('Y', 'U', 'Y', '2'); // YUY2
static ddspp_constexpr unsigned int FOURCC_DXT10 = ddspp_make_fourcc('D', 'X', '1', '0'); // DDS extension header
// These values come from the original D3D9 D3DFORMAT values https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dformat
static ddspp_constexpr unsigned int FOURCC_RGB8 = 20;
static ddspp_constexpr unsigned int FOURCC_A8R8G8B8 = 21;
static ddspp_constexpr unsigned int FOURCC_X8R8G8B8 = 22;
static ddspp_constexpr unsigned int FOURCC_R5G6B5 = 23; // B5G6R5_UNORM (needs swizzling)
static ddspp_constexpr unsigned int FOURCC_X1R5G5B5 = 24;
static ddspp_constexpr unsigned int FOURCC_RGB5A1 = 25; // B5G5R5A1_UNORM (needs swizzling)
static ddspp_constexpr unsigned int FOURCC_RGBA4 = 26; // B4G4R4A4_UNORM (needs swizzling)
static ddspp_constexpr unsigned int FOURCC_R3G3B2 = 27;
static ddspp_constexpr unsigned int FOURCC_A8 = 28;
static ddspp_constexpr unsigned int FOURCC_A8R3G3B2 = 29;
static ddspp_constexpr unsigned int FOURCC_X4R4G4B4 = 30;
static ddspp_constexpr unsigned int FOURCC_A2B10G10R10 = 31;
static ddspp_constexpr unsigned int FOURCC_A8B8G8R8 = 32;
static ddspp_constexpr unsigned int FOURCC_X8B8G8R8 = 33;
static ddspp_constexpr unsigned int FOURCC_G16R16 = 34;
static ddspp_constexpr unsigned int FOURCC_A2R10G10B10 = 35;
static ddspp_constexpr unsigned int FOURCC_RGBA16U = 36; // R16G16B16A16_UNORM
static ddspp_constexpr unsigned int FOURCC_RGBA16S = 110; // R16G16B16A16_SNORM
static ddspp_constexpr unsigned int FOURCC_R16F = 111; // R16_FLOAT
static ddspp_constexpr unsigned int FOURCC_RG16F = 112; // R16G16_FLOAT
static ddspp_constexpr unsigned int FOURCC_RGBA16F = 113; // R16G16B16A16_FLOAT
static ddspp_constexpr unsigned int FOURCC_R32F = 114; // R32_FLOAT
static ddspp_constexpr unsigned int FOURCC_RG32F = 115; // R32G32_FLOAT
static ddspp_constexpr unsigned int FOURCC_RGBA32F = 116; // R32G32B32A32_FLOAT
struct PixelFormat
{
unsigned int size;
unsigned int flags;
unsigned int fourCC;
unsigned int RGBBitCount;
unsigned int RBitMask;
unsigned int GBitMask;
unsigned int BBitMask;
unsigned int ABitMask;
};
static_assert(sizeof(PixelFormat) == 32, "PixelFormat size mismatch");
inline ddspp_constexpr bool is_rgba_mask(const PixelFormat& ddspf, unsigned int rmask, unsigned int gmask, unsigned int bmask, unsigned int amask)
{
return (ddspf.RBitMask == rmask) && (ddspf.GBitMask == gmask) && (ddspf.BBitMask == bmask) && (ddspf.ABitMask == amask);
}
inline ddspp_constexpr bool is_rgb_mask(const PixelFormat& ddspf, unsigned int rmask, unsigned int gmask, unsigned int bmask)
{
return (ddspf.RBitMask == rmask) && (ddspf.GBitMask == gmask) && (ddspf.BBitMask == bmask);
}
}
using namespace internal;
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d11/ne-d3d11-d3d11_resource_dimension
enum DXGIResourceDimension : unsigned char
{
DXGI_Unknown,
DXGI_Buffer,
DXGI_Texture1D,
DXGI_Texture2D,
DXGI_Texture3D
};
// Matches DXGI_FORMAT https://docs.microsoft.com/en-us/windows/desktop/api/dxgiformat/ne-dxgiformat-dxgi_format
enum DXGIFormat : unsigned int
{
UNKNOWN = 0,
R32G32B32A32_TYPELESS = 1,
R32G32B32A32_FLOAT = 2,
R32G32B32A32_UINT = 3,
R32G32B32A32_SINT = 4,
R32G32B32_TYPELESS = 5,
R32G32B32_FLOAT = 6,
R32G32B32_UINT = 7,
R32G32B32_SINT = 8,
R16G16B16A16_TYPELESS = 9,
R16G16B16A16_FLOAT = 10,
R16G16B16A16_UNORM = 11,
R16G16B16A16_UINT = 12,
R16G16B16A16_SNORM = 13,
R16G16B16A16_SINT = 14,
R32G32_TYPELESS = 15,
R32G32_FLOAT = 16,
R32G32_UINT = 17,
R32G32_SINT = 18,
R32G8X24_TYPELESS = 19,
D32_FLOAT_S8X24_UINT = 20,
R32_FLOAT_X8X24_TYPELESS = 21,
X32_TYPELESS_G8X24_UINT = 22,
R10G10B10A2_TYPELESS = 23,
R10G10B10A2_UNORM = 24,
R10G10B10A2_UINT = 25,
R11G11B10_FLOAT = 26,
R8G8B8A8_TYPELESS = 27,
R8G8B8A8_UNORM = 28,
R8G8B8A8_UNORM_SRGB = 29,
R8G8B8A8_UINT = 30,
R8G8B8A8_SNORM = 31,
R8G8B8A8_SINT = 32,
R16G16_TYPELESS = 33,
R16G16_FLOAT = 34,
R16G16_UNORM = 35,
R16G16_UINT = 36,
R16G16_SNORM = 37,
R16G16_SINT = 38,
R32_TYPELESS = 39,
D32_FLOAT = 40,
R32_FLOAT = 41,
R32_UINT = 42,
R32_SINT = 43,
R24G8_TYPELESS = 44,
D24_UNORM_S8_UINT = 45,
R24_UNORM_X8_TYPELESS = 46,
X24_TYPELESS_G8_UINT = 47,
R8G8_TYPELESS = 48,
R8G8_UNORM = 49,
R8G8_UINT = 50,
R8G8_SNORM = 51,
R8G8_SINT = 52,
R16_TYPELESS = 53,
R16_FLOAT = 54,
D16_UNORM = 55,
R16_UNORM = 56,
R16_UINT = 57,
R16_SNORM = 58,
R16_SINT = 59,
R8_TYPELESS = 60,
R8_UNORM = 61,
R8_UINT = 62,
R8_SNORM = 63,
R8_SINT = 64,
A8_UNORM = 65,
R1_UNORM = 66,
R9G9B9E5_SHAREDEXP = 67,
R8G8_B8G8_UNORM = 68,
G8R8_G8B8_UNORM = 69,
BC1_TYPELESS = 70,
BC1_UNORM = 71,
BC1_UNORM_SRGB = 72,
BC2_TYPELESS = 73,
BC2_UNORM = 74,
BC2_UNORM_SRGB = 75,
BC3_TYPELESS = 76,
BC3_UNORM = 77,
BC3_UNORM_SRGB = 78,
BC4_TYPELESS = 79,
BC4_UNORM = 80,
BC4_SNORM = 81,
BC5_TYPELESS = 82,
BC5_UNORM = 83,
BC5_SNORM = 84,
B5G6R5_UNORM = 85,
B5G5R5A1_UNORM = 86,
B8G8R8A8_UNORM = 87,
B8G8R8X8_UNORM = 88,
R10G10B10_XR_BIAS_A2_UNORM = 89,
B8G8R8A8_TYPELESS = 90,
B8G8R8A8_UNORM_SRGB = 91,
B8G8R8X8_TYPELESS = 92,
B8G8R8X8_UNORM_SRGB = 93,
BC6H_TYPELESS = 94,
BC6H_UF16 = 95,
BC6H_SF16 = 96,
BC7_TYPELESS = 97,
BC7_UNORM = 98,
BC7_UNORM_SRGB = 99,
AYUV = 100,
Y410 = 101,
Y416 = 102,
NV12 = 103,
P010 = 104,
P016 = 105,
OPAQUE_420 = 106,
YUY2 = 107,
Y210 = 108,
Y216 = 109,
NV11 = 110,
AI44 = 111,
IA44 = 112,
P8 = 113,
A8P8 = 114,
B4G4R4A4_UNORM = 115,
P208 = 130,
V208 = 131,
V408 = 132,
ASTC_4X4_TYPELESS = 133,
ASTC_4X4_UNORM = 134,
ASTC_4X4_UNORM_SRGB = 135,
ASTC_5X4_TYPELESS = 137,
ASTC_5X4_UNORM = 138,
ASTC_5X4_UNORM_SRGB = 139,
ASTC_5X5_TYPELESS = 141,
ASTC_5X5_UNORM = 142,
ASTC_5X5_UNORM_SRGB = 143,
ASTC_6X5_TYPELESS = 145,
ASTC_6X5_UNORM = 146,
ASTC_6X5_UNORM_SRGB = 147,
ASTC_6X6_TYPELESS = 149,
ASTC_6X6_UNORM = 150,
ASTC_6X6_UNORM_SRGB = 151,
ASTC_8X5_TYPELESS = 153,
ASTC_8X5_UNORM = 154,
ASTC_8X5_UNORM_SRGB = 155,
ASTC_8X6_TYPELESS = 157,
ASTC_8X6_UNORM = 158,
ASTC_8X6_UNORM_SRGB = 159,
ASTC_8X8_TYPELESS = 161,
ASTC_8X8_UNORM = 162,
ASTC_8X8_UNORM_SRGB = 163,
ASTC_10X5_TYPELESS = 165,
ASTC_10X5_UNORM = 166,
ASTC_10X5_UNORM_SRGB = 167,
ASTC_10X6_TYPELESS = 169,
ASTC_10X6_UNORM = 170,
ASTC_10X6_UNORM_SRGB = 171,
ASTC_10X8_TYPELESS = 173,
ASTC_10X8_UNORM = 174,
ASTC_10X8_UNORM_SRGB = 175,
ASTC_10X10_TYPELESS = 177,
ASTC_10X10_UNORM = 178,
ASTC_10X10_UNORM_SRGB = 179,
ASTC_12X10_TYPELESS = 181,
ASTC_12X10_UNORM = 182,
ASTC_12X10_UNORM_SRGB = 183,
ASTC_12X12_TYPELESS = 185,
ASTC_12X12_UNORM = 186,
ASTC_12X12_UNORM_SRGB = 187,
FORCE_UINT = 0xffffffff
};
struct Header
{
unsigned int size;
unsigned int flags;
unsigned int height;
unsigned int width;
unsigned int pitchOrLinearSize;
unsigned int depth;
unsigned int mipMapCount;
unsigned int reserved1[11];
PixelFormat ddspf;
unsigned int caps;
unsigned int caps2;
unsigned int caps3;
unsigned int caps4;
unsigned int reserved2;
};
static_assert(sizeof(Header) == 124, "DDS Header size mismatch");
struct HeaderDXT10
{
DXGIFormat dxgiFormat;
DXGIResourceDimension resourceDimension;
unsigned int miscFlag;
unsigned int arraySize;
unsigned int reserved;
};
static_assert(sizeof(HeaderDXT10) == 20, "DDS DX10 Extended Header size mismatch");
// Maximum possible size of header. Use this to read in only the header, decode, seek to the real header size, then read in the rest of the image data
ddspp_constexpr int MAX_HEADER_SIZE = sizeof(DDS_MAGIC) + sizeof(Header) + sizeof(HeaderDXT10);
enum Result : unsigned char
{
Success,
Error
};
enum TextureType : unsigned char
{
Texture1D,
Texture2D,
Texture3D,
Cubemap,
};
struct Descriptor
{
DXGIFormat format;
TextureType type;
unsigned int width;
unsigned int height;
unsigned int depth;
unsigned int numMips;
unsigned int arraySize;
unsigned int rowPitch; // Row pitch for mip 0
unsigned int depthPitch; // Size of mip 0
unsigned int bitsPerPixelOrBlock; // If compressed bits per block, else bits per pixel
unsigned int blockWidth; // Width of block in pixels (1 if uncompressed)
unsigned int blockHeight;// Height of block in pixels (1 if uncompressed)
bool compressed;
bool srgb;
unsigned int headerSize; // Actual size of header, use this to get to image data
};
inline ddspp_constexpr bool is_dxt10(const Header& header)
{
const PixelFormat& ddspf = header.ddspf;
return (ddspf.flags & DDS_FOURCC) && (ddspf.fourCC == FOURCC_DXT10);
}
inline ddspp_constexpr bool is_compressed(DXGIFormat format)
{
return (format >= BC1_UNORM && format <= BC5_SNORM) ||
(format >= BC6H_TYPELESS && format <= BC7_UNORM_SRGB) ||
(format >= ASTC_4X4_TYPELESS && format <= ASTC_12X12_UNORM_SRGB);
}
inline ddspp_constexpr bool is_srgb(DXGIFormat format)
{
switch(format)
{
case R8G8B8A8_UNORM_SRGB:
case BC1_UNORM_SRGB:
case BC2_UNORM_SRGB:
case BC3_UNORM_SRGB:
case B8G8R8A8_UNORM_SRGB:
case B8G8R8X8_UNORM_SRGB:
case BC7_UNORM_SRGB:
case ASTC_4X4_UNORM_SRGB:
case ASTC_5X4_UNORM_SRGB:
case ASTC_5X5_UNORM_SRGB:
case ASTC_6X5_UNORM_SRGB:
case ASTC_6X6_UNORM_SRGB:
case ASTC_8X5_UNORM_SRGB:
case ASTC_8X6_UNORM_SRGB:
case ASTC_8X8_UNORM_SRGB:
case ASTC_10X5_UNORM_SRGB:
case ASTC_10X6_UNORM_SRGB:
case ASTC_10X8_UNORM_SRGB:
case ASTC_10X10_UNORM_SRGB:
case ASTC_12X10_UNORM_SRGB:
case ASTC_12X12_UNORM_SRGB:
return true;
default:
return false;
}
}
inline ddspp_constexpr unsigned int get_bits_per_pixel_or_block(DXGIFormat format)
{
if(format >= ASTC_4X4_TYPELESS && format <= ASTC_12X12_UNORM_SRGB)
{
return 128; // All ASTC blocks are the same size
}
switch(format)
{
case R1_UNORM:
return 1;
case R8_TYPELESS:
case R8_UNORM:
case R8_UINT:
case R8_SNORM:
case R8_SINT:
case A8_UNORM:
case AI44:
case IA44:
case P8:
return 8;
case NV12:
case OPAQUE_420:
case NV11:
return 12;
case R8G8_TYPELESS:
case R8G8_UNORM:
case R8G8_UINT:
case R8G8_SNORM:
case R8G8_SINT:
case R16_TYPELESS:
case R16_FLOAT:
case D16_UNORM:
case R16_UNORM:
case R16_UINT:
case R16_SNORM:
case R16_SINT:
case B5G6R5_UNORM:
case B5G5R5A1_UNORM:
case A8P8:
case B4G4R4A4_UNORM:
return 16;
case P010:
case P016:
return 24;
case BC1_UNORM:
case BC1_UNORM_SRGB:
case BC1_TYPELESS:
case BC4_UNORM:
case BC4_SNORM:
case BC4_TYPELESS:
case R16G16B16A16_TYPELESS:
case R16G16B16A16_FLOAT:
case R16G16B16A16_UNORM:
case R16G16B16A16_UINT:
case R16G16B16A16_SNORM:
case R16G16B16A16_SINT:
case R32G32_TYPELESS:
case R32G32_FLOAT:
case R32G32_UINT:
case R32G32_SINT:
case R32G8X24_TYPELESS:
case D32_FLOAT_S8X24_UINT:
case R32_FLOAT_X8X24_TYPELESS:
case X32_TYPELESS_G8X24_UINT:
case Y416:
case Y210:
case Y216:
return 64;
case R32G32B32_TYPELESS:
case R32G32B32_FLOAT:
case R32G32B32_UINT:
case R32G32B32_SINT:
return 96;
case BC2_UNORM:
case BC2_UNORM_SRGB:
case BC2_TYPELESS:
case BC3_UNORM:
case BC3_UNORM_SRGB:
case BC3_TYPELESS:
case BC5_UNORM:
case BC5_SNORM:
case BC6H_UF16:
case BC6H_SF16:
case BC7_UNORM:
case BC7_UNORM_SRGB:
case R32G32B32A32_TYPELESS:
case R32G32B32A32_FLOAT:
case R32G32B32A32_UINT:
case R32G32B32A32_SINT:
return 128;
default:
return 32; // Most formats are 32 bits per pixel
break;
}
}
inline ddspp_constexpr void get_block_size(DXGIFormat format, unsigned int& blockWidth, unsigned int& blockHeight)
{
switch(format)
{
case BC1_UNORM:
case BC1_UNORM_SRGB:
case BC1_TYPELESS:
case BC4_UNORM:
case BC4_SNORM:
case BC4_TYPELESS:
case BC2_UNORM:
case BC2_UNORM_SRGB:
case BC2_TYPELESS:
case BC3_UNORM:
case BC3_UNORM_SRGB:
case BC3_TYPELESS:
case BC5_UNORM:
case BC5_SNORM:
case BC6H_UF16:
case BC6H_SF16:
case BC7_UNORM:
case BC7_UNORM_SRGB:
case ASTC_4X4_TYPELESS:
case ASTC_4X4_UNORM:
case ASTC_4X4_UNORM_SRGB:
blockWidth = 4; blockHeight = 4;
break;
case ASTC_5X4_TYPELESS:
case ASTC_5X4_UNORM:
case ASTC_5X4_UNORM_SRGB:
blockWidth = 5; blockHeight = 4;
break;
case ASTC_5X5_TYPELESS:
case ASTC_5X5_UNORM:
case ASTC_5X5_UNORM_SRGB:
blockWidth = 5; blockHeight = 5;
break;
case ASTC_6X5_TYPELESS:
case ASTC_6X5_UNORM:
case ASTC_6X5_UNORM_SRGB:
blockWidth = 6; blockHeight = 5;
case ASTC_6X6_TYPELESS:
case ASTC_6X6_UNORM:
case ASTC_6X6_UNORM_SRGB:
blockWidth = 6; blockHeight = 6;
case ASTC_8X5_TYPELESS:
case ASTC_8X5_UNORM:
case ASTC_8X5_UNORM_SRGB:
blockWidth = 8; blockHeight = 5;
case ASTC_8X6_TYPELESS:
case ASTC_8X6_UNORM:
case ASTC_8X6_UNORM_SRGB:
blockWidth = 8; blockHeight = 6;
case ASTC_8X8_TYPELESS:
case ASTC_8X8_UNORM:
case ASTC_8X8_UNORM_SRGB:
blockWidth = 8; blockHeight = 8;
case ASTC_10X5_TYPELESS:
case ASTC_10X5_UNORM:
case ASTC_10X5_UNORM_SRGB:
blockWidth = 10; blockHeight = 5;
case ASTC_10X6_TYPELESS:
case ASTC_10X6_UNORM:
case ASTC_10X6_UNORM_SRGB:
blockWidth = 10; blockHeight = 6;
case ASTC_10X8_TYPELESS:
case ASTC_10X8_UNORM:
case ASTC_10X8_UNORM_SRGB:
blockWidth = 10; blockHeight = 8;
case ASTC_10X10_TYPELESS:
case ASTC_10X10_UNORM:
case ASTC_10X10_UNORM_SRGB:
blockWidth = 10; blockHeight = 10;
case ASTC_12X10_TYPELESS:
case ASTC_12X10_UNORM:
case ASTC_12X10_UNORM_SRGB:
blockWidth = 12; blockHeight = 10;
case ASTC_12X12_TYPELESS:
case ASTC_12X12_UNORM:
case ASTC_12X12_UNORM_SRGB:
blockWidth = 12; blockHeight = 12;
default:
blockWidth = 1; blockHeight = 1;
break;
}
return;
}
// Returns number of bytes for each row of a given mip. Valid range is [0, desc.numMips)
inline ddspp_constexpr unsigned int get_row_pitch(unsigned int width, unsigned int bitsPerPixelOrBlock, unsigned int blockWidth, unsigned int mip)
{
// Shift width by mipmap index, round to next block size and round to next byte (for the rare less than 1 byte per pixel formats)
// E.g. width = 119, mip = 3, BC1 compression
// ((((119 >> 2) + 4 - 1) / 4) * 64) / 8 = 64 bytes
return ((((((width >> mip) > 1) ? (width >> mip) : 1) + blockWidth - 1) / blockWidth) * bitsPerPixelOrBlock + 7) / 8;
}
inline ddspp_constexpr unsigned int get_row_pitch(const Descriptor& desc, unsigned int mip)
{
return get_row_pitch(desc.width, desc.bitsPerPixelOrBlock, desc.blockWidth, mip);
}
// Return the height for a given mip in either pixels or blocks depending on whether the format is compressed
inline ddspp_constexpr unsigned int get_height_pixels_blocks(const unsigned int height, const unsigned int blockHeight, const unsigned int mip)
{
return ((height / blockHeight) >> mip) ? ((height / blockHeight) >> mip) : 1;
}
inline ddspp_constexpr unsigned int get_height_pixels_blocks(const Descriptor& desc, const unsigned int mip)
{
return get_height_pixels_blocks(desc.height, desc.blockHeight, mip);
}
inline Result decode_header(unsigned char* sourceData, Descriptor& desc)
{
unsigned int magic = *reinterpret_cast<unsigned int*>(sourceData); // First 4 bytes are the magic DDS number
if (magic != DDS_MAGIC)
{
return ddspp::Error;
}
const Header header = *reinterpret_cast<const Header*>(sourceData + sizeof(DDS_MAGIC));
const PixelFormat& ddspf = header.ddspf;
bool dxt10Extension = is_dxt10(header);
const HeaderDXT10 dxt10Header = *reinterpret_cast<const HeaderDXT10*>(sourceData + sizeof(DDS_MAGIC) + sizeof(Header));
// Read basic data from the header
desc.width = header.width > 0 ? header.width : 1;
desc.height = header.height > 0 ? header.height : 1;
desc.depth = header.depth > 0 ? header.depth : 1;
desc.numMips = header.mipMapCount > 0 ? header.mipMapCount : 1;
// Set some sensible defaults
desc.arraySize = 1;
desc.srgb = false;
desc.type = Texture2D;
desc.format = UNKNOWN;
if (dxt10Extension)
{
desc.format = dxt10Header.dxgiFormat;
desc.arraySize = dxt10Header.arraySize;
switch(dxt10Header.resourceDimension)
{
case DXGI_Texture1D:
desc.depth = 1;
desc.type = Texture1D;
break;
case DXGI_Texture2D:
desc.depth = 1;
if(dxt10Header.miscFlag & DXGI_MISC_FLAG_CUBEMAP)
{
desc.type = Cubemap;
}
else
{
desc.type = Texture2D;
}
break;
case DXGI_Texture3D:
desc.type = Texture3D;
desc.arraySize = 1; // There are no 3D texture arrays
break;
default:
break;
}
}
else
{
if(ddspf.flags & DDS_FOURCC)
{
unsigned int fourCC = ddspf.fourCC;
switch(fourCC)
{
// Compressed
case FOURCC_DXT1: desc.format = BC1_UNORM; break;
case FOURCC_DXT2: // fallthrough
case FOURCC_DXT3: desc.format = BC2_UNORM; break;
case FOURCC_DXT4: // fallthrough
case FOURCC_DXT5: desc.format = BC3_UNORM; break;
case FOURCC_ATI1: // fallthrough
case FOURCC_BC4U: desc.format = BC4_UNORM; break;
case FOURCC_BC4S: desc.format = BC4_SNORM; break;
case FOURCC_ATI2: // fallthrough
case FOURCC_BC5U: desc.format = BC5_UNORM; break;
case FOURCC_BC5S: desc.format = BC5_SNORM; break;
// Video
case FOURCC_RGBG: desc.format = R8G8_B8G8_UNORM; break;
case FOURCC_GRBG: desc.format = G8R8_G8B8_UNORM; break;
case FOURCC_YUY2: desc.format = YUY2; break;
// Packed
case FOURCC_R5G6B5: desc.format = B5G6R5_UNORM; break;
case FOURCC_RGB5A1: desc.format = B5G5R5A1_UNORM; break;
case FOURCC_RGBA4: desc.format = B4G4R4A4_UNORM; break;
// Uncompressed
case FOURCC_A8: desc.format = R8_UNORM; break;
case FOURCC_A2B10G10R10: desc.format = R10G10B10A2_UNORM; break;
case FOURCC_RGBA16U: desc.format = R16G16B16A16_UNORM; break;
case FOURCC_RGBA16S: desc.format = R16G16B16A16_SNORM; break;
case FOURCC_R16F: desc.format = R16_FLOAT; break;
case FOURCC_RG16F: desc.format = R16G16_FLOAT; break;
case FOURCC_RGBA16F: desc.format = R16G16B16A16_FLOAT; break;
case FOURCC_R32F: desc.format = R32_FLOAT; break;
case FOURCC_RG32F: desc.format = R32G32_FLOAT; break;
case FOURCC_RGBA32F: desc.format = R32G32B32A32_FLOAT; break;
default: break;
}
}
else if(ddspf.flags & DDS_RGB)
{
switch(ddspf.RGBBitCount)
{
case 32:
if(is_rgba_mask(ddspf, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000))
{
desc.format = R8G8B8A8_UNORM;
}
else if(is_rgba_mask(ddspf, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000))
{
desc.format = B8G8R8A8_UNORM;
}
else if(is_rgba_mask(ddspf, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000))
{
desc.format = B8G8R8X8_UNORM;
}
// No DXGI format maps to (0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000) aka D3DFMT_X8B8G8R8
// No DXGI format maps to (0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000) aka D3DFMT_A2R10G10B10
// Note that many common DDS reader/writers (including D3DX) swap the
// the RED/BLUE masks for 10:10:10:2 formats. We assume
// below that the 'backwards' header mask is being used since it is most
// likely written by D3DX. The more robust solution is to use the 'DX10'
// header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly
// For 'correct' writers, this should be 0x000003ff, 0x000ffc00, 0x3ff00000 for RGB data
else if (is_rgba_mask(ddspf, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000))
{
desc.format = R10G10B10A2_UNORM;
}
else if (is_rgba_mask(ddspf, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000))
{
desc.format = R16G16_UNORM;
}
else if (is_rgba_mask(ddspf, 0xffffffff, 0x00000000, 0x00000000, 0x00000000))
{
// The only 32-bit color channel format in D3D9 was R32F
desc.format = R32_FLOAT; // D3DX writes this out as a FourCC of 114
}
break;
case 24:
if(is_rgb_mask(ddspf, 0x00ff0000, 0x0000ff00, 0x000000ff))
{
desc.format = B8G8R8X8_UNORM;
}
break;
case 16:
if (is_rgba_mask(ddspf, 0x7c00, 0x03e0, 0x001f, 0x8000))
{
desc.format = B5G5R5A1_UNORM;
}
else if (is_rgba_mask(ddspf, 0xf800, 0x07e0, 0x001f, 0x0000))
{
desc.format = B5G6R5_UNORM;
}
else if (is_rgba_mask(ddspf, 0x0f00, 0x00f0, 0x000f, 0xf000))
{
desc.format = B4G4R4A4_UNORM;
}
// No DXGI format maps to (0x7c00, 0x03e0, 0x001f, 0x0000) aka D3DFMT_X1R5G5B5.
// No DXGI format maps to (0x0f00, 0x00f0, 0x000f, 0x0000) aka D3DFMT_X4R4G4B4.
// No 3:3:2, 3:3:2:8, or paletted DXGI formats aka D3DFMT_A8R3G3B2, D3DFMT_R3G3B2, D3DFMT_P8, D3DFMT_A8P8, etc.
break;
default:
break;
}
}
else if (ddspf.flags & DDS_LUMINANCE)
{
switch(ddspf.RGBBitCount)
{
case 16:
if (is_rgba_mask(ddspf, 0x0000ffff, 0x00000000, 0x00000000, 0x00000000))
{
desc.format = R16_UNORM; // D3DX10/11 writes this out as DX10 extension.
}
if (is_rgba_mask(ddspf, 0x000000ff, 0x00000000, 0x00000000, 0x0000ff00))
{
desc.format = R8G8_UNORM; // D3DX10/11 writes this out as DX10 extension.
}
break;
case 8:
if (is_rgba_mask(ddspf, 0x000000ff, 0x00000000, 0x00000000, 0x00000000))
{
desc.format = R8_UNORM; // D3DX10/11 writes this out as DX10 extension
}
// No DXGI format maps to (0x0f, 0x00, 0x00, 0xf0) aka D3DFMT_A4L4
if (is_rgba_mask(ddspf, 0x000000ff, 0x00000000, 0x00000000, 0x0000ff00))
{
desc.format = R8G8_UNORM; // Some DDS writers assume the bitcount should be 8 instead of 16
}
break;
}
}
else if (ddspf.flags & DDS_ALPHA)
{
if (ddspf.RGBBitCount == 8)
{
desc.format = A8_UNORM;
}
}
else if (ddspf.flags & DDS_BUMPDUDV)
{
if (ddspf.RGBBitCount == 32)
{
if (is_rgba_mask(ddspf, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000))
{
desc.format = R8G8B8A8_SNORM; // D3DX10/11 writes this out as DX10 extension
}
if (is_rgba_mask(ddspf, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000))
{
desc.format = R16G16_SNORM; // D3DX10/11 writes this out as DX10 extension
}
// No DXGI format maps to (0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000) aka D3DFMT_A2W10V10U10
}
else if (ddspf.RGBBitCount == 16)
{
if (is_rgba_mask(ddspf, 0x000000ff, 0x0000ff00, 0x00000000, 0x00000000))
{
desc.format = R8G8_SNORM; // D3DX10/11 writes this out as DX10 extension
}
}
}
if((header.flags & DDS_HEADER_FLAGS_VOLUME) || (header.caps2 & DDS_HEADER_CAPS2_VOLUME))
{
desc.type = Texture3D;
}
else if (header.caps2 & DDS_HEADER_CAPS2_CUBEMAP)
{
if((header.caps2 & DDS_HEADER_CAPS2_CUBEMAP_ALLFACES) != DDS_HEADER_CAPS2_CUBEMAP_ALLFACES)
{
return ddspp::Error;
}
desc.type = Cubemap;
desc.arraySize = 1;
desc.depth = 1;
}
else
{
desc.type = Texture2D;
}
}
desc.compressed = is_compressed(desc.format);
desc.srgb = is_srgb(desc.format);
desc.bitsPerPixelOrBlock = get_bits_per_pixel_or_block(desc.format);
get_block_size(desc.format, desc.blockWidth, desc.blockHeight);
desc.rowPitch = get_row_pitch(desc.width, desc.bitsPerPixelOrBlock, desc.blockWidth, 0);
desc.depthPitch = desc.rowPitch * desc.height / desc.blockHeight;
desc.headerSize = sizeof(DDS_MAGIC) + sizeof(Header) + (dxt10Extension ? sizeof(HeaderDXT10) : 0);
return ddspp::Success;
}
inline void encode_header(const DXGIFormat format, const unsigned int width, const unsigned int height, const unsigned int depth,
const TextureType type, const unsigned int mipCount, const unsigned int arraySize,
Header& header, HeaderDXT10& dxt10Header)
{
header.size = sizeof(Header);
// Fill in header flags
header.flags = DDS_HEADER_FLAGS_CAPS | DDS_HEADER_FLAGS_HEIGHT | DDS_HEADER_FLAGS_WIDTH | DDS_HEADER_FLAGS_PIXELFORMAT;
header.caps = DDS_HEADER_CAPS_TEXTURE;
header.caps2 = 0;
if (mipCount > 1)
{
header.flags |= DDS_HEADER_FLAGS_MIPMAP;
header.caps |= DDS_HEADER_CAPS_COMPLEX | DDS_HEADER_CAPS_MIPMAP;
}
unsigned int bitsPerPixelOrBlock = get_bits_per_pixel_or_block(format);
if (is_compressed(format))
{
header.flags |= DDS_HEADER_FLAGS_LINEARSIZE;
unsigned int blockWidth, blockHeight;
get_block_size(format, blockWidth, blockHeight);
header.pitchOrLinearSize = width * height * bitsPerPixelOrBlock / (8 * blockWidth * blockHeight);
}
else
{
header.flags |= DDS_HEADER_FLAGS_PITCH;
header.pitchOrLinearSize = width * bitsPerPixelOrBlock / 8;
}
header.height = height;
header.width = width;
header.depth = depth;
header.mipMapCount = mipCount;
header.reserved1[0] = header.reserved1[1] = header.reserved1[2] = 0;
header.reserved1[3] = header.reserved1[4] = header.reserved1[5] = 0;
header.reserved1[6] = header.reserved1[7] = header.reserved1[8] = 0;
header.reserved1[9] = header.reserved1[10] = 0;
// Fill in pixel format
PixelFormat& ddspf = header.ddspf;
ddspf.size = sizeof(PixelFormat);
ddspf.fourCC = FOURCC_DXT10;
ddspf.flags = 0;
ddspf.flags |= DDS_FOURCC;
dxt10Header.dxgiFormat = format;
dxt10Header.arraySize = arraySize;
dxt10Header.miscFlag = 0;
if (type == Texture1D)
{
dxt10Header.resourceDimension = DXGI_Texture1D;
}
else if (type == Texture2D)
{
dxt10Header.resourceDimension = DXGI_Texture2D;
}
else if (type == Cubemap)
{
dxt10Header.resourceDimension = DXGI_Texture2D;
dxt10Header.miscFlag |= DXGI_MISC_FLAG_CUBEMAP;
header.caps |= DDS_HEADER_CAPS_COMPLEX;
header.caps2 |= DDS_HEADER_CAPS2_CUBEMAP | DDS_HEADER_CAPS2_CUBEMAP_ALLFACES;
}
else if (type == Texture3D)
{
dxt10Header.resourceDimension = DXGI_Texture3D;
header.flags |= DDS_HEADER_FLAGS_VOLUME;
header.caps2 |= DDS_HEADER_CAPS2_VOLUME;
}
// dxt10Header.miscFlag TODO Alpha Mode
// Unused
header.caps3 = 0;
header.caps4 = 0;
header.reserved2 = 0;
}
// Returns the offset from the base pointer to the desired mip and slice
// Slice is either a texture from an array, a face from a cubemap, or a 2D slice of a volume texture
inline ddspp_constexpr unsigned int get_offset(const Descriptor& desc, const unsigned int mip, const unsigned int slice)
{
// The mip/slice arrangement is different between texture arrays and volume textures
//
// Arrays
// __________ _____ __ __________ _____ __ __________ _____ __