summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--dist/changes-5.3.023
-rw-r--r--src/imageformats/doc/src/qtimageformats.qdoc2
-rw-r--r--src/plugins/imageformats/dds/README10
-rw-r--r--src/plugins/imageformats/dds/dds.json4
-rw-r--r--src/plugins/imageformats/dds/dds.pro16
-rw-r--r--src/plugins/imageformats/dds/ddsheader.cpp139
-rw-r--r--src/plugins/imageformats/dds/ddsheader.h240
-rw-r--r--src/plugins/imageformats/dds/main.cpp93
-rw-r--r--src/plugins/imageformats/dds/qddshandler.cpp1476
-rw-r--r--src/plugins/imageformats/dds/qddshandler.h94
-rw-r--r--src/plugins/imageformats/icns/README3
-rw-r--r--src/plugins/imageformats/icns/icns.json4
-rw-r--r--src/plugins/imageformats/icns/icns.pro16
-rw-r--r--src/plugins/imageformats/icns/main.cpp92
-rw-r--r--src/plugins/imageformats/icns/qicnshandler.cpp992
-rw-r--r--src/plugins/imageformats/icns/qicnshandler_p.h165
-rw-r--r--src/plugins/imageformats/imageformats.pro4
-rw-r--r--tests/auto/auto.pro5
-rw-r--r--tests/auto/dds/dds.pro8
-rw-r--r--tests/auto/dds/tst_qdds.cpp183
-rw-r--r--tests/auto/icns/icns.pro8
-rw-r--r--tests/auto/icns/tst_qicns.cpp129
-rw-r--r--tests/shared/images/dds.qrc53
-rw-r--r--tests/shared/images/dds/A16B16G16R16.ddsbin0 -> 32896 bytes
-rw-r--r--tests/shared/images/dds/A16B16G16R16F.ddsbin0 -> 32896 bytes
-rw-r--r--tests/shared/images/dds/A1R5G5B5.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/A2B10G10R10.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/A2R10G10B10.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/A2W10V10U10.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/A32B32G32R32F.ddsbin0 -> 65664 bytes
-rw-r--r--tests/shared/images/dds/A4L4.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/A4R4G4B4.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/A8.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/A8B8G8R8.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/A8L8.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/A8R3G3B2.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/A8R8G8B8.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/ATI2.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/CxV8U8.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/DXT1.ddsbin0 -> 1480 bytes
-rw-r--r--tests/shared/images/dds/DXT2.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/DXT3.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/DXT4.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/DXT5.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/G16R16.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/G16R16F.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/G32R32F.ddsbin0 -> 32896 bytes
-rw-r--r--tests/shared/images/dds/G8R8_G8B8.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/L16.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/L6V5U5.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/L8.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/P4.ddsbin0 -> 2240 bytes
-rw-r--r--tests/shared/images/dds/P8.ddsbin0 -> 5248 bytes
-rw-r--r--tests/shared/images/dds/Q16W16V16U16.ddsbin0 -> 32896 bytes
-rw-r--r--tests/shared/images/dds/Q8W8V8U8.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/R16F.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/R32F.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/R3G3B2.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/R5G6B5.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/R8G8B8.ddsbin0 -> 12416 bytes
-rw-r--r--tests/shared/images/dds/R8G8_B8G8.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/RXGB.ddsbin0 -> 4224 bytes
-rw-r--r--tests/shared/images/dds/UYVY.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/V16U16.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/V8U8.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/X1R5G5B5.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/X4R4G4B4.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/X8B8G8R8.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/X8L8V8U8.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/X8R8G8B8.ddsbin0 -> 16512 bytes
-rw-r--r--tests/shared/images/dds/YUY2.ddsbin0 -> 8320 bytes
-rw-r--r--tests/shared/images/dds/mipmaps.ddsbin0 -> 21972 bytes
-rw-r--r--tests/shared/images/icns.qrc15
-rw-r--r--tests/shared/images/icns/test-32bit.icnsbin0 -> 38865 bytes
-rw-r--r--tests/shared/images/icns/test-jp2.icnsbin0 -> 239220 bytes
-rw-r--r--tests/shared/images/icns/test-legacy.icnsbin0 -> 6712 bytes
-rw-r--r--tests/shared/images/icns/test-png.icnsbin0 -> 38792 bytes
-rw-r--r--tests/shared/images/icns/test-write-1024.pngbin0 -> 48514 bytes
-rw-r--r--tests/shared/images/icns/test-write-128.pngbin0 -> 6338 bytes
-rw-r--r--tests/shared/images/icns/test-write-16.pngbin0 -> 671 bytes
-rw-r--r--tests/shared/images/icns/test-write-256.pngbin0 -> 13260 bytes
-rw-r--r--tests/shared/images/icns/test-write-32.pngbin0 -> 1479 bytes
-rw-r--r--tests/shared/images/icns/test-write-512.pngbin0 -> 22098 bytes
-rw-r--r--tests/shared/images/icns/test-write-64.pngbin0 -> 3071 bytes
85 files changed, 3773 insertions, 3 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 5d8fd36..16328b7 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,3 +1,3 @@
load(qt_build_config)
-MODULE_VERSION = 5.2.2
+MODULE_VERSION = 5.3.0
diff --git a/dist/changes-5.3.0 b/dist/changes-5.3.0
new file mode 100644
index 0000000..5a7a19b
--- /dev/null
+++ b/dist/changes-5.3.0
@@ -0,0 +1,23 @@
+Qt 5.3 introduces many new features and improvements as well as bugfixes
+over the 5.2.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+ http://qt-project.org/doc/qt-5.3
+
+The Qt version 5.3 series is binary compatible with the 5.2.x series.
+Applications compiled for 5.2 will continue to run with 5.3.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+ http://bugreports.qt-project.org/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* General *
+****************************************************************************
+
+ - Add read/write support for Direct Draw Surface images.
+ - Add read/write support for ICNS images.
diff --git a/src/imageformats/doc/src/qtimageformats.qdoc b/src/imageformats/doc/src/qtimageformats.qdoc
index 02ee3b8..2c9f8a3 100644
--- a/src/imageformats/doc/src/qtimageformats.qdoc
+++ b/src/imageformats/doc/src/qtimageformats.qdoc
@@ -52,6 +52,8 @@ libraries. If not found, it may fall back on using a bundled copy (in
\table
\header \li Format \li Description \li Support \li 3rd party codec
+\row \li DDS \li Direct Draw Surface \li Read/write \li No
+\row \li ICNS \li Apple Icon Image \li Read/write \li No
\row \li MNG \li Multiple-image Network Graphics \li Read \li Yes (bundled)
\row \li TGA \li Truevision Graphics Adapter \li Read \li No
\row \li TIFF \li Tagged Image File Format \li Read/write \li Yes (bundled)
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..b645576
--- /dev/null
+++ b/src/plugins/imageformats/dds/dds.pro
@@ -0,0 +1,16 @@
+TARGET = qdds
+
+PLUGIN_TYPE = imageformats
+PLUGIN_CLASS_NAME = QDDSPlugin
+load(qt_plugin)
+
+HEADERS += \
+ ddsheader.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..4190ba9
--- /dev/null
+++ b/src/plugins/imageformats/dds/ddsheader.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** 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"
+
+#ifndef QT_NO_DATASTREAM
+
+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
+
+#endif // QT_NO_DATASTREAM
diff --git a/src/plugins/imageformats/dds/ddsheader.h b/src/plugins/imageformats/dds/ddsheader.h
new file mode 100644
index 0000000..7b2b80c
--- /dev/null
+++ b/src/plugins/imageformats/dds/ddsheader.h
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** 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>
+
+#ifndef QT_NO_DATASTREAM
+
+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,
+
+ FormatP4,
+ FormatA4P4,
+
+ FormatLast = 0x7fffffff
+};
+
+struct DDSPixelFormat
+{
+ enum DDSPixelFormatFlags {
+ FlagAlphaPixels = 0x00000001,
+ FlagAlpha = 0x00000002,
+ FlagFourCC = 0x00000004,
+ FlagPaletteIndexed4 = 0x00000008,
+ 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 // QT_NO_DATASTREAM
+
+#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..08aab46
--- /dev/null
+++ b/src/plugins/imageformats/dds/main.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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 <QtGui/qimageiohandler.h>
+
+#include "qddshandler.h"
+
+#ifndef QT_NO_IMAGEFORMATPLUGIN
+
+#ifndef QT_NO_DATASTREAM
+
+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;
+};
+
+QImageIOPlugin::Capabilities QDDSPlugin::capabilities(QIODevice *device, const QByteArray &format) const
+{
+ if (format == QByteArrayLiteral("dds"))
+ return Capabilities(CanRead | CanWrite);
+ if (!format.isEmpty())
+ return 0;
+ if (!device || !device->isOpen())
+ 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
+
+#include "main.moc"
+
+#endif // QT_NO_DATASTREAM
+
+#endif // QT_NO_IMAGEFORMATPLUGIN
diff --git a/src/plugins/imageformats/dds/qddshandler.cpp b/src/plugins/imageformats/dds/qddshandler.cpp
new file mode 100644
index 0000000..13bb9aa
--- /dev/null
+++ b/src/plugins/imageformats/dds/qddshandler.cpp
@@ -0,0 +1,1476 @@
+/****************************************************************************
+**
+** 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"
+
+#ifndef QT_NO_DATASTREAM
+
+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::FlagPaletteIndexed4) {
+ return FormatP4;
+ } else if (format.flags & DDSPixelFormat::FlagPaletteIndexed8) {
+ return FormatP8;
+ } else 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 k = 0; k < 16; ++k) {
+ quint8 a = qAlpha(arr[k]);
+ arr[k] = 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 readPalette8Image(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 readPalette4Image(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_Indexed8);
+ for (int i = 0; i < 16; ++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++) {
+ quint8 index;
+ for (quint32 x = 0; x < width - 1; ) {
+ s >> index;
+ image.setPixel(x++, y, (index & 0x0f) >> 0);
+ image.setPixel(x++, y, (index & 0xf0) >> 4);
+ }
+ if (width % 2 == 1) {
+ s >> index;
+ image.setPixel(width - 1, y, (index & 0x0f) >> 0);
+ }
+ }
+
+ 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:
+ case FormatA8P8:
+ return readPalette8Image(s, width, height);
+ case FormatP4:
+ case FormatA4P4:
+ return readPalette4Image(s, width, height);
+ 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_scanState(ScanNotScanned)
+{
+}
+
+QByteArray QDDSHandler::name() const
+{
+ return QByteArrayLiteral("dds");
+}
+
+bool QDDSHandler::canRead() const
+{
+ if (m_scanState == ScanNotScanned && !canRead(device()))
+ return false;
+
+ if (m_scanState != ScanError) {
+ setFormat(QByteArrayLiteral("dds"));
+ return true;
+ }
+
+ return false;
+}
+
+bool QDDSHandler::read(QImage *outImage)
+{
+ if (!ensureScanned() || 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)
+{
+ const QImage image = outImage.convertToFormat(QImage::Format_ARGB32);
+
+ QDataStream s(device());
+ s.setByteOrder(QDataStream::LittleEndian);
+
+ DDSHeader dds;
+ // Filling header
+ dds.magic = ddsMagic;
+ dds.size = 124;
+ dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
+ DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat;
+ dds.height = image.height();
+ dds.width = image.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 < image.width(); width++) {
+ for (int height = 0; height < image.height(); height++) {
+ QRgb pixel = image.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;
+}
+
+QVariant QDDSHandler::option(QImageIOHandler::ImageOption option) const
+{
+ if (!supportsOption(option) || !ensureScanned())
+ return QVariant();
+
+ switch (option) {
+ case QImageIOHandler::Size:
+ return QSize(m_header.width, m_header.height);
+ default:
+ break;
+ }
+
+ return QVariant();
+}
+
+bool QDDSHandler::supportsOption(QImageIOHandler::ImageOption option) const
+{
+ return option == QImageIOHandler::Size;
+}
+
+int QDDSHandler::imageCount() const
+{
+ if (!ensureScanned())
+ return 0;
+
+ return qMax<quint32>(1, m_header.mipMapCount);
+}
+
+bool QDDSHandler::jumpToImage(int imageNumber)
+{
+ if (imageNumber >= imageCount())
+ return false;
+
+ m_currentImage = imageNumber;
+ return true;
+}
+
+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) == QByteArrayLiteral("DDS ");
+}
+
+bool QDDSHandler::ensureScanned() const
+{
+ if (m_scanState != ScanNotScanned)
+ return m_scanState == ScanSuccess;
+
+ m_scanState = ScanError;
+
+ 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_scanState = ScanSuccess;
+ 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
+
+#endif // QT_NO_DATASTREAM
diff --git a/src/plugins/imageformats/dds/qddshandler.h b/src/plugins/imageformats/dds/qddshandler.h
new file mode 100644
index 0000000..582a013
--- /dev/null
+++ b/src/plugins/imageformats/dds/qddshandler.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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"
+
+#ifndef QT_NO_DATASTREAM
+
+QT_BEGIN_NAMESPACE
+
+class QDDSHandler : public QImageIOHandler
+{
+public:
+ QDDSHandler();
+
+ QByteArray name() const;
+
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ QVariant option(QImageIOHandler::ImageOption option) const;
+ bool supportsOption(QImageIOHandler::ImageOption option) const;
+
+ int imageCount() const;
+ bool jumpToImage(int imageNumber);
+
+ static bool canRead(QIODevice *device);
+
+private:
+ bool ensureScanned() const;
+ bool verifyHeader(const DDSHeader &dds) const;
+
+private:
+ enum ScanState {
+ ScanError = -1,
+ ScanNotScanned = 0,
+ ScanSuccess = 1,
+ };
+
+ DDSHeader m_header;
+ int m_format;
+ DDSHeaderDX10 m_header10;
+ int m_currentImage;
+ mutable ScanState m_scanState;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DATASTREAM
+
+#endif // QDDSHANDLER_H
diff --git a/src/plugins/imageformats/icns/README b/src/plugins/imageformats/icns/README
new file mode 100644
index 0000000..e2fb24d
--- /dev/null
+++ b/src/plugins/imageformats/icns/README
@@ -0,0 +1,3 @@
+[Useful links]
+* http://en.wikipedia.org/wiki/Apple_Icon_Image_format - Wikipedia article about ICNS format
+* http://www.macdisk.com/maciconen.php - Unofficial tech info about the format \ No newline at end of file
diff --git a/src/plugins/imageformats/icns/icns.json b/src/plugins/imageformats/icns/icns.json
new file mode 100644
index 0000000..860b40c
--- /dev/null
+++ b/src/plugins/imageformats/icns/icns.json
@@ -0,0 +1,4 @@
+{
+ "Keys": [ "icns" ],
+ "MimeTypes": [ "image/x-icns" ]
+}
diff --git a/src/plugins/imageformats/icns/icns.pro b/src/plugins/imageformats/icns/icns.pro
new file mode 100644
index 0000000..eb57df5
--- /dev/null
+++ b/src/plugins/imageformats/icns/icns.pro
@@ -0,0 +1,16 @@
+TARGET = qicns
+
+PLUGIN_TYPE = imageformats
+PLUGIN_CLASS_NAME = QICNSPlugin
+load(qt_plugin)
+
+DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
+
+HEADERS += \
+ qicnshandler_p.h
+
+SOURCES += \
+ main.cpp \
+ qicnshandler.cpp
+
+OTHER_FILES += icns.json
diff --git a/src/plugins/imageformats/icns/main.cpp b/src/plugins/imageformats/icns/main.cpp
new file mode 100644
index 0000000..62d1f8d
--- /dev/null
+++ b/src/plugins/imageformats/icns/main.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Alex Char.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $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 QT_NO_IMAGEFORMATPLUGIN
+
+#include "qicnshandler_p.h"
+
+#ifndef QT_NO_DATASTREAM
+
+QT_BEGIN_NAMESPACE
+
+class QICNSPlugin : public QImageIOPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "icns.json")
+
+public:
+ Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
+ QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
+};
+
+QImageIOPlugin::Capabilities QICNSPlugin::capabilities(QIODevice *device, const QByteArray &format) const
+{
+ if (format == QByteArrayLiteral("icns"))
+ return Capabilities(CanRead | CanWrite);
+ if (!format.isEmpty())
+ return 0;
+ if (!device || !device->isOpen())
+ return 0;
+
+ Capabilities cap;
+ if (device->isReadable() && QICNSHandler::canRead(device))
+ cap |= CanRead;
+ if (device->isWritable())
+ cap |= CanWrite;
+ return cap;
+}
+
+QImageIOHandler *QICNSPlugin::create(QIODevice *device, const QByteArray &format) const
+{
+ QImageIOHandler *handler = new QICNSHandler();
+ handler->setDevice(device);
+ handler->setFormat(format);
+ return handler;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
+
+#endif // QT_NO_DATASTREAM
+
+#endif // QT_NO_IMAGEFORMATPLUGIN
diff --git a/src/plugins/imageformats/icns/qicnshandler.cpp b/src/plugins/imageformats/icns/qicnshandler.cpp
new file mode 100644
index 0000000..b99c667
--- /dev/null
+++ b/src/plugins/imageformats/icns/qicnshandler.cpp
@@ -0,0 +1,992 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Alex Char.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $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 "qicnshandler_p.h"
+
+#include <QtCore/qmath.h>
+#include <QtCore/qendian.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qbuffer.h>
+#include <QtGui/qimage.h>
+
+#ifndef QT_NO_DATASTREAM
+
+QT_BEGIN_NAMESPACE
+
+static const quint8 ICNSBlockHeaderSize = 8;
+
+static const QRgb ICNSColorTableMono[] = {
+ qRgb(0xFF, 0xFF, 0xFF),
+ qRgb(0x00, 0x00, 0x00)
+};
+Q_STATIC_ASSERT(sizeof(ICNSColorTableMono) / sizeof(ICNSColorTableMono[0]) == (1 << ICNSEntry::DepthMono));
+
+static const QRgb ICNSColorTable4bit[] = {
+ qRgb(0xFF, 0xFF, 0xFF),
+ qRgb(0xFC, 0xF3, 0x05),
+ qRgb(0xFF, 0x64, 0x02),
+ qRgb(0xDD, 0x08, 0x06),
+ qRgb(0xF2, 0x08, 0x84),
+ qRgb(0x46, 0x00, 0xA5),
+ qRgb(0x00, 0x00, 0xD4),
+ qRgb(0x02, 0xAB, 0xEA),
+ qRgb(0x1F, 0xB7, 0x14),
+ qRgb(0x00, 0x64, 0x11),
+ qRgb(0x56, 0x2C, 0x05),
+ qRgb(0x90, 0x71, 0x3A),
+ qRgb(0xC0, 0xC0, 0xC0),
+ qRgb(0x80, 0x80, 0x80),
+ qRgb(0x40, 0x40, 0x40),
+ qRgb(0x00, 0x00, 0x00)
+};
+Q_STATIC_ASSERT(sizeof(ICNSColorTable4bit) / sizeof(ICNSColorTable4bit[0]) == (1 << ICNSEntry::Depth4bit));
+
+static const QRgb ICNSColorTable8bit[] = {
+ qRgb(0xFF, 0xFF, 0xFF),
+ qRgb(0xFF, 0xFF, 0xCC),
+ qRgb(0xFF, 0xFF, 0x99),
+ qRgb(0xFF, 0xFF, 0x66),
+ qRgb(0xFF, 0xFF, 0x33),
+ qRgb(0xFF, 0xFF, 0x00),
+ qRgb(0xFF, 0xCC, 0xFF),
+ qRgb(0xFF, 0xCC, 0xCC),
+ qRgb(0xFF, 0xCC, 0x99),
+ qRgb(0xFF, 0xCC, 0x66),
+ qRgb(0xFF, 0xCC, 0x33),
+ qRgb(0xFF, 0xCC, 0x00),
+ qRgb(0xFF, 0x99, 0xFF),
+ qRgb(0xFF, 0x99, 0xCC),
+ qRgb(0xFF, 0x99, 0x99),
+ qRgb(0xFF, 0x99, 0x66),
+ qRgb(0xFF, 0x99, 0x33),
+ qRgb(0xFF, 0x99, 0x00),
+ qRgb(0xFF, 0x66, 0xFF),
+ qRgb(0xFF, 0x66, 0xCC),
+ qRgb(0xFF, 0x66, 0x99),
+ qRgb(0xFF, 0x66, 0x66),
+ qRgb(0xFF, 0x66, 0x33),
+ qRgb(0xFF, 0x66, 0x00),
+ qRgb(0xFF, 0x33, 0xFF),
+ qRgb(0xFF, 0x33, 0xCC),
+ qRgb(0xFF, 0x33, 0x99),
+ qRgb(0xFF, 0x33, 0x66),
+ qRgb(0xFF, 0x33, 0x33),
+ qRgb(0xFF, 0x33, 0x00),
+ qRgb(0xFF, 0x00, 0xFF),
+ qRgb(0xFF, 0x00, 0xCC),
+ qRgb(0xFF, 0x00, 0x99),
+ qRgb(0xFF, 0x00, 0x66),
+ qRgb(0xFF, 0x00, 0x33),
+ qRgb(0xFF, 0x00, 0x00),
+ qRgb(0xCC, 0xFF, 0xFF),
+ qRgb(0xCC, 0xFF, 0xCC),
+ qRgb(0xCC, 0xFF, 0x99),
+ qRgb(0xCC, 0xFF, 0x66),
+ qRgb(0xCC, 0xFF, 0x33),
+ qRgb(0xCC, 0xFF, 0x00),
+ qRgb(0xCC, 0xCC, 0xFF),
+ qRgb(0xCC, 0xCC, 0xCC),
+ qRgb(0xCC, 0xCC, 0x99),
+ qRgb(0xCC, 0xCC, 0x66),
+ qRgb(0xCC, 0xCC, 0x33),
+ qRgb(0xCC, 0xCC, 0x00),
+ qRgb(0xCC, 0x99, 0xFF),
+ qRgb(0xCC, 0x99, 0xCC),
+ qRgb(0xCC, 0x99, 0x99),
+ qRgb(0xCC, 0x99, 0x66),
+ qRgb(0xCC, 0x99, 0x33),
+ qRgb(0xCC, 0x99, 0x00),
+ qRgb(0xCC, 0x66, 0xFF),
+ qRgb(0xCC, 0x66, 0xCC),
+ qRgb(0xCC, 0x66, 0x99),
+ qRgb(0xCC, 0x66, 0x66),
+ qRgb(0xCC, 0x66, 0x33),
+ qRgb(0xCC, 0x66, 0x00),
+ qRgb(0xCC, 0x33, 0xFF),
+ qRgb(0xCC, 0x33, 0xCC),
+ qRgb(0xCC, 0x33, 0x99),
+ qRgb(0xCC, 0x33, 0x66),
+ qRgb(0xCC, 0x33, 0x33),
+ qRgb(0xCC, 0x33, 0x00),
+ qRgb(0xCC, 0x00, 0xFF),
+ qRgb(0xCC, 0x00, 0xCC),
+ qRgb(0xCC, 0x00, 0x99),
+ qRgb(0xCC, 0x00, 0x66),
+ qRgb(0xCC, 0x00, 0x33),
+ qRgb(0xCC, 0x00, 0x00),
+ qRgb(0x99, 0xFF, 0xFF),
+ qRgb(0x99, 0xFF, 0xCC),
+ qRgb(0x99, 0xFF, 0x99),
+ qRgb(0x99, 0xFF, 0x66),
+ qRgb(0x99, 0xFF, 0x33),
+ qRgb(0x99, 0xFF, 0x00),
+ qRgb(0x99, 0xCC, 0xFF),
+ qRgb(0x99, 0xCC, 0xCC),
+ qRgb(0x99, 0xCC, 0x99),
+ qRgb(0x99, 0xCC, 0x66),
+ qRgb(0x99, 0xCC, 0x33),
+ qRgb(0x99, 0xCC, 0x00),
+ qRgb(0x99, 0x99, 0xFF),
+ qRgb(0x99, 0x99, 0xCC),
+ qRgb(0x99, 0x99, 0x99),
+ qRgb(0x99, 0x99, 0x66),
+ qRgb(0x99, 0x99, 0x33),
+ qRgb(0x99, 0x99, 0x00),
+ qRgb(0x99, 0x66, 0xFF),
+ qRgb(0x99, 0x66, 0xCC),
+ qRgb(0x99, 0x66, 0x99),
+ qRgb(0x99, 0x66, 0x66),
+ qRgb(0x99, 0x66, 0x33),
+ qRgb(0x99, 0x66, 0x00),
+ qRgb(0x99, 0x33, 0xFF),
+ qRgb(0x99, 0x33, 0xCC),
+ qRgb(0x99, 0x33, 0x99),
+ qRgb(0x99, 0x33, 0x66),
+ qRgb(0x99, 0x33, 0x33),
+ qRgb(0x99, 0x33, 0x00),
+ qRgb(0x99, 0x00, 0xFF),
+ qRgb(0x99, 0x00, 0xCC),
+ qRgb(0x99, 0x00, 0x99),
+ qRgb(0x99, 0x00, 0x66),
+ qRgb(0x99, 0x00, 0x33),
+ qRgb(0x99, 0x00, 0x00),
+ qRgb(0x66, 0xFF, 0xFF),
+ qRgb(0x66, 0xFF, 0xCC),
+ qRgb(0x66, 0xFF, 0x99),
+ qRgb(0x66, 0xFF, 0x66),
+ qRgb(0x66, 0xFF, 0x33),
+ qRgb(0x66, 0xFF, 0x00),
+ qRgb(0x66, 0xCC, 0xFF),
+ qRgb(0x66, 0xCC, 0xCC),
+ qRgb(0x66, 0xCC, 0x99),
+ qRgb(0x66, 0xCC, 0x66),
+ qRgb(0x66, 0xCC, 0x33),
+ qRgb(0x66, 0xCC, 0x00),
+ qRgb(0x66, 0x99, 0xFF),
+ qRgb(0x66, 0x99, 0xCC),
+ qRgb(0x66, 0x99, 0x99),
+ qRgb(0x66, 0x99, 0x66),
+ qRgb(0x66, 0x99, 0x33),
+ qRgb(0x66, 0x99, 0x00),
+ qRgb(0x66, 0x66, 0xFF),
+ qRgb(0x66, 0x66, 0xCC),
+ qRgb(0x66, 0x66, 0x99),
+ qRgb(0x66, 0x66, 0x66),
+ qRgb(0x66, 0x66, 0x33),
+ qRgb(0x66, 0x66, 0x00),
+ qRgb(0x66, 0x33, 0xFF),
+ qRgb(0x66, 0x33, 0xCC),
+ qRgb(0x66, 0x33, 0x99),
+ qRgb(0x66, 0x33, 0x66),
+ qRgb(0x66, 0x33, 0x33),
+ qRgb(0x66, 0x33, 0x00),
+ qRgb(0x66, 0x00, 0xFF),
+ qRgb(0x66, 0x00, 0xCC),
+ qRgb(0x66, 0x00, 0x99),
+ qRgb(0x66, 0x00, 0x66),
+ qRgb(0x66, 0x00, 0x33),
+ qRgb(0x66, 0x00, 0x00),
+ qRgb(0x33, 0xFF, 0xFF),
+ qRgb(0x33, 0xFF, 0xCC),
+ qRgb(0x33, 0xFF, 0x99),
+ qRgb(0x33, 0xFF, 0x66),
+ qRgb(0x33, 0xFF, 0x33),
+ qRgb(0x33, 0xFF, 0x00),
+ qRgb(0x33, 0xCC, 0xFF),
+ qRgb(0x33, 0xCC, 0xCC),
+ qRgb(0x33, 0xCC, 0x99),
+ qRgb(0x33, 0xCC, 0x66),
+ qRgb(0x33, 0xCC, 0x33),
+ qRgb(0x33, 0xCC, 0x00),
+ qRgb(0x33, 0x99, 0xFF),
+ qRgb(0x33, 0x99, 0xCC),
+ qRgb(0x33, 0x99, 0x99),
+ qRgb(0x33, 0x99, 0x66),
+ qRgb(0x33, 0x99, 0x33),
+ qRgb(0x33, 0x99, 0x00),
+ qRgb(0x33, 0x66, 0xFF),
+ qRgb(0x33, 0x66, 0xCC),
+ qRgb(0x33, 0x66, 0x99),
+ qRgb(0x33, 0x66, 0x66),
+ qRgb(0x33, 0x66, 0x33),
+ qRgb(0x33, 0x66, 0x00),
+ qRgb(0x33, 0x33, 0xFF),
+ qRgb(0x33, 0x33, 0xCC),
+ qRgb(0x33, 0x33, 0x99),
+ qRgb(0x33, 0x33, 0x66),
+ qRgb(0x33, 0x33, 0x33),
+ qRgb(0x33, 0x33, 0x00),
+ qRgb(0x33, 0x00, 0xFF),
+ qRgb(0x33, 0x00, 0xCC),
+ qRgb(0x33, 0x00, 0x99),
+ qRgb(0x33, 0x00, 0x66),
+ qRgb(0x33, 0x00, 0x33),
+ qRgb(0x33, 0x00, 0x00),
+ qRgb(0x00, 0xFF, 0xFF),
+ qRgb(0x00, 0xFF, 0xCC),
+ qRgb(0x00, 0xFF, 0x99),
+ qRgb(0x00, 0xFF, 0x66),
+ qRgb(0x00, 0xFF, 0x33),
+ qRgb(0x00, 0xFF, 0x00),
+ qRgb(0x00, 0xCC, 0xFF),
+ qRgb(0x00, 0xCC, 0xCC),
+ qRgb(0x00, 0xCC, 0x99),
+ qRgb(0x00, 0xCC, 0x66),
+ qRgb(0x00, 0xCC, 0x33),
+ qRgb(0x00, 0xCC, 0x00),
+ qRgb(0x00, 0x99, 0xFF),
+ qRgb(0x00, 0x99, 0xCC),
+ qRgb(0x00, 0x99, 0x99),
+ qRgb(0x00, 0x99, 0x66),
+ qRgb(0x00, 0x99, 0x33),
+ qRgb(0x00, 0x99, 0x00),
+ qRgb(0x00, 0x66, 0xFF),
+ qRgb(0x00, 0x66, 0xCC),
+ qRgb(0x00, 0x66, 0x99),
+ qRgb(0x00, 0x66, 0x66),
+ qRgb(0x00, 0x66, 0x33),
+ qRgb(0x00, 0x66, 0x00),
+ qRgb(0x00, 0x33, 0xFF),
+ qRgb(0x00, 0x33, 0xCC),
+ qRgb(0x00, 0x33, 0x99),
+ qRgb(0x00, 0x33, 0x66),
+ qRgb(0x00, 0x33, 0x33),
+ qRgb(0x00, 0x33, 0x00),
+ qRgb(0x00, 0x00, 0xFF),
+ qRgb(0x00, 0x00, 0xCC),
+ qRgb(0x00, 0x00, 0x99),
+ qRgb(0x00, 0x00, 0x66),
+ qRgb(0x00, 0x00, 0x33),
+ qRgb(0xEE, 0x00, 0x00),
+ qRgb(0xDD, 0x00, 0x00),
+ qRgb(0xBB, 0x00, 0x00),
+ qRgb(0xAA, 0x00, 0x00),
+ qRgb(0x88, 0x00, 0x00),
+ qRgb(0x77, 0x00, 0x00),
+ qRgb(0x55, 0x00, 0x00),
+ qRgb(0x44, 0x00, 0x00),
+ qRgb(0x22, 0x00, 0x00),
+ qRgb(0x11, 0x00, 0x00),
+ qRgb(0x00, 0xEE, 0x00),
+ qRgb(0x00, 0xDD, 0x00),
+ qRgb(0x00, 0xBB, 0x00),
+ qRgb(0x00, 0xAA, 0x00),
+ qRgb(0x00, 0x88, 0x00),
+ qRgb(0x00, 0x77, 0x00),
+ qRgb(0x00, 0x55, 0x00),
+ qRgb(0x00, 0x44, 0x00),
+ qRgb(0x00, 0x22, 0x00),
+ qRgb(0x00, 0x11, 0x00),
+ qRgb(0x00, 0x00, 0xEE),
+ qRgb(0x00, 0x00, 0xDD),
+ qRgb(0x00, 0x00, 0xBB),
+ qRgb(0x00, 0x00, 0xAA),
+ qRgb(0x00, 0x00, 0x88),
+ qRgb(0x00, 0x00, 0x77),
+ qRgb(0x00, 0x00, 0x55),
+ qRgb(0x00, 0x00, 0x44),
+ qRgb(0x00, 0x00, 0x22),
+ qRgb(0x00, 0x00, 0x11),
+ qRgb(0xEE, 0xEE, 0xEE),
+ qRgb(0xDD, 0xDD, 0xDD),
+ qRgb(0xBB, 0xBB, 0xBB),
+ qRgb(0xAA, 0xAA, 0xAA),
+ qRgb(0x88, 0x88, 0x88),
+ qRgb(0x77, 0x77, 0x77),
+ qRgb(0x55, 0x55, 0x55),
+ qRgb(0x44, 0x44, 0x44),
+ qRgb(0x22, 0x22, 0x22),
+ qRgb(0x11, 0x11, 0x11),
+ qRgb(0x00, 0x00, 0x00)
+};
+Q_STATIC_ASSERT(sizeof(ICNSColorTable8bit) / sizeof(ICNSColorTable8bit[0]) == (1 << ICNSEntry::Depth8bit));
+
+static inline QDataStream &operator>>(QDataStream &in, ICNSBlockHeader &p)
+{
+ in >> p.ostype;
+ in >> p.length;
+ return in;
+}
+
+static inline QDataStream &operator<<(QDataStream &out, const ICNSBlockHeader &p)
+{
+ out << p.ostype;
+ out << p.length;
+ return out;
+}
+
+static inline bool isPowOf2OrDividesBy16(quint32 u, qreal r)
+{
+ return u == r && ((u % 16 == 0) || (r >= 16 && (u & (u - 1)) == 0));
+}
+
+static inline bool isBlockHeaderValid(const ICNSBlockHeader &header, quint64 bound = 0)
+{
+ return header.ostype != 0 && (bound == 0
+ || qBound(quint64(ICNSBlockHeaderSize), quint64(header.length), bound) == header.length);
+}
+
+static inline bool isIconCompressed(const ICNSEntry &icon)
+{
+ return icon.dataFormat == ICNSEntry::PNG || icon.dataFormat == ICNSEntry::JP2;
+}
+
+static inline bool isMaskSuitable(const ICNSEntry &mask, const ICNSEntry &icon, ICNSEntry::Depth target)
+{
+ return mask.depth == target && mask.height == icon.height && mask.width == icon.width;
+}
+
+static inline QByteArray nameFromOSType(quint32 ostype)
+{
+ const quint32 bytes = qToBigEndian(ostype);
+ return QByteArray((const char*)&bytes, 4);
+}
+
+static inline quint32 nameToOSType(const QByteArray &ostype)
+{
+ if (ostype.size() != 4)
+ return 0;
+ return qFromBigEndian(*reinterpret_cast<const quint32*>(ostype.constData()));
+}
+
+static inline QByteArray nameForCompressedIcon(quint8 iconNumber)
+{
+ const bool portable = iconNumber < 7;
+ const QByteArray base = portable ? QByteArrayLiteral("icp") : QByteArrayLiteral("ic");
+ if (!portable && iconNumber < 10)
+ return base + "0" + QByteArray::number(iconNumber);
+ return base + QByteArray::number(iconNumber);
+}
+
+static inline QVector<QRgb> getColorTable(ICNSEntry::Depth depth)
+{
+ QVector<QRgb> table;
+ uint n = 1 << depth;
+ const QRgb *data;
+ switch (depth) {
+ case ICNSEntry::DepthMono:
+ data = ICNSColorTableMono;
+ break;
+ case ICNSEntry::Depth4bit:
+ data = ICNSColorTable4bit;
+ break;
+ case ICNSEntry::Depth8bit:
+ data = ICNSColorTable8bit;
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ table.resize(n);
+ memcpy(table.data(), data, sizeof(QRgb) * n);
+ return table;
+}
+
+static bool parseIconEntryData(ICNSEntry &icon, QIODevice *device)
+{
+ const qint64 oldPos = device->pos();
+ if (oldPos != icon.dataOffset && !device->seek(icon.dataOffset))
+ return false;
+
+ const QByteArray magic = device->peek(12);
+ const bool isPNG = magic.startsWith(QByteArrayLiteral("\211PNG\r\n\032\n\000\000\000\r"));
+ const bool isJP2 = !isPNG && magic == QByteArrayLiteral("\000\000\000\014jP \r\n\207\n");
+ if (isPNG || isJP2) {
+ // TODO: Add parsing of png/jp2 headers to enable feature reporting by plugin?
+ icon.flags = ICNSEntry::IsIcon;
+ icon.dataFormat = isPNG? ICNSEntry::PNG : ICNSEntry::JP2;
+ }
+ if (oldPos != icon.dataOffset && !device->seek(oldPos))
+ return false;
+ return true;
+}
+
+static bool parseIconEntryInfo(ICNSEntry &icon)
+{
+ const QString ostype = QString::fromLatin1(nameFromOSType(icon.ostype));
+ // Typical OSType naming: <junk><group><depth><mask>;
+ // For icons OSType should be strictly alphanumeric + '#' character for masks/mono.
+ const QString ptrn = QStringLiteral("^(?<junk>[a-z|A-Z]{0,4})(?<group>[a-z|A-Z]{1})(?<depth>[\\d]{0,2})(?<mask>[#mk]{0,2})$");
+ QRegularExpression regexp(ptrn);
+ QRegularExpressionMatch match = regexp.match(ostype);
+ if (!match.hasMatch()) {
+ qWarning("parseIconEntryInfo(): Failed, OSType doesn't match: \"%s\"", qPrintable(ostype));
+ return false;
+ }
+ const QString group = match.captured(QStringLiteral("group"));
+ const QString depth = match.captured(QStringLiteral("depth"));
+ const QString mask = match.captured(QStringLiteral("mask"));
+ // Icon group:
+ if (!group.isEmpty())
+ icon.group = ICNSEntry::Group(group.at(0).toLatin1());
+
+ // That's enough for compressed ones
+ if (isIconCompressed(icon))
+ return true;
+ // Icon depth:
+ if (!depth.isEmpty())
+ icon.depth = ICNSEntry::Depth(depth.toUInt());
+ // Try mono if depth not found
+ if (icon.depth == ICNSEntry::DepthUnknown)
+ icon.depth = ICNSEntry::DepthMono;
+ // Detect size:
+ const qreal bytespp = (qreal)icon.depth / 8;
+ const qreal r1 = qSqrt(icon.dataLength / bytespp);
+ const qreal r2 = qSqrt((icon.dataLength / bytespp) / 2);
+ const quint32 r1u = qRound(r1);
+ const quint32 r2u = qRound(r2);
+ const bool singleEntry = isPowOf2OrDividesBy16(r1u, r1);
+ const bool doubleSize = isPowOf2OrDividesBy16(r2u, r2);
+ if (singleEntry) {
+ icon.flags = mask.isEmpty() ? ICNSEntry::IsIcon : ICNSEntry::IsMask;
+ icon.dataFormat = ICNSEntry::RawIcon;
+ icon.width = r1u;
+ icon.height = r1u;
+ } else if (doubleSize) {
+ icon.flags = ICNSEntry::IconPlusMask;
+ icon.dataFormat = ICNSEntry::RawIcon;
+ icon.width = r2u;
+ icon.height = r2u;
+ } else if (icon.group == ICNSEntry::GroupMini) {
+ // Legacy 16x12 icons are an exception from the generic square formula
+ const bool doubleSize = icon.dataLength == 192 * bytespp * 2;
+ icon.flags = doubleSize ? ICNSEntry::IconPlusMask : ICNSEntry::IsIcon;
+ icon.dataFormat = ICNSEntry::RawIcon;
+ icon.width = 16;
+ icon.height = 12;
+ } else if (icon.depth == ICNSEntry::Depth32bit) {
+ // We have a formula mismatch in a 32bit icon there, probably RLE24
+ icon.dataFormat = ICNSEntry::RLE24;
+ icon.flags = mask.isEmpty() ? ICNSEntry::IsIcon : ICNSEntry::IsMask;
+ switch (icon.group) {
+ case ICNSEntry::GroupSmall:
+ icon.width = 16;
+ break;
+ case ICNSEntry::GroupLarge:
+ icon.width = 32;
+ break;
+ case ICNSEntry::GroupHuge:
+ icon.width = 48;
+ break;
+ case ICNSEntry::GroupThumbnail:
+ icon.width = 128;
+ break;
+ default:
+ qWarning("parseIconEntryInfo(): Failed, 32bit icon from an unknown group. OSType: \"%s\"",
+ qPrintable(ostype));
+ }
+ icon.height = icon.width;
+ }
+ return true;
+}
+
+static QImage readMask(const ICNSEntry &mask, QDataStream &stream)
+{
+ if ((mask.flags & ICNSEntry::IsMask) == 0)
+ return QImage();
+ if (mask.depth != ICNSEntry::DepthMono && mask.depth != ICNSEntry::Depth8bit) {
+ qWarning("readMask(): Failed, unusual bit depth: %u OSType: \"%s\"",
+ mask.depth, nameFromOSType(mask.ostype).constData());
+ return QImage();
+ }
+ const bool isMono = mask.depth == ICNSEntry::DepthMono;
+ const bool doubleSize = mask.flags == ICNSEntry::IconPlusMask;
+ const quint32 imageDataSize = (mask.width * mask.height * mask.depth) / 8;
+ const qint64 pos = doubleSize ? (mask.dataOffset + imageDataSize) : mask.dataOffset;
+ const qint64 oldPos = stream.device()->pos();
+ if (!stream.device()->seek(pos))
+ return QImage();
+ QImage img(mask.width, mask.height, QImage::Format_RGB32);
+ quint8 byte = 0;
+ quint32 pixel = 0;
+ for (quint32 y = 0; y < mask.height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(img.scanLine(y));
+ for (quint32 x = 0; x < mask.width; x++) {
+ if (pixel % (8 / mask.depth) == 0)
+ stream >> byte;
+ else if (isMono)
+ byte <<= 1;
+ const quint8 alpha = isMono ? (((byte >> 7) & 0x01) * 255) : byte;
+ line[x] = qRgb(alpha, alpha, alpha);
+ pixel++;
+ }
+ }
+ stream.device()->seek(oldPos);
+ return img;
+}
+
+template <ICNSEntry::Depth depth>
+static QImage readLowDepthIcon(const ICNSEntry &icon, QDataStream &stream)
+{
+ Q_STATIC_ASSERT(depth == ICNSEntry::DepthMono || depth == ICNSEntry::Depth4bit
+ || depth == ICNSEntry::Depth8bit);
+
+ const bool isMono = depth == ICNSEntry::DepthMono;
+ const QImage::Format format = isMono ? QImage::Format_Mono : QImage::Format_Indexed8;
+ const QVector<QRgb> colortable = getColorTable(depth);
+ if (colortable.isEmpty())
+ return QImage();
+ QImage img(icon.width, icon.height, format);
+ img.setColorTable(colortable);
+ quint32 pixel = 0;
+ quint8 byte = 0;
+ for (quint32 y = 0; y < icon.height; y++) {
+ for (quint32 x = 0; x < icon.width; x++) {
+ if (pixel % (8 / depth) == 0)
+ stream >> byte;
+ quint8 cindex;
+ switch (depth) {
+ case ICNSEntry::DepthMono:
+ cindex = (byte >> 7) & 0x01; // left 1 bit
+ byte <<= 1;
+ break;
+ case ICNSEntry::Depth4bit:
+ cindex = (byte >> 4) & 0x0F; // left 4 bits
+ byte <<= 4;
+ break;
+ default:
+ cindex = byte; // 8 bits
+ break;
+ }
+ img.setPixel(x, y, cindex);
+ pixel++;
+ }
+ }
+ return img;
+}
+
+static QImage read32bitIcon(const ICNSEntry &icon, QDataStream &stream)
+{
+ QImage img = QImage(icon.width, icon.height, QImage::Format_RGB32);
+ if (icon.dataFormat != ICNSEntry::RLE24) {
+ for (quint32 y = 0; y < icon.height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(img.scanLine(y));
+ for (quint32 x = 0; x < icon.width; x++) {
+ quint8 r, g, b, a;
+ stream >> r >> g >> b >> a;
+ line[x] = qRgb(r, g, b);
+ }
+ }
+ } else {
+ const quint32 estPxsNum = icon.width * icon.height;
+ const QByteArray &bytes = stream.device()->peek(4);
+ if (bytes.isEmpty())
+ return QImage();
+ // Zero-padding may be present:
+ if (qFromBigEndian<quint32>(*bytes.constData()) == 0)
+ stream.skipRawData(4);
+ for (quint8 colorNRun = 0; colorNRun < 3; colorNRun++) {
+ quint32 pixel = 0;
+ QRgb *line = 0;
+ while (pixel < estPxsNum && !stream.atEnd()) {
+ quint8 byte, value;
+ stream >> byte;
+ const bool bitIsClear = (byte & 0x80) == 0;
+ // If high bit is clear: run of different values; else: same value
+ quint8 runLength = bitIsClear ? ((0xFF & byte) + 1) : ((0xFF & byte) - 125);
+ // Length of the run for for different values: 1 <= len <= 128
+ // Length of the run for same values: 3 <= len <= 130
+ if (!bitIsClear)
+ stream >> value;
+ for (quint8 i = 0; i < runLength && pixel < estPxsNum; i++) {
+ if (bitIsClear)
+ stream >> value;
+ const quint32 y = pixel / icon.height;
+ const quint32 x = pixel - (icon.width * y);
+ if (pixel % icon.height == 0)
+ line = reinterpret_cast<QRgb *>(img.scanLine(y));
+ QRgb rgb = line[x];
+ const int r = (colorNRun == 0) ? value : qRed(rgb);
+ const int g = (colorNRun == 1) ? value : qGreen(rgb);
+ const int b = (colorNRun == 2) ? value : qBlue(rgb);
+ line[x] = qRgb(r, g, b);
+ pixel++;
+ }
+ }
+ }
+ }
+ return img;
+}
+
+QICNSHandler::QICNSHandler() :
+ m_currentIconIndex(0), m_state(ScanNotScanned)
+{
+}
+
+QByteArray QICNSHandler::name() const
+{
+ return QByteArrayLiteral("icns");
+}
+
+bool QICNSHandler::canRead(QIODevice *device)
+{
+ if (!device || !device->isReadable()) {
+ qWarning("QICNSHandler::canRead() called without a readable device");
+ return false;
+ }
+
+ if (device->isSequential()) {
+ qWarning("QICNSHandler::canRead() called on a sequential device");
+ return false;
+ }
+
+ return device->peek(4) == QByteArrayLiteral("icns");
+}
+
+bool QICNSHandler::canRead() const
+{
+ if (m_state == ScanNotScanned && !canRead(device()))
+ return false;
+
+ if (m_state != ScanError) {
+ setFormat(QByteArrayLiteral("icns"));
+ return true;
+ }
+
+ return false;
+}
+
+bool QICNSHandler::read(QImage *outImage)
+{
+ QImage img;
+ if (!ensureScanned()) {
+ qWarning("QICNSHandler::read(): The device wasn't parsed properly!");
+ return false;
+ }
+
+ const ICNSEntry &icon = m_icons.at(m_currentIconIndex);
+ QDataStream stream(device());
+ stream.setByteOrder(QDataStream::BigEndian);
+ if (!device()->seek(icon.dataOffset))
+ return false;
+
+ switch (icon.dataFormat) {
+ case ICNSEntry::RawIcon:
+ case ICNSEntry::RLE24:
+ if (qMin(icon.width, icon.height) == 0)
+ break;
+ switch (icon.depth) {
+ case ICNSEntry::DepthMono:
+ img = readLowDepthIcon<ICNSEntry::DepthMono>(icon, stream);
+ break;
+ case ICNSEntry::Depth4bit:
+ img = readLowDepthIcon<ICNSEntry::Depth4bit>(icon, stream);
+ break;
+ case ICNSEntry::Depth8bit:
+ img = readLowDepthIcon<ICNSEntry::Depth8bit>(icon, stream);
+ break;
+ case ICNSEntry::Depth32bit:
+ img = read32bitIcon(icon, stream);
+ break;
+ default:
+ qWarning("QICNSHandler::read(): Failed, unsupported icon bit depth: %u, OSType: \"%s\"",
+ icon.depth, nameFromOSType(icon.ostype).constData());
+ }
+ if (!img.isNull()) {
+ QImage alpha = readMask(getIconMask(icon), stream);
+ if (!alpha.isNull())
+ img.setAlphaChannel(alpha);
+ }
+ break;
+ default:
+ const char *format = 0;
+ if (icon.dataFormat == ICNSEntry::PNG)
+ format = "png";
+ else if (icon.dataFormat == ICNSEntry::JP2)
+ format = "jp2";
+ // Even if JP2 or PNG magic is not detected, try anyway for unknown formats
+ img = QImage::fromData(device()->read(icon.dataLength), format);
+ if (img.isNull()) {
+ if (format == 0)
+ format = "unknown";
+ qWarning("QICNSHandler::read(): Failed, compressed format \"%s\" is not supported " \
+ "by your Qt library or this file is corrupt. OSType: \"%s\"",
+ format, nameFromOSType(icon.ostype).constData());
+ }
+ }
+ *outImage = img;
+ return !img.isNull();
+}
+
+bool QICNSHandler::write(const QImage &image)
+{
+ /*
+ Notes:
+ * Experimental implementation. Just for simple converting tasks / testing purposes.
+ * Min. size is 16x16, Max. size is 1024x1024.
+ * Performs downscale to a square image if width != height.
+ * Performs upscale to 16x16, if the image is smaller.
+ * Performs downscale to a nearest power of two if size is not a power of two.
+ * Currently uses non-hardcoded OSTypes.
+ */
+ QIODevice *device = this->device();
+ if (!device->isWritable() || image.isNull() || qMin(image.width(), image.height()) == 0)
+ return false;
+ const int minSize = qMin(image.width(), image.height());
+ const int oldSize = (minSize < 16) ? 16 : minSize;
+ // Calc power of two:
+ int size = oldSize;
+ uint pow = 0;
+ // Note: Values over 10 are reserved for retina icons.
+ while (pow < 10 && (size >>= 1))
+ pow++;
+ const int newSize = 1 << pow;
+ QImage img = image;
+ // Let's enforce resizing if size differs:
+ if (newSize != oldSize || qMax(image.width(), image.height()) != minSize)
+ img = img.scaled(newSize, newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ // Construct OSType and headers:
+ const quint32 ostype = nameToOSType(nameForCompressedIcon(pow));
+ ICNSBlockHeader fileHeader;
+ fileHeader.ostype = ICNSBlockHeader::TypeIcns;
+ ICNSBlockHeader tocHeader;
+ tocHeader.ostype = ICNSBlockHeader::TypeToc;
+ ICNSBlockHeader iconEntry;
+ iconEntry.ostype = ostype;
+ QByteArray imageData;
+ QBuffer buffer(&imageData);
+ if (!buffer.open(QIODevice::WriteOnly) || !img.save(&buffer, "png"))
+ return false;
+ buffer.close();
+ iconEntry.length = ICNSBlockHeaderSize + imageData.size();
+ tocHeader.length = ICNSBlockHeaderSize * 2;
+ fileHeader.length = ICNSBlockHeaderSize + tocHeader.length + iconEntry.length;
+ if (!isBlockHeaderValid(iconEntry))
+ return false;
+
+ QDataStream stream(device);
+ // iconEntry is also a TOC entry
+ stream << fileHeader << tocHeader << iconEntry << iconEntry;
+ stream.writeRawData(imageData.constData(), imageData.size());
+ return stream.status() == QDataStream::Ok;
+}
+
+bool QICNSHandler::supportsOption(ImageOption option) const
+{
+ return option == SubType;
+}
+
+QVariant QICNSHandler::option(ImageOption option) const
+{
+ if (!supportsOption(option) || !ensureScanned())
+ return QVariant();
+
+ if (option == SubType) {
+ if (imageCount() > 0 && m_currentIconIndex <= imageCount()) {
+ const ICNSEntry &icon = m_icons.at(m_currentIconIndex);
+ return nameFromOSType(icon.ostype);
+ }
+ }
+
+ return QVariant();
+}
+
+int QICNSHandler::imageCount() const
+{
+ if (!ensureScanned())
+ return 0;
+
+ return m_icons.size();
+}
+
+bool QICNSHandler::jumpToImage(int imageNumber)
+{
+ if (imageNumber >= imageCount())
+ return false;
+
+ m_currentIconIndex = imageNumber;
+ return true;
+}
+
+bool QICNSHandler::jumpToNextImage()
+{
+ return jumpToImage(m_currentIconIndex + 1);
+}
+
+bool QICNSHandler::ensureScanned() const
+{
+ if (m_state == ScanNotScanned) {
+ QICNSHandler *that = const_cast<QICNSHandler *>(this);
+ that->m_state = that->scanDevice() ? ScanSuccess : ScanError;
+ }
+
+ return m_state == ScanSuccess;
+}
+
+bool QICNSHandler::addEntry(const ICNSBlockHeader &header, qint64 imgDataOffset)
+{
+ // Note: This function returns false only when a device positioning error occurred
+ ICNSEntry entry;
+ entry.ostype = header.ostype;
+ entry.dataOffset = imgDataOffset;
+ entry.dataLength = header.length - ICNSBlockHeaderSize;
+ // Check for known magic numbers:
+ if (!parseIconEntryData(entry, device()))
+ return false;
+ // Parse everything else and index this entry:
+ if (parseIconEntryInfo(entry)) {
+ if ((entry.flags & ICNSEntry::IsMask) != 0)
+ m_masks << entry;
+ if ((entry.flags & ICNSEntry::IsIcon) != 0)
+ m_icons << entry;
+ }
+ return true;
+}
+
+bool QICNSHandler::scanDevice()
+{
+ if (m_state == ScanSuccess)
+ return true;
+
+ if (!device()->seek(0))
+ return false;
+
+ QDataStream stream(device());
+ stream.setByteOrder(QDataStream::BigEndian);
+
+ bool scanIsIncomplete = false;
+ qint64 filelength = device()->size();
+ ICNSBlockHeader blockHeader;
+ while (!stream.atEnd() || device()->pos() < filelength) {
+ stream >> blockHeader;
+ if (stream.status() != QDataStream::Ok)
+ return false;
+
+ const qint64 blockDataOffset = device()->pos();
+ if (!isBlockHeaderValid(blockHeader)) {
+ qWarning("QICNSHandler::scanDevice(): Failed, bad header at pos %s. OSType \"%s\", length %u",
+ QByteArray::number(blockDataOffset).constData(),
+ nameFromOSType(blockHeader.ostype).constData(), blockHeader.length);
+ return false;
+ }
+ const quint64 blockDataLength = blockHeader.length - ICNSBlockHeaderSize;
+ const qint64 nextBlockOffset = blockDataOffset + blockDataLength;
+
+ switch (blockHeader.ostype) {
+ case ICNSBlockHeader::TypeIcns:
+ if (blockDataOffset != ICNSBlockHeaderSize) {
+ // Icns container definition should be in the beginning of the device.
+ // If we meet this block somewhere else, then just ignore it.
+ stream.skipRawData(blockDataLength);
+ break;
+ }
+ filelength = blockHeader.length;
+ if (device()->size() < blockHeader.length) {
+ qWarning("QICNSHandler::scanDevice(): Failed, file is incomplete.");
+ return false;
+ }
+ break;
+ case ICNSBlockHeader::TypeIcnv:
+ case ICNSBlockHeader::TypeClut:
+ // We don't have a good use for these blocks... yet.
+ stream.skipRawData(blockDataLength);
+ break;
+ case ICNSBlockHeader::TypeToc: {
+ // Quick scan, table of contents
+ if (blockDataOffset != ICNSBlockHeaderSize * 2) {
+ // TOC should be the first block in the file after container definition.
+ // Ignore and go on with a deep scan.
+ stream.skipRawData(blockDataLength);
+ break;
+ }
+ // First image data offset:
+ qint64 imgDataOffset = blockDataOffset + blockHeader.length;
+ for (uint i = 0, count = blockDataLength / ICNSBlockHeaderSize; i < count; i++) {
+ ICNSBlockHeader tocEntry;
+ stream >> tocEntry;
+ if (!isBlockHeaderValid(tocEntry)) {
+ // TOC contains incorrect header, we should skip TOC since we can't trust it
+ qWarning("QICNSHandler::scanDevice(): Warning! Table of contents contains a bad " \
+ "entry! Stop at device pos: %s bytes. This file may be corrupted.",
+ QByteArray::number(device()->pos()).constData());
+ if (!device()->seek(nextBlockOffset))
+ return false;
+ break;
+ }
+ if (!addEntry(tocEntry, imgDataOffset))
+ return false;
+ imgDataOffset += tocEntry.length;
+ // If TOC covers all the blocks in the file, then quick scan is complete
+ if (imgDataOffset == filelength)
+ return true;
+ }
+ // Else just start a deep scan to salvage anything left after TOC's end
+ scanIsIncomplete = true;
+ break;
+ }
+ default:
+ // Deep scan, block by block
+ if (scanIsIncomplete) {
+ // Check if entry with this offset is added somewhere
+ // But only if we have incomplete TOC, otherwise just try to add
+ bool exists = false;
+ for (int i = 0; i < m_icons.size() && !exists; i++)
+ exists = m_icons.at(i).dataOffset == blockDataOffset;
+ for (int i = 0; i < m_masks.size() && !exists; i++)
+ exists = m_masks.at(i).dataOffset == blockDataOffset;
+ if (!exists && !addEntry(blockHeader, blockDataOffset))
+ return false;
+ } else if (!addEntry(blockHeader, blockDataOffset)) {
+ return false;
+ }
+ stream.skipRawData(blockDataLength);
+ break;
+ }
+ }
+ return true;
+}
+
+const ICNSEntry &QICNSHandler::getIconMask(const ICNSEntry &icon) const
+{
+ const bool is32bit = icon.depth == ICNSEntry::Depth32bit;
+ ICNSEntry::Depth targetDepth = is32bit ? ICNSEntry::Depth8bit : ICNSEntry::DepthMono;
+ for (int i = 0; i < m_masks.size(); i++) {
+ const ICNSEntry &mask = m_masks.at(i);
+ if (isMaskSuitable(mask, icon, targetDepth))
+ return mask;
+ }
+ return icon;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DATASTREAM
diff --git a/src/plugins/imageformats/icns/qicnshandler_p.h b/src/plugins/imageformats/icns/qicnshandler_p.h
new file mode 100644
index 0000000..12c165b
--- /dev/null
+++ b/src/plugins/imageformats/icns/qicnshandler_p.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Alex Char.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $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 QICNSHANDLER_P_H
+#define QICNSHANDLER_P_H
+
+#include <QtGui/qimageiohandler.h>
+#include <QtCore/qvector.h>
+
+#ifndef QT_NO_DATASTREAM
+
+#define MAKEOSTYPE(c0,c1,c2,c3) (((quint8)c0 << 24) | ((quint8)c1 << 16) | ((quint8)c2 << 8) | (quint8)c3)
+
+QT_BEGIN_NAMESPACE
+
+struct ICNSBlockHeader
+{
+ enum OS {
+ TypeIcns = MAKEOSTYPE('i', 'c', 'n', 's'), // Icns container magic
+ TypeToc = MAKEOSTYPE('T', 'O', 'C', ' '), // Table of contents
+ TypeIcnv = MAKEOSTYPE('i', 'c', 'n', 'V'), // Icon Composer version
+ // Legacy:
+ TypeClut = MAKEOSTYPE('c', 'l', 'u', 't'), // Color look-up table (pre-OS X resources)
+ };
+
+ quint32 ostype;
+ quint32 length;
+};
+
+struct ICNSEntry
+{
+ enum Group {
+ GroupUnknown = 0, // Default for invalid ones
+ GroupMini = 'm', // "mini" (16x12)
+ GroupSmall = 's', // "small" (16x16)
+ GroupLarge = 'l', // "large" (32x32)
+ GroupHuge = 'h', // "huge" (48x48)
+ GroupThumbnail = 't', // "thumbnail" (128x128)
+ GroupPortable = 'p', // "portable"? (Speculation, used for png/jp2)
+ GroupCompressed = 'c', // "compressed"? (Speculation, used for png/jp2)
+ // Legacy icons:
+ GroupICON = 'N', // "ICON" (32x32)
+ };
+ enum Depth {
+ DepthUnknown = 0, // Default for invalid or compressed ones
+ DepthMono = 1,
+ Depth4bit = 4,
+ Depth8bit = 8,
+ Depth32bit = 32
+ };
+ enum Flags {
+ Unknown = 0x0, // Default for invalid ones
+ IsIcon = 0x1, // Contains a raw icon without alpha or compressed icon
+ IsMask = 0x2, // Contains alpha mask
+ IconPlusMask = IsIcon | IsMask // Contains raw icon and mask combined in one entry (double size)
+ };
+ enum Format {
+ FormatUnknown = 0, // Default for invalid or undetermined ones
+ RawIcon, // Raw legacy icon, uncompressed
+ RLE24, // Raw 32bit icon, data is compressed
+ PNG, // Compressed icon in PNG format
+ JP2 // Compressed icon in JPEG2000 format
+ };
+
+ quint32 ostype; // Real OSType
+ Group group; // ASCII character number
+ quint32 width; // For uncompressed icons only, zero for compressed ones for now
+ quint32 height; // For uncompressed icons only, zero for compressed ones fow now
+ Depth depth; // Color depth
+ Flags flags; // Flags to determine the type of entry
+ Format dataFormat; // Format of the image data
+ quint32 dataLength; // Length of the image data in bytes
+ qint64 dataOffset; // Offset from the initial position of the file/device
+
+ ICNSEntry() :
+ ostype(0), group(GroupUnknown), width(0), height(0), depth(DepthUnknown),
+ flags(Unknown), dataFormat(FormatUnknown), dataLength(0), dataOffset(0)
+ {
+ }
+};
+Q_DECLARE_TYPEINFO(ICNSEntry, Q_MOVABLE_TYPE);
+
+class QICNSHandler : public QImageIOHandler
+{
+public:
+ QICNSHandler();
+
+ bool canRead() const;
+ bool read(QImage *image);
+ bool write(const QImage &image);
+
+ QByteArray name() const;
+
+ bool supportsOption(ImageOption option) const;
+ QVariant option(ImageOption option) const;
+
+ int imageCount() const;
+ bool jumpToImage(int imageNumber);
+ bool jumpToNextImage();
+
+ static bool canRead(QIODevice *device);
+
+private:
+ bool ensureScanned() const;
+ bool scanDevice();
+ bool addEntry(const ICNSBlockHeader &header, qint64 imgDataOffset);
+ const ICNSEntry &getIconMask(const ICNSEntry &icon) const;
+
+private:
+ enum ScanState {
+ ScanError = -1,
+ ScanNotScanned = 0,
+ ScanSuccess = 1,
+ };
+
+ int m_currentIconIndex;
+ QVector<ICNSEntry> m_icons;
+ QVector<ICNSEntry> m_masks;
+ ScanState m_state;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DATASTREAM
+
+#endif /* QICNSHANDLER_P_H */
diff --git a/src/plugins/imageformats/imageformats.pro b/src/plugins/imageformats/imageformats.pro
index 79be7ae..d3a0d3a 100644
--- a/src/plugins/imageformats/imageformats.pro
+++ b/src/plugins/imageformats/imageformats.pro
@@ -3,4 +3,6 @@ SUBDIRS = \
tga \
wbmp \
mng \
- tiff
+ tiff \
+ dds \
+ icns
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 5bde0c7..109f216 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -1,5 +1,8 @@
TEMPLATE = subdirs
SUBDIRS = \
tga \
- wbmp
+ wbmp \
+ dds \
+ icns
+
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..eb25849
--- /dev/null
+++ b/tests/auto/dds/tst_qdds.cpp
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** 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 testWriteImage_data();
+ void testWriteImage();
+};
+
+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);
+ QTest::newRow("48") << QString("P4") << 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));
+ }
+}
+
+void tst_qdds::testWriteImage_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QSize>("size");
+
+ QTest::newRow("1") << QString("A8R8G8B8") << QSize(64, 64);
+}
+
+void tst_qdds::testWriteImage()
+{
+ QFETCH(QString, fileName);
+ QFETCH(QSize, size);
+
+ const QString path = fileName + QStringLiteral(".dds");
+ const QString sourcePath = QStringLiteral(":/dds/") + fileName + QStringLiteral(".dds");
+
+ QImage image(sourcePath);
+ QVERIFY(!image.isNull());
+ QVERIFY(image.size() == size);
+
+ QImageWriter writer(path, QByteArrayLiteral("dds"));
+ QVERIFY2(writer.canWrite(), qPrintable(writer.errorString()));
+ QVERIFY2(writer.write(image), qPrintable(writer.errorString()));
+
+ QVERIFY(image == QImage(path));
+}
+
+QTEST_MAIN(tst_qdds)
+#include "tst_qdds.moc"
diff --git a/tests/auto/icns/icns.pro b/tests/auto/icns/icns.pro
new file mode 100644
index 0000000..ef891b7
--- /dev/null
+++ b/tests/auto/icns/icns.pro
@@ -0,0 +1,8 @@
+TARGET = tst_qicns
+
+QT = core gui testlib
+CONFIG -= app_bundle
+CONFIG += testcase
+
+SOURCES += tst_qicns.cpp
+RESOURCES += $$PWD/../../shared/images/icns.qrc
diff --git a/tests/auto/icns/tst_qicns.cpp b/tests/auto/icns/tst_qicns.cpp
new file mode 100644
index 0000000..261590e
--- /dev/null
+++ b/tests/auto/icns/tst_qicns.cpp
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Alex Char.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the ICNS autotests 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_qicns: public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void readIcons_data();
+ void readIcons();
+ void writeIcons_data();
+ void writeIcons();
+};
+
+void tst_qicns::readIcons_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QSize>("size");
+ QTest::addColumn<int>("imageCount");
+ QTest::addColumn<QByteArray>("format");
+
+ QTest::newRow("1") << QStringLiteral("test-png") << QSize(128, 128) << 7 << QByteArrayLiteral("png");
+ QTest::newRow("2") << QStringLiteral("test-jp2") << QSize(128, 128) << 7 << QByteArrayLiteral("jp2");
+ QTest::newRow("3") << QStringLiteral("test-32bit") << QSize(128, 128) << 4 << QByteArray();
+ QTest::newRow("4") << QStringLiteral("test-legacy") << QSize(48, 48) << 12 << QByteArray();
+}
+
+void tst_qicns::readIcons()
+{
+ QFETCH(QString, fileName);
+ QFETCH(QSize, size);
+ QFETCH(int, imageCount);
+ QFETCH(QByteArray, format);
+
+ if (!format.isEmpty() && !QImageReader::supportedImageFormats().contains(format))
+ QSKIP("This test requires another image format plugin");
+ const QString path = QStringLiteral(":/icns/") + fileName + QStringLiteral(".icns");
+ QImageReader reader(path);
+ QVERIFY(reader.canRead());
+ QCOMPARE(reader.imageCount(), imageCount);
+
+ for (int i = 0; i < reader.imageCount(); ++i) {
+ QVERIFY2(reader.jumpToImage(i), qPrintable(reader.errorString()));
+ QImage image = reader.read();
+ if (i == 0)
+ QCOMPARE(image.size(), size);
+ QVERIFY2(!image.isNull(), qPrintable(reader.errorString()));
+ }
+}
+
+void tst_qicns::writeIcons_data()
+{
+ QTest::addColumn<QString>("fileName");
+ QTest::addColumn<QSize>("size");
+
+ QTest::newRow("1") << QStringLiteral("test-write-16") << QSize(16, 16);
+ QTest::newRow("2") << QStringLiteral("test-write-32") << QSize(32, 32);
+ QTest::newRow("3") << QStringLiteral("test-write-64") << QSize(64, 64);
+ QTest::newRow("4") << QStringLiteral("test-write-128") << QSize(128, 128);
+ QTest::newRow("5") << QStringLiteral("test-write-512") << QSize(512, 512);
+ QTest::newRow("6") << QStringLiteral("test-write-1024") << QSize(1024, 1024);
+}
+
+void tst_qicns::writeIcons()
+{
+ QTemporaryDir temp(QDir::tempPath() + QStringLiteral("/tst_qincs"));
+ QVERIFY2(temp.isValid(), "Unable to create temp dir.");
+
+ QFETCH(QString, fileName);
+ QFETCH(QSize, size);
+
+ const QString distPath = QStringLiteral("%1/%2.icns").arg(temp.path()).arg(fileName);
+ const QString sourcePath = QStringLiteral(":/icns/%1.png").arg(fileName);
+
+ QImage image(sourcePath);
+ QVERIFY(!image.isNull());
+ QVERIFY(image.size() == size);
+
+ QImageWriter writer(distPath, QByteArrayLiteral("icns"));
+ QVERIFY2(writer.canWrite(), qPrintable(writer.errorString()));
+ QVERIFY2(writer.write(image), qPrintable(writer.errorString()));
+
+ QVERIFY(image == QImage(distPath));
+}
+
+QTEST_MAIN(tst_qicns)
+#include "tst_qicns.moc"
diff --git a/tests/shared/images/dds.qrc b/tests/shared/images/dds.qrc
new file mode 100644
index 0000000..128a38d
--- /dev/null
+++ b/tests/shared/images/dds.qrc
@@ -0,0 +1,53 @@
+<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/P4.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
new file mode 100644
index 0000000..9b59982
--- /dev/null
+++ b/tests/shared/images/dds/A16B16G16R16.dds
Binary files differ
diff --git a/tests/shared/images/dds/A16B16G16R16F.dds b/tests/shared/images/dds/A16B16G16R16F.dds
new file mode 100644
index 0000000..5e59107
--- /dev/null
+++ b/tests/shared/images/dds/A16B16G16R16F.dds
Binary files differ
diff --git a/tests/shared/images/dds/A1R5G5B5.dds b/tests/shared/images/dds/A1R5G5B5.dds
new file mode 100644
index 0000000..38c7123
--- /dev/null
+++ b/tests/shared/images/dds/A1R5G5B5.dds
Binary files differ
diff --git a/tests/shared/images/dds/A2B10G10R10.dds b/tests/shared/images/dds/A2B10G10R10.dds
new file mode 100644
index 0000000..37bc09b
--- /dev/null
+++ b/tests/shared/images/dds/A2B10G10R10.dds
Binary files differ
diff --git a/tests/shared/images/dds/A2R10G10B10.dds b/tests/shared/images/dds/A2R10G10B10.dds
new file mode 100644
index 0000000..0754794
--- /dev/null
+++ b/tests/shared/images/dds/A2R10G10B10.dds
Binary files differ
diff --git a/tests/shared/images/dds/A2W10V10U10.dds b/tests/shared/images/dds/A2W10V10U10.dds
new file mode 100644
index 0000000..d7eca43
--- /dev/null
+++ b/tests/shared/images/dds/A2W10V10U10.dds
Binary files differ
diff --git a/tests/shared/images/dds/A32B32G32R32F.dds b/tests/shared/images/dds/A32B32G32R32F.dds
new file mode 100644
index 0000000..c9a1372
--- /dev/null
+++ b/tests/shared/images/dds/A32B32G32R32F.dds
Binary files differ
diff --git a/tests/shared/images/dds/A4L4.dds b/tests/shared/images/dds/A4L4.dds
new file mode 100644
index 0000000..e1872f3
--- /dev/null
+++ b/tests/shared/images/dds/A4L4.dds
Binary files differ
diff --git a/tests/shared/images/dds/A4R4G4B4.dds b/tests/shared/images/dds/A4R4G4B4.dds
new file mode 100644
index 0000000..e477e46
--- /dev/null
+++ b/tests/shared/images/dds/A4R4G4B4.dds
Binary files differ
diff --git a/tests/shared/images/dds/A8.dds b/tests/shared/images/dds/A8.dds
new file mode 100644
index 0000000..90b773b
--- /dev/null
+++ b/tests/shared/images/dds/A8.dds
Binary files differ
diff --git a/tests/shared/images/dds/A8B8G8R8.dds b/tests/shared/images/dds/A8B8G8R8.dds
new file mode 100644
index 0000000..a9bab75
--- /dev/null
+++ b/tests/shared/images/dds/A8B8G8R8.dds
Binary files differ
diff --git a/tests/shared/images/dds/A8L8.dds b/tests/shared/images/dds/A8L8.dds
new file mode 100644
index 0000000..8a26262
--- /dev/null
+++ b/tests/shared/images/dds/A8L8.dds
Binary files differ
diff --git a/tests/shared/images/dds/A8R3G3B2.dds b/tests/shared/images/dds/A8R3G3B2.dds
new file mode 100644
index 0000000..c69b3a3
--- /dev/null
+++ b/tests/shared/images/dds/A8R3G3B2.dds
Binary files differ
diff --git a/tests/shared/images/dds/A8R8G8B8.dds b/tests/shared/images/dds/A8R8G8B8.dds
new file mode 100644
index 0000000..1a44039
--- /dev/null
+++ b/tests/shared/images/dds/A8R8G8B8.dds
Binary files differ
diff --git a/tests/shared/images/dds/ATI2.dds b/tests/shared/images/dds/ATI2.dds
new file mode 100644
index 0000000..8f85817
--- /dev/null
+++ b/tests/shared/images/dds/ATI2.dds
Binary files differ
diff --git a/tests/shared/images/dds/CxV8U8.dds b/tests/shared/images/dds/CxV8U8.dds
new file mode 100644
index 0000000..2b6f69d
--- /dev/null
+++ b/tests/shared/images/dds/CxV8U8.dds
Binary files differ
diff --git a/tests/shared/images/dds/DXT1.dds b/tests/shared/images/dds/DXT1.dds
new file mode 100644
index 0000000..2ba685c
--- /dev/null
+++ b/tests/shared/images/dds/DXT1.dds
Binary files differ
diff --git a/tests/shared/images/dds/DXT2.dds b/tests/shared/images/dds/DXT2.dds
new file mode 100644
index 0000000..f4f006c
--- /dev/null
+++ b/tests/shared/images/dds/DXT2.dds
Binary files differ
diff --git a/tests/shared/images/dds/DXT3.dds b/tests/shared/images/dds/DXT3.dds
new file mode 100644
index 0000000..0498c40
--- /dev/null
+++ b/tests/shared/images/dds/DXT3.dds
Binary files differ
diff --git a/tests/shared/images/dds/DXT4.dds b/tests/shared/images/dds/DXT4.dds
new file mode 100644
index 0000000..ecc6057
--- /dev/null
+++ b/tests/shared/images/dds/DXT4.dds
Binary files differ
diff --git a/tests/shared/images/dds/DXT5.dds b/tests/shared/images/dds/DXT5.dds
new file mode 100644
index 0000000..47a431c
--- /dev/null
+++ b/tests/shared/images/dds/DXT5.dds
Binary files differ
diff --git a/tests/shared/images/dds/G16R16.dds b/tests/shared/images/dds/G16R16.dds
new file mode 100644
index 0000000..1d940ae
--- /dev/null
+++ b/tests/shared/images/dds/G16R16.dds
Binary files differ
diff --git a/tests/shared/images/dds/G16R16F.dds b/tests/shared/images/dds/G16R16F.dds
new file mode 100644
index 0000000..e68d970
--- /dev/null
+++ b/tests/shared/images/dds/G16R16F.dds
Binary files differ
diff --git a/tests/shared/images/dds/G32R32F.dds b/tests/shared/images/dds/G32R32F.dds
new file mode 100644
index 0000000..f3cf9d1
--- /dev/null
+++ b/tests/shared/images/dds/G32R32F.dds
Binary files differ
diff --git a/tests/shared/images/dds/G8R8_G8B8.dds b/tests/shared/images/dds/G8R8_G8B8.dds
new file mode 100644
index 0000000..eaa0210
--- /dev/null
+++ b/tests/shared/images/dds/G8R8_G8B8.dds
Binary files differ
diff --git a/tests/shared/images/dds/L16.dds b/tests/shared/images/dds/L16.dds
new file mode 100644
index 0000000..42710c5
--- /dev/null
+++ b/tests/shared/images/dds/L16.dds
Binary files differ
diff --git a/tests/shared/images/dds/L6V5U5.dds b/tests/shared/images/dds/L6V5U5.dds
new file mode 100644
index 0000000..cf77f71
--- /dev/null
+++ b/tests/shared/images/dds/L6V5U5.dds
Binary files differ
diff --git a/tests/shared/images/dds/L8.dds b/tests/shared/images/dds/L8.dds
new file mode 100644
index 0000000..4eba673
--- /dev/null
+++ b/tests/shared/images/dds/L8.dds
Binary files differ
diff --git a/tests/shared/images/dds/P4.dds b/tests/shared/images/dds/P4.dds
new file mode 100644
index 0000000..c8c3e90
--- /dev/null
+++ b/tests/shared/images/dds/P4.dds
Binary files differ
diff --git a/tests/shared/images/dds/P8.dds b/tests/shared/images/dds/P8.dds
new file mode 100644
index 0000000..4bef9ec
--- /dev/null
+++ b/tests/shared/images/dds/P8.dds
Binary files differ
diff --git a/tests/shared/images/dds/Q16W16V16U16.dds b/tests/shared/images/dds/Q16W16V16U16.dds
new file mode 100644
index 0000000..3b464e5
--- /dev/null
+++ b/tests/shared/images/dds/Q16W16V16U16.dds
Binary files differ
diff --git a/tests/shared/images/dds/Q8W8V8U8.dds b/tests/shared/images/dds/Q8W8V8U8.dds
new file mode 100644
index 0000000..b5c6864
--- /dev/null
+++ b/tests/shared/images/dds/Q8W8V8U8.dds
Binary files differ
diff --git a/tests/shared/images/dds/R16F.dds b/tests/shared/images/dds/R16F.dds
new file mode 100644
index 0000000..e19863b
--- /dev/null
+++ b/tests/shared/images/dds/R16F.dds
Binary files differ
diff --git a/tests/shared/images/dds/R32F.dds b/tests/shared/images/dds/R32F.dds
new file mode 100644
index 0000000..b13dd77
--- /dev/null
+++ b/tests/shared/images/dds/R32F.dds
Binary files differ
diff --git a/tests/shared/images/dds/R3G3B2.dds b/tests/shared/images/dds/R3G3B2.dds
new file mode 100644
index 0000000..866dea6
--- /dev/null
+++ b/tests/shared/images/dds/R3G3B2.dds
Binary files differ
diff --git a/tests/shared/images/dds/R5G6B5.dds b/tests/shared/images/dds/R5G6B5.dds
new file mode 100644
index 0000000..96a77ef
--- /dev/null
+++ b/tests/shared/images/dds/R5G6B5.dds
Binary files differ
diff --git a/tests/shared/images/dds/R8G8B8.dds b/tests/shared/images/dds/R8G8B8.dds
new file mode 100644
index 0000000..7239bfd
--- /dev/null
+++ b/tests/shared/images/dds/R8G8B8.dds
Binary files differ
diff --git a/tests/shared/images/dds/R8G8_B8G8.dds b/tests/shared/images/dds/R8G8_B8G8.dds
new file mode 100644
index 0000000..38cdb84
--- /dev/null
+++ b/tests/shared/images/dds/R8G8_B8G8.dds
Binary files differ
diff --git a/tests/shared/images/dds/RXGB.dds b/tests/shared/images/dds/RXGB.dds
new file mode 100644
index 0000000..c92d281
--- /dev/null
+++ b/tests/shared/images/dds/RXGB.dds
Binary files differ
diff --git a/tests/shared/images/dds/UYVY.dds b/tests/shared/images/dds/UYVY.dds
new file mode 100644
index 0000000..6f54acd
--- /dev/null
+++ b/tests/shared/images/dds/UYVY.dds
Binary files differ
diff --git a/tests/shared/images/dds/V16U16.dds b/tests/shared/images/dds/V16U16.dds
new file mode 100644
index 0000000..8e1f98e
--- /dev/null
+++ b/tests/shared/images/dds/V16U16.dds
Binary files differ
diff --git a/tests/shared/images/dds/V8U8.dds b/tests/shared/images/dds/V8U8.dds
new file mode 100644
index 0000000..e149e29
--- /dev/null
+++ b/tests/shared/images/dds/V8U8.dds
Binary files differ
diff --git a/tests/shared/images/dds/X1R5G5B5.dds b/tests/shared/images/dds/X1R5G5B5.dds
new file mode 100644
index 0000000..db7900d
--- /dev/null
+++ b/tests/shared/images/dds/X1R5G5B5.dds
Binary files differ
diff --git a/tests/shared/images/dds/X4R4G4B4.dds b/tests/shared/images/dds/X4R4G4B4.dds
new file mode 100644
index 0000000..991f9e0
--- /dev/null
+++ b/tests/shared/images/dds/X4R4G4B4.dds
Binary files differ
diff --git a/tests/shared/images/dds/X8B8G8R8.dds b/tests/shared/images/dds/X8B8G8R8.dds
new file mode 100644
index 0000000..a4edfb0
--- /dev/null
+++ b/tests/shared/images/dds/X8B8G8R8.dds
Binary files differ
diff --git a/tests/shared/images/dds/X8L8V8U8.dds b/tests/shared/images/dds/X8L8V8U8.dds
new file mode 100644
index 0000000..00de9f1
--- /dev/null
+++ b/tests/shared/images/dds/X8L8V8U8.dds
Binary files differ
diff --git a/tests/shared/images/dds/X8R8G8B8.dds b/tests/shared/images/dds/X8R8G8B8.dds
new file mode 100644
index 0000000..af84369
--- /dev/null
+++ b/tests/shared/images/dds/X8R8G8B8.dds
Binary files differ
diff --git a/tests/shared/images/dds/YUY2.dds b/tests/shared/images/dds/YUY2.dds
new file mode 100644
index 0000000..09fb9c5
--- /dev/null
+++ b/tests/shared/images/dds/YUY2.dds
Binary files differ
diff --git a/tests/shared/images/dds/mipmaps.dds b/tests/shared/images/dds/mipmaps.dds
new file mode 100644
index 0000000..32bdae6
--- /dev/null
+++ b/tests/shared/images/dds/mipmaps.dds
Binary files differ
diff --git a/tests/shared/images/icns.qrc b/tests/shared/images/icns.qrc
new file mode 100644
index 0000000..072b78c
--- /dev/null
+++ b/tests/shared/images/icns.qrc
@@ -0,0 +1,15 @@
+<RCC>
+ <qresource prefix="/">
+ <file>icns/test-png.icns</file>
+ <file>icns/test-jp2.icns</file>
+ <file>icns/test-32bit.icns</file>
+ <file>icns/test-legacy.icns</file>
+ <file>icns/test-write-16.png</file>
+ <file>icns/test-write-32.png</file>
+ <file>icns/test-write-64.png</file>
+ <file>icns/test-write-128.png</file>
+ <file>icns/test-write-256.png</file>
+ <file>icns/test-write-512.png</file>
+ <file>icns/test-write-1024.png</file>
+ </qresource>
+</RCC>
diff --git a/tests/shared/images/icns/test-32bit.icns b/tests/shared/images/icns/test-32bit.icns
new file mode 100644
index 0000000..be3921e
--- /dev/null
+++ b/tests/shared/images/icns/test-32bit.icns
Binary files differ
diff --git a/tests/shared/images/icns/test-jp2.icns b/tests/shared/images/icns/test-jp2.icns
new file mode 100644
index 0000000..57125bd
--- /dev/null
+++ b/tests/shared/images/icns/test-jp2.icns
Binary files differ
diff --git a/tests/shared/images/icns/test-legacy.icns b/tests/shared/images/icns/test-legacy.icns
new file mode 100644
index 0000000..fc66fac
--- /dev/null
+++ b/tests/shared/images/icns/test-legacy.icns
Binary files differ
diff --git a/tests/shared/images/icns/test-png.icns b/tests/shared/images/icns/test-png.icns
new file mode 100644
index 0000000..600bc67
--- /dev/null
+++ b/tests/shared/images/icns/test-png.icns
Binary files differ
diff --git a/tests/shared/images/icns/test-write-1024.png b/tests/shared/images/icns/test-write-1024.png
new file mode 100644
index 0000000..d62d14f
--- /dev/null
+++ b/tests/shared/images/icns/test-write-1024.png
Binary files differ
diff --git a/tests/shared/images/icns/test-write-128.png b/tests/shared/images/icns/test-write-128.png
new file mode 100644
index 0000000..1570976
--- /dev/null
+++ b/tests/shared/images/icns/test-write-128.png
Binary files differ
diff --git a/tests/shared/images/icns/test-write-16.png b/tests/shared/images/icns/test-write-16.png
new file mode 100644
index 0000000..a3b27e2
--- /dev/null
+++ b/tests/shared/images/icns/test-write-16.png
Binary files differ
diff --git a/tests/shared/images/icns/test-write-256.png b/tests/shared/images/icns/test-write-256.png
new file mode 100644
index 0000000..7a22292
--- /dev/null
+++ b/tests/shared/images/icns/test-write-256.png
Binary files differ
diff --git a/tests/shared/images/icns/test-write-32.png b/tests/shared/images/icns/test-write-32.png
new file mode 100644
index 0000000..ecede6f
--- /dev/null
+++ b/tests/shared/images/icns/test-write-32.png
Binary files differ
diff --git a/tests/shared/images/icns/test-write-512.png b/tests/shared/images/icns/test-write-512.png
new file mode 100644
index 0000000..12ce0d2
--- /dev/null
+++ b/tests/shared/images/icns/test-write-512.png
Binary files differ
diff --git a/tests/shared/images/icns/test-write-64.png b/tests/shared/images/icns/test-write-64.png
new file mode 100644
index 0000000..d11ccc1
--- /dev/null
+++ b/tests/shared/images/icns/test-write-64.png
Binary files differ