KImgIO
dds.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 2003 Ignacio CastaƱo <castano@ludicon.com> 00003 00004 This program is free software; you can redistribute it and/or 00005 modify it under the terms of the Lesser GNU General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 Almost all this code is based on nVidia's DDS-loading example 00010 and the DevIl's source code by Denton Woods. 00011 */ 00012 00013 /* this code supports: 00014 * reading: 00015 * rgb and dxt dds files 00016 * cubemap dds files 00017 * volume dds files -- TODO 00018 * writing: 00019 * rgb dds files only -- TODO 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> // sqrtf 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 // Get supported type. We currently support 10 different types. 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 // Four-color block: derive the other two colors. 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 // Three-color block: derive the other color. 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 // Set all components to 0 to match DXT specs. 00428 color_array[3].r = 0x00; // color_array[2].r; 00429 color_array[3].g = 0x00; // color_array[2].g; 00430 color_array[3].b = 0x00; // color_array[2].b; 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 // 8-alpha or 6-alpha block? 00462 if( alpha_array[0] > alpha_array[1] ) 00463 { 00464 // 8-alpha block: derive the other 6 alphas. 00465 // 000 = alpha_0, 001 = alpha_1, others are interpolated 00466 00467 alpha_array[2] = ( 6 * alpha0 + alpha1) / 7; // bit code 010 00468 alpha_array[3] = ( 5 * alpha0 + 2 * alpha1) / 7; // Bit code 011 00469 alpha_array[4] = ( 4 * alpha0 + 3 * alpha1) / 7; // Bit code 100 00470 alpha_array[5] = ( 3 * alpha0 + 4 * alpha1) / 7; // Bit code 101 00471 alpha_array[6] = ( 2 * alpha0 + 5 * alpha1) / 7; // Bit code 110 00472 alpha_array[7] = ( alpha0 + 6 * alpha1) / 7; // Bit code 111 00473 } 00474 else 00475 { 00476 // 6-alpha block: derive the other alphas. 00477 // 000 = alpha_0, 001 = alpha_1, others are interpolated 00478 00479 alpha_array[2] = (4 * alpha0 + alpha1) / 5; // Bit code 010 00480 alpha_array[3] = (3 * alpha0 + 2 * alpha1) / 5; // Bit code 011 00481 alpha_array[4] = (2 * alpha0 + 3 * alpha1) / 5; // Bit code 100 00482 alpha_array[5] = ( alpha0 + 4 * alpha1) / 5; // Bit code 101 00483 alpha_array[6] = 0x00; // Bit code 110 00484 alpha_array[7] = 0xFF; // Bit code 111 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 // Read 64bit color block. 00533 s >> block; 00534 00535 // Decode color block. 00536 Color8888 color_array[4]; 00537 block.GetColors(color_array); 00538 00539 // bit masks = 00000011, 00001100, 00110000, 11000000 00540 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 }; 00541 const int shift[4] = { 0, 2, 4, 6 }; 00542 00543 // Write color block. 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 // Read 128bit color block. 00573 s >> alpha; 00574 s >> block; 00575 00576 // Decode color block. 00577 Color8888 color_array[4]; 00578 block.GetColors(color_array); 00579 00580 // bit masks = 00000011, 00001100, 00110000, 11000000 00581 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 }; 00582 const int shift[4] = { 0, 2, 4, 6 }; 00583 00584 // Write color block. 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 //UndoPremultiplyAlpha(img); 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 // Read 128bit color block. 00625 s >> alpha; 00626 s >> block; 00627 00628 // Decode color block. 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 // bit masks = 00000011, 00001100, 00110000, 11000000 00639 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 }; 00640 const int shift[4] = { 0, 2, 4, 6 }; 00641 00642 // Write color block. 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 //UndoPremultiplyAlpha(img); 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 // Read 128bit color block. 00680 s >> alpha; 00681 s >> block; 00682 00683 // Decode color block. 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 // bit masks = 00000011, 00001100, 00110000, 11000000 00694 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 }; 00695 const int shift[4] = { 0, 2, 4, 6 }; 00696 00697 // Write color block. 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 // Read 128bit color block. 00729 s >> xblock; 00730 s >> yblock; 00731 00732 // Decode color block. 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 // Write color block. 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 // Get an appropriate texture loader for the given type. 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 // Load a 2d texture. 00805 static bool LoadTexture( QDataStream & s, const DDSHeader & header, QImage & img ) 00806 { 00807 // Create dst image. 00808 img = QImage( header.width, header.height, QImage::Format_RGB32 ); 00809 00810 // Read image. 00811 DDSType type = GetType( header ); 00812 00813 // Enable alpha buffer for transparent or DDS images. 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 // Load unwrapped cube map. 00873 static bool LoadCubeMap( QDataStream & s, const DDSHeader & header, QImage & img ) 00874 { 00875 // Create dst image. 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 // Enable alpha buffer for transparent or DDS images. 00885 if( HasAlpha( header ) || type >= DDS_DXT1 ) { 00886 img = img.convertToFormat( QImage::Format_ARGB32 ); 00887 } 00888 00889 // Select texture loader. 00890 TextureLoader loader = GetTextureLoader( type ); 00891 if( loader == NULL ) { 00892 return false; 00893 } 00894 00895 // Clear background. 00896 img.fill( 0 ); 00897 00898 // Create face image. 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 // Skip face. 00908 continue; 00909 } 00910 00911 // Seek device. 00912 s.device()->seek( offset ); 00913 offset += size; 00914 00915 // Load face from stream. 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 // Compute face offsets. 00927 int offset_x = face_offset[i][0] * header.width; 00928 int offset_y = face_offset[i][1] * header.height; 00929 00930 // Copy face on the image. 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 // Validate header. 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 // Read image header. 00970 DDSHeader header; 00971 s >> header; 00972 00973 // Check image file format. 00974 if( s.atEnd() || !IsValid( header ) ) { 00975 kDebug(399) << "This DDS file is not valid."; 00976 return false; 00977 } 00978 00979 // Determine image type, by now, we only support 2d textures. 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 // TODO Stub! 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)
KDE 4.6 API Reference