diff options
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 Binary files differnew file mode 100644 index 0000000..9b59982 --- /dev/null +++ b/tests/shared/images/dds/A16B16G16R16.dds diff --git a/tests/shared/images/dds/A16B16G16R16F.dds b/tests/shared/images/dds/A16B16G16R16F.dds Binary files differnew file mode 100644 index 0000000..5e59107 --- /dev/null +++ b/tests/shared/images/dds/A16B16G16R16F.dds diff --git a/tests/shared/images/dds/A1R5G5B5.dds b/tests/shared/images/dds/A1R5G5B5.dds Binary files differnew file mode 100644 index 0000000..38c7123 --- /dev/null +++ b/tests/shared/images/dds/A1R5G5B5.dds diff --git a/tests/shared/images/dds/A2B10G10R10.dds b/tests/shared/images/dds/A2B10G10R10.dds Binary files differnew file mode 100644 index 0000000..37bc09b --- /dev/null +++ b/tests/shared/images/dds/A2B10G10R10.dds diff --git a/tests/shared/images/dds/A2R10G10B10.dds b/tests/shared/images/dds/A2R10G10B10.dds Binary files differnew file mode 100644 index 0000000..0754794 --- /dev/null +++ b/tests/shared/images/dds/A2R10G10B10.dds diff --git a/tests/shared/images/dds/A2W10V10U10.dds b/tests/shared/images/dds/A2W10V10U10.dds Binary files differnew file mode 100644 index 0000000..d7eca43 --- /dev/null +++ b/tests/shared/images/dds/A2W10V10U10.dds diff --git a/tests/shared/images/dds/A32B32G32R32F.dds b/tests/shared/images/dds/A32B32G32R32F.dds Binary files differnew file mode 100644 index 0000000..c9a1372 --- /dev/null +++ b/tests/shared/images/dds/A32B32G32R32F.dds diff --git a/tests/shared/images/dds/A4L4.dds b/tests/shared/images/dds/A4L4.dds Binary files differnew file mode 100644 index 0000000..e1872f3 --- /dev/null +++ b/tests/shared/images/dds/A4L4.dds diff --git a/tests/shared/images/dds/A4R4G4B4.dds b/tests/shared/images/dds/A4R4G4B4.dds Binary files differnew file mode 100644 index 0000000..e477e46 --- /dev/null +++ b/tests/shared/images/dds/A4R4G4B4.dds diff --git a/tests/shared/images/dds/A8.dds b/tests/shared/images/dds/A8.dds Binary files differnew file mode 100644 index 0000000..90b773b --- /dev/null +++ b/tests/shared/images/dds/A8.dds diff --git a/tests/shared/images/dds/A8B8G8R8.dds b/tests/shared/images/dds/A8B8G8R8.dds Binary files differnew file mode 100644 index 0000000..a9bab75 --- /dev/null +++ b/tests/shared/images/dds/A8B8G8R8.dds diff --git a/tests/shared/images/dds/A8L8.dds b/tests/shared/images/dds/A8L8.dds Binary files differnew file mode 100644 index 0000000..8a26262 --- /dev/null +++ b/tests/shared/images/dds/A8L8.dds diff --git a/tests/shared/images/dds/A8R3G3B2.dds b/tests/shared/images/dds/A8R3G3B2.dds Binary files differnew file mode 100644 index 0000000..c69b3a3 --- /dev/null +++ b/tests/shared/images/dds/A8R3G3B2.dds diff --git a/tests/shared/images/dds/A8R8G8B8.dds b/tests/shared/images/dds/A8R8G8B8.dds Binary files differnew file mode 100644 index 0000000..1a44039 --- /dev/null +++ b/tests/shared/images/dds/A8R8G8B8.dds diff --git a/tests/shared/images/dds/ATI2.dds b/tests/shared/images/dds/ATI2.dds Binary files differnew file mode 100644 index 0000000..8f85817 --- /dev/null +++ b/tests/shared/images/dds/ATI2.dds diff --git a/tests/shared/images/dds/CxV8U8.dds b/tests/shared/images/dds/CxV8U8.dds Binary files differnew file mode 100644 index 0000000..2b6f69d --- /dev/null +++ b/tests/shared/images/dds/CxV8U8.dds diff --git a/tests/shared/images/dds/DXT1.dds b/tests/shared/images/dds/DXT1.dds Binary files differnew file mode 100644 index 0000000..2ba685c --- /dev/null +++ b/tests/shared/images/dds/DXT1.dds diff --git a/tests/shared/images/dds/DXT2.dds b/tests/shared/images/dds/DXT2.dds Binary files differnew file mode 100644 index 0000000..f4f006c --- /dev/null +++ b/tests/shared/images/dds/DXT2.dds diff --git a/tests/shared/images/dds/DXT3.dds b/tests/shared/images/dds/DXT3.dds Binary files differnew file mode 100644 index 0000000..0498c40 --- /dev/null +++ b/tests/shared/images/dds/DXT3.dds diff --git a/tests/shared/images/dds/DXT4.dds b/tests/shared/images/dds/DXT4.dds Binary files differnew file mode 100644 index 0000000..ecc6057 --- /dev/null +++ b/tests/shared/images/dds/DXT4.dds diff --git a/tests/shared/images/dds/DXT5.dds b/tests/shared/images/dds/DXT5.dds Binary files differnew file mode 100644 index 0000000..47a431c --- /dev/null +++ b/tests/shared/images/dds/DXT5.dds diff --git a/tests/shared/images/dds/G16R16.dds b/tests/shared/images/dds/G16R16.dds Binary files differnew file mode 100644 index 0000000..1d940ae --- /dev/null +++ b/tests/shared/images/dds/G16R16.dds diff --git a/tests/shared/images/dds/G16R16F.dds b/tests/shared/images/dds/G16R16F.dds Binary files differnew file mode 100644 index 0000000..e68d970 --- /dev/null +++ b/tests/shared/images/dds/G16R16F.dds diff --git a/tests/shared/images/dds/G32R32F.dds b/tests/shared/images/dds/G32R32F.dds Binary files differnew file mode 100644 index 0000000..f3cf9d1 --- /dev/null +++ b/tests/shared/images/dds/G32R32F.dds diff --git a/tests/shared/images/dds/G8R8_G8B8.dds b/tests/shared/images/dds/G8R8_G8B8.dds Binary files differnew file mode 100644 index 0000000..eaa0210 --- /dev/null +++ b/tests/shared/images/dds/G8R8_G8B8.dds diff --git a/tests/shared/images/dds/L16.dds b/tests/shared/images/dds/L16.dds Binary files differnew file mode 100644 index 0000000..42710c5 --- /dev/null +++ b/tests/shared/images/dds/L16.dds diff --git a/tests/shared/images/dds/L6V5U5.dds b/tests/shared/images/dds/L6V5U5.dds Binary files differnew file mode 100644 index 0000000..cf77f71 --- /dev/null +++ b/tests/shared/images/dds/L6V5U5.dds diff --git a/tests/shared/images/dds/L8.dds b/tests/shared/images/dds/L8.dds Binary files differnew file mode 100644 index 0000000..4eba673 --- /dev/null +++ b/tests/shared/images/dds/L8.dds diff --git a/tests/shared/images/dds/P4.dds b/tests/shared/images/dds/P4.dds Binary files differnew file mode 100644 index 0000000..c8c3e90 --- /dev/null +++ b/tests/shared/images/dds/P4.dds diff --git a/tests/shared/images/dds/P8.dds b/tests/shared/images/dds/P8.dds Binary files differnew file mode 100644 index 0000000..4bef9ec --- /dev/null +++ b/tests/shared/images/dds/P8.dds diff --git a/tests/shared/images/dds/Q16W16V16U16.dds b/tests/shared/images/dds/Q16W16V16U16.dds Binary files differnew file mode 100644 index 0000000..3b464e5 --- /dev/null +++ b/tests/shared/images/dds/Q16W16V16U16.dds diff --git a/tests/shared/images/dds/Q8W8V8U8.dds b/tests/shared/images/dds/Q8W8V8U8.dds Binary files differnew file mode 100644 index 0000000..b5c6864 --- /dev/null +++ b/tests/shared/images/dds/Q8W8V8U8.dds diff --git a/tests/shared/images/dds/R16F.dds b/tests/shared/images/dds/R16F.dds Binary files differnew file mode 100644 index 0000000..e19863b --- /dev/null +++ b/tests/shared/images/dds/R16F.dds diff --git a/tests/shared/images/dds/R32F.dds b/tests/shared/images/dds/R32F.dds Binary files differnew file mode 100644 index 0000000..b13dd77 --- /dev/null +++ b/tests/shared/images/dds/R32F.dds diff --git a/tests/shared/images/dds/R3G3B2.dds b/tests/shared/images/dds/R3G3B2.dds Binary files differnew file mode 100644 index 0000000..866dea6 --- /dev/null +++ b/tests/shared/images/dds/R3G3B2.dds diff --git a/tests/shared/images/dds/R5G6B5.dds b/tests/shared/images/dds/R5G6B5.dds Binary files differnew file mode 100644 index 0000000..96a77ef --- /dev/null +++ b/tests/shared/images/dds/R5G6B5.dds diff --git a/tests/shared/images/dds/R8G8B8.dds b/tests/shared/images/dds/R8G8B8.dds Binary files differnew file mode 100644 index 0000000..7239bfd --- /dev/null +++ b/tests/shared/images/dds/R8G8B8.dds diff --git a/tests/shared/images/dds/R8G8_B8G8.dds b/tests/shared/images/dds/R8G8_B8G8.dds Binary files differnew file mode 100644 index 0000000..38cdb84 --- /dev/null +++ b/tests/shared/images/dds/R8G8_B8G8.dds diff --git a/tests/shared/images/dds/RXGB.dds b/tests/shared/images/dds/RXGB.dds Binary files differnew file mode 100644 index 0000000..c92d281 --- /dev/null +++ b/tests/shared/images/dds/RXGB.dds diff --git a/tests/shared/images/dds/UYVY.dds b/tests/shared/images/dds/UYVY.dds Binary files differnew file mode 100644 index 0000000..6f54acd --- /dev/null +++ b/tests/shared/images/dds/UYVY.dds diff --git a/tests/shared/images/dds/V16U16.dds b/tests/shared/images/dds/V16U16.dds Binary files differnew file mode 100644 index 0000000..8e1f98e --- /dev/null +++ b/tests/shared/images/dds/V16U16.dds diff --git a/tests/shared/images/dds/V8U8.dds b/tests/shared/images/dds/V8U8.dds Binary files differnew file mode 100644 index 0000000..e149e29 --- /dev/null +++ b/tests/shared/images/dds/V8U8.dds diff --git a/tests/shared/images/dds/X1R5G5B5.dds b/tests/shared/images/dds/X1R5G5B5.dds Binary files differnew file mode 100644 index 0000000..db7900d --- /dev/null +++ b/tests/shared/images/dds/X1R5G5B5.dds diff --git a/tests/shared/images/dds/X4R4G4B4.dds b/tests/shared/images/dds/X4R4G4B4.dds Binary files differnew file mode 100644 index 0000000..991f9e0 --- /dev/null +++ b/tests/shared/images/dds/X4R4G4B4.dds diff --git a/tests/shared/images/dds/X8B8G8R8.dds b/tests/shared/images/dds/X8B8G8R8.dds Binary files differnew file mode 100644 index 0000000..a4edfb0 --- /dev/null +++ b/tests/shared/images/dds/X8B8G8R8.dds diff --git a/tests/shared/images/dds/X8L8V8U8.dds b/tests/shared/images/dds/X8L8V8U8.dds Binary files differnew file mode 100644 index 0000000..00de9f1 --- /dev/null +++ b/tests/shared/images/dds/X8L8V8U8.dds diff --git a/tests/shared/images/dds/X8R8G8B8.dds b/tests/shared/images/dds/X8R8G8B8.dds Binary files differnew file mode 100644 index 0000000..af84369 --- /dev/null +++ b/tests/shared/images/dds/X8R8G8B8.dds diff --git a/tests/shared/images/dds/YUY2.dds b/tests/shared/images/dds/YUY2.dds Binary files differnew file mode 100644 index 0000000..09fb9c5 --- /dev/null +++ b/tests/shared/images/dds/YUY2.dds diff --git a/tests/shared/images/dds/mipmaps.dds b/tests/shared/images/dds/mipmaps.dds Binary files differnew file mode 100644 index 0000000..32bdae6 --- /dev/null +++ b/tests/shared/images/dds/mipmaps.dds 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 Binary files differnew file mode 100644 index 0000000..be3921e --- /dev/null +++ b/tests/shared/images/icns/test-32bit.icns diff --git a/tests/shared/images/icns/test-jp2.icns b/tests/shared/images/icns/test-jp2.icns Binary files differnew file mode 100644 index 0000000..57125bd --- /dev/null +++ b/tests/shared/images/icns/test-jp2.icns diff --git a/tests/shared/images/icns/test-legacy.icns b/tests/shared/images/icns/test-legacy.icns Binary files differnew file mode 100644 index 0000000..fc66fac --- /dev/null +++ b/tests/shared/images/icns/test-legacy.icns diff --git a/tests/shared/images/icns/test-png.icns b/tests/shared/images/icns/test-png.icns Binary files differnew file mode 100644 index 0000000..600bc67 --- /dev/null +++ b/tests/shared/images/icns/test-png.icns diff --git a/tests/shared/images/icns/test-write-1024.png b/tests/shared/images/icns/test-write-1024.png Binary files differnew file mode 100644 index 0000000..d62d14f --- /dev/null +++ b/tests/shared/images/icns/test-write-1024.png diff --git a/tests/shared/images/icns/test-write-128.png b/tests/shared/images/icns/test-write-128.png Binary files differnew file mode 100644 index 0000000..1570976 --- /dev/null +++ b/tests/shared/images/icns/test-write-128.png diff --git a/tests/shared/images/icns/test-write-16.png b/tests/shared/images/icns/test-write-16.png Binary files differnew file mode 100644 index 0000000..a3b27e2 --- /dev/null +++ b/tests/shared/images/icns/test-write-16.png diff --git a/tests/shared/images/icns/test-write-256.png b/tests/shared/images/icns/test-write-256.png Binary files differnew file mode 100644 index 0000000..7a22292 --- /dev/null +++ b/tests/shared/images/icns/test-write-256.png diff --git a/tests/shared/images/icns/test-write-32.png b/tests/shared/images/icns/test-write-32.png Binary files differnew file mode 100644 index 0000000..ecede6f --- /dev/null +++ b/tests/shared/images/icns/test-write-32.png diff --git a/tests/shared/images/icns/test-write-512.png b/tests/shared/images/icns/test-write-512.png Binary files differnew file mode 100644 index 0000000..12ce0d2 --- /dev/null +++ b/tests/shared/images/icns/test-write-512.png diff --git a/tests/shared/images/icns/test-write-64.png b/tests/shared/images/icns/test-write-64.png Binary files differnew file mode 100644 index 0000000..d11ccc1 --- /dev/null +++ b/tests/shared/images/icns/test-write-64.png |