fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kimgio / dds.cpp
blob509878912855a7a46b214dffefbc2c4758e8cf52
1 /* This file is part of the KDE project
2 Copyright (C) 2003 Ignacio CastaƱo <castano@ludicon.com>
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the Lesser GNU General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 Almost all this code is based on nVidia's DDS-loading example
10 and the DevIl's source code by Denton Woods.
13 /* this code supports:
14 * reading:
15 * rgb and dxt dds files
16 * cubemap dds files
17 * volume dds files -- TODO
18 * writing:
19 * rgb dds files only -- TODO
22 #include "dds.h"
24 #include <QtCore/QStringList>
25 #include <QtGui/QImage>
26 #include <QtCore/QDataStream>
28 #include <kglobal.h>
29 #include <kdebug.h>
31 #include <math.h> // sqrtf
33 #ifndef __USE_ISOC99
34 #define sqrtf(x) ((float)sqrt(x))
35 #endif
37 typedef quint32 uint;
38 typedef quint16 ushort;
39 typedef quint8 uchar;
41 #if !defined(MAKEFOURCC)
42 # define MAKEFOURCC(ch0, ch1, ch2, ch3) \
43 (uint(uchar(ch0)) | (uint(uchar(ch1)) << 8) | \
44 (uint(uchar(ch2)) << 16) | (uint(uchar(ch3)) << 24 ))
45 #endif
47 #define HORIZONTAL 1
48 #define VERTICAL 2
49 #define CUBE_LAYOUT HORIZONTAL
51 struct Color8888
53 uchar r, g, b, a;
56 union Color565
58 struct {
59 ushort b : 5;
60 ushort g : 6;
61 ushort r : 5;
62 } c;
63 ushort u;
66 union Color1555 {
67 struct {
68 ushort b : 5;
69 ushort g : 5;
70 ushort r : 5;
71 ushort a : 1;
72 } c;
73 ushort u;
76 union Color4444 {
77 struct {
78 ushort b : 4;
79 ushort g : 4;
80 ushort r : 4;
81 ushort a : 4;
82 } c;
83 ushort u;
87 static const uint FOURCC_DDS = MAKEFOURCC('D', 'D', 'S', ' ');
88 static const uint FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1');
89 static const uint FOURCC_DXT2 = MAKEFOURCC('D', 'X', 'T', '2');
90 static const uint FOURCC_DXT3 = MAKEFOURCC('D', 'X', 'T', '3');
91 static const uint FOURCC_DXT4 = MAKEFOURCC('D', 'X', 'T', '4');
92 static const uint FOURCC_DXT5 = MAKEFOURCC('D', 'X', 'T', '5');
93 static const uint FOURCC_RXGB = MAKEFOURCC('R', 'X', 'G', 'B');
94 static const uint FOURCC_ATI2 = MAKEFOURCC('A', 'T', 'I', '2');
96 static const uint DDSD_CAPS = 0x00000001l;
97 static const uint DDSD_PIXELFORMAT = 0x00001000l;
98 static const uint DDSD_WIDTH = 0x00000004l;
99 static const uint DDSD_HEIGHT = 0x00000002l;
100 static const uint DDSD_PITCH = 0x00000008l;
102 static const uint DDSCAPS_TEXTURE = 0x00001000l;
103 static const uint DDSCAPS2_VOLUME = 0x00200000l;
104 static const uint DDSCAPS2_CUBEMAP = 0x00000200l;
106 static const uint DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400l;
107 static const uint DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800l;
108 static const uint DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000l;
109 static const uint DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000l;
110 static const uint DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000l;
111 static const uint DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000l;
113 static const uint DDPF_RGB = 0x00000040l;
114 static const uint DDPF_FOURCC = 0x00000004l;
115 static const uint DDPF_ALPHAPIXELS = 0x00000001l;
117 enum DDSType {
118 DDS_A8R8G8B8 = 0,
119 DDS_A1R5G5B5 = 1,
120 DDS_A4R4G4B4 = 2,
121 DDS_R8G8B8 = 3,
122 DDS_R5G6B5 = 4,
123 DDS_DXT1 = 5,
124 DDS_DXT2 = 6,
125 DDS_DXT3 = 7,
126 DDS_DXT4 = 8,
127 DDS_DXT5 = 9,
128 DDS_RXGB = 10,
129 DDS_ATI2 = 11,
130 DDS_UNKNOWN
134 struct DDSPixelFormat {
135 uint size;
136 uint flags;
137 uint fourcc;
138 uint bitcount;
139 uint rmask;
140 uint gmask;
141 uint bmask;
142 uint amask;
145 static QDataStream & operator>> ( QDataStream & s, DDSPixelFormat & pf )
147 s >> pf.size;
148 s >> pf.flags;
149 s >> pf.fourcc;
150 s >> pf.bitcount;
151 s >> pf.rmask;
152 s >> pf.gmask;
153 s >> pf.bmask;
154 s >> pf.amask;
155 return s;
158 struct DDSCaps {
159 uint caps1;
160 uint caps2;
161 uint caps3;
162 uint caps4;
165 static QDataStream & operator>> ( QDataStream & s, DDSCaps & caps )
167 s >> caps.caps1;
168 s >> caps.caps2;
169 s >> caps.caps3;
170 s >> caps.caps4;
171 return s;
174 struct DDSHeader {
175 uint size;
176 uint flags;
177 uint height;
178 uint width;
179 uint pitch;
180 uint depth;
181 uint mipmapcount;
182 uint reserved[11];
183 DDSPixelFormat pf;
184 DDSCaps caps;
185 uint notused;
188 static QDataStream & operator>> ( QDataStream & s, DDSHeader & header )
190 s >> header.size;
191 s >> header.flags;
192 s >> header.height;
193 s >> header.width;
194 s >> header.pitch;
195 s >> header.depth;
196 s >> header.mipmapcount;
197 for( int i = 0; i < 11; i++ ) {
198 s >> header.reserved[i];
200 s >> header.pf;
201 s >> header.caps;
202 s >> header.notused;
203 return s;
206 static bool IsValid( const DDSHeader & header )
208 if( header.size != 124 ) {
209 return false;
211 const uint required = (DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT);
212 if( (header.flags & required) != required ) {
213 return false;
215 if( header.pf.size != 32 ) {
216 return false;
218 if( !(header.caps.caps1 & DDSCAPS_TEXTURE) ) {
219 return false;
221 return true;
225 // Get supported type. We currently support 10 different types.
226 static DDSType GetType( const DDSHeader & header )
228 if( header.pf.flags & DDPF_RGB ) {
229 if( header.pf.flags & DDPF_ALPHAPIXELS ) {
230 switch( header.pf.bitcount ) {
231 case 16:
232 return (header.pf.amask == 0x8000) ? DDS_A1R5G5B5 : DDS_A4R4G4B4;
233 case 32:
234 return DDS_A8R8G8B8;
237 else {
238 switch( header.pf.bitcount ) {
239 case 16:
240 return DDS_R5G6B5;
241 case 24:
242 return DDS_R8G8B8;
246 else if( header.pf.flags & DDPF_FOURCC ) {
247 switch( header.pf.fourcc ) {
248 case FOURCC_DXT1:
249 return DDS_DXT1;
250 case FOURCC_DXT2:
251 return DDS_DXT2;
252 case FOURCC_DXT3:
253 return DDS_DXT3;
254 case FOURCC_DXT4:
255 return DDS_DXT4;
256 case FOURCC_DXT5:
257 return DDS_DXT5;
258 case FOURCC_RXGB:
259 return DDS_RXGB;
260 case FOURCC_ATI2:
261 return DDS_ATI2;
264 return DDS_UNKNOWN;
267 static bool HasAlpha( const DDSHeader & header )
269 return header.pf.flags & DDPF_ALPHAPIXELS;
272 static bool IsCubeMap( const DDSHeader & header )
274 return header.caps.caps2 & DDSCAPS2_CUBEMAP;
277 static bool IsSupported( const DDSHeader & header )
279 if( header.caps.caps2 & DDSCAPS2_VOLUME ) {
280 return false;
282 if( GetType(header) == DDS_UNKNOWN ) {
283 return false;
285 return true;
288 static bool LoadA8R8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
290 const uint w = header.width;
291 const uint h = header.height;
293 for( uint y = 0; y < h; y++ ) {
294 QRgb * scanline = (QRgb *) img.scanLine( y );
295 for( uint x = 0; x < w; x++ ) {
296 uchar r, g, b, a;
297 s >> b >> g >> r >> a;
298 scanline[x] = qRgba(r, g, b, a);
302 return true;
305 static bool LoadR8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
307 const uint w = header.width;
308 const uint h = header.height;
310 for( uint y = 0; y < h; y++ ) {
311 QRgb * scanline = (QRgb *) img.scanLine( y );
312 for( uint x = 0; x < w; x++ ) {
313 uchar r, g, b;
314 s >> b >> g >> r;
315 scanline[x] = qRgb(r, g, b);
319 return true;
322 static bool LoadA1R5G5B5( QDataStream & s, const DDSHeader & header, QImage & img )
324 const uint w = header.width;
325 const uint h = header.height;
327 for( uint y = 0; y < h; y++ ) {
328 QRgb * scanline = (QRgb *) img.scanLine( y );
329 for( uint x = 0; x < w; x++ ) {
330 Color1555 color;
331 s >> color.u;
332 uchar a = (color.c.a != 0) ? 0xFF : 0;
333 uchar r = (color.c.r << 3) | (color.c.r >> 2);
334 uchar g = (color.c.g << 3) | (color.c.g >> 2);
335 uchar b = (color.c.b << 3) | (color.c.b >> 2);
336 scanline[x] = qRgba(r, g, b, a);
340 return true;
343 static bool LoadA4R4G4B4( QDataStream & s, const DDSHeader & header, QImage & img )
345 const uint w = header.width;
346 const uint h = header.height;
348 for( uint y = 0; y < h; y++ ) {
349 QRgb * scanline = (QRgb *) img.scanLine( y );
350 for( uint x = 0; x < w; x++ ) {
351 Color4444 color;
352 s >> color.u;
353 uchar a = (color.c.a << 4) | color.c.a;
354 uchar r = (color.c.r << 4) | color.c.r;
355 uchar g = (color.c.g << 4) | color.c.g;
356 uchar b = (color.c.b << 4) | color.c.b;
357 scanline[x] = qRgba(r, g, b, a);
361 return true;
364 static bool LoadR5G6B5( QDataStream & s, const DDSHeader & header, QImage & img )
366 const uint w = header.width;
367 const uint h = header.height;
369 for( uint y = 0; y < h; y++ ) {
370 QRgb * scanline = (QRgb *) img.scanLine( y );
371 for( uint x = 0; x < w; x++ ) {
372 Color565 color;
373 s >> color.u;
374 uchar r = (color.c.r << 3) | (color.c.r >> 2);
375 uchar g = (color.c.g << 2) | (color.c.g >> 4);
376 uchar b = (color.c.b << 3) | (color.c.b >> 2);
377 scanline[x] = qRgb(r, g, b);
381 return true;
384 static QDataStream & operator>> ( QDataStream & s, Color565 & c )
386 return s >> c.u;
390 struct BlockDXT
392 Color565 col0;
393 Color565 col1;
394 uchar row[4];
396 void GetColors( Color8888 color_array[4] )
398 color_array[0].r = (col0.c.r << 3) | (col0.c.r >> 2);
399 color_array[0].g = (col0.c.g << 2) | (col0.c.g >> 4);
400 color_array[0].b = (col0.c.b << 3) | (col0.c.b >> 2);
401 color_array[0].a = 0xFF;
403 color_array[1].r = (col1.c.r << 3) | (col1.c.r >> 2);
404 color_array[1].g = (col1.c.g << 2) | (col1.c.g >> 4);
405 color_array[1].b = (col1.c.b << 3) | (col1.c.b >> 2);
406 color_array[1].a = 0xFF;
408 if( col0.u > col1.u ) {
409 // Four-color block: derive the other two colors.
410 color_array[2].r = (2 * color_array[0].r + color_array[1].r) / 3;
411 color_array[2].g = (2 * color_array[0].g + color_array[1].g) / 3;
412 color_array[2].b = (2 * color_array[0].b + color_array[1].b) / 3;
413 color_array[2].a = 0xFF;
415 color_array[3].r = (2 * color_array[1].r + color_array[0].r) / 3;
416 color_array[3].g = (2 * color_array[1].g + color_array[0].g) / 3;
417 color_array[3].b = (2 * color_array[1].b + color_array[0].b) / 3;
418 color_array[3].a = 0xFF;
420 else {
421 // Three-color block: derive the other color.
422 color_array[2].r = (color_array[0].r + color_array[1].r) / 2;
423 color_array[2].g = (color_array[0].g + color_array[1].g) / 2;
424 color_array[2].b = (color_array[0].b + color_array[1].b) / 2;
425 color_array[2].a = 0xFF;
427 // Set all components to 0 to match DXT specs.
428 color_array[3].r = 0x00; // color_array[2].r;
429 color_array[3].g = 0x00; // color_array[2].g;
430 color_array[3].b = 0x00; // color_array[2].b;
431 color_array[3].a = 0x00;
437 static QDataStream & operator>> ( QDataStream & s, BlockDXT & c )
439 return s >> c.col0 >> c.col1 >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
442 struct BlockDXTAlphaExplicit {
443 ushort row[4];
446 static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaExplicit & c )
448 return s >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
451 struct BlockDXTAlphaLinear {
452 uchar alpha0;
453 uchar alpha1;
454 uchar bits[6];
456 void GetAlphas( uchar alpha_array[8] )
458 alpha_array[0] = alpha0;
459 alpha_array[1] = alpha1;
461 // 8-alpha or 6-alpha block?
462 if( alpha_array[0] > alpha_array[1] )
464 // 8-alpha block: derive the other 6 alphas.
465 // 000 = alpha_0, 001 = alpha_1, others are interpolated
467 alpha_array[2] = ( 6 * alpha0 + alpha1) / 7; // bit code 010
468 alpha_array[3] = ( 5 * alpha0 + 2 * alpha1) / 7; // Bit code 011
469 alpha_array[4] = ( 4 * alpha0 + 3 * alpha1) / 7; // Bit code 100
470 alpha_array[5] = ( 3 * alpha0 + 4 * alpha1) / 7; // Bit code 101
471 alpha_array[6] = ( 2 * alpha0 + 5 * alpha1) / 7; // Bit code 110
472 alpha_array[7] = ( alpha0 + 6 * alpha1) / 7; // Bit code 111
474 else
476 // 6-alpha block: derive the other alphas.
477 // 000 = alpha_0, 001 = alpha_1, others are interpolated
479 alpha_array[2] = (4 * alpha0 + alpha1) / 5; // Bit code 010
480 alpha_array[3] = (3 * alpha0 + 2 * alpha1) / 5; // Bit code 011
481 alpha_array[4] = (2 * alpha0 + 3 * alpha1) / 5; // Bit code 100
482 alpha_array[5] = ( alpha0 + 4 * alpha1) / 5; // Bit code 101
483 alpha_array[6] = 0x00; // Bit code 110
484 alpha_array[7] = 0xFF; // Bit code 111
488 void GetBits( uchar bit_array[16] )
490 uint b = (uint &) bits[0];
491 bit_array[0] = uchar(b & 0x07); b >>= 3;
492 bit_array[1] = uchar(b & 0x07); b >>= 3;
493 bit_array[2] = uchar(b & 0x07); b >>= 3;
494 bit_array[3] = uchar(b & 0x07); b >>= 3;
495 bit_array[4] = uchar(b & 0x07); b >>= 3;
496 bit_array[5] = uchar(b & 0x07); b >>= 3;
497 bit_array[6] = uchar(b & 0x07); b >>= 3;
498 bit_array[7] = uchar(b & 0x07); b >>= 3;
500 b = (uint &) bits[3];
501 bit_array[8] = uchar(b & 0x07); b >>= 3;
502 bit_array[9] = uchar(b & 0x07); b >>= 3;
503 bit_array[10] = uchar(b & 0x07); b >>= 3;
504 bit_array[11] = uchar(b & 0x07); b >>= 3;
505 bit_array[12] = uchar(b & 0x07); b >>= 3;
506 bit_array[13] = uchar(b & 0x07); b >>= 3;
507 bit_array[14] = uchar(b & 0x07); b >>= 3;
508 bit_array[15] = uchar(b & 0x07); b >>= 3;
512 static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaLinear & c )
514 s >> c.alpha0 >> c.alpha1;
515 return s >> c.bits[0] >> c.bits[1] >> c.bits[2] >> c.bits[3] >> c.bits[4] >> c.bits[5];
518 static bool LoadDXT1( QDataStream & s, const DDSHeader & header, QImage & img )
520 const uint w = header.width;
521 const uint h = header.height;
523 BlockDXT block;
524 QRgb * scanline[4];
526 for( uint y = 0; y < h; y += 4 ) {
527 for( uint j = 0; j < 4; j++ ) {
528 scanline[j] = (QRgb *) img.scanLine( y + j );
530 for( uint x = 0; x < w; x += 4 ) {
532 // Read 64bit color block.
533 s >> block;
535 // Decode color block.
536 Color8888 color_array[4];
537 block.GetColors(color_array);
539 // bit masks = 00000011, 00001100, 00110000, 11000000
540 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
541 const int shift[4] = { 0, 2, 4, 6 };
543 // Write color block.
544 for( uint j = 0; j < 4; j++ ) {
545 for( uint i = 0; i < 4; i++ ) {
546 if( img.valid( x+i, y+j ) ) {
547 uint idx = (block.row[j] & masks[i]) >> shift[i];
548 scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
554 return true;
557 static bool LoadDXT3( QDataStream & s, const DDSHeader & header, QImage & img )
559 const uint w = header.width;
560 const uint h = header.height;
562 BlockDXT block;
563 BlockDXTAlphaExplicit alpha;
564 QRgb * scanline[4];
566 for( uint y = 0; y < h; y += 4 ) {
567 for( uint j = 0; j < 4; j++ ) {
568 scanline[j] = (QRgb *) img.scanLine( y + j );
570 for( uint x = 0; x < w; x += 4 ) {
572 // Read 128bit color block.
573 s >> alpha;
574 s >> block;
576 // Decode color block.
577 Color8888 color_array[4];
578 block.GetColors(color_array);
580 // bit masks = 00000011, 00001100, 00110000, 11000000
581 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
582 const int shift[4] = { 0, 2, 4, 6 };
584 // Write color block.
585 for( uint j = 0; j < 4; j++ ) {
586 ushort a = alpha.row[j];
587 for( uint i = 0; i < 4; i++ ) {
588 if( img.valid( x+i, y+j ) ) {
589 uint idx = (block.row[j] & masks[i]) >> shift[i];
590 color_array[idx].a = a & 0x0f;
591 color_array[idx].a = color_array[idx].a | (color_array[idx].a << 4);
592 scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
594 a >>= 4;
599 return true;
602 static bool LoadDXT2( QDataStream & s, const DDSHeader & header, QImage & img )
604 if( !LoadDXT3(s, header, img) ) return false;
605 //UndoPremultiplyAlpha(img);
606 return true;
609 static bool LoadDXT5( QDataStream & s, const DDSHeader & header, QImage & img )
611 const uint w = header.width;
612 const uint h = header.height;
614 BlockDXT block;
615 BlockDXTAlphaLinear alpha;
616 QRgb * scanline[4];
618 for( uint y = 0; y < h; y += 4 ) {
619 for( uint j = 0; j < 4; j++ ) {
620 scanline[j] = (QRgb *) img.scanLine( y + j );
622 for( uint x = 0; x < w; x += 4 ) {
624 // Read 128bit color block.
625 s >> alpha;
626 s >> block;
628 // Decode color block.
629 Color8888 color_array[4];
630 block.GetColors(color_array);
632 uchar alpha_array[8];
633 alpha.GetAlphas(alpha_array);
635 uchar bit_array[16];
636 alpha.GetBits(bit_array);
638 // bit masks = 00000011, 00001100, 00110000, 11000000
639 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
640 const int shift[4] = { 0, 2, 4, 6 };
642 // Write color block.
643 for( uint j = 0; j < 4; j++ ) {
644 for( uint i = 0; i < 4; i++ ) {
645 if( img.valid( x+i, y+j ) ) {
646 uint idx = (block.row[j] & masks[i]) >> shift[i];
647 color_array[idx].a = alpha_array[bit_array[j*4+i]];
648 scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
655 return true;
657 static bool LoadDXT4( QDataStream & s, const DDSHeader & header, QImage & img )
659 if( !LoadDXT5(s, header, img) ) return false;
660 //UndoPremultiplyAlpha(img);
661 return true;
664 static bool LoadRXGB( QDataStream & s, const DDSHeader & header, QImage & img )
666 const uint w = header.width;
667 const uint h = header.height;
669 BlockDXT block;
670 BlockDXTAlphaLinear alpha;
671 QRgb * scanline[4];
673 for( uint y = 0; y < h; y += 4 ) {
674 for( uint j = 0; j < 4; j++ ) {
675 scanline[j] = (QRgb *) img.scanLine( y + j );
677 for( uint x = 0; x < w; x += 4 ) {
679 // Read 128bit color block.
680 s >> alpha;
681 s >> block;
683 // Decode color block.
684 Color8888 color_array[4];
685 block.GetColors(color_array);
687 uchar alpha_array[8];
688 alpha.GetAlphas(alpha_array);
690 uchar bit_array[16];
691 alpha.GetBits(bit_array);
693 // bit masks = 00000011, 00001100, 00110000, 11000000
694 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
695 const int shift[4] = { 0, 2, 4, 6 };
697 // Write color block.
698 for( uint j = 0; j < 4; j++ ) {
699 for( uint i = 0; i < 4; i++ ) {
700 if( img.valid( x+i, y+j ) ) {
701 uint idx = (block.row[j] & masks[i]) >> shift[i];
702 color_array[idx].a = alpha_array[bit_array[j*4+i]];
703 scanline[j][x+i] = qRgb(color_array[idx].a, color_array[idx].g, color_array[idx].b);
710 return true;
713 static bool LoadATI2( QDataStream & s, const DDSHeader & header, QImage & img )
715 const uint w = header.width;
716 const uint h = header.height;
718 BlockDXTAlphaLinear xblock;
719 BlockDXTAlphaLinear yblock;
720 QRgb * scanline[4];
722 for( uint y = 0; y < h; y += 4 ) {
723 for( uint j = 0; j < 4; j++ ) {
724 scanline[j] = (QRgb *) img.scanLine( y + j );
726 for( uint x = 0; x < w; x += 4 ) {
728 // Read 128bit color block.
729 s >> xblock;
730 s >> yblock;
732 // Decode color block.
733 uchar xblock_array[8];
734 xblock.GetAlphas(xblock_array);
736 uchar xbit_array[16];
737 xblock.GetBits(xbit_array);
739 uchar yblock_array[8];
740 yblock.GetAlphas(yblock_array);
742 uchar ybit_array[16];
743 yblock.GetBits(ybit_array);
745 // Write color block.
746 for( uint j = 0; j < 4; j++ ) {
747 for( uint i = 0; i < 4; i++ ) {
748 if( img.valid( x+i, y+j ) ) {
749 const uchar nx = xblock_array[xbit_array[j*4+i]];
750 const uchar ny = yblock_array[ybit_array[j*4+i]];
752 const float fx = float(nx) / 127.5f - 1.0f;
753 const float fy = float(ny) / 127.5f - 1.0f;
754 const float fz = sqrtf(1.0f - fx*fx - fy*fy);
755 const uchar nz = uchar((fz + 1.0f) * 127.5f);
757 scanline[j][x+i] = qRgb(nx, ny, nz);
764 return true;
769 typedef bool (* TextureLoader)( QDataStream & s, const DDSHeader & header, QImage & img );
771 // Get an appropriate texture loader for the given type.
772 static TextureLoader GetTextureLoader( DDSType type ) {
773 switch( type ) {
774 case DDS_A8R8G8B8:
775 return LoadA8R8G8B8;
776 case DDS_A1R5G5B5:
777 return LoadA1R5G5B5;
778 case DDS_A4R4G4B4:
779 return LoadA4R4G4B4;
780 case DDS_R8G8B8:
781 return LoadR8G8B8;
782 case DDS_R5G6B5:
783 return LoadR5G6B5;
784 case DDS_DXT1:
785 return LoadDXT1;
786 case DDS_DXT2:
787 return LoadDXT2;
788 case DDS_DXT3:
789 return LoadDXT3;
790 case DDS_DXT4:
791 return LoadDXT4;
792 case DDS_DXT5:
793 return LoadDXT5;
794 case DDS_RXGB:
795 return LoadRXGB;
796 case DDS_ATI2:
797 return LoadATI2;
798 default:
799 return NULL;
804 // Load a 2d texture.
805 static bool LoadTexture( QDataStream & s, const DDSHeader & header, QImage & img )
807 // Create dst image.
808 img = QImage( header.width, header.height, QImage::Format_RGB32 );
810 // Read image.
811 DDSType type = GetType( header );
813 // Enable alpha buffer for transparent or DDS images.
814 if( HasAlpha( header ) || type >= DDS_DXT1 ) {
815 img = img.convertToFormat( QImage::Format_ARGB32 );
818 TextureLoader loader = GetTextureLoader( type );
819 if( loader == NULL ) {
820 return false;
823 return loader( s, header, img );
827 static int FaceOffset( const DDSHeader & header ) {
829 DDSType type = GetType( header );
831 int mipmap = qMax(header.mipmapcount, 1U);
832 int size = 0;
833 int w = header.width;
834 int h = header.height;
836 if( type >= DDS_DXT1 ) {
837 int multiplier = (type == DDS_DXT1) ? 8 : 16;
838 do {
839 int face_size = qMax(w/4,1) * qMax(h/4,1) * multiplier;
840 size += face_size;
841 w >>= 1;
842 h >>= 1;
843 } while( --mipmap );
845 else {
846 int multiplier = header.pf.bitcount / 8;
847 do {
848 int face_size = w * h * multiplier;
849 size += face_size;
850 w = qMax( w>>1, 1 );
851 h = qMax( h>>1, 1 );
852 } while( --mipmap );
855 return size;
858 #if CUBE_LAYOUT == HORIZONTAL
859 static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {3, 1} };
860 #elif CUBE_LAYOUT == VERTICAL
861 static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {1, 3} };
862 #endif
863 static int face_flags[6] = {
864 DDSCAPS2_CUBEMAP_POSITIVEX,
865 DDSCAPS2_CUBEMAP_NEGATIVEX,
866 DDSCAPS2_CUBEMAP_POSITIVEY,
867 DDSCAPS2_CUBEMAP_NEGATIVEY,
868 DDSCAPS2_CUBEMAP_POSITIVEZ,
869 DDSCAPS2_CUBEMAP_NEGATIVEZ
872 // Load unwrapped cube map.
873 static bool LoadCubeMap( QDataStream & s, const DDSHeader & header, QImage & img )
875 // Create dst image.
876 #if CUBE_LAYOUT == HORIZONTAL
877 img = QImage( 4 * header.width, 3 * header.height, QImage::Format_RGB32 );
878 #elif CUBE_LAYOUT == VERTICAL
879 img = QImage( 3 * header.width, 4 * header.height, QImage::Format_RGB32 );
880 #endif
882 DDSType type = GetType( header );
884 // Enable alpha buffer for transparent or DDS images.
885 if( HasAlpha( header ) || type >= DDS_DXT1 ) {
886 img = img.convertToFormat( QImage::Format_ARGB32 );
889 // Select texture loader.
890 TextureLoader loader = GetTextureLoader( type );
891 if( loader == NULL ) {
892 return false;
895 // Clear background.
896 img.fill( 0 );
898 // Create face image.
899 QImage face(header.width, header.height, QImage::Format_RGB32);
901 int offset = s.device()->pos();
902 int size = FaceOffset( header );
904 for( int i = 0; i < 6; i++ ) {
906 if( !(header.caps.caps2 & face_flags[i]) ) {
907 // Skip face.
908 continue;
911 // Seek device.
912 s.device()->seek( offset );
913 offset += size;
915 // Load face from stream.
916 if( !loader( s, header, face ) ) {
917 return false;
920 #if CUBE_LAYOUT == VERTICAL
921 if( i == 5 ) {
922 face = face.mirror(true, true);
924 #endif
926 // Compute face offsets.
927 int offset_x = face_offset[i][0] * header.width;
928 int offset_y = face_offset[i][1] * header.height;
930 // Copy face on the image.
931 for( uint y = 0; y < header.height; y++ ) {
932 QRgb * src = (QRgb *) face.scanLine( y );
933 QRgb * dst = (QRgb *) img.scanLine( y + offset_y ) + offset_x;
934 memcpy( dst, src, sizeof(QRgb) * header.width );
938 return true;
943 DDSHandler::DDSHandler()
947 bool DDSHandler::canRead() const
949 if (canRead(device())) {
950 setFormat("dds");
951 return true;
953 return false;
956 bool DDSHandler::read(QImage *image)
958 QDataStream s( device() );
959 s.setByteOrder( QDataStream::LittleEndian );
961 // Validate header.
962 uint fourcc;
963 s >> fourcc;
964 if( fourcc != FOURCC_DDS ) {
965 kDebug(399) << "This is not a DDS file.";
966 return false;
969 // Read image header.
970 DDSHeader header;
971 s >> header;
973 // Check image file format.
974 if( s.atEnd() || !IsValid( header ) ) {
975 kDebug(399) << "This DDS file is not valid.";
976 return false;
979 // Determine image type, by now, we only support 2d textures.
980 if( !IsSupported( header ) ) {
981 kDebug(399) << "This DDS file is not supported.";
982 return false;
985 bool result;
987 if( IsCubeMap( header ) ) {
988 result = LoadCubeMap( s, header, *image );
990 else {
991 result = LoadTexture( s, header, *image );
994 return result;
997 bool DDSHandler::write(const QImage &)
999 // TODO Stub!
1000 return false;
1003 QByteArray DDSHandler::name() const
1005 return "dds";
1008 bool DDSHandler::canRead(QIODevice *device)
1010 if (!device) {
1011 qWarning("DDSHandler::canRead() called with no device");
1012 return false;
1015 qint64 oldPos = device->pos();
1017 char head[3];
1018 qint64 readBytes = device->read(head, sizeof(head));
1019 if (readBytes != sizeof(head)) {
1020 if (device->isSequential()) {
1021 while (readBytes > 0)
1022 device->ungetChar(head[readBytes-- - 1]);
1023 } else {
1024 device->seek(oldPos);
1026 return false;
1029 if (device->isSequential()) {
1030 while (readBytes > 0)
1031 device->ungetChar(head[readBytes-- - 1]);
1032 } else {
1033 device->seek(oldPos);
1036 return qstrncmp(head, "DDS", 3) == 0;
1039 class DDSPlugin : public QImageIOPlugin
1041 public:
1042 QStringList keys() const;
1043 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
1044 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
1047 QStringList DDSPlugin::keys() const
1049 return QStringList() << "dds";
1052 QImageIOPlugin::Capabilities DDSPlugin::capabilities(QIODevice *device, const QByteArray &format) const
1054 if (format == "dds")
1055 return Capabilities(CanRead);
1056 if (!format.isEmpty())
1057 return 0;
1058 if (!device->isOpen())
1059 return 0;
1061 Capabilities cap;
1062 if (device->isReadable() && DDSHandler::canRead(device))
1063 cap |= CanRead;
1064 return cap;
1067 QImageIOHandler *DDSPlugin::create(QIODevice *device, const QByteArray &format) const
1069 QImageIOHandler *handler = new DDSHandler;
1070 handler->setDevice(device);
1071 handler->setFormat(format);
1072 return handler;
1075 Q_EXPORT_STATIC_PLUGIN(DDSPlugin)
1076 Q_EXPORT_PLUGIN2(dds, DDSPlugin)