1 // DDS decoders
2 // Based on code from Nvidia's DDS example:
3 // http://www.nvidia.com/object/dxtc_decompression_code.html
4 //
5 // Copyright (c) 2003 Randy Reddig
6 // All rights reserved.
7 //
8 // Redistribution and use in source and binary forms, with or without modification,
9 // are permitted provided that the following conditions are met:
10 //
11 // Redistributions of source code must retain the above copyright notice, this list
12 // of conditions and the following disclaimer.
13 //
14 // Redistributions in binary form must reproduce the above copyright notice, this
15 // list of conditions and the following disclaimer in the documentation and/or
16 // other materials provided with the distribution.
17 //
18 // Neither the names of the copyright holders nor the names of its contributors may
19 // be used to endorse or promote products derived from this software without
20 // specific prior written permission.
21 //
22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // D port and further changes by Ketmar // Invisible Vector
34 module arsd.dds;
35 
36 import arsd.color : Color, TrueColorImage;
37 
38 
39 // ////////////////////////////////////////////////////////////////////////// //
40 public bool ddsDetect (const(void)[] buf, int* width=null, int* height=null) nothrow @trusted @nogc {
41   if (buf.length < 128) return false;
42   auto data = cast(const(ubyte)[])buf;
43 
44   uint getUInt (uint ofs) nothrow @trusted @nogc {
45     if (ofs >= data.length) return uint.max;
46     if (data.length-ofs < 4) return uint.max;
47     return data.ptr[ofs]|(data.ptr[ofs+1]<<8)|(data.ptr[ofs+2]<<16)|(data.ptr[ofs+3]<<24);
48   }
49 
50   // signature
51   if (data.ptr[0] != 'D' || data.ptr[1] != 'D' || data.ptr[2] != 'S' || data.ptr[3] != ' ') return false;
52   // header size check
53   if (getUInt(4) != 124) return false;
54 
55   int w = getUInt(4*4);
56   int h = getUInt(3*4);
57   // arbitrary limits
58   if (w < 1 || h < 1 || w > 65500 || h > 65500) return false;
59   if (width !is null) *width = w;
60   if (height !is null) *height = h;
61 
62   // check pixel format
63   if (getUInt(76) < 8) return false; // size
64   immutable flags = getUInt(80);
65   if (flags&DDS_FOURCC) {
66     // DXTn
67     if (data.ptr[84+0] != 'D' || data.ptr[84+1] != 'X' || data.ptr[84+2] != 'T') return false;
68     if (data.ptr[84+3] < '1' || data.ptr[84+3] > '5') return false;
69   } else if (flags == DDS_RGB || flags == DDS_RGBA) {
70     immutable bitcount = getUInt(88);
71     if (bitcount != 24 && bitcount != 32) return false;
72     // ARGB8888
73     //if (data.ptr[84+0] == 0 || data.ptr[84+1] == 0 || data.ptr[84+2] == 0 || data.ptr[84+3] == 0) return true;
74   }
75   return true;
76 }
77 
78 
79 // ////////////////////////////////////////////////////////////////////////// //
80 public TrueColorImage ddsLoadFromMemory (const(void)[] buf) {
81   int w, h;
82   if (!ddsDetect(buf, &w, &h)) throw new Exception("not a DDS image");
83 
84   //FIXME: check for OOB access in decoders
85   const(ddsBuffer_t)* dds = cast(const(ddsBuffer_t)*)buf.ptr;
86 
87   auto tc = new TrueColorImage(w, h);
88   scope(failure) .destroy(tc);
89 
90   if (!DDSDecompress(dds, tc.imageData.colors)) throw new Exception("invalid dds image");
91 
92   return tc;
93 }
94 
95 
96 static import std.stdio;
97 public TrueColorImage ddsLoadFromFile() (std.stdio.File fl) {
98   import core.stdc.stdlib : malloc, free;
99   auto fsize = fl.size-fl.tell;
100   if (fsize < 128 || fsize > int.max/8) throw new Exception("invalid dds size");
101   ddsBuffer_t* dds = cast(ddsBuffer_t*)malloc(cast(uint)fsize);
102   if (dds is null) throw new Exception("out of memory");
103   scope(exit) free(dds);
104   ubyte[] lb = (cast(ubyte*)dds)[0..cast(uint)fsize];
105   while (lb.length > 0) {
106     auto rd = fl.rawRead(lb[]);
107     if (rd.length < 1) throw new Exception("read error");
108     lb = lb[rd.length..$];
109   }
110   return ddsLoadFromMemory((cast(ubyte*)dds)[0..cast(uint)fsize]);
111 }
112 
113 
114 static if (__traits(compiles, { import iv.vfs; })) {
115   import iv.vfs;
116   public TrueColorImage ddsLoadFromFile() (VFile fl) {
117     import core.stdc.stdlib : malloc, free;
118     auto fsize = fl.size-fl.tell;
119     if (fsize < 128 || fsize > int.max/8) throw new Exception("invalid dds size");
120     ddsBuffer_t* dds = cast(ddsBuffer_t*)malloc(cast(uint)fsize);
121     if (dds is null) throw new Exception("out of memory");
122     scope(exit) free(dds);
123     ubyte[] lb = (cast(ubyte*)dds)[0..cast(uint)fsize];
124     fl.rawReadExact(lb);
125     return ddsLoadFromMemory(lb);
126   }
127 }
128 
129 
130 
131 // ////////////////////////////////////////////////////////////////////////// //
132 private nothrow @trusted @nogc:
133 
134 // dds definition
135 enum DDSPixelFormat {
136   Unknown,
137   RGB888,
138   ARGB8888,
139   DXT1,
140   DXT2,
141   DXT3,
142   DXT4,
143   DXT5,
144 }
145 
146 
147 // 16bpp stuff
148 enum DDS_LOW_5 = 0x001F;
149 enum DDS_MID_6 = 0x07E0;
150 enum DDS_HIGH_5 = 0xF800;
151 enum DDS_MID_555 = 0x03E0;
152 enum DDS_HI_555 = 0x7C00;
153 
154 enum DDS_FOURCC = 0x00000004U;
155 enum DDS_RGB = 0x00000040U;
156 enum DDS_RGBA = 0x00000041U;
157 enum DDS_DEPTH = 0x00800000U;
158 
159 enum DDS_COMPLEX = 0x00000008U;
160 enum DDS_CUBEMAP = 0x00000200U;
161 enum DDS_VOLUME = 0x00200000U;
162 
163 
164 // structures
165 align(1) struct ddsColorKey_t {
166 align(1):
167   uint colorSpaceLowValue;
168   uint colorSpaceHighValue;
169 }
170 
171 
172 align(1) struct ddsCaps_t {
173 align(1):
174   uint caps1;
175   uint caps2;
176   uint caps3;
177   uint caps4;
178 }
179 
180 
181 align(1) struct ddsMultiSampleCaps_t {
182 align(1):
183   ushort flipMSTypes;
184   ushort bltMSTypes;
185 }
186 
187 
188 align(1) struct ddsPixelFormat_t {
189 align(1):
190   uint size;
191   uint flags;
192   char[4] fourCC;
193   union {
194     uint rgbBitCount;
195     uint yuvBitCount;
196     uint zBufferBitDepth;
197     uint alphaBitDepth;
198     uint luminanceBitCount;
199     uint bumpBitCount;
200     uint privateFormatBitCount;
201   }
202   union {
203     uint rBitMask;
204     uint yBitMask;
205     uint stencilBitDepth;
206     uint luminanceBitMask;
207     uint bumpDuBitMask;
208     uint operations;
209   }
210   union {
211     uint gBitMask;
212     uint uBitMask;
213     uint zBitMask;
214     uint bumpDvBitMask;
215     ddsMultiSampleCaps_t multiSampleCaps;
216   }
217   union {
218     uint bBitMask;
219     uint vBitMask;
220     uint stencilBitMask;
221     uint bumpLuminanceBitMask;
222   }
223   union {
224     uint rgbAlphaBitMask;
225     uint yuvAlphaBitMask;
226     uint luminanceAlphaBitMask;
227     uint rgbZBitMask;
228     uint yuvZBitMask;
229   }
230 }
231 //pragma(msg, ddsPixelFormat_t.sizeof);
232 
233 
234 align(1) struct ddsBuffer_t {
235 align(1):
236   // magic: 'dds '
237   char[4] magic;
238 
239   // directdraw surface
240   uint size;
241   uint flags;
242   uint height;
243   uint width;
244   union {
245     int pitch;
246     uint linearSize;
247   }
248   uint backBufferCount;
249   union {
250     uint mipMapCount;
251     uint refreshRate;
252     uint srcVBHandle;
253   }
254   uint alphaBitDepth;
255   uint reserved;
256   void* surface;
257   union {
258     ddsColorKey_t ckDestOverlay;
259     uint emptyFaceColor;
260   }
261   ddsColorKey_t ckDestBlt;
262   ddsColorKey_t ckSrcOverlay;
263   ddsColorKey_t ckSrcBlt;
264   union {
265     ddsPixelFormat_t pixelFormat;
266     uint fvf;
267   }
268   ddsCaps_t ddsCaps;
269   uint textureStage;
270 
271   // data (Varying size)
272   ubyte[0] data;
273 }
274 //pragma(msg, ddsBuffer_t.sizeof);
275 //pragma(msg, ddsBuffer_t.pixelFormat.offsetof+4*2);
276 
277 
278 align(1) struct ddsColorBlock_t {
279 align(1):
280   ushort[2] colors;
281   ubyte[4] row;
282 }
283 static assert(ddsColorBlock_t.sizeof == 8);
284 
285 
286 align(1) struct ddsAlphaBlockExplicit_t {
287 align(1):
288   ushort[4] row;
289 }
290 static assert(ddsAlphaBlockExplicit_t.sizeof == 8);
291 
292 
293 align(1) struct ddsAlphaBlock3BitLinear_t {
294 align(1):
295   ubyte alpha0;
296   ubyte alpha1;
297   ubyte[6] stuff;
298 }
299 static assert(ddsAlphaBlock3BitLinear_t.sizeof == 8);
300 
301 
302 // ////////////////////////////////////////////////////////////////////////// //
303 //public int DDSGetInfo( ddsBuffer_t *dds, int *width, int *height, DDSPixelFormat *pf );
304 //public int DDSDecompress( ddsBuffer_t *dds, ubyte *pixels );
305 
306 // extracts relevant info from a dds texture, returns `true` on success
307 /*public*/ bool DDSGetInfo (const(ddsBuffer_t)* dds, int* width, int* height, DDSPixelFormat* pf) {
308   // dummy test
309   if (dds is null) return false;
310 
311   // test dds header
312   if (dds.magic != "DDS ") return false;
313   if (DDSLittleLong(dds.size) != 124) return false;
314   // arbitrary limits
315   if (DDSLittleLong(dds.width) < 1 || DDSLittleLong(dds.width) > 65535) return false;
316   if (DDSLittleLong(dds.height) < 1 || DDSLittleLong(dds.height) > 65535) return false;
317 
318   // extract width and height
319   if (width !is null) *width = DDSLittleLong(dds.width);
320   if (height !is null) *height = DDSLittleLong(dds.height);
321 
322   // get pixel format
323   DDSDecodePixelFormat(dds, pf);
324 
325   // return ok
326   return true;
327 }
328 
329 
330 // decompresses a dds texture into an rgba image buffer, returns 0 on success
331 /*public*/ bool DDSDecompress (const(ddsBuffer_t)* dds, Color[] pixels) {
332   int width, height;
333   DDSPixelFormat pf;
334 
335   // get dds info
336   if (!DDSGetInfo(dds, &width, &height, &pf)) return false;
337   // arbitrary limits
338   if (DDSLittleLong(dds.width) < 1 || DDSLittleLong(dds.width) > 65535) return false;
339   if (DDSLittleLong(dds.height) < 1 || DDSLittleLong(dds.height) > 65535) return false;
340   if (pixels.length < width*height) return false;
341 
342   // decompress
343   final switch (pf) {
344     // FIXME: support other [a]rgb formats
345     case DDSPixelFormat.RGB888: return DDSDecompressRGB888(dds, width, height, pixels.ptr);
346     case DDSPixelFormat.ARGB8888: return DDSDecompressARGB8888(dds, width, height, pixels.ptr);
347     case DDSPixelFormat.DXT1: return DDSDecompressDXT1(dds, width, height, pixels.ptr);
348     case DDSPixelFormat.DXT2: return DDSDecompressDXT2(dds, width, height, pixels.ptr);
349     case DDSPixelFormat.DXT3: return DDSDecompressDXT3(dds, width, height, pixels.ptr);
350     case DDSPixelFormat.DXT4: return DDSDecompressDXT4(dds, width, height, pixels.ptr);
351     case DDSPixelFormat.DXT5: return DDSDecompressDXT5(dds, width, height, pixels.ptr);
352     case DDSPixelFormat.Unknown: break;
353   }
354 
355   return false;
356 }
357 
358 
359 // ////////////////////////////////////////////////////////////////////////// //
360 private:
361 
362 version(BigEndian) {
363   int DDSLittleLong (int src) pure nothrow @safe @nogc {
364     pragma(inline, true);
365     return
366       ((src&0xFF000000)>>24)|
367       ((src&0x00FF0000)>>8)|
368       ((src&0x0000FF00)<<8)|
369       ((src&0x000000FF)<<24);
370   }
371   short DDSLittleShort (short src) pure nothrow @safe @nogc {
372     pragma(inline, true);
373     return cast(short)(((src&0xFF00)>>8)|((src&0x00FF)<<8));
374   }
375 } else {
376   // little endian
377   int DDSLittleLong (int src) pure nothrow @safe @nogc { pragma(inline, true); return src; }
378   short DDSLittleShort (short src) pure nothrow @safe @nogc { pragma(inline, true); return src; }
379 }
380 
381 
382 // determines which pixel format the dds texture is in
383 private void DDSDecodePixelFormat (const(ddsBuffer_t)* dds, DDSPixelFormat* pf) {
384   // dummy check
385   if (dds is null || pf is null) return;
386   *pf = DDSPixelFormat.Unknown;
387 
388   if (dds.pixelFormat.size < 8) return;
389 
390   if (dds.pixelFormat.flags&DDS_FOURCC) {
391     // DXTn
392          if (dds.pixelFormat.fourCC == "DXT1") *pf = DDSPixelFormat.DXT1;
393     else if (dds.pixelFormat.fourCC == "DXT2") *pf = DDSPixelFormat.DXT2;
394     else if (dds.pixelFormat.fourCC == "DXT3") *pf = DDSPixelFormat.DXT3;
395     else if (dds.pixelFormat.fourCC == "DXT4") *pf = DDSPixelFormat.DXT4;
396     else if (dds.pixelFormat.fourCC == "DXT5") *pf = DDSPixelFormat.DXT5;
397     else return;
398   } else if (dds.pixelFormat.flags == DDS_RGB || dds.pixelFormat.flags == DDS_RGBA) {
399     //immutable bitcount = getUInt(88);
400          if (dds.pixelFormat.rgbBitCount == 24) *pf = DDSPixelFormat.RGB888;
401     else if (dds.pixelFormat.rgbBitCount == 32) *pf = DDSPixelFormat.ARGB8888;
402     else return;
403   }
404 }
405 
406 
407 // extracts colors from a dds color block
408 private void DDSGetColorBlockColors (const(ddsColorBlock_t)* block, Color* colors) {
409   ushort word;
410 
411   // color 0
412   word = DDSLittleShort(block.colors.ptr[0]);
413   colors[0].a = 0xff;
414 
415   // extract rgb bits
416   colors[0].b = cast(ubyte)word;
417   colors[0].b <<= 3;
418   colors[0].b |= (colors[0].b>>5);
419   word >>= 5;
420   colors[0].g = cast(ubyte)word;
421   colors[0].g <<= 2;
422   colors[0].g |= (colors[0].g>>5);
423   word >>= 6;
424   colors[0].r = cast(ubyte)word;
425   colors[0].r <<= 3;
426   colors[0].r |= (colors[0].r>>5);
427 
428   // same for color 1
429   word = DDSLittleShort(block.colors.ptr[1]);
430   colors[1].a = 0xff;
431 
432   // extract rgb bits
433   colors[1].b = cast(ubyte)word;
434   colors[1].b <<= 3;
435   colors[1].b |= (colors[1].b>>5);
436   word >>= 5;
437   colors[1].g = cast(ubyte)word;
438   colors[1].g <<= 2;
439   colors[1].g |= (colors[1].g>>5);
440   word >>= 6;
441   colors[1].r = cast(ubyte)word;
442   colors[1].r <<= 3;
443   colors[1].r |= (colors[1].r>>5);
444 
445   // use this for all but the super-freak math method
446   if (block.colors.ptr[0] > block.colors.ptr[1]) {
447     /* four-color block: derive the other two colors.
448        00 = color 0, 01 = color 1, 10 = color 2, 11 = color 3
449        these two bit codes correspond to the 2-bit fields
450        stored in the 64-bit block. */
451     word = (cast(ushort)colors[0].r*2+cast(ushort)colors[1].r)/3;
452                       // no +1 for rounding
453                       // as bits have been shifted to 888
454     colors[2].r = cast(ubyte) word;
455     word = (cast(ushort)colors[0].g*2+cast(ushort)colors[1].g)/3;
456     colors[2].g = cast(ubyte) word;
457     word = (cast(ushort)colors[0].b*2+cast(ushort)colors[1].b)/3;
458     colors[2].b = cast(ubyte)word;
459     colors[2].a = 0xff;
460 
461     word = (cast(ushort)colors[0].r+cast(ushort)colors[1].r*2)/3;
462     colors[3].r = cast(ubyte)word;
463     word = (cast(ushort)colors[0].g+cast(ushort)colors[1].g*2)/3;
464     colors[3].g = cast(ubyte)word;
465     word = (cast(ushort)colors[0].b+cast(ushort)colors[1].b*2)/3;
466     colors[3].b = cast(ubyte)word;
467     colors[3].a = 0xff;
468   } else {
469     /* three-color block: derive the other color.
470        00 = color 0, 01 = color 1, 10 = color 2,
471        11 = transparent.
472        These two bit codes correspond to the 2-bit fields
473        stored in the 64-bit block */
474     word = (cast(ushort)colors[0].r+cast(ushort)colors[1].r)/2;
475     colors[2].r = cast(ubyte)word;
476     word = (cast(ushort)colors[0].g+cast(ushort)colors[1].g)/2;
477     colors[2].g = cast(ubyte)word;
478     word = (cast(ushort)colors[0].b+cast(ushort)colors[1].b)/2;
479     colors[2].b = cast(ubyte)word;
480     colors[2].a = 0xff;
481 
482     // random color to indicate alpha
483     colors[3].r = 0x00;
484     colors[3].g = 0xff;
485     colors[3].b = 0xff;
486     colors[3].a = 0x00;
487   }
488 }
489 
490 
491 //decodes a dds color block
492 //FIXME: make endian-safe
493 private void DDSDecodeColorBlock (uint* pixel, const(ddsColorBlock_t)* block, int width, const(Color)* colors) {
494   int r, n;
495   uint bits;
496   static immutable uint[4] masks = [ 3, 12, 3<<4, 3<<6 ];  // bit masks = 00000011, 00001100, 00110000, 11000000
497   static immutable ubyte[4] shift = [ 0, 2, 4, 6 ];
498   // r steps through lines in y
499   // no width * 4 as unsigned int ptr inc will * 4
500   for (r = 0; r < 4; ++r, pixel += width-4) {
501     // width * 4 bytes per pixel per line, each j dxtc row is 4 lines of pixels
502     // n steps through pixels
503     for (n = 0; n < 4; ++n) {
504       bits = block.row.ptr[r]&masks.ptr[n];
505       bits >>= shift.ptr[n];
506       switch (bits) {
507         case 0: *pixel++ = colors[0].asUint; break;
508         case 1: *pixel++ = colors[1].asUint; break;
509         case 2: *pixel++ = colors[2].asUint; break;
510         case 3: *pixel++ = colors[3].asUint; break;
511         default: ++pixel; break; // invalid
512       }
513     }
514   }
515 }
516 
517 
518 // decodes a dds explicit alpha block
519 //FIXME: endianness
520 private void DDSDecodeAlphaExplicit (uint* pixel, const(ddsAlphaBlockExplicit_t)* alphaBlock, int width, uint alphaZero) {
521   int row, pix;
522   ushort word;
523   Color color;
524 
525   // clear color
526   color.r = 0;
527   color.g = 0;
528   color.b = 0;
529 
530   // walk rows
531   for (row = 0; row < 4; ++row, pixel += width-4) {
532     word = DDSLittleShort(alphaBlock.row.ptr[row]);
533     // walk pixels
534     for (pix = 0; pix < 4; ++pix) {
535       // zero the alpha bits of image pixel
536       *pixel &= alphaZero;
537       color.a = word&0x000F;
538       color.a = cast(ubyte)(color.a|(color.a<<4));
539       *pixel |= *(cast(const(uint)*)&color);
540       word >>= 4; // move next bits to lowest 4
541       ++pixel; // move to next pixel in the row
542     }
543   }
544 }
545 
546 
547 // decodes interpolated alpha block
548 private void DDSDecodeAlpha3BitLinear (uint* pixel, const(ddsAlphaBlock3BitLinear_t)* alphaBlock, int width, uint alphaZero) {
549   int row, pix;
550   uint stuff;
551   ubyte[4][4] bits;
552   ushort[8] alphas;
553   Color[4][4] aColors;
554 
555   // get initial alphas
556   alphas.ptr[0] = alphaBlock.alpha0;
557   alphas.ptr[1] = alphaBlock.alpha1;
558 
559   if (alphas.ptr[0] > alphas.ptr[1]) {
560     // 8-alpha block
561     // 000 = alpha_0, 001 = alpha_1, others are interpolated
562     alphas.ptr[2] = (6*alphas.ptr[0]+alphas.ptr[1])/7; // bit code 010
563     alphas.ptr[3] = (5*alphas.ptr[0]+2*alphas.ptr[1])/7; // bit code 011
564     alphas.ptr[4] = (4*alphas.ptr[0]+3*alphas.ptr[1])/7; // bit code 100
565     alphas.ptr[5] = (3*alphas.ptr[0]+4*alphas.ptr[1])/7; // bit code 101
566     alphas.ptr[6] = (2*alphas.ptr[0]+5*alphas.ptr[1])/7; // bit code 110
567     alphas.ptr[7] = (alphas.ptr[0]+6*alphas.ptr[1])/7; // bit code 111
568   } else {
569     // 6-alpha block
570     // 000 = alpha_0, 001 = alpha_1, others are interpolated
571     alphas.ptr[2] = (4*alphas.ptr[0]+alphas.ptr[1])/5; // bit code 010
572     alphas.ptr[3] = (3*alphas.ptr[0]+2*alphas.ptr[1])/5; // bit code 011
573     alphas.ptr[4] = (2*alphas.ptr[0]+3*alphas.ptr[1])/5; // bit code 100
574     alphas.ptr[5] = (alphas.ptr[0]+4*alphas.ptr[1])/5; // bit code 101
575     alphas.ptr[6] = 0; // bit code 110
576     alphas.ptr[7] = 255; // bit code 111
577   }
578 
579   // decode 3-bit fields into array of 16 bytes with same value
580 
581   // first two rows of 4 pixels each
582   stuff = *(cast(const(uint)*)&(alphaBlock.stuff.ptr[0]));
583 
584   bits.ptr[0].ptr[0] = cast(ubyte)(stuff&0x00000007);
585   stuff >>= 3;
586   bits.ptr[0].ptr[1] = cast(ubyte)(stuff&0x00000007);
587   stuff >>= 3;
588   bits.ptr[0].ptr[2] = cast(ubyte)(stuff&0x00000007);
589   stuff >>= 3;
590   bits.ptr[0].ptr[3] = cast(ubyte)(stuff&0x00000007);
591   stuff >>= 3;
592   bits.ptr[1].ptr[0] = cast(ubyte)(stuff&0x00000007);
593   stuff >>= 3;
594   bits.ptr[1].ptr[1] = cast(ubyte)(stuff&0x00000007);
595   stuff >>= 3;
596   bits.ptr[1].ptr[2] = cast(ubyte)(stuff&0x00000007);
597   stuff >>= 3;
598   bits.ptr[1].ptr[3] = cast(ubyte)(stuff&0x00000007);
599 
600   // last two rows
601   stuff = *(cast(const(uint)*)&(alphaBlock.stuff.ptr[3])); // last 3 bytes
602 
603   bits.ptr[2].ptr[0] = cast(ubyte)(stuff&0x00000007);
604   stuff >>= 3;
605   bits.ptr[2].ptr[1] = cast(ubyte)(stuff&0x00000007);
606   stuff >>= 3;
607   bits.ptr[2].ptr[2] = cast(ubyte)(stuff&0x00000007);
608   stuff >>= 3;
609   bits.ptr[2].ptr[3] = cast(ubyte)(stuff&0x00000007);
610   stuff >>= 3;
611   bits.ptr[3].ptr[0] = cast(ubyte)(stuff&0x00000007);
612   stuff >>= 3;
613   bits.ptr[3].ptr[1] = cast(ubyte)(stuff&0x00000007);
614   stuff >>= 3;
615   bits.ptr[3].ptr[2] = cast(ubyte)(stuff&0x00000007);
616   stuff >>= 3;
617   bits.ptr[3].ptr[3] = cast(ubyte)(stuff&0x00000007);
618 
619   // decode the codes into alpha values
620   for (row = 0; row < 4; ++row) {
621     for (pix = 0; pix < 4; ++pix) {
622       aColors.ptr[row].ptr[pix].r = 0;
623       aColors.ptr[row].ptr[pix].g = 0;
624       aColors.ptr[row].ptr[pix].b = 0;
625       aColors.ptr[row].ptr[pix].a = cast(ubyte)alphas.ptr[bits.ptr[row].ptr[pix]];
626     }
627   }
628 
629   // write out alpha values to the image bits
630   for (row = 0; row < 4; ++row, pixel += width-4) {
631     for (pix = 0; pix < 4; ++pix) {
632       // zero the alpha bits of image pixel
633       *pixel &= alphaZero;
634       // or the bits into the prev. nulled alpha
635       *pixel |= *(cast(const(uint)*)&(aColors.ptr[row].ptr[pix]));
636       ++pixel;
637     }
638   }
639 }
640 
641 
642 // decompresses a dxt1 format texture
643 private bool DDSDecompressDXT1 (const(ddsBuffer_t)* dds, int width, int height, Color* pixels) {
644   Color[4] colors;
645   immutable int xBlocks = width/4;
646   immutable int yBlocks = height/4;
647   // 8 bytes per block
648   auto block = cast(const(ddsColorBlock_t)*)dds.data.ptr;
649   foreach (immutable y; 0..yBlocks) {
650     foreach (immutable x; 0..xBlocks) {
651       DDSGetColorBlockColors(block, colors.ptr);
652       auto pixel = cast(uint*)(pixels+x*4+(y*4)*width);
653       DDSDecodeColorBlock(pixel, block, width, colors.ptr);
654       ++block;
655     }
656   }
657   // return ok
658   return true;
659 }
660 
661 
662 // decompresses a dxt3 format texture
663 private bool DDSDecompressDXT3 (const(ddsBuffer_t)* dds, int width, int height, Color* pixels) {
664   Color[4] colors;
665 
666   // setup
667   immutable int xBlocks = width/4;
668   immutable int yBlocks = height/4;
669 
670   // create zero alpha
671   colors.ptr[0].a = 0;
672   colors.ptr[0].r = 0xFF;
673   colors.ptr[0].g = 0xFF;
674   colors.ptr[0].b = 0xFF;
675   immutable uint alphaZero = colors.ptr[0].asUint;
676 
677   // 8 bytes per block, 1 block for alpha, 1 block for color
678   auto block = cast(const(ddsColorBlock_t)*)dds.data.ptr;
679   foreach (immutable y; 0..yBlocks) {
680     foreach (immutable x; 0..xBlocks) {
681       // get alpha block
682       auto alphaBlock = cast(const(ddsAlphaBlockExplicit_t)*)block++;
683       // get color block
684       DDSGetColorBlockColors(block, colors.ptr);
685       // decode color block
686       auto pixel = cast(uint*)(pixels+x*4+(y*4)*width);
687       DDSDecodeColorBlock(pixel, block, width, colors.ptr);
688       // overwrite alpha bits with alpha block
689       DDSDecodeAlphaExplicit(pixel, alphaBlock, width, alphaZero);
690       ++block;
691     }
692   }
693 
694   // return ok
695   return true;
696 }
697 
698 
699 // decompresses a dxt5 format texture
700 private bool DDSDecompressDXT5 (const(ddsBuffer_t)* dds, int width, int height, Color* pixels) {
701   Color[4] colors;
702 
703   // setup
704   immutable int xBlocks = width/4;
705   immutable int yBlocks = height/4;
706 
707   // create zero alpha
708   colors.ptr[0].a = 0;
709   colors.ptr[0].r = 0xFF;
710   colors.ptr[0].g = 0xFF;
711   colors.ptr[0].b = 0xFF;
712   immutable uint alphaZero = colors.ptr[0].asUint;
713 
714   // 8 bytes per block, 1 block for alpha, 1 block for color
715   auto block = cast(const(ddsColorBlock_t)*)dds.data.ptr;
716   foreach (immutable y; 0..yBlocks) {
717     //block = cast(ddsColorBlock_t*)(dds.data.ptr+y*xBlocks*16);
718     foreach (immutable x; 0..xBlocks) {
719       // get alpha block
720       auto alphaBlock = cast(const(ddsAlphaBlock3BitLinear_t)*)block++;
721       // get color block
722       DDSGetColorBlockColors(block, colors.ptr);
723       // decode color block
724       auto pixel = cast(uint*)(pixels+x*4+(y*4)*width);
725       DDSDecodeColorBlock(pixel, block, width, colors.ptr);
726       // overwrite alpha bits with alpha block
727       DDSDecodeAlpha3BitLinear(pixel, alphaBlock, width, alphaZero);
728       ++block;
729     }
730   }
731 
732   // return ok
733   return true;
734 }
735 
736 
737 private void unmultiply (Color[] pixels) {
738   // premultiplied alpha
739   foreach (ref Color clr; pixels) {
740     if (clr.a != 0) {
741       clr.r = Color.clampToByte(clr.r*255/clr.a);
742       clr.g = Color.clampToByte(clr.g*255/clr.a);
743       clr.b = Color.clampToByte(clr.b*255/clr.a);
744     }
745   }
746 }
747 
748 
749 // decompresses a dxt2 format texture (FIXME: un-premultiply alpha)
750 private bool DDSDecompressDXT2 (const(ddsBuffer_t)* dds, int width, int height, Color* pixels) {
751   // decompress dxt3 first
752   if (!DDSDecompressDXT3(dds, width, height, pixels)) return false;
753   //FIXME: is un-premultiply correct?
754   unmultiply(pixels[0..width*height]);
755   return true;
756 }
757 
758 
759 // decompresses a dxt4 format texture (FIXME: un-premultiply alpha)
760 private bool DDSDecompressDXT4 (const(ddsBuffer_t)* dds, int width, int height, Color* pixels) {
761   // decompress dxt5 first
762   if (!DDSDecompressDXT5(dds, width, height, pixels)) return false;
763   //FIXME: is un-premultiply correct?
764   unmultiply(pixels[0..width*height]);
765   return true;
766 }
767 
768 
769 // decompresses an argb 8888 format texture
770 private bool DDSDecompressARGB8888 (const(ddsBuffer_t)* dds, int width, int height, Color* pixels) {
771   auto zin = cast(const(Color)*)dds.data.ptr;
772   //pixels[0..width*height] = zin[0..width*height];
773   foreach (immutable idx; 0..width*height) {
774     pixels.r = zin.b;
775     pixels.g = zin.g;
776     pixels.b = zin.r;
777     pixels.a = zin.a;
778     ++pixels;
779     ++zin;
780   }
781   return true;
782 }
783 
784 
785 // decompresses an rgb 888 format texture
786 private bool DDSDecompressRGB888 (const(ddsBuffer_t)* dds, int width, int height, Color* pixels) {
787   auto zin = cast(const(ubyte)*)dds.data.ptr;
788   //pixels[0..width*height] = zin[0..width*height];
789   foreach (immutable idx; 0..width*height) {
790     pixels.b = *zin++;
791     pixels.g = *zin++;
792     pixels.r = *zin++;
793     pixels.a = 255;
794     ++pixels;
795   }
796   return true;
797 }