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