1/* This file is part of the KDE project
2 Copyright (C) 2003 Ignacio CastaƱo <castano@ludicon.com>
3
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.
8
9 Almost all this code is based on nVidia's DDS-loading example
10 and the DevIl's source code by Denton Woods.
11*/
12
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
20 */
21
22#include "dds.h"
23
24#include <QtCore/QStringList>
25#include <QtGui/QImage>
26#include <QtCore/QDataStream>
27
28#include <kdebug.h>
29
30#include <math.h> // sqrtf
31
32#ifndef __USE_ISOC99
33#define sqrtf(x) ((float)sqrt(x))
34#endif
35
36typedef quint32 uint;
37typedef quint16 ushort;
38typedef quint8 uchar;
39
40#if !defined(MAKEFOURCC)
41# define MAKEFOURCC(ch0, ch1, ch2, ch3) \
42 (uint(uchar(ch0)) | (uint(uchar(ch1)) << 8) | \
43 (uint(uchar(ch2)) << 16) | (uint(uchar(ch3)) << 24 ))
44#endif
45
46#define HORIZONTAL 1
47#define VERTICAL 2
48#define CUBE_LAYOUT HORIZONTAL
49
50struct Color8888
51{
52 uchar r, g, b, a;
53};
54
55union Color565
56{
57 struct {
58 ushort b : 5;
59 ushort g : 6;
60 ushort r : 5;
61 } c;
62 ushort u;
63};
64
65union Color1555 {
66 struct {
67 ushort b : 5;
68 ushort g : 5;
69 ushort r : 5;
70 ushort a : 1;
71 } c;
72 ushort u;
73};
74
75union Color4444 {
76 struct {
77 ushort b : 4;
78 ushort g : 4;
79 ushort r : 4;
80 ushort a : 4;
81 } c;
82 ushort u;
83};
84
85
86static const uint FOURCC_DDS = MAKEFOURCC('D', 'D', 'S', ' ');
87static const uint FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1');
88static const uint FOURCC_DXT2 = MAKEFOURCC('D', 'X', 'T', '2');
89static const uint FOURCC_DXT3 = MAKEFOURCC('D', 'X', 'T', '3');
90static const uint FOURCC_DXT4 = MAKEFOURCC('D', 'X', 'T', '4');
91static const uint FOURCC_DXT5 = MAKEFOURCC('D', 'X', 'T', '5');
92static const uint FOURCC_RXGB = MAKEFOURCC('R', 'X', 'G', 'B');
93static const uint FOURCC_ATI2 = MAKEFOURCC('A', 'T', 'I', '2');
94
95static const uint DDSD_CAPS = 0x00000001l;
96static const uint DDSD_PIXELFORMAT = 0x00001000l;
97static const uint DDSD_WIDTH = 0x00000004l;
98static const uint DDSD_HEIGHT = 0x00000002l;
99static const uint DDSD_PITCH = 0x00000008l;
100
101static const uint DDSCAPS_TEXTURE = 0x00001000l;
102static const uint DDSCAPS2_VOLUME = 0x00200000l;
103static const uint DDSCAPS2_CUBEMAP = 0x00000200l;
104
105static const uint DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400l;
106static const uint DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800l;
107static const uint DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000l;
108static const uint DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000l;
109static const uint DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000l;
110static const uint DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000l;
111
112static const uint DDPF_RGB = 0x00000040l;
113static const uint DDPF_FOURCC = 0x00000004l;
114static const uint DDPF_ALPHAPIXELS = 0x00000001l;
115
116enum DDSType {
117 DDS_A8R8G8B8 = 0,
118 DDS_A1R5G5B5 = 1,
119 DDS_A4R4G4B4 = 2,
120 DDS_R8G8B8 = 3,
121 DDS_R5G6B5 = 4,
122 DDS_DXT1 = 5,
123 DDS_DXT2 = 6,
124 DDS_DXT3 = 7,
125 DDS_DXT4 = 8,
126 DDS_DXT5 = 9,
127 DDS_RXGB = 10,
128 DDS_ATI2 = 11,
129 DDS_UNKNOWN
130};
131
132
133struct DDSPixelFormat {
134 uint size;
135 uint flags;
136 uint fourcc;
137 uint bitcount;
138 uint rmask;
139 uint gmask;
140 uint bmask;
141 uint amask;
142};
143
144static QDataStream & operator>> ( QDataStream & s, DDSPixelFormat & pf )
145{
146 s >> pf.size;
147 s >> pf.flags;
148 s >> pf.fourcc;
149 s >> pf.bitcount;
150 s >> pf.rmask;
151 s >> pf.gmask;
152 s >> pf.bmask;
153 s >> pf.amask;
154 return s;
155}
156
157struct DDSCaps {
158 uint caps1;
159 uint caps2;
160 uint caps3;
161 uint caps4;
162};
163
164static QDataStream & operator>> ( QDataStream & s, DDSCaps & caps )
165{
166 s >> caps.caps1;
167 s >> caps.caps2;
168 s >> caps.caps3;
169 s >> caps.caps4;
170 return s;
171}
172
173struct DDSHeader {
174 uint size;
175 uint flags;
176 uint height;
177 uint width;
178 uint pitch;
179 uint depth;
180 uint mipmapcount;
181 uint reserved[11];
182 DDSPixelFormat pf;
183 DDSCaps caps;
184 uint notused;
185};
186
187static QDataStream & operator>> ( QDataStream & s, DDSHeader & header )
188{
189 s >> header.size;
190 s >> header.flags;
191 s >> header.height;
192 s >> header.width;
193 s >> header.pitch;
194 s >> header.depth;
195 s >> header.mipmapcount;
196 for( int i = 0; i < 11; i++ ) {
197 s >> header.reserved[i];
198 }
199 s >> header.pf;
200 s >> header.caps;
201 s >> header.notused;
202 return s;
203}
204
205static bool IsValid( const DDSHeader & header )
206{
207 if( header.size != 124 ) {
208 return false;
209 }
210 const uint required = (DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT);
211 if( (header.flags & required) != required ) {
212 return false;
213 }
214 if( header.pf.size != 32 ) {
215 return false;
216 }
217 if( !(header.caps.caps1 & DDSCAPS_TEXTURE) ) {
218 return false;
219 }
220 return true;
221}
222
223
224 // Get supported type. We currently support 10 different types.
225static DDSType GetType( const DDSHeader & header )
226{
227 if( header.pf.flags & DDPF_RGB ) {
228 if( header.pf.flags & DDPF_ALPHAPIXELS ) {
229 switch( header.pf.bitcount ) {
230 case 16:
231 return (header.pf.amask == 0x8000) ? DDS_A1R5G5B5 : DDS_A4R4G4B4;
232 case 32:
233 return DDS_A8R8G8B8;
234 }
235 }
236 else {
237 switch( header.pf.bitcount ) {
238 case 16:
239 return DDS_R5G6B5;
240 case 24:
241 return DDS_R8G8B8;
242 }
243 }
244 }
245 else if( header.pf.flags & DDPF_FOURCC ) {
246 switch( header.pf.fourcc ) {
247 case FOURCC_DXT1:
248 return DDS_DXT1;
249 case FOURCC_DXT2:
250 return DDS_DXT2;
251 case FOURCC_DXT3:
252 return DDS_DXT3;
253 case FOURCC_DXT4:
254 return DDS_DXT4;
255 case FOURCC_DXT5:
256 return DDS_DXT5;
257 case FOURCC_RXGB:
258 return DDS_RXGB;
259 case FOURCC_ATI2:
260 return DDS_ATI2;
261 }
262 }
263 return DDS_UNKNOWN;
264}
265
266static bool HasAlpha( const DDSHeader & header )
267{
268 return header.pf.flags & DDPF_ALPHAPIXELS;
269}
270
271static bool IsCubeMap( const DDSHeader & header )
272{
273 return header.caps.caps2 & DDSCAPS2_CUBEMAP;
274}
275
276static bool IsSupported( const DDSHeader & header )
277{
278 if( header.caps.caps2 & DDSCAPS2_VOLUME ) {
279 return false;
280 }
281 if( GetType(header) == DDS_UNKNOWN ) {
282 return false;
283 }
284 return true;
285}
286
287static bool LoadA8R8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
288{
289 const uint w = header.width;
290 const uint h = header.height;
291
292 for( uint y = 0; y < h; y++ ) {
293 QRgb * scanline = (QRgb *) img.scanLine( y );
294 for( uint x = 0; x < w; x++ ) {
295 uchar r, g, b, a;
296 s >> b >> g >> r >> a;
297 scanline[x] = qRgba(r, g, b, a);
298 }
299 }
300
301 return true;
302}
303
304static bool LoadR8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
305{
306 const uint w = header.width;
307 const uint h = header.height;
308
309 for( uint y = 0; y < h; y++ ) {
310 QRgb * scanline = (QRgb *) img.scanLine( y );
311 for( uint x = 0; x < w; x++ ) {
312 uchar r, g, b;
313 s >> b >> g >> r;
314 scanline[x] = qRgb(r, g, b);
315 }
316 }
317
318 return true;
319}
320
321static bool LoadA1R5G5B5( QDataStream & s, const DDSHeader & header, QImage & img )
322{
323 const uint w = header.width;
324 const uint h = header.height;
325
326 for( uint y = 0; y < h; y++ ) {
327 QRgb * scanline = (QRgb *) img.scanLine( y );
328 for( uint x = 0; x < w; x++ ) {
329 Color1555 color;
330 s >> color.u;
331 uchar a = (color.c.a != 0) ? 0xFF : 0;
332 uchar r = (color.c.r << 3) | (color.c.r >> 2);
333 uchar g = (color.c.g << 3) | (color.c.g >> 2);
334 uchar b = (color.c.b << 3) | (color.c.b >> 2);
335 scanline[x] = qRgba(r, g, b, a);
336 }
337 }
338
339 return true;
340}
341
342static bool LoadA4R4G4B4( QDataStream & s, const DDSHeader & header, QImage & img )
343{
344 const uint w = header.width;
345 const uint h = header.height;
346
347 for( uint y = 0; y < h; y++ ) {
348 QRgb * scanline = (QRgb *) img.scanLine( y );
349 for( uint x = 0; x < w; x++ ) {
350 Color4444 color;
351 s >> color.u;
352 uchar a = (color.c.a << 4) | color.c.a;
353 uchar r = (color.c.r << 4) | color.c.r;
354 uchar g = (color.c.g << 4) | color.c.g;
355 uchar b = (color.c.b << 4) | color.c.b;
356 scanline[x] = qRgba(r, g, b, a);
357 }
358 }
359
360 return true;
361}
362
363static bool LoadR5G6B5( QDataStream & s, const DDSHeader & header, QImage & img )
364{
365 const uint w = header.width;
366 const uint h = header.height;
367
368 for( uint y = 0; y < h; y++ ) {
369 QRgb * scanline = (QRgb *) img.scanLine( y );
370 for( uint x = 0; x < w; x++ ) {
371 Color565 color;
372 s >> color.u;
373 uchar r = (color.c.r << 3) | (color.c.r >> 2);
374 uchar g = (color.c.g << 2) | (color.c.g >> 4);
375 uchar b = (color.c.b << 3) | (color.c.b >> 2);
376 scanline[x] = qRgb(r, g, b);
377 }
378 }
379
380 return true;
381}
382
383static QDataStream & operator>> ( QDataStream & s, Color565 & c )
384{
385 return s >> c.u;
386}
387
388
389struct BlockDXT
390{
391 Color565 col0;
392 Color565 col1;
393 uchar row[4];
394
395 void GetColors( Color8888 color_array[4] )
396 {
397 color_array[0].r = (col0.c.r << 3) | (col0.c.r >> 2);
398 color_array[0].g = (col0.c.g << 2) | (col0.c.g >> 4);
399 color_array[0].b = (col0.c.b << 3) | (col0.c.b >> 2);
400 color_array[0].a = 0xFF;
401
402 color_array[1].r = (col1.c.r << 3) | (col1.c.r >> 2);
403 color_array[1].g = (col1.c.g << 2) | (col1.c.g >> 4);
404 color_array[1].b = (col1.c.b << 3) | (col1.c.b >> 2);
405 color_array[1].a = 0xFF;
406
407 if( col0.u > col1.u ) {
408 // Four-color block: derive the other two colors.
409 color_array[2].r = (2 * color_array[0].r + color_array[1].r) / 3;
410 color_array[2].g = (2 * color_array[0].g + color_array[1].g) / 3;
411 color_array[2].b = (2 * color_array[0].b + color_array[1].b) / 3;
412 color_array[2].a = 0xFF;
413
414 color_array[3].r = (2 * color_array[1].r + color_array[0].r) / 3;
415 color_array[3].g = (2 * color_array[1].g + color_array[0].g) / 3;
416 color_array[3].b = (2 * color_array[1].b + color_array[0].b) / 3;
417 color_array[3].a = 0xFF;
418 }
419 else {
420 // Three-color block: derive the other color.
421 color_array[2].r = (color_array[0].r + color_array[1].r) / 2;
422 color_array[2].g = (color_array[0].g + color_array[1].g) / 2;
423 color_array[2].b = (color_array[0].b + color_array[1].b) / 2;
424 color_array[2].a = 0xFF;
425
426 // Set all components to 0 to match DXT specs.
427 color_array[3].r = 0x00; // color_array[2].r;
428 color_array[3].g = 0x00; // color_array[2].g;
429 color_array[3].b = 0x00; // color_array[2].b;
430 color_array[3].a = 0x00;
431 }
432 }
433};
434
435
436static QDataStream & operator>> ( QDataStream & s, BlockDXT & c )
437{
438 return s >> c.col0 >> c.col1 >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
439}
440
441struct BlockDXTAlphaExplicit {
442 ushort row[4];
443};
444
445static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaExplicit & c )
446{
447 return s >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
448}
449
450struct BlockDXTAlphaLinear {
451 uchar alpha0;
452 uchar alpha1;
453 uchar bits[6];
454
455 void GetAlphas( uchar alpha_array[8] )
456 {
457 alpha_array[0] = alpha0;
458 alpha_array[1] = alpha1;
459
460 // 8-alpha or 6-alpha block?
461 if( alpha_array[0] > alpha_array[1] )
462 {
463 // 8-alpha block: derive the other 6 alphas.
464 // 000 = alpha_0, 001 = alpha_1, others are interpolated
465
466 alpha_array[2] = ( 6 * alpha0 + alpha1) / 7; // bit code 010
467 alpha_array[3] = ( 5 * alpha0 + 2 * alpha1) / 7; // Bit code 011
468 alpha_array[4] = ( 4 * alpha0 + 3 * alpha1) / 7; // Bit code 100
469 alpha_array[5] = ( 3 * alpha0 + 4 * alpha1) / 7; // Bit code 101
470 alpha_array[6] = ( 2 * alpha0 + 5 * alpha1) / 7; // Bit code 110
471 alpha_array[7] = ( alpha0 + 6 * alpha1) / 7; // Bit code 111
472 }
473 else
474 {
475 // 6-alpha block: derive the other alphas.
476 // 000 = alpha_0, 001 = alpha_1, others are interpolated
477
478 alpha_array[2] = (4 * alpha0 + alpha1) / 5; // Bit code 010
479 alpha_array[3] = (3 * alpha0 + 2 * alpha1) / 5; // Bit code 011
480 alpha_array[4] = (2 * alpha0 + 3 * alpha1) / 5; // Bit code 100
481 alpha_array[5] = ( alpha0 + 4 * alpha1) / 5; // Bit code 101
482 alpha_array[6] = 0x00; // Bit code 110
483 alpha_array[7] = 0xFF; // Bit code 111
484 }
485 }
486
487 void GetBits( uchar bit_array[16] )
488 {
489 // Split 24 packed bits into 8 bytes, 3 bits at a time.
490 uint b = bits[0] | bits[1] << 8 | bits[2] << 16;
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);
499
500 b = bits[3] | bits[4] << 8 | bits[5] << 16;
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);
509 }
510};
511
512static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaLinear & c )
513{
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];
516}
517
518static bool LoadDXT1( QDataStream & s, const DDSHeader & header, QImage & img )
519{
520 const uint w = header.width;
521 const uint h = header.height;
522
523 BlockDXT block;
524 QRgb * scanline[4];
525
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 );
529 }
530 for( uint x = 0; x < w; x += 4 ) {
531
532 // Read 64bit color block.
533 s >> block;
534
535 // Decode color block.
536 Color8888 color_array[4];
537 block.GetColors(color_array);
538
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 };
542
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);
549 }
550 }
551 }
552 }
553 }
554 return true;
555}
556
557static bool LoadDXT3( QDataStream & s, const DDSHeader & header, QImage & img )
558{
559 const uint w = header.width;
560 const uint h = header.height;
561
562 BlockDXT block;
563 BlockDXTAlphaExplicit alpha;
564 QRgb * scanline[4];
565
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 );
569 }
570 for( uint x = 0; x < w; x += 4 ) {
571
572 // Read 128bit color block.
573 s >> alpha;
574 s >> block;
575
576 // Decode color block.
577 Color8888 color_array[4];
578 block.GetColors(color_array);
579
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 };
583
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);
593 }
594 a >>= 4;
595 }
596 }
597 }
598 }
599 return true;
600}
601
602static bool LoadDXT2( QDataStream & s, const DDSHeader & header, QImage & img )
603{
604 if( !LoadDXT3(s, header, img) ) return false;
605 //UndoPremultiplyAlpha(img);
606 return true;
607}
608
609static bool LoadDXT5( QDataStream & s, const DDSHeader & header, QImage & img )
610{
611 const uint w = header.width;
612 const uint h = header.height;
613
614 BlockDXT block;
615 BlockDXTAlphaLinear alpha;
616 QRgb * scanline[4];
617
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 );
621 }
622 for( uint x = 0; x < w; x += 4 ) {
623
624 // Read 128bit color block.
625 s >> alpha;
626 s >> block;
627
628 // Decode color block.
629 Color8888 color_array[4];
630 block.GetColors(color_array);
631
632 uchar alpha_array[8];
633 alpha.GetAlphas(alpha_array);
634
635 uchar bit_array[16];
636 alpha.GetBits(bit_array);
637
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 };
641
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);
649 }
650 }
651 }
652 }
653 }
654
655 return true;
656}
657static bool LoadDXT4( QDataStream & s, const DDSHeader & header, QImage & img )
658{
659 if( !LoadDXT5(s, header, img) ) return false;
660 //UndoPremultiplyAlpha(img);
661 return true;
662}
663
664static bool LoadRXGB( QDataStream & s, const DDSHeader & header, QImage & img )
665{
666 const uint w = header.width;
667 const uint h = header.height;
668
669 BlockDXT block;
670 BlockDXTAlphaLinear alpha;
671 QRgb * scanline[4];
672
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 );
676 }
677 for( uint x = 0; x < w; x += 4 ) {
678
679 // Read 128bit color block.
680 s >> alpha;
681 s >> block;
682
683 // Decode color block.
684 Color8888 color_array[4];
685 block.GetColors(color_array);
686
687 uchar alpha_array[8];
688 alpha.GetAlphas(alpha_array);
689
690 uchar bit_array[16];
691 alpha.GetBits(bit_array);
692
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 };
696
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);
704 }
705 }
706 }
707 }
708 }
709
710 return true;
711}
712
713static bool LoadATI2( QDataStream & s, const DDSHeader & header, QImage & img )
714{
715 const uint w = header.width;
716 const uint h = header.height;
717
718 BlockDXTAlphaLinear xblock;
719 BlockDXTAlphaLinear yblock;
720 QRgb * scanline[4];
721
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 );
725 }
726 for( uint x = 0; x < w; x += 4 ) {
727
728 // Read 128bit color block.
729 s >> xblock;
730 s >> yblock;
731
732 // Decode color block.
733 uchar xblock_array[8];
734 xblock.GetAlphas(xblock_array);
735
736 uchar xbit_array[16];
737 xblock.GetBits(xbit_array);
738
739 uchar yblock_array[8];
740 yblock.GetAlphas(yblock_array);
741
742 uchar ybit_array[16];
743 yblock.GetBits(ybit_array);
744
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]];
751
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);
756
757 scanline[j][x+i] = qRgb(nx, ny, nz);
758 }
759 }
760 }
761 }
762 }
763
764 return true;
765}
766
767
768
769typedef bool (* TextureLoader)( QDataStream & s, const DDSHeader & header, QImage & img );
770
771// Get an appropriate texture loader for the given type.
772static 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;
800 };
801}
802
803
804// Load a 2d texture.
805static bool LoadTexture( QDataStream & s, const DDSHeader & header, QImage & img )
806{
807 // Create dst image.
808 img = QImage( header.width, header.height, QImage::Format_RGB32 );
809
810 // Read image.
811 DDSType type = GetType( header );
812
813 // Enable alpha buffer for transparent or DDS images.
814 if( HasAlpha( header ) || type >= DDS_DXT1 ) {
815 img = img.convertToFormat( QImage::Format_ARGB32 );
816 }
817
818 TextureLoader loader = GetTextureLoader( type );
819 if( loader == NULL ) {
820 return false;
821 }
822
823 return loader( s, header, img );
824}
825
826
827static int FaceOffset( const DDSHeader & header ) {
828
829 DDSType type = GetType( header );
830
831 int mipmap = qMax(header.mipmapcount, 1U);
832 int size = 0;
833 int w = header.width;
834 int h = header.height;
835
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 );
844 }
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 );
853 }
854
855 return size;
856}
857
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
870 };
871
872// Load unwrapped cube map.
873static bool LoadCubeMap( QDataStream & s, const DDSHeader & header, QImage & img )
874{
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
881
882 DDSType type = GetType( header );
883
884 // Enable alpha buffer for transparent or DDS images.
885 if( HasAlpha( header ) || type >= DDS_DXT1 ) {
886 img = img.convertToFormat( QImage::Format_ARGB32 );
887 }
888
889 // Select texture loader.
890 TextureLoader loader = GetTextureLoader( type );
891 if( loader == NULL ) {
892 return false;
893 }
894
895 // Clear background.
896 img.fill( 0 );
897
898 // Create face image.
899 QImage face(header.width, header.height, QImage::Format_RGB32);
900
901 int offset = s.device()->pos();
902 int size = FaceOffset( header );
903
904 for( int i = 0; i < 6; i++ ) {
905
906 if( !(header.caps.caps2 & face_flags[i]) ) {
907 // Skip face.
908 continue;
909 }
910
911 // Seek device.
912 s.device()->seek( offset );
913 offset += size;
914
915 // Load face from stream.
916 if( !loader( s, header, face ) ) {
917 return false;
918 }
919
920#if CUBE_LAYOUT == VERTICAL
921 if( i == 5 ) {
922 face = face.mirror(true, true);
923 }
924#endif
925
926 // Compute face offsets.
927 int offset_x = face_offset[i][0] * header.width;
928 int offset_y = face_offset[i][1] * header.height;
929
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 );
935 }
936 }
937
938 return true;
939}
940
941
942
943DDSHandler::DDSHandler()
944{
945}
946
947bool DDSHandler::canRead() const
948{
949 if (canRead(device())) {
950 setFormat("dds");
951 return true;
952 }
953 return false;
954}
955
956bool DDSHandler::read(QImage *image)
957{
958 QDataStream s( device() );
959 s.setByteOrder( QDataStream::LittleEndian );
960
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;
967 }
968
969 // Read image header.
970 DDSHeader header;
971 s >> header;
972
973 // Check image file format.
974 if( s.atEnd() || !IsValid( header ) ) {
975 kDebug(399) << "This DDS file is not valid.";
976 return false;
977 }
978
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;
983 }
984
985 bool result;
986
987 if( IsCubeMap( header ) ) {
988 result = LoadCubeMap( s, header, *image );
989 }
990 else {
991 result = LoadTexture( s, header, *image );
992 }
993
994 return result;
995}
996
997bool DDSHandler::write(const QImage &)
998{
999 // TODO Stub!
1000 return false;
1001}
1002
1003QByteArray DDSHandler::name() const
1004{
1005 return "dds";
1006}
1007
1008bool DDSHandler::canRead(QIODevice *device)
1009{
1010 if (!device) {
1011 qWarning("DDSHandler::canRead() called with no device");
1012 return false;
1013 }
1014
1015 qint64 oldPos = device->pos();
1016
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);
1025 }
1026 return false;
1027 }
1028
1029 if (device->isSequential()) {
1030 while (readBytes > 0)
1031 device->ungetChar(head[readBytes-- - 1]);
1032 } else {
1033 device->seek(oldPos);
1034 }
1035
1036 return qstrncmp(head, "DDS", 3) == 0;
1037}
1038
1039class DDSPlugin : public QImageIOPlugin
1040{
1041public:
1042 QStringList keys() const;
1043 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
1044 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
1045};
1046
1047QStringList DDSPlugin::keys() const
1048{
1049 return QStringList() << "dds";
1050}
1051
1052QImageIOPlugin::Capabilities DDSPlugin::capabilities(QIODevice *device, const QByteArray &format) const
1053{
1054 if (format == "dds")
1055 return Capabilities(CanRead);
1056 if (!format.isEmpty())
1057 return 0;
1058 if (!device->isOpen())
1059 return 0;
1060
1061 Capabilities cap;
1062 if (device->isReadable() && DDSHandler::canRead(device))
1063 cap |= CanRead;
1064 return cap;
1065}
1066
1067QImageIOHandler *DDSPlugin::create(QIODevice *device, const QByteArray &format) const
1068{
1069 QImageIOHandler *handler = new DDSHandler;
1070 handler->setDevice(device);
1071 handler->setFormat(format);
1072 return handler;
1073}
1074
1075Q_EXPORT_STATIC_PLUGIN(DDSPlugin)
1076Q_EXPORT_PLUGIN2(dds, DDSPlugin)
1077