00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "dds.h"
00023
00024 #include <QtCore/QStringList>
00025 #include <QtGui/QImage>
00026 #include <QtCore/QDataStream>
00027
00028 #include <kglobal.h>
00029 #include <kdebug.h>
00030
00031 #include <math.h>
00032
00033 #ifndef __USE_ISOC99
00034 #define sqrtf(x) ((float)sqrt(x))
00035 #endif
00036
00037 typedef quint32 uint;
00038 typedef quint16 ushort;
00039 typedef quint8 uchar;
00040
00041 #if !defined(MAKEFOURCC)
00042 # define MAKEFOURCC(ch0, ch1, ch2, ch3) \
00043 (uint(uchar(ch0)) | (uint(uchar(ch1)) << 8) | \
00044 (uint(uchar(ch2)) << 16) | (uint(uchar(ch3)) << 24 ))
00045 #endif
00046
00047 #define HORIZONTAL 1
00048 #define VERTICAL 2
00049 #define CUBE_LAYOUT HORIZONTAL
00050
00051 struct Color8888
00052 {
00053 uchar r, g, b, a;
00054 };
00055
00056 union Color565
00057 {
00058 struct {
00059 ushort b : 5;
00060 ushort g : 6;
00061 ushort r : 5;
00062 } c;
00063 ushort u;
00064 };
00065
00066 union Color1555 {
00067 struct {
00068 ushort b : 5;
00069 ushort g : 5;
00070 ushort r : 5;
00071 ushort a : 1;
00072 } c;
00073 ushort u;
00074 };
00075
00076 union Color4444 {
00077 struct {
00078 ushort b : 4;
00079 ushort g : 4;
00080 ushort r : 4;
00081 ushort a : 4;
00082 } c;
00083 ushort u;
00084 };
00085
00086
00087 static const uint FOURCC_DDS = MAKEFOURCC('D', 'D', 'S', ' ');
00088 static const uint FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1');
00089 static const uint FOURCC_DXT2 = MAKEFOURCC('D', 'X', 'T', '2');
00090 static const uint FOURCC_DXT3 = MAKEFOURCC('D', 'X', 'T', '3');
00091 static const uint FOURCC_DXT4 = MAKEFOURCC('D', 'X', 'T', '4');
00092 static const uint FOURCC_DXT5 = MAKEFOURCC('D', 'X', 'T', '5');
00093 static const uint FOURCC_RXGB = MAKEFOURCC('R', 'X', 'G', 'B');
00094 static const uint FOURCC_ATI2 = MAKEFOURCC('A', 'T', 'I', '2');
00095
00096 static const uint DDSD_CAPS = 0x00000001l;
00097 static const uint DDSD_PIXELFORMAT = 0x00001000l;
00098 static const uint DDSD_WIDTH = 0x00000004l;
00099 static const uint DDSD_HEIGHT = 0x00000002l;
00100 static const uint DDSD_PITCH = 0x00000008l;
00101
00102 static const uint DDSCAPS_TEXTURE = 0x00001000l;
00103 static const uint DDSCAPS2_VOLUME = 0x00200000l;
00104 static const uint DDSCAPS2_CUBEMAP = 0x00000200l;
00105
00106 static const uint DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400l;
00107 static const uint DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800l;
00108 static const uint DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000l;
00109 static const uint DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000l;
00110 static const uint DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000l;
00111 static const uint DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000l;
00112
00113 static const uint DDPF_RGB = 0x00000040l;
00114 static const uint DDPF_FOURCC = 0x00000004l;
00115 static const uint DDPF_ALPHAPIXELS = 0x00000001l;
00116
00117 enum DDSType {
00118 DDS_A8R8G8B8 = 0,
00119 DDS_A1R5G5B5 = 1,
00120 DDS_A4R4G4B4 = 2,
00121 DDS_R8G8B8 = 3,
00122 DDS_R5G6B5 = 4,
00123 DDS_DXT1 = 5,
00124 DDS_DXT2 = 6,
00125 DDS_DXT3 = 7,
00126 DDS_DXT4 = 8,
00127 DDS_DXT5 = 9,
00128 DDS_RXGB = 10,
00129 DDS_ATI2 = 11,
00130 DDS_UNKNOWN
00131 };
00132
00133
00134 struct DDSPixelFormat {
00135 uint size;
00136 uint flags;
00137 uint fourcc;
00138 uint bitcount;
00139 uint rmask;
00140 uint gmask;
00141 uint bmask;
00142 uint amask;
00143 };
00144
00145 static QDataStream & operator>> ( QDataStream & s, DDSPixelFormat & pf )
00146 {
00147 s >> pf.size;
00148 s >> pf.flags;
00149 s >> pf.fourcc;
00150 s >> pf.bitcount;
00151 s >> pf.rmask;
00152 s >> pf.gmask;
00153 s >> pf.bmask;
00154 s >> pf.amask;
00155 return s;
00156 }
00157
00158 struct DDSCaps {
00159 uint caps1;
00160 uint caps2;
00161 uint caps3;
00162 uint caps4;
00163 };
00164
00165 static QDataStream & operator>> ( QDataStream & s, DDSCaps & caps )
00166 {
00167 s >> caps.caps1;
00168 s >> caps.caps2;
00169 s >> caps.caps3;
00170 s >> caps.caps4;
00171 return s;
00172 }
00173
00174 struct DDSHeader {
00175 uint size;
00176 uint flags;
00177 uint height;
00178 uint width;
00179 uint pitch;
00180 uint depth;
00181 uint mipmapcount;
00182 uint reserved[11];
00183 DDSPixelFormat pf;
00184 DDSCaps caps;
00185 uint notused;
00186 };
00187
00188 static QDataStream & operator>> ( QDataStream & s, DDSHeader & header )
00189 {
00190 s >> header.size;
00191 s >> header.flags;
00192 s >> header.height;
00193 s >> header.width;
00194 s >> header.pitch;
00195 s >> header.depth;
00196 s >> header.mipmapcount;
00197 for( int i = 0; i < 11; i++ ) {
00198 s >> header.reserved[i];
00199 }
00200 s >> header.pf;
00201 s >> header.caps;
00202 s >> header.notused;
00203 return s;
00204 }
00205
00206 static bool IsValid( const DDSHeader & header )
00207 {
00208 if( header.size != 124 ) {
00209 return false;
00210 }
00211 const uint required = (DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT);
00212 if( (header.flags & required) != required ) {
00213 return false;
00214 }
00215 if( header.pf.size != 32 ) {
00216 return false;
00217 }
00218 if( !(header.caps.caps1 & DDSCAPS_TEXTURE) ) {
00219 return false;
00220 }
00221 return true;
00222 }
00223
00224
00225
00226 static DDSType GetType( const DDSHeader & header )
00227 {
00228 if( header.pf.flags & DDPF_RGB ) {
00229 if( header.pf.flags & DDPF_ALPHAPIXELS ) {
00230 switch( header.pf.bitcount ) {
00231 case 16:
00232 return (header.pf.amask == 0x8000) ? DDS_A1R5G5B5 : DDS_A4R4G4B4;
00233 case 32:
00234 return DDS_A8R8G8B8;
00235 }
00236 }
00237 else {
00238 switch( header.pf.bitcount ) {
00239 case 16:
00240 return DDS_R5G6B5;
00241 case 24:
00242 return DDS_R8G8B8;
00243 }
00244 }
00245 }
00246 else if( header.pf.flags & DDPF_FOURCC ) {
00247 switch( header.pf.fourcc ) {
00248 case FOURCC_DXT1:
00249 return DDS_DXT1;
00250 case FOURCC_DXT2:
00251 return DDS_DXT2;
00252 case FOURCC_DXT3:
00253 return DDS_DXT3;
00254 case FOURCC_DXT4:
00255 return DDS_DXT4;
00256 case FOURCC_DXT5:
00257 return DDS_DXT5;
00258 case FOURCC_RXGB:
00259 return DDS_RXGB;
00260 case FOURCC_ATI2:
00261 return DDS_ATI2;
00262 }
00263 }
00264 return DDS_UNKNOWN;
00265 }
00266
00267 static bool HasAlpha( const DDSHeader & header )
00268 {
00269 return header.pf.flags & DDPF_ALPHAPIXELS;
00270 }
00271
00272 static bool IsCubeMap( const DDSHeader & header )
00273 {
00274 return header.caps.caps2 & DDSCAPS2_CUBEMAP;
00275 }
00276
00277 static bool IsSupported( const DDSHeader & header )
00278 {
00279 if( header.caps.caps2 & DDSCAPS2_VOLUME ) {
00280 return false;
00281 }
00282 if( GetType(header) == DDS_UNKNOWN ) {
00283 return false;
00284 }
00285 return true;
00286 }
00287
00288 static bool LoadA8R8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
00289 {
00290 const uint w = header.width;
00291 const uint h = header.height;
00292
00293 for( uint y = 0; y < h; y++ ) {
00294 QRgb * scanline = (QRgb *) img.scanLine( y );
00295 for( uint x = 0; x < w; x++ ) {
00296 uchar r, g, b, a;
00297 s >> b >> g >> r >> a;
00298 scanline[x] = qRgba(r, g, b, a);
00299 }
00300 }
00301
00302 return true;
00303 }
00304
00305 static bool LoadR8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
00306 {
00307 const uint w = header.width;
00308 const uint h = header.height;
00309
00310 for( uint y = 0; y < h; y++ ) {
00311 QRgb * scanline = (QRgb *) img.scanLine( y );
00312 for( uint x = 0; x < w; x++ ) {
00313 uchar r, g, b;
00314 s >> b >> g >> r;
00315 scanline[x] = qRgb(r, g, b);
00316 }
00317 }
00318
00319 return true;
00320 }
00321
00322 static bool LoadA1R5G5B5( QDataStream & s, const DDSHeader & header, QImage & img )
00323 {
00324 const uint w = header.width;
00325 const uint h = header.height;
00326
00327 for( uint y = 0; y < h; y++ ) {
00328 QRgb * scanline = (QRgb *) img.scanLine( y );
00329 for( uint x = 0; x < w; x++ ) {
00330 Color1555 color;
00331 s >> color.u;
00332 uchar a = (color.c.a != 0) ? 0xFF : 0;
00333 uchar r = (color.c.r << 3) | (color.c.r >> 2);
00334 uchar g = (color.c.g << 3) | (color.c.g >> 2);
00335 uchar b = (color.c.b << 3) | (color.c.b >> 2);
00336 scanline[x] = qRgba(r, g, b, a);
00337 }
00338 }
00339
00340 return true;
00341 }
00342
00343 static bool LoadA4R4G4B4( QDataStream & s, const DDSHeader & header, QImage & img )
00344 {
00345 const uint w = header.width;
00346 const uint h = header.height;
00347
00348 for( uint y = 0; y < h; y++ ) {
00349 QRgb * scanline = (QRgb *) img.scanLine( y );
00350 for( uint x = 0; x < w; x++ ) {
00351 Color4444 color;
00352 s >> color.u;
00353 uchar a = (color.c.a << 4) | color.c.a;
00354 uchar r = (color.c.r << 4) | color.c.r;
00355 uchar g = (color.c.g << 4) | color.c.g;
00356 uchar b = (color.c.b << 4) | color.c.b;
00357 scanline[x] = qRgba(r, g, b, a);
00358 }
00359 }
00360
00361 return true;
00362 }
00363
00364 static bool LoadR5G6B5( QDataStream & s, const DDSHeader & header, QImage & img )
00365 {
00366 const uint w = header.width;
00367 const uint h = header.height;
00368
00369 for( uint y = 0; y < h; y++ ) {
00370 QRgb * scanline = (QRgb *) img.scanLine( y );
00371 for( uint x = 0; x < w; x++ ) {
00372 Color565 color;
00373 s >> color.u;
00374 uchar r = (color.c.r << 3) | (color.c.r >> 2);
00375 uchar g = (color.c.g << 2) | (color.c.g >> 4);
00376 uchar b = (color.c.b << 3) | (color.c.b >> 2);
00377 scanline[x] = qRgb(r, g, b);
00378 }
00379 }
00380
00381 return true;
00382 }
00383
00384 static QDataStream & operator>> ( QDataStream & s, Color565 & c )
00385 {
00386 return s >> c.u;
00387 }
00388
00389
00390 struct BlockDXT
00391 {
00392 Color565 col0;
00393 Color565 col1;
00394 uchar row[4];
00395
00396 void GetColors( Color8888 color_array[4] )
00397 {
00398 color_array[0].r = (col0.c.r << 3) | (col0.c.r >> 2);
00399 color_array[0].g = (col0.c.g << 2) | (col0.c.g >> 4);
00400 color_array[0].b = (col0.c.b << 3) | (col0.c.b >> 2);
00401 color_array[0].a = 0xFF;
00402
00403 color_array[1].r = (col1.c.r << 3) | (col1.c.r >> 2);
00404 color_array[1].g = (col1.c.g << 2) | (col1.c.g >> 4);
00405 color_array[1].b = (col1.c.b << 3) | (col1.c.b >> 2);
00406 color_array[1].a = 0xFF;
00407
00408 if( col0.u > col1.u ) {
00409
00410 color_array[2].r = (2 * color_array[0].r + color_array[1].r) / 3;
00411 color_array[2].g = (2 * color_array[0].g + color_array[1].g) / 3;
00412 color_array[2].b = (2 * color_array[0].b + color_array[1].b) / 3;
00413 color_array[2].a = 0xFF;
00414
00415 color_array[3].r = (2 * color_array[1].r + color_array[0].r) / 3;
00416 color_array[3].g = (2 * color_array[1].g + color_array[0].g) / 3;
00417 color_array[3].b = (2 * color_array[1].b + color_array[0].b) / 3;
00418 color_array[3].a = 0xFF;
00419 }
00420 else {
00421
00422 color_array[2].r = (color_array[0].r + color_array[1].r) / 2;
00423 color_array[2].g = (color_array[0].g + color_array[1].g) / 2;
00424 color_array[2].b = (color_array[0].b + color_array[1].b) / 2;
00425 color_array[2].a = 0xFF;
00426
00427
00428 color_array[3].r = 0x00;
00429 color_array[3].g = 0x00;
00430 color_array[3].b = 0x00;
00431 color_array[3].a = 0x00;
00432 }
00433 }
00434 };
00435
00436
00437 static QDataStream & operator>> ( QDataStream & s, BlockDXT & c )
00438 {
00439 return s >> c.col0 >> c.col1 >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
00440 }
00441
00442 struct BlockDXTAlphaExplicit {
00443 ushort row[4];
00444 };
00445
00446 static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaExplicit & c )
00447 {
00448 return s >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
00449 }
00450
00451 struct BlockDXTAlphaLinear {
00452 uchar alpha0;
00453 uchar alpha1;
00454 uchar bits[6];
00455
00456 void GetAlphas( uchar alpha_array[8] )
00457 {
00458 alpha_array[0] = alpha0;
00459 alpha_array[1] = alpha1;
00460
00461
00462 if( alpha_array[0] > alpha_array[1] )
00463 {
00464
00465
00466
00467 alpha_array[2] = ( 6 * alpha0 + alpha1) / 7;
00468 alpha_array[3] = ( 5 * alpha0 + 2 * alpha1) / 7;
00469 alpha_array[4] = ( 4 * alpha0 + 3 * alpha1) / 7;
00470 alpha_array[5] = ( 3 * alpha0 + 4 * alpha1) / 7;
00471 alpha_array[6] = ( 2 * alpha0 + 5 * alpha1) / 7;
00472 alpha_array[7] = ( alpha0 + 6 * alpha1) / 7;
00473 }
00474 else
00475 {
00476
00477
00478
00479 alpha_array[2] = (4 * alpha0 + alpha1) / 5;
00480 alpha_array[3] = (3 * alpha0 + 2 * alpha1) / 5;
00481 alpha_array[4] = (2 * alpha0 + 3 * alpha1) / 5;
00482 alpha_array[5] = ( alpha0 + 4 * alpha1) / 5;
00483 alpha_array[6] = 0x00;
00484 alpha_array[7] = 0xFF;
00485 }
00486 }
00487
00488 void GetBits( uchar bit_array[16] )
00489 {
00490 uint b = (uint &) bits[0];
00491 bit_array[0] = uchar(b & 0x07); b >>= 3;
00492 bit_array[1] = uchar(b & 0x07); b >>= 3;
00493 bit_array[2] = uchar(b & 0x07); b >>= 3;
00494 bit_array[3] = uchar(b & 0x07); b >>= 3;
00495 bit_array[4] = uchar(b & 0x07); b >>= 3;
00496 bit_array[5] = uchar(b & 0x07); b >>= 3;
00497 bit_array[6] = uchar(b & 0x07); b >>= 3;
00498 bit_array[7] = uchar(b & 0x07); b >>= 3;
00499
00500 b = (uint &) bits[3];
00501 bit_array[8] = uchar(b & 0x07); b >>= 3;
00502 bit_array[9] = uchar(b & 0x07); b >>= 3;
00503 bit_array[10] = uchar(b & 0x07); b >>= 3;
00504 bit_array[11] = uchar(b & 0x07); b >>= 3;
00505 bit_array[12] = uchar(b & 0x07); b >>= 3;
00506 bit_array[13] = uchar(b & 0x07); b >>= 3;
00507 bit_array[14] = uchar(b & 0x07); b >>= 3;
00508 bit_array[15] = uchar(b & 0x07); b >>= 3;
00509 }
00510 };
00511
00512 static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaLinear & c )
00513 {
00514 s >> c.alpha0 >> c.alpha1;
00515 return s >> c.bits[0] >> c.bits[1] >> c.bits[2] >> c.bits[3] >> c.bits[4] >> c.bits[5];
00516 }
00517
00518 static bool LoadDXT1( QDataStream & s, const DDSHeader & header, QImage & img )
00519 {
00520 const uint w = header.width;
00521 const uint h = header.height;
00522
00523 BlockDXT block;
00524 QRgb * scanline[4];
00525
00526 for( uint y = 0; y < h; y += 4 ) {
00527 for( uint j = 0; j < 4; j++ ) {
00528 scanline[j] = (QRgb *) img.scanLine( y + j );
00529 }
00530 for( uint x = 0; x < w; x += 4 ) {
00531
00532
00533 s >> block;
00534
00535
00536 Color8888 color_array[4];
00537 block.GetColors(color_array);
00538
00539
00540 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
00541 const int shift[4] = { 0, 2, 4, 6 };
00542
00543
00544 for( uint j = 0; j < 4; j++ ) {
00545 for( uint i = 0; i < 4; i++ ) {
00546 if( img.valid( x+i, y+j ) ) {
00547 uint idx = (block.row[j] & masks[i]) >> shift[i];
00548 scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
00549 }
00550 }
00551 }
00552 }
00553 }
00554 return true;
00555 }
00556
00557 static bool LoadDXT3( QDataStream & s, const DDSHeader & header, QImage & img )
00558 {
00559 const uint w = header.width;
00560 const uint h = header.height;
00561
00562 BlockDXT block;
00563 BlockDXTAlphaExplicit alpha;
00564 QRgb * scanline[4];
00565
00566 for( uint y = 0; y < h; y += 4 ) {
00567 for( uint j = 0; j < 4; j++ ) {
00568 scanline[j] = (QRgb *) img.scanLine( y + j );
00569 }
00570 for( uint x = 0; x < w; x += 4 ) {
00571
00572
00573 s >> alpha;
00574 s >> block;
00575
00576
00577 Color8888 color_array[4];
00578 block.GetColors(color_array);
00579
00580
00581 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
00582 const int shift[4] = { 0, 2, 4, 6 };
00583
00584
00585 for( uint j = 0; j < 4; j++ ) {
00586 ushort a = alpha.row[j];
00587 for( uint i = 0; i < 4; i++ ) {
00588 if( img.valid( x+i, y+j ) ) {
00589 uint idx = (block.row[j] & masks[i]) >> shift[i];
00590 color_array[idx].a = a & 0x0f;
00591 color_array[idx].a = color_array[idx].a | (color_array[idx].a << 4);
00592 scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
00593 }
00594 a >>= 4;
00595 }
00596 }
00597 }
00598 }
00599 return true;
00600 }
00601
00602 static bool LoadDXT2( QDataStream & s, const DDSHeader & header, QImage & img )
00603 {
00604 if( !LoadDXT3(s, header, img) ) return false;
00605
00606 return true;
00607 }
00608
00609 static bool LoadDXT5( QDataStream & s, const DDSHeader & header, QImage & img )
00610 {
00611 const uint w = header.width;
00612 const uint h = header.height;
00613
00614 BlockDXT block;
00615 BlockDXTAlphaLinear alpha;
00616 QRgb * scanline[4];
00617
00618 for( uint y = 0; y < h; y += 4 ) {
00619 for( uint j = 0; j < 4; j++ ) {
00620 scanline[j] = (QRgb *) img.scanLine( y + j );
00621 }
00622 for( uint x = 0; x < w; x += 4 ) {
00623
00624
00625 s >> alpha;
00626 s >> block;
00627
00628
00629 Color8888 color_array[4];
00630 block.GetColors(color_array);
00631
00632 uchar alpha_array[8];
00633 alpha.GetAlphas(alpha_array);
00634
00635 uchar bit_array[16];
00636 alpha.GetBits(bit_array);
00637
00638
00639 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
00640 const int shift[4] = { 0, 2, 4, 6 };
00641
00642
00643 for( uint j = 0; j < 4; j++ ) {
00644 for( uint i = 0; i < 4; i++ ) {
00645 if( img.valid( x+i, y+j ) ) {
00646 uint idx = (block.row[j] & masks[i]) >> shift[i];
00647 color_array[idx].a = alpha_array[bit_array[j*4+i]];
00648 scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
00649 }
00650 }
00651 }
00652 }
00653 }
00654
00655 return true;
00656 }
00657 static bool LoadDXT4( QDataStream & s, const DDSHeader & header, QImage & img )
00658 {
00659 if( !LoadDXT5(s, header, img) ) return false;
00660
00661 return true;
00662 }
00663
00664 static bool LoadRXGB( QDataStream & s, const DDSHeader & header, QImage & img )
00665 {
00666 const uint w = header.width;
00667 const uint h = header.height;
00668
00669 BlockDXT block;
00670 BlockDXTAlphaLinear alpha;
00671 QRgb * scanline[4];
00672
00673 for( uint y = 0; y < h; y += 4 ) {
00674 for( uint j = 0; j < 4; j++ ) {
00675 scanline[j] = (QRgb *) img.scanLine( y + j );
00676 }
00677 for( uint x = 0; x < w; x += 4 ) {
00678
00679
00680 s >> alpha;
00681 s >> block;
00682
00683
00684 Color8888 color_array[4];
00685 block.GetColors(color_array);
00686
00687 uchar alpha_array[8];
00688 alpha.GetAlphas(alpha_array);
00689
00690 uchar bit_array[16];
00691 alpha.GetBits(bit_array);
00692
00693
00694 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
00695 const int shift[4] = { 0, 2, 4, 6 };
00696
00697
00698 for( uint j = 0; j < 4; j++ ) {
00699 for( uint i = 0; i < 4; i++ ) {
00700 if( img.valid( x+i, y+j ) ) {
00701 uint idx = (block.row[j] & masks[i]) >> shift[i];
00702 color_array[idx].a = alpha_array[bit_array[j*4+i]];
00703 scanline[j][x+i] = qRgb(color_array[idx].a, color_array[idx].g, color_array[idx].b);
00704 }
00705 }
00706 }
00707 }
00708 }
00709
00710 return true;
00711 }
00712
00713 static bool LoadATI2( QDataStream & s, const DDSHeader & header, QImage & img )
00714 {
00715 const uint w = header.width;
00716 const uint h = header.height;
00717
00718 BlockDXTAlphaLinear xblock;
00719 BlockDXTAlphaLinear yblock;
00720 QRgb * scanline[4];
00721
00722 for( uint y = 0; y < h; y += 4 ) {
00723 for( uint j = 0; j < 4; j++ ) {
00724 scanline[j] = (QRgb *) img.scanLine( y + j );
00725 }
00726 for( uint x = 0; x < w; x += 4 ) {
00727
00728
00729 s >> xblock;
00730 s >> yblock;
00731
00732
00733 uchar xblock_array[8];
00734 xblock.GetAlphas(xblock_array);
00735
00736 uchar xbit_array[16];
00737 xblock.GetBits(xbit_array);
00738
00739 uchar yblock_array[8];
00740 yblock.GetAlphas(yblock_array);
00741
00742 uchar ybit_array[16];
00743 yblock.GetBits(ybit_array);
00744
00745
00746 for( uint j = 0; j < 4; j++ ) {
00747 for( uint i = 0; i < 4; i++ ) {
00748 if( img.valid( x+i, y+j ) ) {
00749 const uchar nx = xblock_array[xbit_array[j*4+i]];
00750 const uchar ny = yblock_array[ybit_array[j*4+i]];
00751
00752 const float fx = float(nx) / 127.5f - 1.0f;
00753 const float fy = float(ny) / 127.5f - 1.0f;
00754 const float fz = sqrtf(1.0f - fx*fx - fy*fy);
00755 const uchar nz = uchar((fz + 1.0f) * 127.5f);
00756
00757 scanline[j][x+i] = qRgb(nx, ny, nz);
00758 }
00759 }
00760 }
00761 }
00762 }
00763
00764 return true;
00765 }
00766
00767
00768
00769 typedef bool (* TextureLoader)( QDataStream & s, const DDSHeader & header, QImage & img );
00770
00771
00772 static TextureLoader GetTextureLoader( DDSType type ) {
00773 switch( type ) {
00774 case DDS_A8R8G8B8:
00775 return LoadA8R8G8B8;
00776 case DDS_A1R5G5B5:
00777 return LoadA1R5G5B5;
00778 case DDS_A4R4G4B4:
00779 return LoadA4R4G4B4;
00780 case DDS_R8G8B8:
00781 return LoadR8G8B8;
00782 case DDS_R5G6B5:
00783 return LoadR5G6B5;
00784 case DDS_DXT1:
00785 return LoadDXT1;
00786 case DDS_DXT2:
00787 return LoadDXT2;
00788 case DDS_DXT3:
00789 return LoadDXT3;
00790 case DDS_DXT4:
00791 return LoadDXT4;
00792 case DDS_DXT5:
00793 return LoadDXT5;
00794 case DDS_RXGB:
00795 return LoadRXGB;
00796 case DDS_ATI2:
00797 return LoadATI2;
00798 default:
00799 return NULL;
00800 };
00801 }
00802
00803
00804
00805 static bool LoadTexture( QDataStream & s, const DDSHeader & header, QImage & img )
00806 {
00807
00808 img = QImage( header.width, header.height, QImage::Format_RGB32 );
00809
00810
00811 DDSType type = GetType( header );
00812
00813
00814 if( HasAlpha( header ) || type >= DDS_DXT1 ) {
00815 img = img.convertToFormat( QImage::Format_ARGB32 );
00816 }
00817
00818 TextureLoader loader = GetTextureLoader( type );
00819 if( loader == NULL ) {
00820 return false;
00821 }
00822
00823 return loader( s, header, img );
00824 }
00825
00826
00827 static int FaceOffset( const DDSHeader & header ) {
00828
00829 DDSType type = GetType( header );
00830
00831 int mipmap = qMax(header.mipmapcount, 1U);
00832 int size = 0;
00833 int w = header.width;
00834 int h = header.height;
00835
00836 if( type >= DDS_DXT1 ) {
00837 int multiplier = (type == DDS_DXT1) ? 8 : 16;
00838 do {
00839 int face_size = qMax(w/4,1) * qMax(h/4,1) * multiplier;
00840 size += face_size;
00841 w >>= 1;
00842 h >>= 1;
00843 } while( --mipmap );
00844 }
00845 else {
00846 int multiplier = header.pf.bitcount / 8;
00847 do {
00848 int face_size = w * h * multiplier;
00849 size += face_size;
00850 w = qMax( w>>1, 1 );
00851 h = qMax( h>>1, 1 );
00852 } while( --mipmap );
00853 }
00854
00855 return size;
00856 }
00857
00858 #if CUBE_LAYOUT == HORIZONTAL
00859 static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {3, 1} };
00860 #elif CUBE_LAYOUT == VERTICAL
00861 static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {1, 3} };
00862 #endif
00863 static int face_flags[6] = {
00864 DDSCAPS2_CUBEMAP_POSITIVEX,
00865 DDSCAPS2_CUBEMAP_NEGATIVEX,
00866 DDSCAPS2_CUBEMAP_POSITIVEY,
00867 DDSCAPS2_CUBEMAP_NEGATIVEY,
00868 DDSCAPS2_CUBEMAP_POSITIVEZ,
00869 DDSCAPS2_CUBEMAP_NEGATIVEZ
00870 };
00871
00872
00873 static bool LoadCubeMap( QDataStream & s, const DDSHeader & header, QImage & img )
00874 {
00875
00876 #if CUBE_LAYOUT == HORIZONTAL
00877 img = QImage( 4 * header.width, 3 * header.height, QImage::Format_RGB32 );
00878 #elif CUBE_LAYOUT == VERTICAL
00879 img = QImage( 3 * header.width, 4 * header.height, QImage::Format_RGB32 );
00880 #endif
00881
00882 DDSType type = GetType( header );
00883
00884
00885 if( HasAlpha( header ) || type >= DDS_DXT1 ) {
00886 img = img.convertToFormat( QImage::Format_ARGB32 );
00887 }
00888
00889
00890 TextureLoader loader = GetTextureLoader( type );
00891 if( loader == NULL ) {
00892 return false;
00893 }
00894
00895
00896 img.fill( 0 );
00897
00898
00899 QImage face(header.width, header.height, QImage::Format_RGB32);
00900
00901 int offset = s.device()->pos();
00902 int size = FaceOffset( header );
00903
00904 for( int i = 0; i < 6; i++ ) {
00905
00906 if( !(header.caps.caps2 & face_flags[i]) ) {
00907
00908 continue;
00909 }
00910
00911
00912 s.device()->seek( offset );
00913 offset += size;
00914
00915
00916 if( !loader( s, header, face ) ) {
00917 return false;
00918 }
00919
00920 #if CUBE_LAYOUT == VERTICAL
00921 if( i == 5 ) {
00922 face = face.mirror(true, true);
00923 }
00924 #endif
00925
00926
00927 int offset_x = face_offset[i][0] * header.width;
00928 int offset_y = face_offset[i][1] * header.height;
00929
00930
00931 for( uint y = 0; y < header.height; y++ ) {
00932 QRgb * src = (QRgb *) face.scanLine( y );
00933 QRgb * dst = (QRgb *) img.scanLine( y + offset_y ) + offset_x;
00934 memcpy( dst, src, sizeof(QRgb) * header.width );
00935 }
00936 }
00937
00938 return true;
00939 }
00940
00941
00942
00943 DDSHandler::DDSHandler()
00944 {
00945 }
00946
00947 bool DDSHandler::canRead() const
00948 {
00949 if (canRead(device())) {
00950 setFormat("dds");
00951 return true;
00952 }
00953 return false;
00954 }
00955
00956 bool DDSHandler::read(QImage *image)
00957 {
00958 QDataStream s( device() );
00959 s.setByteOrder( QDataStream::LittleEndian );
00960
00961
00962 uint fourcc;
00963 s >> fourcc;
00964 if( fourcc != FOURCC_DDS ) {
00965 kDebug(399) << "This is not a DDS file.";
00966 return false;
00967 }
00968
00969
00970 DDSHeader header;
00971 s >> header;
00972
00973
00974 if( s.atEnd() || !IsValid( header ) ) {
00975 kDebug(399) << "This DDS file is not valid.";
00976 return false;
00977 }
00978
00979
00980 if( !IsSupported( header ) ) {
00981 kDebug(399) << "This DDS file is not supported.";
00982 return false;
00983 }
00984
00985 bool result;
00986
00987 if( IsCubeMap( header ) ) {
00988 result = LoadCubeMap( s, header, *image );
00989 }
00990 else {
00991 result = LoadTexture( s, header, *image );
00992 }
00993
00994 return result;
00995 }
00996
00997 bool DDSHandler::write(const QImage &)
00998 {
00999
01000 return false;
01001 }
01002
01003 QByteArray DDSHandler::name() const
01004 {
01005 return "dds";
01006 }
01007
01008 bool DDSHandler::canRead(QIODevice *device)
01009 {
01010 if (!device) {
01011 qWarning("DDSHandler::canRead() called with no device");
01012 return false;
01013 }
01014
01015 qint64 oldPos = device->pos();
01016
01017 char head[3];
01018 qint64 readBytes = device->read(head, sizeof(head));
01019 if (readBytes != sizeof(head)) {
01020 if (device->isSequential()) {
01021 while (readBytes > 0)
01022 device->ungetChar(head[readBytes-- - 1]);
01023 } else {
01024 device->seek(oldPos);
01025 }
01026 return false;
01027 }
01028
01029 if (device->isSequential()) {
01030 while (readBytes > 0)
01031 device->ungetChar(head[readBytes-- - 1]);
01032 } else {
01033 device->seek(oldPos);
01034 }
01035
01036 return qstrncmp(head, "DDS", 3) == 0;
01037 }
01038
01039 class DDSPlugin : public QImageIOPlugin
01040 {
01041 public:
01042 QStringList keys() const;
01043 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
01044 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
01045 };
01046
01047 QStringList DDSPlugin::keys() const
01048 {
01049 return QStringList() << "dds";
01050 }
01051
01052 QImageIOPlugin::Capabilities DDSPlugin::capabilities(QIODevice *device, const QByteArray &format) const
01053 {
01054 if (format == "dds")
01055 return Capabilities(CanRead);
01056 if (!format.isEmpty())
01057 return 0;
01058 if (!device->isOpen())
01059 return 0;
01060
01061 Capabilities cap;
01062 if (device->isReadable() && DDSHandler::canRead(device))
01063 cap |= CanRead;
01064 return cap;
01065 }
01066
01067 QImageIOHandler *DDSPlugin::create(QIODevice *device, const QByteArray &format) const
01068 {
01069 QImageIOHandler *handler = new DDSHandler;
01070 handler->setDevice(device);
01071 handler->setFormat(format);
01072 return handler;
01073 }
01074
01075 Q_EXPORT_STATIC_PLUGIN(DDSPlugin)
01076 Q_EXPORT_PLUGIN2(dds, DDSPlugin)