1 /*
2 Copyright (c) 2019-2020 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.resource.dds;
29 
30 import std.stdio;
31 import std.file;
32 
33 import dlib.core.memory;
34 import dlib.core.stream;
35 import dlib.core.compound;
36 import dlib.image.color;
37 import dlib.image.image;
38 import dlib.image.io.utils;
39 
40 import dagon.graphics.containerimage;
41 
42 // version = DDSDebug;
43 
44 struct DDSPixelFormat
45 {
46     uint size;
47     uint flags;
48     uint fourCC;
49     uint bpp;
50     uint redMask;
51     uint greenMask;
52     uint blueMask;
53     uint alphaMask;
54 }
55 
56 struct DDSCaps
57 {
58     uint caps;
59     uint caps2;
60     uint caps3;
61     uint caps4;
62 }
63 
64 struct DDSColorKey
65 {
66     uint lowVal;
67     uint highVal;
68 }
69 
70 struct DDSHeader
71 {
72     uint size;
73     uint flags;
74     uint height;
75     uint width;
76     uint pitch;
77     uint depth;
78     uint mipMapLevels;
79     uint alphaBitDepth;
80     uint reserved;
81     uint surface;
82 
83     DDSColorKey ckDestOverlay;
84     DDSColorKey ckDestBlt;
85     DDSColorKey ckSrcOverlay;
86     DDSColorKey ckSrcBlt;
87 
88     DDSPixelFormat format;
89     DDSCaps caps;
90 
91     uint textureStage;
92 }
93 
94 enum DXGIFormat
95 {
96     UNKNOWN = 0,
97     R32G32B32A32_TYPELESS = 1,
98     R32G32B32A32_FLOAT = 2,
99     R32G32B32A32_UINT = 3,
100     R32G32B32A32_SINT = 4,
101     R32G32B32_TYPELESS = 5,
102     R32G32B32_FLOAT = 6,
103     R32G32B32_UINT = 7,
104     R32G32B32_SINT = 8,
105     R16G16B16A16_TYPELESS = 9,
106     R16G16B16A16_FLOAT = 10,
107     R16G16B16A16_UNORM = 11,
108     R16G16B16A16_UINT = 12,
109     R16G16B16A16_SNORM = 13,
110     R16G16B16A16_SINT = 14,
111     R32G32_TYPELESS = 15,
112     R32G32_FLOAT = 16,
113     R32G32_UINT = 17,
114     R32G32_SINT = 18,
115     R32G8X24_TYPELESS = 19,
116     D32_FLOAT_S8X24_UINT = 20,
117     R32_FLOAT_X8X24_TYPELESS = 21,
118     X32_TYPELESS_G8X24_UINT = 22,
119     R10G10B10A2_TYPELESS = 23,
120     R10G10B10A2_UNORM = 24,
121     R10G10B10A2_UINT = 25,
122     R11G11B10_FLOAT = 26,
123     R8G8B8A8_TYPELESS = 27,
124     R8G8B8A8_UNORM = 28,
125     R8G8B8A8_UNORM_SRGB = 29,
126     R8G8B8A8_UINT = 30,
127     R8G8B8A8_SNORM = 31,
128     R8G8B8A8_SINT = 32,
129     R16G16_TYPELESS = 33,
130     R16G16_FLOAT = 34,
131     R16G16_UNORM = 35,
132     R16G16_UINT = 36,
133     R16G16_SNORM = 37,
134     R16G16_SINT = 38,
135     R32_TYPELESS = 39,
136     D32_FLOAT = 40,
137     R32_FLOAT = 41,
138     R32_UINT = 42,
139     R32_SINT = 43,
140     R24G8_TYPELESS = 44,
141     D24_UNORM_S8_UINT = 45,
142     R24_UNORM_X8_TYPELESS = 46,
143     X24_TYPELESS_G8_UINT = 47,
144     R8G8_TYPELESS = 48,
145     R8G8_UNORM = 49,
146     R8G8_UINT = 50,
147     R8G8_SNORM = 51,
148     R8G8_SINT = 52,
149     R16_TYPELESS = 53,
150     R16_FLOAT = 54,
151     D16_UNORM = 55,
152     R16_UNORM = 56,
153     R16_UINT = 57,
154     R16_SNORM = 58,
155     R16_SINT = 59,
156     R8_TYPELESS = 60,
157     R8_UNORM = 61,
158     R8_UINT = 62,
159     R8_SNORM = 63,
160     R8_SINT = 64,
161     A8_UNORM = 65,
162     R1_UNORM = 66,
163     R9G9B9E5_SHAREDEXP = 67,
164     R8G8_B8G8_UNORM = 68,
165     G8R8_G8B8_UNORM = 69,
166     BC1_TYPELESS = 70,
167     BC1_UNORM = 71,
168     BC1_UNORM_SRGB = 72,
169     BC2_TYPELESS = 73,
170     BC2_UNORM = 74,
171     BC2_UNORM_SRGB = 75,
172     BC3_TYPELESS = 76,
173     BC3_UNORM = 77,
174     BC3_UNORM_SRGB = 78,
175     BC4_TYPELESS = 79,
176     BC4_UNORM = 80,
177     BC4_SNORM = 81,
178     BC5_TYPELESS = 82,
179     BC5_UNORM = 83,
180     BC5_SNORM = 84,
181     B5G6R5_UNORM = 85,
182     B5G5R5A1_UNORM = 86,
183     B8G8R8A8_UNORM = 87,
184     B8G8R8X8_UNORM = 88,
185     R10G10B10_XR_BIAS_A2_UNORM = 89,
186     B8G8R8A8_TYPELESS = 90,
187     B8G8R8A8_UNORM_SRGB = 91,
188     B8G8R8X8_TYPELESS = 92,
189     B8G8R8X8_UNORM_SRGB = 93,
190     BC6H_TYPELESS = 94,
191     BC6H_UF16 = 95,
192     BC6H_SF16 = 96,
193     BC7_TYPELESS = 97,
194     BC7_UNORM = 98,
195     BC7_UNORM_SRGB = 99,
196     AYUV = 100,
197     Y410 = 101,
198     Y416 = 102,
199     NV12 = 103,
200     P010 = 104,
201     P016 = 105,
202     OPAQUE_420 = 106,
203     YUY2 = 107,
204     Y210 = 108,
205     Y216 = 109,
206     NV11 = 110,
207     AI44 = 111,
208     IA44 = 112,
209     P8 = 113,
210     A8P8 = 114,
211     B4G4R4A4_UNORM = 115,
212     P208 = 130,
213     V208 = 131,
214     V408 = 132
215 }
216 
217 enum Caps
218 {
219     Complex = 0x8,
220     Mipmap = 0x400000,
221     Texture = 0x1000
222 }
223 
224 enum Caps2
225 {
226     Cubemap = 0x200,
227     CubemapPositiveX = 0x400,
228     CubemapNegativeX = 0x800,
229     CubemapPositiveY = 0x1000,
230     CubemapNegativeY = 0x2000,
231     CubemapPositiveZ = 0x4000,
232     CubemapNegativeZ = 0x8000,
233     CubemapAllFaces = 0xFC00
234 }
235 
236 struct DDSHeaderDXT10
237 {
238     uint dxgiFormat;
239     uint resourceDimension;
240     uint miscFlag;
241     uint arraySize;
242     uint miscFlags2;
243 }
244 
245 enum D3D10ResourceDimension
246 {
247     Unknown = 0,
248     Buffer = 1,
249     Texture1D = 2,
250     Texture2D = 3,
251     Texture3D = 4
252 }
253 
254 enum D3D10ResourceMisc
255 {
256     GenerateMips = 0x01,
257     MiscShared = 0x02,
258     TextureCube = 0x04,
259     SharedKeyedMutex = 0x10,
260     GDICompatible = 0x20
261 }
262 
263 uint makeFourCC(char ch0, char ch1, char ch2, char ch3)
264 {
265     return
266         ((cast(uint)ch3 << 24) & 0xFF000000) |
267         ((cast(uint)ch2 << 16) & 0x00FF0000) |
268         ((cast(uint)ch1 << 8)  & 0x0000FF00) |
269         ((cast(uint)ch0)       & 0x000000FF);
270 }
271 
272 enum FOURCC_DXT1 = makeFourCC('D', 'X', 'T', '1');
273 enum FOURCC_DXT3 = makeFourCC('D', 'X', 'T', '3');
274 enum FOURCC_DXT5 = makeFourCC('D', 'X', 'T', '5');
275 enum FOURCC_DX10 = makeFourCC('D', 'X', '1', '0');
276 
277 enum FOURCC_BC4U = makeFourCC('B', 'C', '4', 'U');
278 enum FOURCC_BC4S = makeFourCC('B', 'C', '4', 'S');
279 enum FOURCC_ATI2 = makeFourCC('A', 'T', 'I', '2');
280 enum FOURCC_BC5S = makeFourCC('B', 'C', '5', 'S');
281 enum FOURCC_RGBG = makeFourCC('R', 'G', 'B', 'G');
282 enum FOURCC_GRGB = makeFourCC('G', 'R', 'G', 'B');
283 
284 enum FOURCC_DXT2 = makeFourCC('D', 'X', 'T', '2');
285 
286 DXGIFormat resourceFormatFromFourCC(uint fourCC)
287 {
288     DXGIFormat format;
289     
290     switch(fourCC)
291     {
292         case FOURCC_DXT1: format = DXGIFormat.BC1_UNORM; break;
293         case FOURCC_DXT3: format = DXGIFormat.BC2_UNORM; break;
294         case FOURCC_DXT5: format = DXGIFormat.BC3_UNORM; break;
295         case FOURCC_BC4U: format = DXGIFormat.BC4_UNORM; break;
296         case FOURCC_BC4S: format = DXGIFormat.BC4_SNORM; break;
297         case FOURCC_ATI2: format = DXGIFormat.BC5_UNORM; break;
298         case FOURCC_BC5S: format = DXGIFormat.BC5_SNORM; break;
299         case FOURCC_RGBG: format = DXGIFormat.R8G8_B8G8_UNORM; break;
300         case FOURCC_GRGB: format = DXGIFormat.G8R8_G8B8_UNORM; break;
301         case 36:          format = DXGIFormat.R16G16B16A16_UNORM; break;
302         case 110:         format = DXGIFormat.R16G16B16A16_SNORM; break;
303         case 111:         format = DXGIFormat.R16_FLOAT; break;
304         case 112:         format = DXGIFormat.R16G16_FLOAT; break;
305         case 113:         format = DXGIFormat.R16G16B16A16_FLOAT; break;
306         case 114:         format = DXGIFormat.R32_FLOAT; break;
307         case 115:         format = DXGIFormat.R32G32_FLOAT; break;
308         case 116:         format = DXGIFormat.R32G32B32A32_FLOAT; break;
309         default:          format = DXGIFormat.UNKNOWN; break;
310     }
311     
312     return format;
313 }
314 
315 Compound!(ContainerImage, string) loadDDS(InputStream istrm)
316 {
317     ContainerImage img = null;
318 
319     void finalize()
320     {
321     }
322 
323     Compound!(ContainerImage, string) error(string errorMsg)
324     {
325         finalize();
326         if (img)
327         {
328             Delete(img);
329             img = null;
330         }
331         return compound(img, errorMsg);
332     }
333 
334     char[4] magic;
335 
336     if (!istrm.fillArray(magic))
337     {
338         return error("loadDDS error: not a DDS file or corrupt data");
339     }
340 
341     version(DDSDebug)
342     {
343         writeln("Signature: ", magic);
344     }
345 
346     if (magic != "DDS ")
347     {
348         return error("loadDDS error: not a DDS file");
349     }
350 
351     DDSHeader hdr = readStruct!DDSHeader(istrm);
352 
353     version(DDSDebug)
354     {
355         writeln("hdr.size: ", hdr.size);
356         writeln("hdr.flags: ", hdr.flags);
357         writeln("hdr.height: ", hdr.height);
358         writeln("hdr.width: ", hdr.width);
359         writeln("hdr.pitch: ", hdr.pitch);
360         writeln("hdr.depth: ", hdr.depth);
361         writeln("hdr.mipMapLevels: ", hdr.mipMapLevels);
362         writeln("hdr.alphaBitDepth: ", hdr.alphaBitDepth);
363         writeln("hdr.reserved: ", hdr.reserved);
364         writeln("hdr.surface: ", hdr.surface);
365 
366         writeln("hdr.ckDestOverlay.lowVal: ", hdr.ckDestOverlay.lowVal);
367         writeln("hdr.ckDestOverlay.highVal: ", hdr.ckDestOverlay.highVal);
368         writeln("hdr.ckDestBlt.lowVal: ", hdr.ckDestBlt.lowVal);
369         writeln("hdr.ckDestBlt.highVal: ", hdr.ckDestBlt.highVal);
370         writeln("hdr.ckSrcOverlay.lowVal: ", hdr.ckSrcOverlay.lowVal);
371         writeln("hdr.ckSrcOverlay.highVal: ", hdr.ckSrcOverlay.highVal);
372         writeln("hdr.ckSrcBlt.lowVal: ", hdr.ckSrcBlt.lowVal);
373         writeln("hdr.ckSrcBlt.highVal: ", hdr.ckSrcBlt.highVal);
374 
375         writeln("hdr.format.size: ", hdr.format.size);
376         writeln("hdr.format.flags: ", hdr.format.flags);
377         writeln("hdr.format.fourCC: ", hdr.format.fourCC);
378         writeln("hdr.format.bpp: ", hdr.format.bpp);
379         writeln("hdr.format.redMask: ", hdr.format.redMask);
380         writeln("hdr.format.greenMask: ", hdr.format.greenMask);
381         writeln("hdr.format.blueMask: ", hdr.format.blueMask);
382         writeln("hdr.format.alphaMask: ", hdr.format.alphaMask);
383 
384         writeln("hdr.caps.caps: ", hdr.caps.caps);
385         writeln("hdr.caps.caps2: ", hdr.caps.caps2);
386         writeln("hdr.caps.caps3: ", hdr.caps.caps3);
387         writeln("hdr.caps.caps4: ", hdr.caps.caps4);
388 
389         writeln("hdr.textureStage: ", hdr.textureStage);
390     }
391 
392     ContainerImageFormat format;
393 
394     DXGIFormat fmt;
395     if (hdr.format.fourCC == FOURCC_DX10)
396     {
397         DDSHeaderDXT10 dx10 = readStruct!DDSHeaderDXT10(istrm);
398         fmt = cast(DXGIFormat)dx10.dxgiFormat;
399     }
400     else
401     {
402         fmt = resourceFormatFromFourCC(hdr.format.fourCC);
403     }
404     
405     version(DDSDebug) writeln("format: ", fmt);
406 
407     switch(fmt)
408     {
409         case DXGIFormat.R8_UNORM: format = ContainerImageFormat.R8; break;
410         case DXGIFormat.R8G8_UNORM: format = ContainerImageFormat.RG8; break;
411         case DXGIFormat.R8G8B8A8_UNORM: format = ContainerImageFormat.RGBA8; break;
412         case DXGIFormat.R32G32B32A32_FLOAT: format = ContainerImageFormat.RGBAF32; break;
413         case DXGIFormat.R16G16B16A16_FLOAT: format = ContainerImageFormat.RGBAF16; break;
414         case DXGIFormat.BC1_UNORM: format = ContainerImageFormat.S3TC_RGB_DXT1; break;
415         case DXGIFormat.BC2_UNORM: format = ContainerImageFormat.S3TC_RGBA_DXT3; break;
416         case DXGIFormat.BC3_UNORM: format = ContainerImageFormat.S3TC_RGBA_DXT5; break;
417         case DXGIFormat.BC4_UNORM: format = ContainerImageFormat.RGTC1_R; break;
418         case DXGIFormat.BC4_SNORM: format = ContainerImageFormat.RGTC1_R_S; break;
419         case DXGIFormat.BC5_UNORM: format = ContainerImageFormat.RGTC2_RG; break;
420         case DXGIFormat.BC5_SNORM: format = ContainerImageFormat.RGTC2_RG_S; break;
421         case DXGIFormat.BC7_UNORM: format = ContainerImageFormat.BPTC_RGBA_UNORM; break;
422         case DXGIFormat.BC7_UNORM_SRGB: format = ContainerImageFormat.BPTC_SRGBA_UNORM; break;
423         case DXGIFormat.BC6H_SF16: format = ContainerImageFormat.BPTC_RGB_SF; break;
424         case DXGIFormat.BC6H_UF16: format = ContainerImageFormat.BPTC_RGB_UF; break;
425         default:
426             return error("loadDDS error: unsupported resource format");
427     }
428     
429     bool isCubemap = false;
430     if (hdr.caps.caps2 & Caps2.Cubemap)
431     {
432         if (hdr.caps.caps2 & Caps2.CubemapAllFaces)
433             isCubemap = true;
434         else
435             return error("loadDDS error: incomplete cubemap");
436     }
437 
438     size_t bufferSize = cast(size_t)(istrm.size - istrm.getPosition);
439     version(DDSDebug) writeln("bufferSize: ", bufferSize);
440 
441     img = New!ContainerImage(hdr.width, hdr.height, format, hdr.mipMapLevels, bufferSize, isCubemap);
442     istrm.readBytes(img.data.ptr, bufferSize);
443 
444     return compound(img, "");
445 }