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 }