dds.cpp
/* This file is part of the KDE project
Copyright (C) 2003 Ignacio Castaņo <castano@ludicon.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the Lesser GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
Almost all this code is based on nVidia's DDS-loading example
and the DevIl's source code by Denton Woods.
*/
/* this code supports:
* reading:
* rgb and dxt dds files
* cubemap dds files
* volume dds files -- TODO
* writing:
* rgb dds files only -- TODO
*/
#include "dds.h"
#include <qimage.h>
#include <qdatastream.h>
#include <kglobal.h>
#include <kdebug.h>
#include <math.h> // sqrtf
#ifndef __USE_ISOC99
#define sqrtf(x) ((float)sqrt(x))
#endif
typedef Q_UINT32 uint;
typedef Q_UINT16 ushort;
typedef Q_UINT8 uchar;
namespace { // Private.
#if !defined(MAKEFOURCC)
# define MAKEFOURCC(ch0, ch1, ch2, ch3) \
(uint(uchar(ch0)) | (uint(uchar(ch1)) << 8) | \
(uint(uchar(ch2)) << 16) | (uint(uchar(ch3)) << 24 ))
#endif
#define HORIZONTAL 1
#define VERTICAL 2
#define CUBE_LAYOUT HORIZONTAL
struct Color8888
{
uchar r, g, b, a;
};
union Color565
{
struct {
ushort b : 5;
ushort g : 6;
ushort r : 5;
} c;
ushort u;
};
union Color1555 {
struct {
ushort b : 5;
ushort g : 5;
ushort r : 5;
ushort a : 1;
} c;
ushort u;
};
union Color4444 {
struct {
ushort b : 4;
ushort g : 4;
ushort r : 4;
ushort a : 4;
} c;
ushort u;
};
static const uint FOURCC_DDS = MAKEFOURCC('D', 'D', 'S', ' ');
static const uint FOURCC_DXT1 = MAKEFOURCC('D', 'X', 'T', '1');
static const uint FOURCC_DXT2 = MAKEFOURCC('D', 'X', 'T', '2');
static const uint FOURCC_DXT3 = MAKEFOURCC('D', 'X', 'T', '3');
static const uint FOURCC_DXT4 = MAKEFOURCC('D', 'X', 'T', '4');
static const uint FOURCC_DXT5 = MAKEFOURCC('D', 'X', 'T', '5');
static const uint FOURCC_RXGB = MAKEFOURCC('R', 'X', 'G', 'B');
static const uint FOURCC_ATI2 = MAKEFOURCC('A', 'T', 'I', '2');
static const uint DDSD_CAPS = 0x00000001l;
static const uint DDSD_PIXELFORMAT = 0x00001000l;
static const uint DDSD_WIDTH = 0x00000004l;
static const uint DDSD_HEIGHT = 0x00000002l;
static const uint DDSD_PITCH = 0x00000008l;
static const uint DDSCAPS_TEXTURE = 0x00001000l;
static const uint DDSCAPS2_VOLUME = 0x00200000l;
static const uint DDSCAPS2_CUBEMAP = 0x00000200l;
static const uint DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400l;
static const uint DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800l;
static const uint DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000l;
static const uint DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000l;
static const uint DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000l;
static const uint DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000l;
static const uint DDPF_RGB = 0x00000040l;
static const uint DDPF_FOURCC = 0x00000004l;
static const uint DDPF_ALPHAPIXELS = 0x00000001l;
enum DDSType {
DDS_A8R8G8B8 = 0,
DDS_A1R5G5B5 = 1,
DDS_A4R4G4B4 = 2,
DDS_R8G8B8 = 3,
DDS_R5G6B5 = 4,
DDS_DXT1 = 5,
DDS_DXT2 = 6,
DDS_DXT3 = 7,
DDS_DXT4 = 8,
DDS_DXT5 = 9,
DDS_RXGB = 10,
DDS_ATI2 = 11,
DDS_UNKNOWN
};
struct DDSPixelFormat {
uint size;
uint flags;
uint fourcc;
uint bitcount;
uint rmask;
uint gmask;
uint bmask;
uint amask;
};
static QDataStream & operator>> ( QDataStream & s, DDSPixelFormat & pf )
{
s >> pf.size;
s >> pf.flags;
s >> pf.fourcc;
s >> pf.bitcount;
s >> pf.rmask;
s >> pf.gmask;
s >> pf.bmask;
s >> pf.amask;
return s;
}
struct DDSCaps {
uint caps1;
uint caps2;
uint caps3;
uint caps4;
};
static QDataStream & operator>> ( QDataStream & s, DDSCaps & caps )
{
s >> caps.caps1;
s >> caps.caps2;
s >> caps.caps3;
s >> caps.caps4;
return s;
}
struct DDSHeader {
uint size;
uint flags;
uint height;
uint width;
uint pitch;
uint depth;
uint mipmapcount;
uint reserved[11];
DDSPixelFormat pf;
DDSCaps caps;
uint notused;
};
static QDataStream & operator>> ( QDataStream & s, DDSHeader & header )
{
s >> header.size;
s >> header.flags;
s >> header.height;
s >> header.width;
s >> header.pitch;
s >> header.depth;
s >> header.mipmapcount;
for( int i = 0; i < 11; i++ ) {
s >> header.reserved[i];
}
s >> header.pf;
s >> header.caps;
s >> header.notused;
return s;
}
static bool IsValid( const DDSHeader & header )
{
if( header.size != 124 ) {
return false;
}
const uint required = (DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS|DDSD_PIXELFORMAT);
if( (header.flags & required) != required ) {
return false;
}
if( header.pf.size != 32 ) {
return false;
}
if( !(header.caps.caps1 & DDSCAPS_TEXTURE) ) {
return false;
}
return true;
}
// Get supported type. We currently support 10 different types.
static DDSType GetType( const DDSHeader & header )
{
if( header.pf.flags & DDPF_RGB ) {
if( header.pf.flags & DDPF_ALPHAPIXELS ) {
switch( header.pf.bitcount ) {
case 16:
return (header.pf.amask == 0x8000) ? DDS_A1R5G5B5 : DDS_A4R4G4B4;
case 32:
return DDS_A8R8G8B8;
}
}
else {
switch( header.pf.bitcount ) {
case 16:
return DDS_R5G6B5;
case 24:
return DDS_R8G8B8;
}
}
}
else if( header.pf.flags & DDPF_FOURCC ) {
switch( header.pf.fourcc ) {
case FOURCC_DXT1:
return DDS_DXT1;
case FOURCC_DXT2:
return DDS_DXT2;
case FOURCC_DXT3:
return DDS_DXT3;
case FOURCC_DXT4:
return DDS_DXT4;
case FOURCC_DXT5:
return DDS_DXT5;
case FOURCC_RXGB:
return DDS_RXGB;
case FOURCC_ATI2:
return DDS_ATI2;
}
}
return DDS_UNKNOWN;
}
static bool HasAlpha( const DDSHeader & header )
{
return header.pf.flags & DDPF_ALPHAPIXELS;
}
static bool IsCubeMap( const DDSHeader & header )
{
return header.caps.caps2 & DDSCAPS2_CUBEMAP;
}
static bool IsSupported( const DDSHeader & header )
{
if( header.caps.caps2 & DDSCAPS2_VOLUME ) {
return false;
}
if( GetType(header) == DDS_UNKNOWN ) {
return false;
}
return true;
}
static bool LoadA8R8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
for( uint y = 0; y < h; y++ ) {
QRgb * scanline = (QRgb *) img.scanLine( y );
for( uint x = 0; x < w; x++ ) {
uchar r, g, b, a;
s >> b >> g >> r >> a;
scanline[x] = qRgba(r, g, b, a);
}
}
return true;
}
static bool LoadR8G8B8( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
for( uint y = 0; y < h; y++ ) {
QRgb * scanline = (QRgb *) img.scanLine( y );
for( uint x = 0; x < w; x++ ) {
uchar r, g, b;
s >> b >> g >> r;
scanline[x] = qRgb(r, g, b);
}
}
return true;
}
static bool LoadA1R5G5B5( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
for( uint y = 0; y < h; y++ ) {
QRgb * scanline = (QRgb *) img.scanLine( y );
for( uint x = 0; x < w; x++ ) {
Color1555 color;
s >> color.u;
uchar a = (color.c.a != 0) ? 0xFF : 0;
uchar r = (color.c.r << 3) | (color.c.r >> 2);
uchar g = (color.c.g << 3) | (color.c.g >> 2);
uchar b = (color.c.b << 3) | (color.c.b >> 2);
scanline[x] = qRgba(r, g, b, a);
}
}
return true;
}
static bool LoadA4R4G4B4( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
for( uint y = 0; y < h; y++ ) {
QRgb * scanline = (QRgb *) img.scanLine( y );
for( uint x = 0; x < w; x++ ) {
Color4444 color;
s >> color.u;
uchar a = (color.c.a << 4) | color.c.a;
uchar r = (color.c.r << 4) | color.c.r;
uchar g = (color.c.g << 4) | color.c.g;
uchar b = (color.c.b << 4) | color.c.b;
scanline[x] = qRgba(r, g, b, a);
}
}
return true;
}
static bool LoadR5G6B5( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
for( uint y = 0; y < h; y++ ) {
QRgb * scanline = (QRgb *) img.scanLine( y );
for( uint x = 0; x < w; x++ ) {
Color565 color;
s >> color.u;
uchar r = (color.c.r << 3) | (color.c.r >> 2);
uchar g = (color.c.g << 2) | (color.c.g >> 4);
uchar b = (color.c.b << 3) | (color.c.b >> 2);
scanline[x] = qRgb(r, g, b);
}
}
return true;
}
static QDataStream & operator>> ( QDataStream & s, Color565 & c )
{
return s >> c.u;
}
struct BlockDXT
{
Color565 col0;
Color565 col1;
uchar row[4];
void GetColors( Color8888 color_array[4] )
{
color_array[0].r = (col0.c.r << 3) | (col0.c.r >> 2);
color_array[0].g = (col0.c.g << 2) | (col0.c.g >> 4);
color_array[0].b = (col0.c.b << 3) | (col0.c.b >> 2);
color_array[0].a = 0xFF;
color_array[1].r = (col1.c.r << 3) | (col1.c.r >> 2);
color_array[1].g = (col1.c.g << 2) | (col1.c.g >> 4);
color_array[1].b = (col1.c.b << 3) | (col1.c.b >> 2);
color_array[1].a = 0xFF;
if( col0.u > col1.u ) {
// Four-color block: derive the other two colors.
color_array[2].r = (2 * color_array[0].r + color_array[1].r) / 3;
color_array[2].g = (2 * color_array[0].g + color_array[1].g) / 3;
color_array[2].b = (2 * color_array[0].b + color_array[1].b) / 3;
color_array[2].a = 0xFF;
color_array[3].r = (2 * color_array[1].r + color_array[0].r) / 3;
color_array[3].g = (2 * color_array[1].g + color_array[0].g) / 3;
color_array[3].b = (2 * color_array[1].b + color_array[0].b) / 3;
color_array[3].a = 0xFF;
}
else {
// Three-color block: derive the other color.
color_array[2].r = (color_array[0].r + color_array[1].r) / 2;
color_array[2].g = (color_array[0].g + color_array[1].g) / 2;
color_array[2].b = (color_array[0].b + color_array[1].b) / 2;
color_array[2].a = 0xFF;
// Set all components to 0 to match DXT specs.
color_array[3].r = 0x00; // color_array[2].r;
color_array[3].g = 0x00; // color_array[2].g;
color_array[3].b = 0x00; // color_array[2].b;
color_array[3].a = 0x00;
}
}
};
static QDataStream & operator>> ( QDataStream & s, BlockDXT & c )
{
return s >> c.col0 >> c.col1 >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
}
struct BlockDXTAlphaExplicit {
ushort row[4];
};
static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaExplicit & c )
{
return s >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
}
struct BlockDXTAlphaLinear {
uchar alpha0;
uchar alpha1;
uchar bits[6];
void GetAlphas( uchar alpha_array[8] )
{
alpha_array[0] = alpha0;
alpha_array[1] = alpha1;
// 8-alpha or 6-alpha block?
if( alpha_array[0] > alpha_array[1] )
{
// 8-alpha block: derive the other 6 alphas.
// 000 = alpha_0, 001 = alpha_1, others are interpolated
alpha_array[2] = ( 6 * alpha0 + alpha1) / 7; // bit code 010
alpha_array[3] = ( 5 * alpha0 + 2 * alpha1) / 7; // Bit code 011
alpha_array[4] = ( 4 * alpha0 + 3 * alpha1) / 7; // Bit code 100
alpha_array[5] = ( 3 * alpha0 + 4 * alpha1) / 7; // Bit code 101
alpha_array[6] = ( 2 * alpha0 + 5 * alpha1) / 7; // Bit code 110
alpha_array[7] = ( alpha0 + 6 * alpha1) / 7; // Bit code 111
}
else
{
// 6-alpha block: derive the other alphas.
// 000 = alpha_0, 001 = alpha_1, others are interpolated
alpha_array[2] = (4 * alpha0 + alpha1) / 5; // Bit code 010
alpha_array[3] = (3 * alpha0 + 2 * alpha1) / 5; // Bit code 011
alpha_array[4] = (2 * alpha0 + 3 * alpha1) / 5; // Bit code 100
alpha_array[5] = ( alpha0 + 4 * alpha1) / 5; // Bit code 101
alpha_array[6] = 0x00; // Bit code 110
alpha_array[7] = 0xFF; // Bit code 111
}
}
void GetBits( uchar bit_array[16] )
{
uint b = (uint &) bits[0];
bit_array[0] = uchar(b & 0x07); b >>= 3;
bit_array[1] = uchar(b & 0x07); b >>= 3;
bit_array[2] = uchar(b & 0x07); b >>= 3;
bit_array[3] = uchar(b & 0x07); b >>= 3;
bit_array[4] = uchar(b & 0x07); b >>= 3;
bit_array[5] = uchar(b & 0x07); b >>= 3;
bit_array[6] = uchar(b & 0x07); b >>= 3;
bit_array[7] = uchar(b & 0x07); b >>= 3;
b = (uint &) bits[3];
bit_array[8] = uchar(b & 0x07); b >>= 3;
bit_array[9] = uchar(b & 0x07); b >>= 3;
bit_array[10] = uchar(b & 0x07); b >>= 3;
bit_array[11] = uchar(b & 0x07); b >>= 3;
bit_array[12] = uchar(b & 0x07); b >>= 3;
bit_array[13] = uchar(b & 0x07); b >>= 3;
bit_array[14] = uchar(b & 0x07); b >>= 3;
bit_array[15] = uchar(b & 0x07); b >>= 3;
}
};
static QDataStream & operator>> ( QDataStream & s, BlockDXTAlphaLinear & c )
{
s >> c.alpha0 >> c.alpha1;
return s >> c.bits[0] >> c.bits[1] >> c.bits[2] >> c.bits[3] >> c.bits[4] >> c.bits[5];
}
static bool LoadDXT1( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
BlockDXT block;
QRgb * scanline[4];
for( uint y = 0; y < h; y += 4 ) {
for( uint j = 0; j < 4; j++ ) {
scanline[j] = (QRgb *) img.scanLine( y + j );
}
for( uint x = 0; x < w; x += 4 ) {
// Read 64bit color block.
s >> block;
// Decode color block.
Color8888 color_array[4];
block.GetColors(color_array);
// bit masks = 00000011, 00001100, 00110000, 11000000
const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
const int shift[4] = { 0, 2, 4, 6 };
// Write color block.
for( uint j = 0; j < 4; j++ ) {
for( uint i = 0; i < 4; i++ ) {
if( img.valid( x+i, y+j ) ) {
uint idx = (block.row[j] & masks[i]) >> shift[i];
scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
}
}
}
}
}
return true;
}
static bool LoadDXT3( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
BlockDXT block;
BlockDXTAlphaExplicit alpha;
QRgb * scanline[4];
for( uint y = 0; y < h; y += 4 ) {
for( uint j = 0; j < 4; j++ ) {
scanline[j] = (QRgb *) img.scanLine( y + j );
}
for( uint x = 0; x < w; x += 4 ) {
// Read 128bit color block.
s >> alpha;
s >> block;
// Decode color block.
Color8888 color_array[4];
block.GetColors(color_array);
// bit masks = 00000011, 00001100, 00110000, 11000000
const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
const int shift[4] = { 0, 2, 4, 6 };
// Write color block.
for( uint j = 0; j < 4; j++ ) {
ushort a = alpha.row[j];
for( uint i = 0; i < 4; i++ ) {
if( img.valid( x+i, y+j ) ) {
uint idx = (block.row[j] & masks[i]) >> shift[i];
color_array[idx].a = a & 0x0f;
color_array[idx].a = color_array[idx].a | (color_array[idx].a << 4);
scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
}
a >>= 4;
}
}
}
}
return true;
}
static bool LoadDXT2( QDataStream & s, const DDSHeader & header, QImage & img )
{
if( !LoadDXT3(s, header, img) ) return false;
//UndoPremultiplyAlpha(img);
return true;
}
static bool LoadDXT5( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
BlockDXT block;
BlockDXTAlphaLinear alpha;
QRgb * scanline[4];
for( uint y = 0; y < h; y += 4 ) {
for( uint j = 0; j < 4; j++ ) {
scanline[j] = (QRgb *) img.scanLine( y + j );
}
for( uint x = 0; x < w; x += 4 ) {
// Read 128bit color block.
s >> alpha;
s >> block;
// Decode color block.
Color8888 color_array[4];
block.GetColors(color_array);
uchar alpha_array[8];
alpha.GetAlphas(alpha_array);
uchar bit_array[16];
alpha.GetBits(bit_array);
// bit masks = 00000011, 00001100, 00110000, 11000000
const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
const int shift[4] = { 0, 2, 4, 6 };
// Write color block.
for( uint j = 0; j < 4; j++ ) {
for( uint i = 0; i < 4; i++ ) {
if( img.valid( x+i, y+j ) ) {
uint idx = (block.row[j] & masks[i]) >> shift[i];
color_array[idx].a = alpha_array[bit_array[j*4+i]];
scanline[j][x+i] = qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
}
}
}
}
}
return true;
}
static bool LoadDXT4( QDataStream & s, const DDSHeader & header, QImage & img )
{
if( !LoadDXT5(s, header, img) ) return false;
//UndoPremultiplyAlpha(img);
return true;
}
static bool LoadRXGB( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
BlockDXT block;
BlockDXTAlphaLinear alpha;
QRgb * scanline[4];
for( uint y = 0; y < h; y += 4 ) {
for( uint j = 0; j < 4; j++ ) {
scanline[j] = (QRgb *) img.scanLine( y + j );
}
for( uint x = 0; x < w; x += 4 ) {
// Read 128bit color block.
s >> alpha;
s >> block;
// Decode color block.
Color8888 color_array[4];
block.GetColors(color_array);
uchar alpha_array[8];
alpha.GetAlphas(alpha_array);
uchar bit_array[16];
alpha.GetBits(bit_array);
// bit masks = 00000011, 00001100, 00110000, 11000000
const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
const int shift[4] = { 0, 2, 4, 6 };
// Write color block.
for( uint j = 0; j < 4; j++ ) {
for( uint i = 0; i < 4; i++ ) {
if( img.valid( x+i, y+j ) ) {
uint idx = (block.row[j] & masks[i]) >> shift[i];
color_array[idx].a = alpha_array[bit_array[j*4+i]];
scanline[j][x+i] = qRgb(color_array[idx].a, color_array[idx].g, color_array[idx].b);
}
}
}
}
}
return true;
}
static bool LoadATI2( QDataStream & s, const DDSHeader & header, QImage & img )
{
const uint w = header.width;
const uint h = header.height;
BlockDXTAlphaLinear xblock;
BlockDXTAlphaLinear yblock;
QRgb * scanline[4];
for( uint y = 0; y < h; y += 4 ) {
for( uint j = 0; j < 4; j++ ) {
scanline[j] = (QRgb *) img.scanLine( y + j );
}
for( uint x = 0; x < w; x += 4 ) {
// Read 128bit color block.
s >> xblock;
s >> yblock;
// Decode color block.
uchar xblock_array[8];
xblock.GetAlphas(xblock_array);
uchar xbit_array[16];
xblock.GetBits(xbit_array);
uchar yblock_array[8];
yblock.GetAlphas(yblock_array);
uchar ybit_array[16];
yblock.GetBits(ybit_array);
// Write color block.
for( uint j = 0; j < 4; j++ ) {
for( uint i = 0; i < 4; i++ ) {
if( img.valid( x+i, y+j ) ) {
const uchar nx = xblock_array[xbit_array[j*4+i]];
const uchar ny = yblock_array[ybit_array[j*4+i]];
const float fx = float(nx) / 127.5f - 1.0f;
const float fy = float(ny) / 127.5f - 1.0f;
const float fz = sqrtf(1.0f - fx*fx - fy*fy);
const uchar nz = uchar((fz + 1.0f) * 127.5f);
scanline[j][x+i] = qRgb(nx, ny, nz);
}
}
}
}
}
return true;
}
typedef bool (* TextureLoader)( QDataStream & s, const DDSHeader & header, QImage & img );
// Get an appropiate texture loader for the given type.
static TextureLoader GetTextureLoader( DDSType type ) {
switch( type ) {
case DDS_A8R8G8B8:
return LoadA8R8G8B8;
case DDS_A1R5G5B5:
return LoadA1R5G5B5;
case DDS_A4R4G4B4:
return LoadA4R4G4B4;
case DDS_R8G8B8:
return LoadR8G8B8;
case DDS_R5G6B5:
return LoadR5G6B5;
case DDS_DXT1:
return LoadDXT1;
case DDS_DXT2:
return LoadDXT2;
case DDS_DXT3:
return LoadDXT3;
case DDS_DXT4:
return LoadDXT4;
case DDS_DXT5:
return LoadDXT5;
case DDS_RXGB:
return LoadRXGB;
case DDS_ATI2:
return LoadATI2;
default:
return NULL;
};
}
// Load a 2d texture.
static bool LoadTexture( QDataStream & s, const DDSHeader & header, QImage & img )
{
// Create dst image.
if( !img.create( header.width, header.height, 32 )) {
return false;
}
// Read image.
DDSType type = GetType( header );
// Enable alpha buffer for transparent or DDS images.
if( HasAlpha( header ) || type >= DDS_DXT1 ) {
img.setAlphaBuffer( true );
}
TextureLoader loader = GetTextureLoader( type );
if( loader == NULL ) {
return false;
}
return loader( s, header, img );
}
static int FaceOffset( const DDSHeader & header ) {
DDSType type = GetType( header );
int mipmap = kMax(int(header.mipmapcount), 1);
int size = 0;
int w = header.width;
int h = header.height;
if( type >= DDS_DXT1 ) {
int multiplier = (type == DDS_DXT1) ? 8 : 16;
do {
int face_size = kMax(w/4,1) * kMax(h/4,1) * multiplier;
size += face_size;
w >>= 1;
h >>= 1;
} while( --mipmap );
}
else {
int multiplier = header.pf.bitcount / 8;
do {
int face_size = w * h * multiplier;
size += face_size;
w = kMax( w>>1, 1 );
h = kMax( h>>1, 1 );
} while( --mipmap );
}
return size;
}
#if CUBE_LAYOUT == HORIZONTAL
static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {3, 1} };
#elif CUBE_LAYOUT == VERTICAL
static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {1, 3} };
#endif
static int face_flags[6] = {
DDSCAPS2_CUBEMAP_POSITIVEX,
DDSCAPS2_CUBEMAP_NEGATIVEX,
DDSCAPS2_CUBEMAP_POSITIVEY,
DDSCAPS2_CUBEMAP_NEGATIVEY,
DDSCAPS2_CUBEMAP_POSITIVEZ,
DDSCAPS2_CUBEMAP_NEGATIVEZ
};
// Load unwrapped cube map.
static bool LoadCubeMap( QDataStream & s, const DDSHeader & header, QImage & img )
{
// Create dst image.
#if CUBE_LAYOUT == HORIZONTAL
if( !img.create( 4 * header.width, 3 * header.height, 32 )) {
return false; // duplicate code for correct syntax coloring.
}
#elif CUBE_LAYOUT == VERTICAL
if( !img.create( 3 * header.width, 4 * header.height, 32 )) {
return false;
}
#endif
DDSType type = GetType( header );
// Enable alpha buffer for transparent or DDS images.
if( HasAlpha( header ) || type >= DDS_DXT1 ) {
img.setAlphaBuffer( true );
}
// Select texture loader.
TextureLoader loader = GetTextureLoader( type );
if( loader == NULL ) {
return false;
}
// Clear background.
img.fill( 0 );
// Create face image.
QImage face;
if( !face.create( header.width, header.height, 32 )) {
return false;
}
int offset = s.device()->at();
int size = FaceOffset( header );
for( int i = 0; i < 6; i++ ) {
if( !(header.caps.caps2 & face_flags[i]) ) {
// Skip face.
continue;
}
// Seek device.
s.device()->at( offset );
offset += size;
// Load face from stream.
if( !loader( s, header, face ) ) {
return false;
}
#if CUBE_LAYOUT == VERTICAL
if( i == 5 ) {
face = face.mirror(true, true);
}
#endif
// Compute face offsets.
int offset_x = face_offset[i][0] * header.width;
int offset_y = face_offset[i][1] * header.height;
// Copy face on the image.
for( uint y = 0; y < header.height; y++ ) {
QRgb * src = (QRgb *) face.scanLine( y );
QRgb * dst = (QRgb *) img.scanLine( y + offset_y ) + offset_x;
memcpy( dst, src, sizeof(QRgb) * header.width );
}
}
return true;
}
}
KDE_EXPORT void kimgio_dds_read( QImageIO *io )
{
QDataStream s( io->ioDevice() );
s.setByteOrder( QDataStream::LittleEndian );
// Validate header.
uint fourcc;
s >> fourcc;
if( fourcc != FOURCC_DDS ) {
kdDebug(399) << "This is not a DDS file." << endl;
io->setImage( 0 );
io->setStatus( -1 );
return;
}
// Read image header.
DDSHeader header;
s >> header;
// Check image file format.
if( s.atEnd() || !IsValid( header ) ) {
kdDebug(399) << "This DDS file is not valid." << endl;
io->setImage( 0 );
io->setStatus( -1 );
return;
}
// Determine image type, by now, we only support 2d textures.
if( !IsSupported( header ) ) {
kdDebug(399) << "This DDS file is not supported." << endl;
io->setImage( 0 );
io->setStatus( -1 );
return;
}
QImage img;
bool result;
if( IsCubeMap( header ) ) {
result = LoadCubeMap( s, header, img );
}
else {
result = LoadTexture( s, header, img );
}
if( result == false ) {
kdDebug(399) << "Error loading DDS file." << endl;
io->setImage( 0 );
io->setStatus( -1 );
return;
}
io->setImage( img );
io->setStatus( 0 );
}
KDE_EXPORT void kimgio_dds_write( QImageIO * )
{
// TODO Stub!
}

KDE Home | KDE Accessibility Home | Description of Access Keys