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