diff options
author | ABBAPOH <ABBAPOH@nextmail.ru> | 2013-11-13 20:00:27 +0400 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-11-26 11:35:06 +0100 |
commit | e39aa8a59ea511f7fa1fe416fad54952b5297b7b (patch) | |
tree | 96713a96ebb8744c0004a2fd0d242bea703a48d7 | |
parent | 8cc21cdcbf7e31da54b0e9f51df829abcf72ea65 (diff) |
Add Direct Draw Surface plugin
Change-Id: I8dfa6001c1aae2dcb5f2e6e0bfd142ef1f9dddbc
Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
62 files changed, 2255 insertions, 2 deletions
diff --git a/src/plugins/imageformats/dds/README b/src/plugins/imageformats/dds/README new file mode 100644 index 0000000..873a949 --- /dev/null +++ b/src/plugins/imageformats/dds/README @@ -0,0 +1,10 @@ +[Useful links] +* http://en.wikipedia.org/wiki/DirectDraw_Surface - Wiki:) +* http://msdn.microsoft.com/en-us/library/bb943990.aspx - MSDN +* http://msdn.microsoft.com/en-us/library/bb943991(v=vs.85).aspx - Programming Guide +* http://msdn.microsoft.com/en-us/library/bb943982(v=vs.85).aspx - DDS Header +* http://msdn.microsoft.com/en-us/library/bb943983(v=vs.85).aspx - DDS Header DXT10 +* http://msdn.microsoft.com/en-us/library/bb943984(v=vs.85).aspx - PixelFormat +* http://msdn.microsoft.com/en-us/library/windows/desktop/bb153349(v=vs.85).aspx - Surface formats (aka Format enum) +* http://www.poopinmymouth.com/tutorial/dds_types.html - A bit better explanation than in MSDN +* http://en.wikipedia.org/wiki/S3_Texture_Compression - DXT compression diff --git a/src/plugins/imageformats/dds/dds.json b/src/plugins/imageformats/dds/dds.json new file mode 100644 index 0000000..bc30795 --- /dev/null +++ b/src/plugins/imageformats/dds/dds.json @@ -0,0 +1,4 @@ +{ + "Keys": [ "dds" ], + "MimeTypes": [ "image/x-dds" ] +} diff --git a/src/plugins/imageformats/dds/dds.pro b/src/plugins/imageformats/dds/dds.pro new file mode 100644 index 0000000..331a912 --- /dev/null +++ b/src/plugins/imageformats/dds/dds.pro @@ -0,0 +1,17 @@ +TARGET = qdds + +PLUGIN_TYPE = imageformats +PLUGIN_CLASS_NAME = QDDSPlugin +load(qt_plugin) + +HEADERS += \ + ddsheader.h \ + main.h \ + qddshandler.h + +SOURCES += \ + ddsheader.cpp \ + main.cpp \ + qddshandler.cpp + +OTHER_FILES += dds.json diff --git a/src/plugins/imageformats/dds/ddsheader.cpp b/src/plugins/imageformats/dds/ddsheader.cpp new file mode 100644 index 0000000..f5560be --- /dev/null +++ b/src/plugins/imageformats/dds/ddsheader.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Ivan Komissarov. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the DDS plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "ddsheader.h" + +QT_BEGIN_NAMESPACE + +QDataStream &operator>>(QDataStream &s, DDSPixelFormat &pixelFormat) +{ + s >> pixelFormat.size; + s >> pixelFormat.flags; + s >> pixelFormat.fourCC; + s >> pixelFormat.rgbBitCount; + s >> pixelFormat.rBitMask; + s >> pixelFormat.gBitMask; + s >> pixelFormat.bBitMask; + s >> pixelFormat.aBitMask; + return s; +} + +QDataStream &operator<<(QDataStream &s, const DDSPixelFormat &pixelFormat) +{ + s << pixelFormat.size; + s << pixelFormat.flags; + s << pixelFormat.fourCC; + s << pixelFormat.rgbBitCount; + s << pixelFormat.rBitMask; + s << pixelFormat.gBitMask; + s << pixelFormat.bBitMask; + s << pixelFormat.aBitMask; + return s; +} + +QDataStream &operator>>(QDataStream &s, DDSHeader &header) +{ + s >> header.magic; + s >> header.size; + s >> header.flags; + s >> header.height; + s >> header.width; + s >> header.pitchOrLinearSize; + s >> header.depth; + s >> header.mipMapCount; + for (int i = 0; i < DDSHeader::ReservedCount; i++) + s >> header.reserved1[i]; + s >> header.pixelFormat; + s >> header.caps; + s >> header.caps2; + s >> header.caps3; + s >> header.caps4; + s >> header.reserved2; + return s; +} + +QDataStream &operator<<(QDataStream &s, const DDSHeader &header) +{ + s << header.magic; + s << header.size; + s << header.flags; + s << header.height; + s << header.width; + s << header.pitchOrLinearSize; + s << header.depth; + s << header.mipMapCount; + for (int i = 0; i < DDSHeader::ReservedCount; i++) + s << header.reserved1[i]; + s << header.pixelFormat; + s << header.caps; + s << header.caps2; + s << header.caps3; + s << header.caps4; + s << header.reserved2; + return s; +} + +QDataStream &operator>>(QDataStream &s, DDSHeaderDX10 &header) +{ + s >> header.dxgiFormat; + s >> header.resourceDimension; + s >> header.miscFlag; + s >> header.arraySize; + s >> header.reserved; + return s; +} + +QDataStream &operator<<(QDataStream &s, const DDSHeaderDX10 &header) +{ + s << header.dxgiFormat; + s << header.resourceDimension; + s << header.miscFlag; + s << header.arraySize; + s << header.reserved; + return s; +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/dds/ddsheader.h b/src/plugins/imageformats/dds/ddsheader.h new file mode 100644 index 0000000..c3f8c0f --- /dev/null +++ b/src/plugins/imageformats/dds/ddsheader.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Ivan Komissarov. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the DDS plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DDSHEADER_H +#define DDSHEADER_H + +#include <QtCore/QDataStream> + +QT_BEGIN_NAMESPACE + +enum Format { + FormatUnknown = 0, + + FormatR8G8B8 = 20, + FormatA8R8G8B8 = 21, + FormatX8R8G8B8 = 22, + FormatR5G6B5 = 23, + FormatX1R5G5B5 = 24, + FormatA1R5G5B5 = 25, + FormatA4R4G4B4 = 26, + FormatR3G3B2 = 27, + FormatA8 = 28, + FormatA8R3G3B2 = 29, + FormatX4R4G4B4 = 30, + FormatA2B10G10R10 = 31, + FormatA8B8G8R8 = 32, + FormatX8B8G8R8 = 33, + FormatG16R16 = 34, + FormatA2R10G10B10 = 35, + FormatA16B16G16R16 = 36, + + FormatA8P8 = 40, + FormatP8 = 41, + + FormatL8 = 50, + FormatA8L8 = 51, + FormatA4L4 = 52, + + FormatV8U8 = 60, + FormatL6V5U5 = 61, + FormatX8L8V8U8 = 62, + FormatQ8W8V8U8 = 63, + FormatV16U16 = 64, + FormatA2W10V10U10 = 67, + + FormatUYVY = 0x59565955, // "UYVY" + FormatR8G8B8G8 = 0x47424752, // "RGBG" + FormatYUY2 = 0x32595559, // "YUY2" + FormatG8R8G8B8 = 0x42475247, // "GRGB" + FormatDXT1 = 0x31545844, // "DXT1" + FormatDXT2 = 0x32545844, // "DXT2" + FormatDXT3 = 0x33545844, // "DXT3" + FormatDXT4 = 0x34545844, // "DXT4" + FormatDXT5 = 0x35545844, // "DXT5" + FormatRXGB = 0x42475852, // "RXGB" + FormatATI2 = 0x32495441, // "ATI2" + + FormatD16Lockable = 70, + FormatD32 = 71, + FormatD15S1 = 73, + FormatD24S8 = 75, + FormatD24X8 = 77, + FormatD24X4S4 = 79, + FormatD16 = 80, + + FormatD32FLockable = 82, + FormatD24FS8 = 83, + + FormatD32Lockable = 84, + FormatS8Lockable = 85, + + FormatL16 = 81, + + FormatVertexData =100, + FormatIndex16 =101, + FormatIndex32 =102, + + FormatQ16W16V16U16 = 110, + + FormatMulti2ARGB8 = 0x3154454d, // "MET1" + + FormatR16F = 111, + FormatG16R16F = 112, + FormatA16B16G16R16F = 113, + + FormatR32F = 114, + FormatG32R32F = 115, + FormatA32B32G32R32F = 116, + + FormatCxV8U8 = 117, + + FormatA1 = 118, + FormatA2B10G10R10_XR_BIAS = 119, + FormatBinaryBuffer = 199, + + FormatLast = 0x7fffffff +}; + +struct DDSPixelFormat +{ + enum DDSPixelFormatFlags { + FlagAlphaPixels = 0x00000001, + FlagAlpha = 0x00000002, + FlagFourCC = 0x00000004, + FlagPaletteIndexed8 = 0x00000020, + FlagRGB = 0x00000040, + FlagYUV = 0x00000200, + FlagLuminance = 0x00020000, + FlagNormal = 0x00080000, + FlagRGBA = FlagAlphaPixels | FlagRGB, + FlagLA = FlagAlphaPixels | FlagLuminance + }; + + quint32 size; + quint32 flags; + quint32 fourCC; + quint32 rgbBitCount; + quint32 rBitMask; + quint32 gBitMask; + quint32 bBitMask; + quint32 aBitMask; +}; + +QDataStream &operator>>(QDataStream &s, DDSPixelFormat &pixelFormat); +QDataStream &operator<<(QDataStream &s, const DDSPixelFormat &pixelFormat); + +struct DDSHeader +{ + enum DDSFlags { + FlagCaps = 0x000001, + FlagHeight = 0x000002, + FlagWidth = 0x000004, + FlagPitch = 0x000008, + FlagPixelFormat = 0x001000, + FlagMipmapCount = 0x020000, + FlagLinearSize = 0x080000, + FlagDepth = 0x800000 + }; + + enum DDSCapsFlags { + CapsComplex = 0x000008, + CapsTexture = 0x001000, + CapsMipmap = 0x400000 + }; + + enum DDSCaps2Flags { + Caps2CubeMap = 0x0200, + Caps2CubeMapPositiveX = 0x0400, + Caps2CubeMapNegativeX = 0x0800, + Caps2CubeMapPositiveY = 0x1000, + Caps2CubeMapNegativeY = 0x2000, + Caps2CubeMapPositiveZ = 0x4000, + Caps2CubeMapNegativeZ = 0x8000, + Caps2Volume = 0x200000 + }; + + enum { ReservedCount = 11 }; + + quint32 magic; + quint32 size; + quint32 flags; + quint32 height; + quint32 width; + quint32 pitchOrLinearSize; + quint32 depth; + quint32 mipMapCount; + quint32 reserved1[ReservedCount]; + DDSPixelFormat pixelFormat; + quint32 caps; + quint32 caps2; + quint32 caps3; + quint32 caps4; + quint32 reserved2; +}; + +QDataStream &operator>>(QDataStream &s, DDSHeader &header); +QDataStream &operator<<(QDataStream &s, const DDSHeader &header); + +struct DDSHeaderDX10 +{ + quint32 dxgiFormat; + quint32 resourceDimension; + quint32 miscFlag; + quint32 arraySize; + quint32 reserved; +}; + +QDataStream &operator>>(QDataStream &s, DDSHeaderDX10 &header); +QDataStream &operator<<(QDataStream &s, const DDSHeaderDX10 &header); + +QT_END_NAMESPACE + +#endif // DDSHEADER_H diff --git a/src/plugins/imageformats/dds/main.cpp b/src/plugins/imageformats/dds/main.cpp new file mode 100644 index 0000000..7553407 --- /dev/null +++ b/src/plugins/imageformats/dds/main.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Ivan Komissarov. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the DDS plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "main.h" + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#include "qddshandler.h" + +QT_BEGIN_NAMESPACE + +QImageIOPlugin::Capabilities QDDSPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (!device || !device->isOpen()) + return 0; + if (format.toLower() != "dds") + return 0; + + Capabilities cap; + if (device->isReadable() && QDDSHandler::canRead(device)) + cap |= CanRead; + if (device->isWritable()) + cap |= CanWrite; + return cap; +} + +QImageIOHandler *QDDSPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *handler = new QDDSHandler; + handler->setDevice(device); + handler->setFormat(format); + return handler; +} + +QT_END_NAMESPACE + +#endif // QT_NO_IMAGEFORMATPLUGIN diff --git a/src/plugins/imageformats/dds/main.h b/src/plugins/imageformats/dds/main.h new file mode 100644 index 0000000..937caca --- /dev/null +++ b/src/plugins/imageformats/dds/main.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Ivan Komissarov. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the DDS plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAIN_H +#define MAIN_H + +#include <QtGui/qimageiohandler.h> + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#ifdef QT_NO_IMAGEFORMAT_DDS +#undef QT_NO_IMAGEFORMAT_DDS +#endif + +QT_BEGIN_NAMESPACE + +class QDDSPlugin : public QImageIOPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "dds.json") +public: + Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_IMAGEFORMATPLUGIN + +#endif // MAIN_H diff --git a/src/plugins/imageformats/dds/qddshandler.cpp b/src/plugins/imageformats/dds/qddshandler.cpp new file mode 100644 index 0000000..bca8a4d --- /dev/null +++ b/src/plugins/imageformats/dds/qddshandler.cpp @@ -0,0 +1,1414 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Ivan Komissarov. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the DDS plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qddshandler.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qmath.h> + +#include <QtGui/qimage.h> + +#include "ddsheader.h" + +QT_BEGIN_NAMESPACE + +enum Colors { + Red = 0, + Green, + Blue, + Alpha, + ColorCount +}; + +enum DXTVersions { + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + RXGB = 6 +}; + +// All magic numbers are little-endian as long as dds format has little +// endian byte order +static const quint32 ddsMagic = 0x20534444; // "DDS " +static const quint32 dx10Magic = 0x30315844; // "DX10" + +static const qint64 headerSize = 128; +static const quint32 ddsSize = 124; // headerSize without magic +static const quint32 pixelFormatSize = 32; + +struct FaceOffset +{ + int x, y; +}; + +static const FaceOffset faceOffsets[6] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {3, 1} }; + +static int faceFlags[6] = { + DDSHeader::Caps2CubeMapPositiveX, + DDSHeader::Caps2CubeMapNegativeX, + DDSHeader::Caps2CubeMapPositiveY, + DDSHeader::Caps2CubeMapNegativeY, + DDSHeader::Caps2CubeMapPositiveZ, + DDSHeader::Caps2CubeMapNegativeZ +}; + +struct FormatInfo +{ + Format format; + quint32 flags; + quint32 bitCount; + quint32 rBitMask; + quint32 gBitMask; + quint32 bBitMask; + quint32 aBitMask; +}; + +static const FormatInfo formatInfos[] = { + { FormatA8R8G8B8, DDSPixelFormat::FlagRGBA, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 }, + { FormatX8R8G8B8, DDSPixelFormat::FlagRGB, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }, + { FormatA2B10G10R10, DDSPixelFormat::FlagRGBA, 32, 0x000003ff, 0x0000fc00, 0x3ff00000, 0xc0000000 }, + { FormatA8B8G8R8, DDSPixelFormat::FlagRGBA, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }, + { FormatX8B8G8R8, DDSPixelFormat::FlagRGB, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 }, + { FormatG16R16, DDSPixelFormat::FlagRGBA, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }, + { FormatG16R16, DDSPixelFormat::FlagRGB, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }, + { FormatA2R10G10B10, DDSPixelFormat::FlagRGBA, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }, + + { FormatR8G8B8, DDSPixelFormat::FlagRGB, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 }, + + { FormatR5G6B5, DDSPixelFormat::FlagRGB, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000 }, + { FormatX1R5G5B5, DDSPixelFormat::FlagRGB, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00000000 }, + { FormatA1R5G5B5, DDSPixelFormat::FlagRGBA, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000 }, + { FormatA4R4G4B4, DDSPixelFormat::FlagRGBA, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000 }, + { FormatA8R3G3B2, DDSPixelFormat::FlagRGBA, 16, 0x000000e0, 0x0000001c, 0x00000003, 0x0000ff00 }, + { FormatX4R4G4B4, DDSPixelFormat::FlagRGB, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x00000000 }, + { FormatA8L8, DDSPixelFormat::FlagLA, 16, 0x000000ff, 0x00000000, 0x00000000, 0x0000ff00 }, + { FormatL16, DDSPixelFormat::FlagLuminance, 16, 0x0000ffff, 0x00000000, 0x00000000, 0x00000000 }, + + { FormatR3G3B2, DDSPixelFormat::FlagRGB, 8, 0x000000e0, 0x0000001c, 0x00000003, 0x00000000 }, + { FormatA8, DDSPixelFormat::FlagAlpha, 8, 0x00000000, 0x00000000, 0x00000000, 0x000000ff }, + { FormatL8, DDSPixelFormat::FlagLuminance, 8, 0x000000ff, 0x00000000, 0x00000000, 0x00000000 }, + { FormatA4L4, DDSPixelFormat::FlagLA, 8, 0x0000000f, 0x00000000, 0x00000000, 0x000000f0 }, + + { FormatV8U8, DDSPixelFormat::FlagNormal, 16, 0x000000ff, 0x0000ff00, 0x00000000, 0x00000000 }, + { FormatL6V5U5, 0, 16, 0x0000001f, 0x000003e0, 0x0000fc00, 0x00000000 }, + { FormatX8L8V8U8, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 }, + { FormatQ8W8V8U8, DDSPixelFormat::FlagNormal, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 }, + { FormatV16U16, DDSPixelFormat::FlagNormal, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 }, + { FormatA2W10V10U10, DDSPixelFormat::FlagNormal, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 } +}; +static const size_t formatInfosSize = sizeof(formatInfos)/sizeof(FormatInfo); + +static const Format knownFourCCs[] = { + FormatA16B16G16R16, + FormatV8U8, + FormatUYVY, + FormatR8G8B8G8, + FormatYUY2, + FormatG8R8G8B8, + FormatDXT1, + FormatDXT2, + FormatDXT3, + FormatDXT4, + FormatDXT5, + FormatRXGB, + FormatATI2, + FormatQ16W16V16U16, + FormatR16F, + FormatG16R16F, + FormatA16B16G16R16F, + FormatR32F, + FormatG32R32F, + FormatA32B32G32R32F, + FormatCxV8U8 +}; +static const size_t knownFourCCsSize = sizeof(knownFourCCs)/sizeof(Format); + +static inline int maskToShift(quint32 mask) +{ + if (mask == 0) + return 0; + + int result = 0; + while (!((mask >> result) & 1)) + result++; + return result; +} + +static inline int maskLength(quint32 mask) +{ + int result = 0; + while (mask) { + if (mask & 1) + result++; + mask >>= 1; + } + return result; +} + +static inline quint32 readValue(QDataStream &s, quint32 size) +{ + Q_ASSERT(size == 8 || size == 16 || size == 24 || size == 32); + + quint32 value = 0; + quint8 tmp; + for (unsigned bit = 0; bit < size; bit += 8) { + s >> tmp; + value += (quint32(tmp) << bit); + } + return value; +} + +static inline bool hasAlpha(const DDSHeader &dds) +{ + return (dds.pixelFormat.flags & (DDSPixelFormat::FlagAlphaPixels | DDSPixelFormat::FlagAlpha)) != 0; +} + +static inline bool isCubeMap(const DDSHeader &dds) +{ + return (dds.caps2 & DDSHeader::Caps2CubeMap) != 0; +} + +static inline QRgb yuv2rgb(quint8 Y, quint8 U, quint8 V) +{ + return qRgb(quint8(Y + 1.13983 * (V - 128)), + quint8(Y - 0.39465 * (U - 128) - 0.58060 * (V - 128)), + quint8(Y + 2.03211 * (U - 128))); +} + +static Format getFormat(const DDSHeader &dds) +{ + const DDSPixelFormat &format = dds.pixelFormat; + if (format.flags & DDSPixelFormat::FlagFourCC) { + for (size_t i = 0; i < knownFourCCsSize; ++i) { + if (dds.pixelFormat.fourCC == knownFourCCs[i]) + return knownFourCCs[i]; + } + } else { + for (size_t i = 0; i < formatInfosSize; ++i) { + const FormatInfo &info = formatInfos[i]; + if ((format.flags & info.flags) == info.flags && + format.rgbBitCount == info.bitCount && + format.rBitMask == info.rBitMask && + format.bBitMask == info.bBitMask && + format.bBitMask == info.bBitMask && + format.aBitMask == info.aBitMask) { + return info.format; + } + } + } + + return FormatUnknown; +} + +static inline void decodeColor(quint16 color, quint8 & red, quint8 & green, quint8 & blue) +{ + red = ((color >> 11) & 0x1f) << 3; + green = ((color >> 5) & 0x3f) << 2; + blue = (color & 0x1f) << 3; +} + +static inline quint8 calcC2(quint8 c0, quint8 c1) +{ + return 2.0 * c0 / 3.0 + c1 / 3.0; +} + +static inline quint8 calcC2a(quint8 c0, quint8 c1) +{ + return c0 / 2.0 + c1 / 2.0; +} + +static inline quint8 calcC3(quint8 c0, quint8 c1) +{ + return c0 / 3.0 + 2.0 * c1 / 3.0; +} + +static void DXTFillColors(QRgb *result, quint16 c0, quint16 c1, quint32 table, bool dxt1a = false) +{ + quint8 r[4]; + quint8 g[4]; + quint8 b[4]; + quint8 a[4]; + + a[0] = a[1] = a[2] = a[3] = 255; + + decodeColor(c0, r[0], g[0], b[0]); + decodeColor(c1, r[1], g[1], b[1]); + if (!dxt1a) { + r[2] = calcC2(r[0], r[1]); + g[2] = calcC2(g[0], g[1]); + b[2] = calcC2(b[0], b[1]); + r[3] = calcC3(r[0], r[1]); + g[3] = calcC3(g[0], g[1]); + b[3] = calcC3(b[0], b[1]); + } else { + r[2] = calcC2a(r[0], r[1]); + g[2] = calcC2a(g[0], g[1]); + b[2] = calcC2a(b[0], b[1]); + r[3] = g[3] = b[3] = a[3] = 0; + } + + for (int k = 0; k < 4; k++) + for (int l = 0; l < 4; l++) { + unsigned index = table & 0x0003; + table >>= 2; + + result[k * 4 + l] = qRgba(r[index], g[index], b[index], a[index]); + } +} + +template <DXTVersions version> +inline void setAlphaDXT32Helper(QRgb *rgbArr, quint64 alphas) +{ + Q_STATIC_ASSERT(version == Two || version == Three); + for (int i = 0; i < 16; i++) { + quint8 alpha = 16 * (alphas & 0x0f); + QRgb rgb = rgbArr[i]; + if (version == Two) // DXT2 + rgbArr[i] = qRgba(qRed(rgb) * alpha / 0xff, qGreen(rgb) * alpha / 0xff, qBlue(rgb) * alpha / 0xff, alpha); + else if (version == Three) // DXT3 + rgbArr[i] = qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), alpha); + alphas = alphas >> 4; + } +} + +template <DXTVersions version> +inline void setAlphaDXT45Helper(QRgb *rgbArr, quint64 alphas) +{ + Q_STATIC_ASSERT(version == Four || version == Five); + quint8 a[8]; + a[0] = alphas & 0xff; + a[1] = (alphas >> 8) & 0xff; + if (a[0] > a[1]) { + a[2] = (6*a[0] + 1*a[1]) / 7; + a[3] = (5*a[0] + 2*a[1]) / 7; + a[4] = (4*a[0] + 3*a[1]) / 7; + a[5] = (3*a[0] + 4*a[1]) / 7; + a[6] = (2*a[0] + 5*a[1]) / 7; + a[7] = (1*a[0] + 6*a[1]) / 7; + } else { + a[2] = (4*a[0] + 1*a[1]) / 5; + a[3] = (3*a[0] + 2*a[1]) / 5; + a[4] = (2*a[0] + 3*a[1]) / 5; + a[5] = (1*a[0] + 4*a[1]) / 5; + a[6] = 0; + a[7] = 255; + } + alphas >>= 16; + for (int i = 0; i < 16; i++) { + quint8 index = alphas & 0x07; + quint8 alpha = a[index]; + QRgb rgb = rgbArr[i]; + if (version == Four) // DXT4 + rgbArr[i] = qRgba(qRed(rgb) * alpha / 0xff, qGreen(rgb) * alpha / 0xff, qBlue(rgb) * alpha / 0xff, alpha); + else if (version == Five) // DXT5 + rgbArr[i] = qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), alpha); + alphas = alphas >> 3; + } +} + +template <DXTVersions version> +inline void setAlphaDXT(QRgb *rgbArr, quint64 alphas) +{ + Q_UNUSED(rgbArr); + Q_UNUSED(alphas); +} + +template <> +inline void setAlphaDXT<Two>(QRgb *rgbArr, quint64 alphas) +{ + setAlphaDXT32Helper<Two>(rgbArr, alphas); +} + +template <> +inline void setAlphaDXT<Three>(QRgb *rgbArr, quint64 alphas) +{ + setAlphaDXT32Helper<Three>(rgbArr, alphas); +} + +template <> +inline void setAlphaDXT<Four>(QRgb *rgbArr, quint64 alphas) +{ + setAlphaDXT45Helper<Four>(rgbArr, alphas); +} + +template <> +inline void setAlphaDXT<Five>(QRgb *rgbArr, quint64 alphas) +{ + setAlphaDXT45Helper<Five>(rgbArr, alphas); +} + +template <> +inline void setAlphaDXT<RXGB>(QRgb *rgbArr, quint64 alphas) +{ + setAlphaDXT45Helper<Five>(rgbArr, alphas); +} + +static inline QRgb invertRXGBColors(QRgb pixel) +{ + return qRgb(qAlpha(pixel), qGreen(pixel), qBlue(pixel)); +} + +template <DXTVersions version> +static QImage readDXT(QDataStream &s, quint32 width, quint32 height) +{ + QImage::Format format = (version == Two || version == Four) ? + QImage::Format_ARGB32_Premultiplied : QImage::Format_ARGB32; + + QImage image(width, height, format); + + for (quint32 i = 0; i < height; i += 4) { + for (quint32 j = 0; j < width; j += 4) { + quint64 alpha = 0; + quint16 c0, c1; + quint32 table; + if (version != One) + s >> alpha; + s >> c0; + s >> c1; + s >> table; + + QRgb arr[16]; + + DXTFillColors(arr, c0, c1, table, version == One && c0 <= c1); + setAlphaDXT<version>(arr, alpha); + + const quint32 kMax = qMin<quint32>(4, height - i); + const quint32 lMax = qMin<quint32>(4, width - j); + for (quint32 k = 0; k < kMax; k++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(i + k)); + for (quint32 l = 0; l < lMax; l++) { + QRgb pixel = arr[k * 4 + l]; + if (version == RXGB) + pixel = invertRXGBColors(pixel); + + line[j + l] = pixel; + } + } + } + } + return image; +} + +static inline QImage readDXT1(QDataStream &s, quint32 width, quint32 height) +{ + return readDXT<One>(s, width, height); +} + +static inline QImage readDXT2(QDataStream &s, quint32 width, quint32 height) +{ + return readDXT<Two>(s, width, height); +} + +static inline QImage readDXT3(QDataStream &s, quint32 width, quint32 height) +{ + return readDXT<Three>(s, width, height); +} + +static inline QImage readDXT4(QDataStream &s, quint32 width, quint32 height) +{ + return readDXT<Four>(s, width, height); +} + +static inline QImage readDXT5(QDataStream &s, quint32 width, quint32 height) +{ + return readDXT<Five>(s, width, height); +} + +static inline QImage readRXGB(QDataStream &s, quint32 width, quint32 height) +{ + return readDXT<RXGB>(s, width, height); +} + +static QImage readATI2(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + for (quint32 i = 0; i < height; i += 4) { + for (quint32 j = 0; j < width; j += 4) { + quint64 alpha1; + quint64 alpha2; + s >> alpha1; + s >> alpha2; + + QRgb arr[16]; + memset(arr, 0, sizeof(QRgb) * 16); + setAlphaDXT<Five>(arr, alpha1); + for (int i = 0; i < 16; ++i) { + quint8 a = qAlpha(arr[i]); + arr[i] = qRgba(0, 0, a, 0); + } + setAlphaDXT<Five>(arr, alpha2); + + const quint32 kMax = qMin<quint32>(4, height - i); + const quint32 lMax = qMin<quint32>(4, width - j); + for (quint32 k = 0; k < kMax; k++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(i + k)); + for (quint32 l = 0; l < lMax; l++) { + QRgb pixel = arr[k * 4 + l]; + const quint8 nx = qAlpha(pixel); + const quint8 ny = qBlue(pixel); + + // TODO: formulas can be incorrect + const double fx = nx / 127.5 - 1.0; + const double fy = ny / 127.5 - 1.0; + const double fxfy = 1.0 - fx * fx - fy * fy; + const double fz = fxfy > 0 ? sqrt(fxfy) : -1.0; + const quint8 nz = quint8((fz + 1.0) * 127.5); + + line[j + l] = qRgb(nx, ny, nz); + } + } + } + } + return image; +} + +static QImage readUnsignedImage(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height, bool hasAlpha) +{ + quint32 flags = dds.pixelFormat.flags; + + quint32 masks[ColorCount]; + quint8 shifts[ColorCount]; + quint8 bits[ColorCount]; + masks[Red] = dds.pixelFormat.rBitMask; + masks[Green] = dds.pixelFormat.gBitMask; + masks[Blue] = dds.pixelFormat.bBitMask; + masks[Alpha] = hasAlpha ? dds.pixelFormat.aBitMask : 0; + for (int i = 0; i < ColorCount; ++i) { + shifts[i] = maskToShift(masks[i]); + bits[i] = maskLength(masks[i]); + + // move mask to the left + if (bits[i] <= 8) + masks[i] = (masks[i] >> shifts[i]) << (8 - bits[i]); + } + + const QImage::Format format = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32; + + QImage image(width, height, format); + + for (quint32 y = 0; y < height; y++) { + for (quint32 x = 0; x < width; x++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + + quint32 value = readValue(s, dds.pixelFormat.rgbBitCount); + quint8 colors[ColorCount]; + + for (int c = 0; c < ColorCount; ++c) { + if (bits[c] > 8) { + // truncate unneseccary bits + colors[c] = (value & masks[c]) >> shifts[c] >> (bits[c] - 8); + } else { + // move color to the left + quint8 color = value >> shifts[c] << (8 - bits[c]) & masks[c]; + if (masks[c]) + colors[c] = color * 0xff / masks[c]; + else + colors[c] = 0; + } + } + + if (flags & DDSPixelFormat::FlagLuminance) + line[x] = qRgba(colors[Red], colors[Red], colors[Red], colors[Alpha]); + else if (flags & DDSPixelFormat::FlagYUV) + line[x] = yuv2rgb(colors[Red], colors[Green], colors[Blue]); + else + line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]); + } + } + + return image; +} + +static double readFloat16(QDataStream &s) +{ + quint16 value; + s >> value; + + double sign = (value & 0x8000) == 0x8000 ? -1.0 : 1.0; + qint8 exp = (value & 0x7C00) >> 10; + quint16 fraction = value & 0x3FF; + + if (exp == 0) + return sign * qPow(2.0, -14.0) * fraction / 1024.0; + else + return sign * qPow(2.0, exp - 15) * (1 + fraction / 1024.0); +} + +static inline float readFloat32(QDataStream &s) +{ + Q_ASSERT(sizeof(float) == 4); + float value; + // TODO: find better way to avoid setting precision each time + QDataStream::FloatingPointPrecision precision = s.floatingPointPrecision(); + s.setFloatingPointPrecision(QDataStream::SinglePrecision); + s >> value; + s.setFloatingPointPrecision(precision); + return value; +} + +static QImage readR16F(QDataStream &s, const quint32 width, const quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + quint8 r = readFloat16(s) * 255; + line[x] = qRgba(r, 0, 0, 0); + } + } + + return image; +} + +static QImage readRG16F(QDataStream &s, const quint32 width, const quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + quint8 r = readFloat16(s) * 255; + quint8 g = readFloat16(s) * 255; + line[x] = qRgba(r, g, 0, 0); + } + } + + return image; +} + +static QImage readARGB16F(QDataStream &s, const quint32 width, const quint32 height) +{ + QImage image(width, height, QImage::Format_ARGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + quint8 colors[ColorCount]; + for (int c = 0; c < ColorCount; ++c) + colors[c] = readFloat16(s) * 255; + + line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]); + } + } + + return image; +} + +static QImage readR32F(QDataStream &s, const quint32 width, const quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + quint8 r = readFloat32(s) * 255; + line[x] = qRgba(r, 0, 0, 0); + } + } + + return image; +} + +static QImage readRG32F(QDataStream &s, const quint32 width, const quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + quint8 r = readFloat32(s) * 255; + quint8 g = readFloat32(s) * 255; + line[x] = qRgba(r, g, 0, 0); + } + } + + return image; +} + +static QImage readARGB32F(QDataStream &s, const quint32 width, const quint32 height) +{ + QImage image(width, height, QImage::Format_ARGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + quint8 colors[ColorCount]; + for (int c = 0; c < ColorCount; ++c) + colors[c] = readFloat32(s) * 255; + line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]); + } + } + + return image; +} + +static QImage readQ16W16V16U16(QDataStream &s, const quint32 width, const quint32 height) +{ + QImage image(width, height, QImage::Format_ARGB32); + + quint8 colors[ColorCount]; + qint16 tmp; + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + for (int i = 0; i < ColorCount; i++) { + s >> tmp; + colors[i] = (tmp + 0x7FFF) >> 8; + } + line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]); + } + } + + return image; +} + +// TODO: this seems incorrect +static QImage readCxV8U8(QDataStream &s, const quint32 width, const quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + qint8 v, u; + s >> v >> u; + + const quint8 vn = v + 128, un = u + 128; + + const double vd = vn / 127.5 - 1.0, ud = un / 127.5 - 1.0; + const quint8 c = 255 * sqrt(1.0 - vd * vd - ud * ud); + line[x] = qRgb(vn, un, c); + } + } + + return image; +} + +static QImage readPaletteImage(QDataStream & s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_Indexed8); + for (int i = 0; i < 256; ++i) { + quint8 r, g, b, a; + s >> r >> g >> b >> a; + image.setColor(i, qRgba(r, g, b, a)); + } + + for (quint32 y = 0; y < height; y++) { + for (quint32 x = 0; x < width; x++) { + quint8 index; + s >> index; + image.setPixel(x, y, index); + } + } + + return image; +} + +static QImage readARGB16(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_ARGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + quint8 colors[ColorCount]; + for (int i = 0; i < ColorCount; ++i) { + quint16 color; + s >> color; + colors[i] = quint8(color >> 8); + } + line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]); + } + } + + return image; +} + +static QImage readV8U8(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + qint8 v, u; + s >> v >> u; + line[x] = qRgb(v + 128, u + 128, 255); + } + } + + return image; +} + +static QImage readL6V5U5(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_ARGB32); + + quint16 tmp; + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + s >> tmp; + quint8 r = qint8((tmp & 0x001f) >> 0) * 0xff/0x1f + 128; + quint8 g = qint8((tmp & 0x03e0) >> 5) * 0xff/0x1f + 128; + quint8 b = quint8((tmp & 0xfc00) >> 10) * 0xff/0x3f; + line[x] = qRgba(r, g, 0xff, b); + } + } + return image; +} + +static QImage readX8L8V8U8(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_ARGB32); + + quint8 a, l; + qint8 v, u; + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + s >> v >> u >> a >> l; + line[x] = qRgba(v + 128, u + 128, 255, a); + } + } + + return image; +} + +static QImage readQ8W8V8U8(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_ARGB32); + + quint8 colors[ColorCount]; + qint8 tmp; + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + for (int i = 0; i < ColorCount; i++) { + s >> tmp; + colors[i] = tmp + 128; + } + line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]); + } + } + + return image; +} + +static QImage readV16U16(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + qint16 v, u; + s >> v >> u; + v = (v + 0x8000) >> 8; + u = (u + 0x8000) >> 8; + line[x] = qRgb(v, u, 255); + } + } + + return image; +} + +static QImage readA2W10V10U10(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_ARGB32); + + quint32 tmp; + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + s >> tmp; + quint8 r = qint8((tmp & 0x3ff00000) >> 20 >> 2) + 128; + quint8 g = qint8((tmp & 0x000ffc00) >> 10 >> 2) + 128; + quint8 b = qint8((tmp & 0x000003ff) >> 0 >> 2) + 128; + quint8 a = 0xff * ((tmp & 0xc0000000) >> 30) / 3; + // dunno why we should swap b and r here + line[x] = qRgba(b, g, r, a); + } + } + + return image; +} + +static QImage readUYVY(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + quint8 uyvy[4]; + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width - 1; ) { + s >> uyvy[0] >> uyvy[1] >> uyvy[2] >> uyvy[3]; + line[x++] = yuv2rgb(uyvy[1], uyvy[0], uyvy[2]); + line[x++] = yuv2rgb(uyvy[3], uyvy[0], uyvy[2]); + } + if (width % 2 == 1) { + s >> uyvy[0] >> uyvy[1] >> uyvy[2] >> uyvy[3]; + line[width - 1] = yuv2rgb(uyvy[1], uyvy[0], uyvy[2]); + } + } + + return image; +} + +static QImage readR8G8B8G8(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + quint8 rgbg[4]; + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width - 1; ) { + s >> rgbg[1] >> rgbg[0] >> rgbg[3] >> rgbg[2]; + line[x++] = qRgb(rgbg[0], rgbg[1], rgbg[2]); + line[x++] = qRgb(rgbg[0], rgbg[3], rgbg[2]); + } + if (width % 2 == 1) { + s >> rgbg[1] >> rgbg[0] >> rgbg[3] >> rgbg[2]; + line[width - 1] = qRgb(rgbg[0], rgbg[1], rgbg[2]); + } + } + + return image; +} + +static QImage readYUY2(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + + quint8 yuyv[4]; + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width - 1; ) { + s >> yuyv[0] >> yuyv[1] >> yuyv[2] >> yuyv[3]; + line[x++] = yuv2rgb(yuyv[0], yuyv[1], yuyv[3]); + line[x++] = yuv2rgb(yuyv[2], yuyv[1], yuyv[3]); + } + if (width % 2 == 1) { + s >> yuyv[0] >> yuyv[1] >> yuyv[2] >> yuyv[3]; + line[width - 1] = yuv2rgb(yuyv[2], yuyv[1], yuyv[3]); + } + } + + return image; +} + +static QImage readG8R8G8B8(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_RGB32); + quint8 grgb[4]; + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width - 1; ) { + s >> grgb[1] >> grgb[0] >> grgb[3] >> grgb[2]; + line[x++] = qRgb(grgb[1], grgb[0], grgb[3]); + line[x++] = qRgb(grgb[1], grgb[2], grgb[3]); + } + if (width % 2 == 1) { + s >> grgb[1] >> grgb[0] >> grgb[3] >> grgb[2]; + line[width - 1] = qRgb(grgb[1], grgb[0], grgb[3]); + } + } + + return image; +} + +static QImage readA2R10G10B10(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height) +{ + QImage image = readUnsignedImage(s, dds, width, height, true); + for (quint32 y = 0; y < height; y++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)); + for (quint32 x = 0; x < width; x++) { + QRgb pixel = image.pixel(x, y); + line[x] = qRgba(qBlue(pixel), qGreen(pixel), qRed(pixel), qAlpha(pixel)); + } + } + return image; +} + +static QImage readLayer(QDataStream & s, const DDSHeader & dds, const int format, quint32 width, quint32 height) +{ + if (width * height == 0) + return QImage(); + + switch (format) { + case FormatR8G8B8: + case FormatX8R8G8B8: + case FormatR5G6B5: + case FormatR3G3B2: + case FormatX1R5G5B5: + case FormatX4R4G4B4: + case FormatX8B8G8R8: + case FormatG16R16: + case FormatL8: + case FormatL16: + return readUnsignedImage(s, dds, width, height, false); + case FormatA8R8G8B8: + case FormatA1R5G5B5: + case FormatA4R4G4B4: + case FormatA8: + case FormatA8R3G3B2: + case FormatA8B8G8R8: + case FormatA8L8: + case FormatA4L4: + return readUnsignedImage(s, dds, width, height, true); + case FormatA2R10G10B10: + case FormatA2B10G10R10: + return readA2R10G10B10(s, dds, width, height); + case FormatP8: + return readPaletteImage(s, width, height); + case FormatA8P8: + break; + case FormatA16B16G16R16: + return readARGB16(s, width, height); + case FormatV8U8: + return readV8U8(s, width, height); + case FormatL6V5U5: + return readL6V5U5(s, width, height); + case FormatX8L8V8U8: + return readX8L8V8U8(s, width, height); + case FormatQ8W8V8U8: + return readQ8W8V8U8(s, width, height); + case FormatV16U16: + return readV16U16(s, width, height); + case FormatA2W10V10U10: + return readA2W10V10U10(s, width, height); + case FormatUYVY: + return readUYVY(s, width, height); + case FormatR8G8B8G8: + return readR8G8B8G8(s, width, height); + case FormatYUY2: + return readYUY2(s, width, height); + case FormatG8R8G8B8: + return readG8R8G8B8(s, width, height); + case FormatDXT1: + return readDXT1(s, width, height); + case FormatDXT2: + return readDXT2(s, width, height); + case FormatDXT3: + return readDXT3(s, width, height); + case FormatDXT4: + return readDXT4(s, width, height); + case FormatDXT5: + return readDXT5(s, width, height); + case FormatRXGB: + return readRXGB(s, width, height); + case FormatATI2: + return readATI2(s, width, height); + case FormatR16F: + return readR16F(s, width, height); + case FormatG16R16F: + return readRG16F(s, width, height); + case FormatA16B16G16R16F: + return readARGB16F(s, width, height); + case FormatR32F: + return readR32F(s, width, height); + case FormatG32R32F: + return readRG32F(s, width, height); + case FormatA32B32G32R32F: + return readARGB32F(s, width, height); + case FormatD16Lockable: + case FormatD32: + case FormatD15S1: + case FormatD24S8: + case FormatD24X8: + case FormatD24X4S4: + case FormatD16: + case FormatD32FLockable: + case FormatD24FS8: + case FormatD32Lockable: + case FormatS8Lockable: + case FormatVertexData: + case FormatIndex16: + case FormatIndex32: + break; + case FormatQ16W16V16U16: + return readQ16W16V16U16(s, width, height); + case FormatMulti2ARGB8: + break; + case FormatCxV8U8: + return readCxV8U8(s, width, height); + case FormatA1: + case FormatA2B10G10R10_XR_BIAS: + case FormatBinaryBuffer: + case FormatLast: + break; + } + + return QImage(); +} + +static inline QImage readTexture(QDataStream & s, const DDSHeader & dds, const int format, const int mipmapLevel) +{ + quint32 width = dds.width / (1 << mipmapLevel); + quint32 height = dds.height / (1 << mipmapLevel); + return readLayer(s, dds, format, width, height); +} + +static qint64 mipmapSize(const DDSHeader &dds, const int format, const int level) +{ + quint32 w = dds.width/(1 << level); + quint32 h = dds.height/(1 << level); + + switch (format) { + case FormatR8G8B8: + case FormatX8R8G8B8: + case FormatR5G6B5: + case FormatX1R5G5B5: + case FormatX4R4G4B4: + case FormatX8B8G8R8: + case FormatG16R16: + case FormatL8: + case FormatL16: + return w * h * dds.pixelFormat.rgbBitCount / 8; + case FormatA8R8G8B8: + case FormatA1R5G5B5: + case FormatA4R4G4B4: + case FormatA8: + case FormatA8R3G3B2: + case FormatA2B10G10R10: + case FormatA8B8G8R8: + case FormatA2R10G10B10: + case FormatA8L8: + case FormatA4L4: + return w * h * dds.pixelFormat.rgbBitCount / 8; + case FormatP8: + return 256 + w * h * 8; + case FormatA16B16G16R16: + return w * h * 4 * 2; + case FormatA8P8: + break; + case FormatV8U8: + case FormatL6V5U5: + return w * h * 2; + case FormatX8L8V8U8: + case FormatQ8W8V8U8: + case FormatV16U16: + case FormatA2W10V10U10: + return w * h * 4; + case FormatUYVY: + case FormatR8G8B8G8: + case FormatYUY2: + case FormatG8R8G8B8: + return w * h * 2; + case FormatDXT1: + return ((w + 3)/4) * ((h + 3)/4) * 8; + case FormatDXT2: + case FormatDXT3: + case FormatDXT4: + case FormatDXT5: + return ((w + 3)/4) * ((h + 3)/4) * 16; + case FormatD16Lockable: + case FormatD32: + case FormatD15S1: + case FormatD24S8: + case FormatD24X8: + case FormatD24X4S4: + case FormatD16: + case FormatD32FLockable: + case FormatD24FS8: + case FormatD32Lockable: + case FormatS8Lockable: + case FormatVertexData: + case FormatIndex16: + case FormatIndex32: + break; + case FormatQ16W16V16U16: + return w * h * 4 * 2; + case FormatMulti2ARGB8: + break; + case FormatR16F: + return w * h * 1 * 2; + case FormatG16R16F: + return w * h * 2 * 2; + case FormatA16B16G16R16F: + return w * h * 4 * 2; + case FormatR32F: + return w * h * 1 * 4; + case FormatG32R32F: + return w * h * 2 * 4; + case FormatA32B32G32R32F: + return w * h * 4 * 4; + case FormatCxV8U8: + return w * h * 2; + case FormatA1: + case FormatA2B10G10R10_XR_BIAS: + case FormatBinaryBuffer: + case FormatLast: + break; + } + + return 0; +} + +static qint64 mipmapOffset(const DDSHeader &dds, const int format, const int level) +{ + qint64 result = 0; + for (int i = 0; i < level; ++i) + result += mipmapSize(dds, format, i); + return result; +} + +static QImage readCubeMap(QDataStream & s, const DDSHeader & dds, const int fmt) +{ + QImage::Format format = hasAlpha(dds) ? QImage::Format_ARGB32 : QImage::Format_RGB32; + QImage image(4 * dds.width, 3 * dds.height, format); + + image.fill(0); + + for (int i = 0; i < 6; i++) { + if (!(dds.caps2 & faceFlags[i])) + continue; // Skip face. + + const QImage face = readLayer(s, dds, fmt, dds.width, dds.height); + + // Compute face offsets. + int offset_x = faceOffsets[i].x * dds.width; + int offset_y = faceOffsets[i].y * dds.height; + + // Copy face on the image. + for (quint32 y = 0; y < dds.height; y++) { + const QRgb *src = reinterpret_cast<const QRgb *>(face.scanLine(y)); + QRgb *dst = reinterpret_cast<QRgb *>(image.scanLine(y + offset_y)) + offset_x; + memcpy(dst, src, sizeof(QRgb) * dds.width); + } + } + + return image; +} + +QDDSHandler::QDDSHandler() : + m_currentImage(0), + m_headerCached(false) +{ +} + +bool QDDSHandler::canRead() const +{ + if (canRead(device())) { + setFormat(QByteArrayLiteral("dds")); + return true; + } + return false; +} + +bool QDDSHandler::read(QImage *outImage) +{ + if (!ensureHeaderCached() || device()->isSequential()) + return false; + + qint64 pos = headerSize + mipmapOffset(m_header, m_format, m_currentImage); + if (!device()->seek(pos)) + return false; + QDataStream s(device()); + s.setByteOrder(QDataStream::LittleEndian); + + QImage image = isCubeMap(m_header) ? + readCubeMap(s, m_header, m_format) : + readTexture(s, m_header, m_format, m_currentImage); + + bool ok = s.status() == QDataStream::Ok && !image.isNull(); + if (ok) + *outImage = image; + return ok; +} + +bool QDDSHandler::write(const QImage &outImage) +{ + QDataStream s( device() ); + s.setByteOrder(QDataStream::LittleEndian); + + // Filling header + DDSHeader dds; + // Filling header + dds.magic = ddsMagic; + dds.size = 124; + dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight | + DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat; + dds.height = outImage.height(); + dds.width = outImage.width(); + dds.pitchOrLinearSize = 128; + dds.depth = 0; + dds.mipMapCount = 0; + for (int i = 0; i < DDSHeader::ReservedCount; i++) + dds.reserved1[i] = 0; + dds.caps = DDSHeader::CapsTexture; + dds.caps2 = 0; + dds.caps3 = 0; + dds.caps4 = 0; + dds.reserved2 = 0; + + // Filling pixelformat + dds.pixelFormat.size = 32; + dds.pixelFormat.flags = DDSPixelFormat::FlagAlphaPixels | DDSPixelFormat::FlagRGB; + dds.pixelFormat.fourCC = 0; + dds.pixelFormat.rgbBitCount = 32; + dds.pixelFormat.aBitMask = 0xff000000; + dds.pixelFormat.rBitMask = 0x00ff0000; + dds.pixelFormat.gBitMask = 0x0000ff00; + dds.pixelFormat.bBitMask = 0x000000ff; + + s << dds; + for (int width = 0; width < outImage.width(); width++) { + for (int height = 0; height < outImage.height(); height++) { + QRgb pixel = outImage.pixel(height, width);; + quint32 color; + quint8 alpha = qAlpha(pixel); + quint8 red = qRed(pixel); + quint8 green = qGreen(pixel); + quint8 blue = qBlue(pixel); + color = (alpha << 24) + (red << 16) + (green << 8) + blue; + s << color; + } + } + + return true; +} + +int QDDSHandler::imageCount() const +{ + if (!ensureHeaderCached()) + return 0; + + return qMax<quint32>(1, m_header.mipMapCount); +} + +bool QDDSHandler::jumpToImage(int imageNumber) +{ + if (imageNumber >= imageCount()) + return false; + + m_currentImage = imageNumber; + return true; +} + +QByteArray QDDSHandler::name() const +{ + return "dds"; +} + +bool QDDSHandler::canRead(QIODevice *device) +{ + if (!device) { + qWarning() << "DDSHandler::canRead() called with no device"; + return false; + } + + if (device->isSequential()) + return false; + + return device->peek(4) == "DDS "; +} + +bool QDDSHandler::ensureHeaderCached() const +{ + if (m_headerCached) + return true; + + if (device()->isSequential()) { + qWarning() << "Sequential devices are not supported"; + return false; + } + + qint64 oldPos = device()->pos(); + device()->seek(0); + + QDDSHandler *that = const_cast<QDDSHandler *>(this); + QDataStream s(device()); + s.setByteOrder(QDataStream::LittleEndian); + s >> that->m_header; + if (m_header.pixelFormat.fourCC == dx10Magic) + s >> that->m_header10; + + device()->seek(oldPos); + + if (s.status() != QDataStream::Ok) + return false; + + if (!verifyHeader(m_header)) + return false; + + that->m_format = getFormat(m_header); + + m_headerCached = true; + return true; +} + +bool QDDSHandler::verifyHeader(const DDSHeader &dds) const +{ + quint32 flags = dds.flags; + quint32 requiredFlags = DDSHeader::FlagCaps | DDSHeader::FlagHeight + | DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat; + if ((flags & requiredFlags) != requiredFlags) { + qWarning() << "Wrong dds.flags - not all required flags present. " + "Actual flags :" << flags; + return false; + } + + if (dds.size != ddsSize) { + qWarning() << "Wrong dds.size: actual =" << dds.size + << "expected =" << ddsSize; + return false; + } + + if (dds.pixelFormat.size != pixelFormatSize) { + qWarning() << "Wrong dds.pixelFormat.size: actual =" << dds.pixelFormat.size + << "expected =" << pixelFormatSize; + return false; + } + + if (dds.width > INT_MAX || dds.height > INT_MAX) { + qWarning() << "Can't read image with w/h bigger than INT_MAX"; + return false; + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/dds/qddshandler.h b/src/plugins/imageformats/dds/qddshandler.h new file mode 100644 index 0000000..5142bb7 --- /dev/null +++ b/src/plugins/imageformats/dds/qddshandler.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Ivan Komissarov. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the DDS plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDDSHANDLER_H +#define QDDSHANDLER_H + +#include <QtGui/qimageiohandler.h> +#include "ddsheader.h" + +QT_BEGIN_NAMESPACE + +class QDDSHandler : public QImageIOHandler +{ +public: + QDDSHandler(); + + bool canRead() const; + bool read(QImage *image); + bool write(const QImage &image); + + int imageCount() const; + bool jumpToImage(int imageNumber); + + QByteArray name() const; + + static bool canRead(QIODevice *device); + +private: + bool ensureHeaderCached() const; + bool verifyHeader(const DDSHeader &dds) const; + +private: + DDSHeader m_header; + int m_format; + DDSHeaderDX10 m_header10; + int m_currentImage; + mutable bool m_headerCached; +}; + +QT_END_NAMESPACE + +#endif // QDDSHANDLER_H diff --git a/src/plugins/imageformats/imageformats.pro b/src/plugins/imageformats/imageformats.pro index 79be7ae..ef9eb6f 100644 --- a/src/plugins/imageformats/imageformats.pro +++ b/src/plugins/imageformats/imageformats.pro @@ -3,4 +3,5 @@ SUBDIRS = \ tga \ wbmp \ mng \ - tiff + tiff \ + dds diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 5bde0c7..6ac74e2 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -1,5 +1,6 @@ TEMPLATE = subdirs SUBDIRS = \ tga \ - wbmp + wbmp \ + dds contains(QT_CONFIG, system-zlib): SUBDIRS += mng tiff diff --git a/tests/auto/dds/dds.pro b/tests/auto/dds/dds.pro new file mode 100644 index 0000000..da63b84 --- /dev/null +++ b/tests/auto/dds/dds.pro @@ -0,0 +1,8 @@ +TARGET = tst_qdds + +QT = core gui testlib +CONFIG -= app_bundle +CONFIG += testcase + +SOURCES += tst_qdds.cpp +RESOURCES += $$PWD/../../shared/images/dds.qrc diff --git a/tests/auto/dds/tst_qdds.cpp b/tests/auto/dds/tst_qdds.cpp new file mode 100644 index 0000000..de05ec4 --- /dev/null +++ b/tests/auto/dds/tst_qdds.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Ivan Komissarov. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the DDS plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtGui/QtGui> + +class tst_qdds: public QObject +{ + Q_OBJECT + +private slots: + void readImage_data(); + void readImage(); + void testMipmaps_data(); + void testMipmaps(); +}; + +void tst_qdds::readImage_data() +{ + QTest::addColumn<QString>("fileName"); + QTest::addColumn<QSize>("size"); + + QTest::newRow("1") << QString("A1R5G5B5") << QSize(64, 64); + QTest::newRow("2") << QString("A2B10G10R10") << QSize(64, 64); + QTest::newRow("3") << QString("A2R10G10B10") << QSize(64, 64); + QTest::newRow("4") << QString("A2W10V10U10") << QSize(64, 64); + QTest::newRow("5") << QString("A4L4") << QSize(64, 64); + QTest::newRow("6") << QString("A4R4G4B4") << QSize(64, 64); + QTest::newRow("7") << QString("A8") << QSize(64, 64); + QTest::newRow("8") << QString("A8B8G8R8") << QSize(64, 64); + QTest::newRow("9") << QString("A8L8") << QSize(64, 64); + QTest::newRow("10") << QString("A8R3G3B2") << QSize(64, 64); + QTest::newRow("11") << QString("A8R8G8B8") << QSize(64, 64); + QTest::newRow("12") << QString("A16B16G16R16") << QSize(64, 64); + QTest::newRow("13") << QString("A16B16G16R16F") << QSize(64, 64); + QTest::newRow("14") << QString("A32B32G32R32F") << QSize(64, 64); + QTest::newRow("15") << QString("CxV8U8") << QSize(64, 64); + QTest::newRow("16") << QString("DXT1") << QSize(50, 50); + QTest::newRow("17") << QString("DXT2") << QSize(64, 64); + QTest::newRow("18") << QString("DXT3") << QSize(64, 64); + QTest::newRow("19") << QString("DXT4") << QSize(64, 64); + QTest::newRow("20") << QString("DXT5") << QSize(64, 64); + QTest::newRow("21") << QString("G8R8_G8B8") << QSize(64, 64); + QTest::newRow("22") << QString("G16R16") << QSize(64, 64); + QTest::newRow("23") << QString("G16R16F") << QSize(64, 64); + QTest::newRow("24") << QString("G32R32F") << QSize(64, 64); + QTest::newRow("25") << QString("L6V5U5") << QSize(64, 64); + QTest::newRow("26") << QString("L8") << QSize(64, 64); + QTest::newRow("27") << QString("L16") << QSize(64, 64); + QTest::newRow("28") << QString("P8") << QSize(64, 64); + QTest::newRow("29") << QString("Q8W8V8U8") << QSize(64, 64); + QTest::newRow("30") << QString("Q16W16V16U16") << QSize(64, 64); + QTest::newRow("31") << QString("R3G3B2") << QSize(64, 64); + QTest::newRow("32") << QString("R5G6B5") << QSize(64, 64); + QTest::newRow("33") << QString("R8G8_B8G8") << QSize(64, 64); + QTest::newRow("34") << QString("R8G8B8") << QSize(64, 64); + QTest::newRow("35") << QString("R16F") << QSize(64, 64); + QTest::newRow("36") << QString("R32F") << QSize(64, 64); + QTest::newRow("37") << QString("UYVY") << QSize(64, 64); + QTest::newRow("38") << QString("V8U8") << QSize(64, 64); + QTest::newRow("39") << QString("V16U16") << QSize(64, 64); + QTest::newRow("40") << QString("X1R5G5B5") << QSize(64, 64); + QTest::newRow("41") << QString("X4R4G4B4") << QSize(64, 64); + QTest::newRow("42") << QString("X8B8G8R8") << QSize(64, 64); + QTest::newRow("43") << QString("X8L8V8U8") << QSize(64, 64); + QTest::newRow("44") << QString("X8R8G8B8") << QSize(64, 64); + QTest::newRow("45") << QString("YUY2") << QSize(64, 64); + QTest::newRow("46") << QString("RXGB") << QSize(64, 64); + QTest::newRow("47") << QString("ATI2") << QSize(64, 64); +} + +void tst_qdds::readImage() +{ + QFETCH(QString, fileName); + QFETCH(QSize, size); + + const QString path = QStringLiteral(":/dds/") + fileName + QStringLiteral(".dds"); + QImageReader reader(path); + QVERIFY(reader.canRead()); + QImage image = reader.read(); + QVERIFY2(!image.isNull(), qPrintable(reader.errorString())); + QCOMPARE(image.size(), size); +} + +void tst_qdds::testMipmaps_data() +{ + QTest::addColumn<QString>("fileName"); + QTest::addColumn<QSize>("size"); + QTest::addColumn<int>("imageCount"); + + QTest::newRow("1") << QString("mipmaps") << QSize(64, 64) << 7; +} + +void tst_qdds::testMipmaps() +{ + QFETCH(QString, fileName); + QFETCH(QSize, size); + QFETCH(int, imageCount); + + const QString path = QStringLiteral(":/dds/") + fileName + QStringLiteral(".dds"); + QImageReader reader(path); + QVERIFY(reader.canRead()); + QCOMPARE(reader.imageCount(), imageCount); + + for (int i = 0; i < reader.imageCount(); ++i) { + reader.jumpToImage(i); + QImage image = reader.read(); + QVERIFY2(!image.isNull(), qPrintable(reader.errorString())); + QCOMPARE(image.size(), size / (1 << i)); + } +} + +QTEST_MAIN(tst_qdds) +#include "tst_qdds.moc" diff --git a/tests/shared/images/dds.qrc b/tests/shared/images/dds.qrc new file mode 100644 index 0000000..e19eee9 --- /dev/null +++ b/tests/shared/images/dds.qrc @@ -0,0 +1,52 @@ +<RCC> + <qresource prefix="/"> + <file>dds/A1R5G5B5.dds</file> + <file>dds/A2B10G10R10.dds</file> + <file>dds/A2R10G10B10.dds</file> + <file>dds/A2W10V10U10.dds</file> + <file>dds/A4L4.dds</file> + <file>dds/A4R4G4B4.dds</file> + <file>dds/A8.dds</file> + <file>dds/A8B8G8R8.dds</file> + <file>dds/A8L8.dds</file> + <file>dds/A8R3G3B2.dds</file> + <file>dds/A8R8G8B8.dds</file> + <file>dds/A16B16G16R16.dds</file> + <file>dds/A16B16G16R16F.dds</file> + <file>dds/A32B32G32R32F.dds</file> + <file>dds/CxV8U8.dds</file> + <file>dds/DXT1.dds</file> + <file>dds/DXT2.dds</file> + <file>dds/DXT3.dds</file> + <file>dds/DXT4.dds</file> + <file>dds/DXT5.dds</file> + <file>dds/G8R8_G8B8.dds</file> + <file>dds/G16R16.dds</file> + <file>dds/G16R16F.dds</file> + <file>dds/G32R32F.dds</file> + <file>dds/L6V5U5.dds</file> + <file>dds/L8.dds</file> + <file>dds/L16.dds</file> + <file>dds/mipmaps.dds</file> + <file>dds/P8.dds</file> + <file>dds/Q8W8V8U8.dds</file> + <file>dds/Q16W16V16U16.dds</file> + <file>dds/R3G3B2.dds</file> + <file>dds/R5G6B5.dds</file> + <file>dds/R8G8_B8G8.dds</file> + <file>dds/R8G8B8.dds</file> + <file>dds/R16F.dds</file> + <file>dds/R32F.dds</file> + <file>dds/UYVY.dds</file> + <file>dds/V8U8.dds</file> + <file>dds/V16U16.dds</file> + <file>dds/X1R5G5B5.dds</file> + <file>dds/X4R4G4B4.dds</file> + <file>dds/X8B8G8R8.dds</file> + <file>dds/X8L8V8U8.dds</file> + <file>dds/X8R8G8B8.dds</file> + <file>dds/YUY2.dds</file> + <file>dds/RXGB.dds</file> + <file>dds/ATI2.dds</file> + </qresource> +</RCC> diff --git a/tests/shared/images/dds/A16B16G16R16.dds b/tests/shared/images/dds/A16B16G16R16.dds Binary files differnew file mode 100644 index 0000000..9b59982 --- /dev/null +++ b/tests/shared/images/dds/A16B16G16R16.dds diff --git a/tests/shared/images/dds/A16B16G16R16F.dds b/tests/shared/images/dds/A16B16G16R16F.dds Binary files differnew file mode 100644 index 0000000..5e59107 --- /dev/null +++ b/tests/shared/images/dds/A16B16G16R16F.dds diff --git a/tests/shared/images/dds/A1R5G5B5.dds b/tests/shared/images/dds/A1R5G5B5.dds Binary files differnew file mode 100644 index 0000000..38c7123 --- /dev/null +++ b/tests/shared/images/dds/A1R5G5B5.dds diff --git a/tests/shared/images/dds/A2B10G10R10.dds b/tests/shared/images/dds/A2B10G10R10.dds Binary files differnew file mode 100644 index 0000000..37bc09b --- /dev/null +++ b/tests/shared/images/dds/A2B10G10R10.dds diff --git a/tests/shared/images/dds/A2R10G10B10.dds b/tests/shared/images/dds/A2R10G10B10.dds Binary files differnew file mode 100644 index 0000000..0754794 --- /dev/null +++ b/tests/shared/images/dds/A2R10G10B10.dds diff --git a/tests/shared/images/dds/A2W10V10U10.dds b/tests/shared/images/dds/A2W10V10U10.dds Binary files differnew file mode 100644 index 0000000..d7eca43 --- /dev/null +++ b/tests/shared/images/dds/A2W10V10U10.dds diff --git a/tests/shared/images/dds/A32B32G32R32F.dds b/tests/shared/images/dds/A32B32G32R32F.dds Binary files differnew file mode 100644 index 0000000..c9a1372 --- /dev/null +++ b/tests/shared/images/dds/A32B32G32R32F.dds diff --git a/tests/shared/images/dds/A4L4.dds b/tests/shared/images/dds/A4L4.dds Binary files differnew file mode 100644 index 0000000..e1872f3 --- /dev/null +++ b/tests/shared/images/dds/A4L4.dds diff --git a/tests/shared/images/dds/A4R4G4B4.dds b/tests/shared/images/dds/A4R4G4B4.dds Binary files differnew file mode 100644 index 0000000..e477e46 --- /dev/null +++ b/tests/shared/images/dds/A4R4G4B4.dds diff --git a/tests/shared/images/dds/A8.dds b/tests/shared/images/dds/A8.dds Binary files differnew file mode 100644 index 0000000..90b773b --- /dev/null +++ b/tests/shared/images/dds/A8.dds diff --git a/tests/shared/images/dds/A8B8G8R8.dds b/tests/shared/images/dds/A8B8G8R8.dds Binary files differnew file mode 100644 index 0000000..a9bab75 --- /dev/null +++ b/tests/shared/images/dds/A8B8G8R8.dds diff --git a/tests/shared/images/dds/A8L8.dds b/tests/shared/images/dds/A8L8.dds Binary files differnew file mode 100644 index 0000000..8a26262 --- /dev/null +++ b/tests/shared/images/dds/A8L8.dds diff --git a/tests/shared/images/dds/A8R3G3B2.dds b/tests/shared/images/dds/A8R3G3B2.dds Binary files differnew file mode 100644 index 0000000..c69b3a3 --- /dev/null +++ b/tests/shared/images/dds/A8R3G3B2.dds diff --git a/tests/shared/images/dds/A8R8G8B8.dds b/tests/shared/images/dds/A8R8G8B8.dds Binary files differnew file mode 100644 index 0000000..1a44039 --- /dev/null +++ b/tests/shared/images/dds/A8R8G8B8.dds diff --git a/tests/shared/images/dds/ATI2.dds b/tests/shared/images/dds/ATI2.dds Binary files differnew file mode 100644 index 0000000..8f85817 --- /dev/null +++ b/tests/shared/images/dds/ATI2.dds diff --git a/tests/shared/images/dds/CxV8U8.dds b/tests/shared/images/dds/CxV8U8.dds Binary files differnew file mode 100644 index 0000000..2b6f69d --- /dev/null +++ b/tests/shared/images/dds/CxV8U8.dds diff --git a/tests/shared/images/dds/DXT1.dds b/tests/shared/images/dds/DXT1.dds Binary files differnew file mode 100644 index 0000000..2ba685c --- /dev/null +++ b/tests/shared/images/dds/DXT1.dds diff --git a/tests/shared/images/dds/DXT2.dds b/tests/shared/images/dds/DXT2.dds Binary files differnew file mode 100644 index 0000000..f4f006c --- /dev/null +++ b/tests/shared/images/dds/DXT2.dds diff --git a/tests/shared/images/dds/DXT3.dds b/tests/shared/images/dds/DXT3.dds Binary files differnew file mode 100644 index 0000000..0498c40 --- /dev/null +++ b/tests/shared/images/dds/DXT3.dds diff --git a/tests/shared/images/dds/DXT4.dds b/tests/shared/images/dds/DXT4.dds Binary files differnew file mode 100644 index 0000000..ecc6057 --- /dev/null +++ b/tests/shared/images/dds/DXT4.dds diff --git a/tests/shared/images/dds/DXT5.dds b/tests/shared/images/dds/DXT5.dds Binary files differnew file mode 100644 index 0000000..47a431c --- /dev/null +++ b/tests/shared/images/dds/DXT5.dds diff --git a/tests/shared/images/dds/G16R16.dds b/tests/shared/images/dds/G16R16.dds Binary files differnew file mode 100644 index 0000000..1d940ae --- /dev/null +++ b/tests/shared/images/dds/G16R16.dds diff --git a/tests/shared/images/dds/G16R16F.dds b/tests/shared/images/dds/G16R16F.dds Binary files differnew file mode 100644 index 0000000..e68d970 --- /dev/null +++ b/tests/shared/images/dds/G16R16F.dds diff --git a/tests/shared/images/dds/G32R32F.dds b/tests/shared/images/dds/G32R32F.dds Binary files differnew file mode 100644 index 0000000..f3cf9d1 --- /dev/null +++ b/tests/shared/images/dds/G32R32F.dds diff --git a/tests/shared/images/dds/G8R8_G8B8.dds b/tests/shared/images/dds/G8R8_G8B8.dds Binary files differnew file mode 100644 index 0000000..eaa0210 --- /dev/null +++ b/tests/shared/images/dds/G8R8_G8B8.dds diff --git a/tests/shared/images/dds/L16.dds b/tests/shared/images/dds/L16.dds Binary files differnew file mode 100644 index 0000000..42710c5 --- /dev/null +++ b/tests/shared/images/dds/L16.dds diff --git a/tests/shared/images/dds/L6V5U5.dds b/tests/shared/images/dds/L6V5U5.dds Binary files differnew file mode 100644 index 0000000..cf77f71 --- /dev/null +++ b/tests/shared/images/dds/L6V5U5.dds diff --git a/tests/shared/images/dds/L8.dds b/tests/shared/images/dds/L8.dds Binary files differnew file mode 100644 index 0000000..4eba673 --- /dev/null +++ b/tests/shared/images/dds/L8.dds diff --git a/tests/shared/images/dds/P8.dds b/tests/shared/images/dds/P8.dds Binary files differnew file mode 100644 index 0000000..936b870 --- /dev/null +++ b/tests/shared/images/dds/P8.dds diff --git a/tests/shared/images/dds/Q16W16V16U16.dds b/tests/shared/images/dds/Q16W16V16U16.dds Binary files differnew file mode 100644 index 0000000..3b464e5 --- /dev/null +++ b/tests/shared/images/dds/Q16W16V16U16.dds diff --git a/tests/shared/images/dds/Q8W8V8U8.dds b/tests/shared/images/dds/Q8W8V8U8.dds Binary files differnew file mode 100644 index 0000000..b5c6864 --- /dev/null +++ b/tests/shared/images/dds/Q8W8V8U8.dds diff --git a/tests/shared/images/dds/R16F.dds b/tests/shared/images/dds/R16F.dds Binary files differnew file mode 100644 index 0000000..e19863b --- /dev/null +++ b/tests/shared/images/dds/R16F.dds diff --git a/tests/shared/images/dds/R32F.dds b/tests/shared/images/dds/R32F.dds Binary files differnew file mode 100644 index 0000000..b13dd77 --- /dev/null +++ b/tests/shared/images/dds/R32F.dds diff --git a/tests/shared/images/dds/R3G3B2.dds b/tests/shared/images/dds/R3G3B2.dds Binary files differnew file mode 100644 index 0000000..866dea6 --- /dev/null +++ b/tests/shared/images/dds/R3G3B2.dds diff --git a/tests/shared/images/dds/R5G6B5.dds b/tests/shared/images/dds/R5G6B5.dds Binary files differnew file mode 100644 index 0000000..96a77ef --- /dev/null +++ b/tests/shared/images/dds/R5G6B5.dds diff --git a/tests/shared/images/dds/R8G8B8.dds b/tests/shared/images/dds/R8G8B8.dds Binary files differnew file mode 100644 index 0000000..7239bfd --- /dev/null +++ b/tests/shared/images/dds/R8G8B8.dds diff --git a/tests/shared/images/dds/R8G8_B8G8.dds b/tests/shared/images/dds/R8G8_B8G8.dds Binary files differnew file mode 100644 index 0000000..38cdb84 --- /dev/null +++ b/tests/shared/images/dds/R8G8_B8G8.dds diff --git a/tests/shared/images/dds/RXGB.dds b/tests/shared/images/dds/RXGB.dds Binary files differnew file mode 100644 index 0000000..c92d281 --- /dev/null +++ b/tests/shared/images/dds/RXGB.dds diff --git a/tests/shared/images/dds/UYVY.dds b/tests/shared/images/dds/UYVY.dds Binary files differnew file mode 100644 index 0000000..6f54acd --- /dev/null +++ b/tests/shared/images/dds/UYVY.dds diff --git a/tests/shared/images/dds/V16U16.dds b/tests/shared/images/dds/V16U16.dds Binary files differnew file mode 100644 index 0000000..8e1f98e --- /dev/null +++ b/tests/shared/images/dds/V16U16.dds diff --git a/tests/shared/images/dds/V8U8.dds b/tests/shared/images/dds/V8U8.dds Binary files differnew file mode 100644 index 0000000..e149e29 --- /dev/null +++ b/tests/shared/images/dds/V8U8.dds diff --git a/tests/shared/images/dds/X1R5G5B5.dds b/tests/shared/images/dds/X1R5G5B5.dds Binary files differnew file mode 100644 index 0000000..db7900d --- /dev/null +++ b/tests/shared/images/dds/X1R5G5B5.dds diff --git a/tests/shared/images/dds/X4R4G4B4.dds b/tests/shared/images/dds/X4R4G4B4.dds Binary files differnew file mode 100644 index 0000000..991f9e0 --- /dev/null +++ b/tests/shared/images/dds/X4R4G4B4.dds diff --git a/tests/shared/images/dds/X8B8G8R8.dds b/tests/shared/images/dds/X8B8G8R8.dds Binary files differnew file mode 100644 index 0000000..a4edfb0 --- /dev/null +++ b/tests/shared/images/dds/X8B8G8R8.dds diff --git a/tests/shared/images/dds/X8L8V8U8.dds b/tests/shared/images/dds/X8L8V8U8.dds Binary files differnew file mode 100644 index 0000000..00de9f1 --- /dev/null +++ b/tests/shared/images/dds/X8L8V8U8.dds diff --git a/tests/shared/images/dds/X8R8G8B8.dds b/tests/shared/images/dds/X8R8G8B8.dds Binary files differnew file mode 100644 index 0000000..af84369 --- /dev/null +++ b/tests/shared/images/dds/X8R8G8B8.dds diff --git a/tests/shared/images/dds/YUY2.dds b/tests/shared/images/dds/YUY2.dds Binary files differnew file mode 100644 index 0000000..09fb9c5 --- /dev/null +++ b/tests/shared/images/dds/YUY2.dds diff --git a/tests/shared/images/dds/mipmaps.dds b/tests/shared/images/dds/mipmaps.dds Binary files differnew file mode 100644 index 0000000..32bdae6 --- /dev/null +++ b/tests/shared/images/dds/mipmaps.dds |