• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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 <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)

KImgIO

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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