24 #include <QtCore/QStringList>
25 #include <QtGui/QImage>
26 #include <QtCore/QDataStream>
33 #define sqrtf(x) ((float)sqrt(x))
40 #if !defined(MAKEFOURCC)
41 # define MAKEFOURCC(ch0, ch1, ch2, ch3) \
42 (uint(uchar(ch0)) | (uint(uchar(ch1)) << 8) | \
43 (uint(uchar(ch2)) << 16) | (uint(uchar(ch3)) << 24 ))
48 #define CUBE_LAYOUT HORIZONTAL
133 struct DDSPixelFormat {
144 static QDataStream &
operator>> ( QDataStream & s, DDSPixelFormat & pf )
164 static QDataStream &
operator>> ( QDataStream & s, DDSCaps & caps )
187 static QDataStream &
operator>> ( QDataStream & s, DDSHeader & header )
195 s >> header.mipmapcount;
196 for(
int i = 0; i < 11; i++ ) {
197 s >> header.reserved[i];
205 static bool IsValid(
const DDSHeader & header )
207 if( header.size != 124 ) {
211 if( (header.flags & required) != required ) {
214 if( header.pf.size != 32 ) {
229 switch( header.pf.bitcount ) {
237 switch( header.pf.bitcount ) {
246 switch( header.pf.fourcc ) {
287 static bool LoadA8R8G8B8( QDataStream & s,
const DDSHeader & header, QImage & img )
289 const uint w = header.width;
290 const uint h = header.height;
292 for(
uint y = 0; y < h; y++ ) {
293 QRgb * scanline = (QRgb *) img.scanLine( y );
294 for(
uint x = 0; x < w; x++ ) {
296 s >> b >> g >> r >> a;
297 scanline[x] =
qRgba(r, g, b, a);
304 static bool LoadR8G8B8( QDataStream & s,
const DDSHeader & header, QImage & img )
306 const uint w = header.width;
307 const uint h = header.height;
309 for(
uint y = 0; y < h; y++ ) {
310 QRgb * scanline = (QRgb *) img.scanLine( y );
311 for(
uint x = 0; x < w; x++ ) {
314 scanline[x] = qRgb(r, g, b);
321 static bool LoadA1R5G5B5( QDataStream & s,
const DDSHeader & header, QImage & img )
323 const uint w = header.width;
324 const uint h = header.height;
326 for(
uint y = 0; y < h; y++ ) {
327 QRgb * scanline = (QRgb *) img.scanLine( y );
328 for(
uint x = 0; x < w; x++ ) {
331 uchar a = (color.c.a != 0) ? 0xFF : 0;
332 uchar r = (color.c.r << 3) | (color.c.r >> 2);
333 uchar g = (color.c.g << 3) | (color.c.g >> 2);
334 uchar b = (color.c.b << 3) | (color.c.b >> 2);
335 scanline[x] =
qRgba(r, g, b, a);
342 static bool LoadA4R4G4B4( QDataStream & s,
const DDSHeader & header, QImage & img )
344 const uint w = header.width;
345 const uint h = header.height;
347 for(
uint y = 0; y < h; y++ ) {
348 QRgb * scanline = (QRgb *) img.scanLine( y );
349 for(
uint x = 0; x < w; x++ ) {
352 uchar a = (color.c.a << 4) | color.c.a;
353 uchar r = (color.c.r << 4) | color.c.r;
354 uchar g = (color.c.g << 4) | color.c.g;
355 uchar b = (color.c.b << 4) | color.c.b;
356 scanline[x] =
qRgba(r, g, b, a);
363 static bool LoadR5G6B5( QDataStream & s,
const DDSHeader & header, QImage & img )
365 const uint w = header.width;
366 const uint h = header.height;
368 for(
uint y = 0; y < h; y++ ) {
369 QRgb * scanline = (QRgb *) img.scanLine( y );
370 for(
uint x = 0; x < w; x++ ) {
373 uchar r = (color.c.r << 3) | (color.c.r >> 2);
374 uchar g = (color.c.g << 2) | (color.c.g >> 4);
375 uchar b = (color.c.b << 3) | (color.c.b >> 2);
376 scanline[x] = qRgb(r, g, b);
383 static QDataStream &
operator>> ( QDataStream & s, Color565 & c )
395 void GetColors( Color8888 color_array[4] )
397 color_array[0].r = (col0.c.r << 3) | (col0.c.r >> 2);
398 color_array[0].g = (col0.c.g << 2) | (col0.c.g >> 4);
399 color_array[0].b = (col0.c.b << 3) | (col0.c.b >> 2);
400 color_array[0].a = 0xFF;
402 color_array[1].r = (col1.c.r << 3) | (col1.c.r >> 2);
403 color_array[1].g = (col1.c.g << 2) | (col1.c.g >> 4);
404 color_array[1].b = (col1.c.b << 3) | (col1.c.b >> 2);
405 color_array[1].a = 0xFF;
407 if( col0.u > col1.u ) {
409 color_array[2].r = (2 * color_array[0].r + color_array[1].r) / 3;
410 color_array[2].g = (2 * color_array[0].g + color_array[1].g) / 3;
411 color_array[2].b = (2 * color_array[0].b + color_array[1].b) / 3;
412 color_array[2].a = 0xFF;
414 color_array[3].r = (2 * color_array[1].r + color_array[0].r) / 3;
415 color_array[3].g = (2 * color_array[1].g + color_array[0].g) / 3;
416 color_array[3].b = (2 * color_array[1].b + color_array[0].b) / 3;
417 color_array[3].a = 0xFF;
421 color_array[2].r = (color_array[0].r + color_array[1].r) / 2;
422 color_array[2].g = (color_array[0].g + color_array[1].g) / 2;
423 color_array[2].b = (color_array[0].b + color_array[1].b) / 2;
424 color_array[2].a = 0xFF;
427 color_array[3].r = 0x00;
428 color_array[3].g = 0x00;
429 color_array[3].b = 0x00;
430 color_array[3].a = 0x00;
436 static QDataStream &
operator>> ( QDataStream & s, BlockDXT & c )
438 return s >> c.col0 >> c.col1 >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
441 struct BlockDXTAlphaExplicit {
445 static QDataStream &
operator>> ( QDataStream & s, BlockDXTAlphaExplicit & c )
447 return s >> c.row[0] >> c.row[1] >> c.row[2] >> c.row[3];
450 struct BlockDXTAlphaLinear {
455 void GetAlphas(
uchar alpha_array[8] )
457 alpha_array[0] = alpha0;
458 alpha_array[1] = alpha1;
461 if( alpha_array[0] > alpha_array[1] )
466 alpha_array[2] = ( 6 * alpha0 + alpha1) / 7;
467 alpha_array[3] = ( 5 * alpha0 + 2 * alpha1) / 7;
468 alpha_array[4] = ( 4 * alpha0 + 3 * alpha1) / 7;
469 alpha_array[5] = ( 3 * alpha0 + 4 * alpha1) / 7;
470 alpha_array[6] = ( 2 * alpha0 + 5 * alpha1) / 7;
471 alpha_array[7] = ( alpha0 + 6 * alpha1) / 7;
478 alpha_array[2] = (4 * alpha0 + alpha1) / 5;
479 alpha_array[3] = (3 * alpha0 + 2 * alpha1) / 5;
480 alpha_array[4] = (2 * alpha0 + 3 * alpha1) / 5;
481 alpha_array[5] = ( alpha0 + 4 * alpha1) / 5;
482 alpha_array[6] = 0x00;
483 alpha_array[7] = 0xFF;
487 void GetBits(
uchar bit_array[16] )
490 bit_array[0] =
uchar(b & 0x07); b >>= 3;
491 bit_array[1] =
uchar(b & 0x07); b >>= 3;
492 bit_array[2] =
uchar(b & 0x07); b >>= 3;
493 bit_array[3] =
uchar(b & 0x07); b >>= 3;
494 bit_array[4] =
uchar(b & 0x07); b >>= 3;
495 bit_array[5] =
uchar(b & 0x07); b >>= 3;
496 bit_array[6] =
uchar(b & 0x07); b >>= 3;
497 bit_array[7] =
uchar(b & 0x07); b >>= 3;
499 b = (
uint &) bits[3];
500 bit_array[8] =
uchar(b & 0x07); b >>= 3;
501 bit_array[9] =
uchar(b & 0x07); b >>= 3;
502 bit_array[10] =
uchar(b & 0x07); b >>= 3;
503 bit_array[11] =
uchar(b & 0x07); b >>= 3;
504 bit_array[12] =
uchar(b & 0x07); b >>= 3;
505 bit_array[13] =
uchar(b & 0x07); b >>= 3;
506 bit_array[14] =
uchar(b & 0x07); b >>= 3;
507 bit_array[15] =
uchar(b & 0x07); b >>= 3;
511 static QDataStream &
operator>> ( QDataStream & s, BlockDXTAlphaLinear & c )
513 s >> c.alpha0 >> c.alpha1;
514 return s >> c.bits[0] >> c.bits[1] >> c.bits[2] >> c.bits[3] >> c.bits[4] >> c.bits[5];
517 static bool LoadDXT1( QDataStream & s,
const DDSHeader & header, QImage & img )
519 const uint w = header.width;
520 const uint h = header.height;
525 for(
uint y = 0; y < h; y += 4 ) {
526 for(
uint j = 0; j < 4; j++ ) {
527 scanline[j] = (QRgb *) img.scanLine( y + j );
529 for(
uint x = 0; x < w; x += 4 ) {
535 Color8888 color_array[4];
536 block.GetColors(color_array);
539 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
540 const int shift[4] = { 0, 2, 4, 6 };
543 for(
uint j = 0; j < 4; j++ ) {
544 for(
uint i = 0; i < 4; i++ ) {
545 if( img.valid( x+i, y+j ) ) {
546 uint idx = (block.row[j] & masks[i]) >> shift[i];
547 scanline[j][x+i] =
qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
556 static bool LoadDXT3( QDataStream & s,
const DDSHeader & header, QImage & img )
558 const uint w = header.width;
559 const uint h = header.height;
562 BlockDXTAlphaExplicit alpha;
565 for(
uint y = 0; y < h; y += 4 ) {
566 for(
uint j = 0; j < 4; j++ ) {
567 scanline[j] = (QRgb *) img.scanLine( y + j );
569 for(
uint x = 0; x < w; x += 4 ) {
576 Color8888 color_array[4];
577 block.GetColors(color_array);
580 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
581 const int shift[4] = { 0, 2, 4, 6 };
584 for(
uint j = 0; j < 4; j++ ) {
586 for(
uint i = 0; i < 4; i++ ) {
587 if( img.valid( x+i, y+j ) ) {
588 uint idx = (block.row[j] & masks[i]) >> shift[i];
589 color_array[idx].a = a & 0x0f;
590 color_array[idx].a = color_array[idx].a | (color_array[idx].a << 4);
591 scanline[j][x+i] =
qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
601 static bool LoadDXT2( QDataStream & s,
const DDSHeader & header, QImage & img )
603 if( !
LoadDXT3(s, header, img) )
return false;
608 static bool LoadDXT5( QDataStream & s,
const DDSHeader & header, QImage & img )
610 const uint w = header.width;
611 const uint h = header.height;
614 BlockDXTAlphaLinear alpha;
617 for(
uint y = 0; y < h; y += 4 ) {
618 for(
uint j = 0; j < 4; j++ ) {
619 scanline[j] = (QRgb *) img.scanLine( y + j );
621 for(
uint x = 0; x < w; x += 4 ) {
628 Color8888 color_array[4];
629 block.GetColors(color_array);
631 uchar alpha_array[8];
632 alpha.GetAlphas(alpha_array);
635 alpha.GetBits(bit_array);
638 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
639 const int shift[4] = { 0, 2, 4, 6 };
642 for(
uint j = 0; j < 4; j++ ) {
643 for(
uint i = 0; i < 4; i++ ) {
644 if( img.valid( x+i, y+j ) ) {
645 uint idx = (block.row[j] & masks[i]) >> shift[i];
646 color_array[idx].a = alpha_array[bit_array[j*4+i]];
647 scanline[j][x+i] =
qRgba(color_array[idx].r, color_array[idx].g, color_array[idx].b, color_array[idx].a);
656 static bool LoadDXT4( QDataStream & s,
const DDSHeader & header, QImage & img )
658 if( !
LoadDXT5(s, header, img) )
return false;
663 static bool LoadRXGB( QDataStream & s,
const DDSHeader & header, QImage & img )
665 const uint w = header.width;
666 const uint h = header.height;
669 BlockDXTAlphaLinear alpha;
672 for(
uint y = 0; y < h; y += 4 ) {
673 for(
uint j = 0; j < 4; j++ ) {
674 scanline[j] = (QRgb *) img.scanLine( y + j );
676 for(
uint x = 0; x < w; x += 4 ) {
683 Color8888 color_array[4];
684 block.GetColors(color_array);
686 uchar alpha_array[8];
687 alpha.GetAlphas(alpha_array);
690 alpha.GetBits(bit_array);
693 const uint masks[4] = { 3, 3<<2, 3<<4, 3<<6 };
694 const int shift[4] = { 0, 2, 4, 6 };
697 for(
uint j = 0; j < 4; j++ ) {
698 for(
uint i = 0; i < 4; i++ ) {
699 if( img.valid( x+i, y+j ) ) {
700 uint idx = (block.row[j] & masks[i]) >> shift[i];
701 color_array[idx].a = alpha_array[bit_array[j*4+i]];
702 scanline[j][x+i] = qRgb(color_array[idx].a, color_array[idx].g, color_array[idx].b);
712 static bool LoadATI2( QDataStream & s,
const DDSHeader & header, QImage & img )
714 const uint w = header.width;
715 const uint h = header.height;
717 BlockDXTAlphaLinear xblock;
718 BlockDXTAlphaLinear yblock;
721 for(
uint y = 0; y < h; y += 4 ) {
722 for(
uint j = 0; j < 4; j++ ) {
723 scanline[j] = (QRgb *) img.scanLine( y + j );
725 for(
uint x = 0; x < w; x += 4 ) {
732 uchar xblock_array[8];
733 xblock.GetAlphas(xblock_array);
735 uchar xbit_array[16];
736 xblock.GetBits(xbit_array);
738 uchar yblock_array[8];
739 yblock.GetAlphas(yblock_array);
741 uchar ybit_array[16];
742 yblock.GetBits(ybit_array);
745 for(
uint j = 0; j < 4; j++ ) {
746 for(
uint i = 0; i < 4; i++ ) {
747 if( img.valid( x+i, y+j ) ) {
748 const uchar nx = xblock_array[xbit_array[j*4+i]];
749 const uchar ny = yblock_array[ybit_array[j*4+i]];
751 const float fx = float(nx) / 127.5f - 1.0f;
752 const float fy = float(ny) / 127.5f - 1.0f;
753 const float fz =
sqrtf(1.0f - fx*fx - fy*fy);
756 scanline[j][x+i] = qRgb(nx, ny, nz);
768 typedef bool (*
TextureLoader)( QDataStream & s,
const DDSHeader & header, QImage & img );
804 static bool LoadTexture( QDataStream & s,
const DDSHeader & header, QImage & img )
807 img = QImage( header.width, header.height, QImage::Format_RGB32 );
814 img = img.convertToFormat( QImage::Format_ARGB32 );
818 if( loader == NULL ) {
822 return loader( s, header, img );
830 int mipmap = qMax(header.mipmapcount, 1U);
832 int w = header.width;
833 int h = header.height;
836 int multiplier = (type ==
DDS_DXT1) ? 8 : 16;
838 int face_size = qMax(w/4,1) * qMax(h/4,1) * multiplier;
845 int multiplier = header.pf.bitcount / 8;
847 int face_size = w * h * multiplier;
857 #if CUBE_LAYOUT == HORIZONTAL
858 static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {3, 1} };
859 #elif CUBE_LAYOUT == VERTICAL
860 static int face_offset[6][2] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {1, 3} };
872 static bool LoadCubeMap( QDataStream & s,
const DDSHeader & header, QImage & img )
875 #if CUBE_LAYOUT == HORIZONTAL
876 img = QImage( 4 * header.width, 3 * header.height, QImage::Format_RGB32 );
877 #elif CUBE_LAYOUT == VERTICAL
878 img = QImage( 3 * header.width, 4 * header.height, QImage::Format_RGB32 );
885 img = img.convertToFormat( QImage::Format_ARGB32 );
890 if( loader == NULL ) {
898 QImage face(header.width, header.height, QImage::Format_RGB32);
900 int offset = s.device()->pos();
903 for(
int i = 0; i < 6; i++ ) {
905 if( !(header.caps.caps2 & face_flags[i]) ) {
911 s.device()->seek( offset );
915 if( !loader( s, header, face ) ) {
919 #if CUBE_LAYOUT == VERTICAL
921 face = face.mirror(
true,
true);
926 int offset_x = face_offset[i][0] * header.width;
927 int offset_y = face_offset[i][1] * header.height;
930 for(
uint y = 0; y < header.height; y++ ) {
931 QRgb * src = (QRgb *) face.scanLine( y );
932 QRgb * dst = (QRgb *) img.scanLine( y + offset_y ) + offset_x;
933 memcpy( dst, src,
sizeof(QRgb) * header.width );
957 QDataStream s( device() );
958 s.setByteOrder( QDataStream::LittleEndian );
964 kDebug(399) <<
"This is not a DDS file.";
973 if( s.atEnd() || !
IsValid( header ) ) {
974 kDebug(399) <<
"This DDS file is not valid.";
980 kDebug(399) <<
"This DDS file is not supported.";
1010 qWarning(
"DDSHandler::canRead() called with no device");
1014 qint64 oldPos = device->pos();
1017 qint64 readBytes = device->read(head,
sizeof(head));
1018 if (readBytes !=
sizeof(head)) {
1019 if (device->isSequential()) {
1020 while (readBytes > 0)
1021 device->ungetChar(head[readBytes-- - 1]);
1023 device->seek(oldPos);
1028 if (device->isSequential()) {
1029 while (readBytes > 0)
1030 device->ungetChar(head[readBytes-- - 1]);
1032 device->seek(oldPos);
1035 return qstrncmp(head,
"DDS", 3) == 0;
1038 class DDSPlugin :
public QImageIOPlugin
1041 QStringList keys()
const;
1042 Capabilities capabilities(QIODevice *device,
const QByteArray &format)
const;
1043 QImageIOHandler *create(QIODevice *device,
const QByteArray &format = QByteArray())
const;
1046 QStringList DDSPlugin::keys()
const
1048 return QStringList() <<
"dds";
1051 QImageIOPlugin::Capabilities DDSPlugin::capabilities(QIODevice *device,
const QByteArray &format)
const
1053 if (format ==
"dds")
1054 return Capabilities(CanRead);
1055 if (!format.isEmpty())
1057 if (!device->isOpen())
1066 QImageIOHandler *DDSPlugin::create(QIODevice *device,
const QByteArray &format)
const
1069 handler->setDevice(device);
1070 handler->setFormat(format);
1074 Q_EXPORT_STATIC_PLUGIN(DDSPlugin)
1075 Q_EXPORT_PLUGIN2(dds, DDSPlugin)