• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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)

KImgIO

Skip menu "KImgIO"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal