summaryrefslogtreecommitdiffstats
path: root/src/plugins/imageformats/dds/qddshandler.cpp
diff options
context:
space:
mode:
authorABBAPOH <ABBAPOH@nextmail.ru>2013-11-13 20:00:27 +0400
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-11-26 11:35:06 +0100
commite39aa8a59ea511f7fa1fe416fad54952b5297b7b (patch)
tree96713a96ebb8744c0004a2fd0d242bea703a48d7 /src/plugins/imageformats/dds/qddshandler.cpp
parent8cc21cdcbf7e31da54b0e9f51df829abcf72ea65 (diff)
Add Direct Draw Surface plugin
Change-Id: I8dfa6001c1aae2dcb5f2e6e0bfd142ef1f9dddbc Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Diffstat (limited to 'src/plugins/imageformats/dds/qddshandler.cpp')
-rw-r--r--src/plugins/imageformats/dds/qddshandler.cpp1414
1 files changed, 1414 insertions, 0 deletions
diff --git a/src/plugins/imageformats/dds/qddshandler.cpp b/src/plugins/imageformats/dds/qddshandler.cpp
new file mode 100644
index 0000000..bca8a4d
--- /dev/null
+++ b/src/plugins/imageformats/dds/qddshandler.cpp
@@ -0,0 +1,1414 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Ivan Komissarov.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the DDS plugin in the Qt ImageFormats module.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qddshandler.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qmath.h>
+
+#include <QtGui/qimage.h>
+
+#include "ddsheader.h"
+
+QT_BEGIN_NAMESPACE
+
+enum Colors {
+ Red = 0,
+ Green,
+ Blue,
+ Alpha,
+ ColorCount
+};
+
+enum DXTVersions {
+ One = 1,
+ Two = 2,
+ Three = 3,
+ Four = 4,
+ Five = 5,
+ RXGB = 6
+};
+
+// All magic numbers are little-endian as long as dds format has little
+// endian byte order
+static const quint32 ddsMagic = 0x20534444; // "DDS "
+static const quint32 dx10Magic = 0x30315844; // "DX10"
+
+static const qint64 headerSize = 128;
+static const quint32 ddsSize = 124; // headerSize without magic
+static const quint32 pixelFormatSize = 32;
+
+struct FaceOffset
+{
+ int x, y;
+};
+
+static const FaceOffset faceOffsets[6] = { {2, 1}, {0, 1}, {1, 0}, {1, 2}, {1, 1}, {3, 1} };
+
+static int faceFlags[6] = {
+ DDSHeader::Caps2CubeMapPositiveX,
+ DDSHeader::Caps2CubeMapNegativeX,
+ DDSHeader::Caps2CubeMapPositiveY,
+ DDSHeader::Caps2CubeMapNegativeY,
+ DDSHeader::Caps2CubeMapPositiveZ,
+ DDSHeader::Caps2CubeMapNegativeZ
+};
+
+struct FormatInfo
+{
+ Format format;
+ quint32 flags;
+ quint32 bitCount;
+ quint32 rBitMask;
+ quint32 gBitMask;
+ quint32 bBitMask;
+ quint32 aBitMask;
+};
+
+static const FormatInfo formatInfos[] = {
+ { FormatA8R8G8B8, DDSPixelFormat::FlagRGBA, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 },
+ { FormatX8R8G8B8, DDSPixelFormat::FlagRGB, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 },
+ { FormatA2B10G10R10, DDSPixelFormat::FlagRGBA, 32, 0x000003ff, 0x0000fc00, 0x3ff00000, 0xc0000000 },
+ { FormatA8B8G8R8, DDSPixelFormat::FlagRGBA, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 },
+ { FormatX8B8G8R8, DDSPixelFormat::FlagRGB, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 },
+ { FormatG16R16, DDSPixelFormat::FlagRGBA, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 },
+ { FormatG16R16, DDSPixelFormat::FlagRGB, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 },
+ { FormatA2R10G10B10, DDSPixelFormat::FlagRGBA, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 },
+
+ { FormatR8G8B8, DDSPixelFormat::FlagRGB, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 },
+
+ { FormatR5G6B5, DDSPixelFormat::FlagRGB, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000 },
+ { FormatX1R5G5B5, DDSPixelFormat::FlagRGB, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00000000 },
+ { FormatA1R5G5B5, DDSPixelFormat::FlagRGBA, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000 },
+ { FormatA4R4G4B4, DDSPixelFormat::FlagRGBA, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000 },
+ { FormatA8R3G3B2, DDSPixelFormat::FlagRGBA, 16, 0x000000e0, 0x0000001c, 0x00000003, 0x0000ff00 },
+ { FormatX4R4G4B4, DDSPixelFormat::FlagRGB, 16, 0x00000f00, 0x000000f0, 0x0000000f, 0x00000000 },
+ { FormatA8L8, DDSPixelFormat::FlagLA, 16, 0x000000ff, 0x00000000, 0x00000000, 0x0000ff00 },
+ { FormatL16, DDSPixelFormat::FlagLuminance, 16, 0x0000ffff, 0x00000000, 0x00000000, 0x00000000 },
+
+ { FormatR3G3B2, DDSPixelFormat::FlagRGB, 8, 0x000000e0, 0x0000001c, 0x00000003, 0x00000000 },
+ { FormatA8, DDSPixelFormat::FlagAlpha, 8, 0x00000000, 0x00000000, 0x00000000, 0x000000ff },
+ { FormatL8, DDSPixelFormat::FlagLuminance, 8, 0x000000ff, 0x00000000, 0x00000000, 0x00000000 },
+ { FormatA4L4, DDSPixelFormat::FlagLA, 8, 0x0000000f, 0x00000000, 0x00000000, 0x000000f0 },
+
+ { FormatV8U8, DDSPixelFormat::FlagNormal, 16, 0x000000ff, 0x0000ff00, 0x00000000, 0x00000000 },
+ { FormatL6V5U5, 0, 16, 0x0000001f, 0x000003e0, 0x0000fc00, 0x00000000 },
+ { FormatX8L8V8U8, 0, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 },
+ { FormatQ8W8V8U8, DDSPixelFormat::FlagNormal, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 },
+ { FormatV16U16, DDSPixelFormat::FlagNormal, 32, 0x0000ffff, 0xffff0000, 0x00000000, 0x00000000 },
+ { FormatA2W10V10U10, DDSPixelFormat::FlagNormal, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 }
+};
+static const size_t formatInfosSize = sizeof(formatInfos)/sizeof(FormatInfo);
+
+static const Format knownFourCCs[] = {
+ FormatA16B16G16R16,
+ FormatV8U8,
+ FormatUYVY,
+ FormatR8G8B8G8,
+ FormatYUY2,
+ FormatG8R8G8B8,
+ FormatDXT1,
+ FormatDXT2,
+ FormatDXT3,
+ FormatDXT4,
+ FormatDXT5,
+ FormatRXGB,
+ FormatATI2,
+ FormatQ16W16V16U16,
+ FormatR16F,
+ FormatG16R16F,
+ FormatA16B16G16R16F,
+ FormatR32F,
+ FormatG32R32F,
+ FormatA32B32G32R32F,
+ FormatCxV8U8
+};
+static const size_t knownFourCCsSize = sizeof(knownFourCCs)/sizeof(Format);
+
+static inline int maskToShift(quint32 mask)
+{
+ if (mask == 0)
+ return 0;
+
+ int result = 0;
+ while (!((mask >> result) & 1))
+ result++;
+ return result;
+}
+
+static inline int maskLength(quint32 mask)
+{
+ int result = 0;
+ while (mask) {
+ if (mask & 1)
+ result++;
+ mask >>= 1;
+ }
+ return result;
+}
+
+static inline quint32 readValue(QDataStream &s, quint32 size)
+{
+ Q_ASSERT(size == 8 || size == 16 || size == 24 || size == 32);
+
+ quint32 value = 0;
+ quint8 tmp;
+ for (unsigned bit = 0; bit < size; bit += 8) {
+ s >> tmp;
+ value += (quint32(tmp) << bit);
+ }
+ return value;
+}
+
+static inline bool hasAlpha(const DDSHeader &dds)
+{
+ return (dds.pixelFormat.flags & (DDSPixelFormat::FlagAlphaPixels | DDSPixelFormat::FlagAlpha)) != 0;
+}
+
+static inline bool isCubeMap(const DDSHeader &dds)
+{
+ return (dds.caps2 & DDSHeader::Caps2CubeMap) != 0;
+}
+
+static inline QRgb yuv2rgb(quint8 Y, quint8 U, quint8 V)
+{
+ return qRgb(quint8(Y + 1.13983 * (V - 128)),
+ quint8(Y - 0.39465 * (U - 128) - 0.58060 * (V - 128)),
+ quint8(Y + 2.03211 * (U - 128)));
+}
+
+static Format getFormat(const DDSHeader &dds)
+{
+ const DDSPixelFormat &format = dds.pixelFormat;
+ if (format.flags & DDSPixelFormat::FlagFourCC) {
+ for (size_t i = 0; i < knownFourCCsSize; ++i) {
+ if (dds.pixelFormat.fourCC == knownFourCCs[i])
+ return knownFourCCs[i];
+ }
+ } else {
+ for (size_t i = 0; i < formatInfosSize; ++i) {
+ const FormatInfo &info = formatInfos[i];
+ if ((format.flags & info.flags) == info.flags &&
+ format.rgbBitCount == info.bitCount &&
+ format.rBitMask == info.rBitMask &&
+ format.bBitMask == info.bBitMask &&
+ format.bBitMask == info.bBitMask &&
+ format.aBitMask == info.aBitMask) {
+ return info.format;
+ }
+ }
+ }
+
+ return FormatUnknown;
+}
+
+static inline void decodeColor(quint16 color, quint8 & red, quint8 & green, quint8 & blue)
+{
+ red = ((color >> 11) & 0x1f) << 3;
+ green = ((color >> 5) & 0x3f) << 2;
+ blue = (color & 0x1f) << 3;
+}
+
+static inline quint8 calcC2(quint8 c0, quint8 c1)
+{
+ return 2.0 * c0 / 3.0 + c1 / 3.0;
+}
+
+static inline quint8 calcC2a(quint8 c0, quint8 c1)
+{
+ return c0 / 2.0 + c1 / 2.0;
+}
+
+static inline quint8 calcC3(quint8 c0, quint8 c1)
+{
+ return c0 / 3.0 + 2.0 * c1 / 3.0;
+}
+
+static void DXTFillColors(QRgb *result, quint16 c0, quint16 c1, quint32 table, bool dxt1a = false)
+{
+ quint8 r[4];
+ quint8 g[4];
+ quint8 b[4];
+ quint8 a[4];
+
+ a[0] = a[1] = a[2] = a[3] = 255;
+
+ decodeColor(c0, r[0], g[0], b[0]);
+ decodeColor(c1, r[1], g[1], b[1]);
+ if (!dxt1a) {
+ r[2] = calcC2(r[0], r[1]);
+ g[2] = calcC2(g[0], g[1]);
+ b[2] = calcC2(b[0], b[1]);
+ r[3] = calcC3(r[0], r[1]);
+ g[3] = calcC3(g[0], g[1]);
+ b[3] = calcC3(b[0], b[1]);
+ } else {
+ r[2] = calcC2a(r[0], r[1]);
+ g[2] = calcC2a(g[0], g[1]);
+ b[2] = calcC2a(b[0], b[1]);
+ r[3] = g[3] = b[3] = a[3] = 0;
+ }
+
+ for (int k = 0; k < 4; k++)
+ for (int l = 0; l < 4; l++) {
+ unsigned index = table & 0x0003;
+ table >>= 2;
+
+ result[k * 4 + l] = qRgba(r[index], g[index], b[index], a[index]);
+ }
+}
+
+template <DXTVersions version>
+inline void setAlphaDXT32Helper(QRgb *rgbArr, quint64 alphas)
+{
+ Q_STATIC_ASSERT(version == Two || version == Three);
+ for (int i = 0; i < 16; i++) {
+ quint8 alpha = 16 * (alphas & 0x0f);
+ QRgb rgb = rgbArr[i];
+ if (version == Two) // DXT2
+ rgbArr[i] = qRgba(qRed(rgb) * alpha / 0xff, qGreen(rgb) * alpha / 0xff, qBlue(rgb) * alpha / 0xff, alpha);
+ else if (version == Three) // DXT3
+ rgbArr[i] = qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), alpha);
+ alphas = alphas >> 4;
+ }
+}
+
+template <DXTVersions version>
+inline void setAlphaDXT45Helper(QRgb *rgbArr, quint64 alphas)
+{
+ Q_STATIC_ASSERT(version == Four || version == Five);
+ quint8 a[8];
+ a[0] = alphas & 0xff;
+ a[1] = (alphas >> 8) & 0xff;
+ if (a[0] > a[1]) {
+ a[2] = (6*a[0] + 1*a[1]) / 7;
+ a[3] = (5*a[0] + 2*a[1]) / 7;
+ a[4] = (4*a[0] + 3*a[1]) / 7;
+ a[5] = (3*a[0] + 4*a[1]) / 7;
+ a[6] = (2*a[0] + 5*a[1]) / 7;
+ a[7] = (1*a[0] + 6*a[1]) / 7;
+ } else {
+ a[2] = (4*a[0] + 1*a[1]) / 5;
+ a[3] = (3*a[0] + 2*a[1]) / 5;
+ a[4] = (2*a[0] + 3*a[1]) / 5;
+ a[5] = (1*a[0] + 4*a[1]) / 5;
+ a[6] = 0;
+ a[7] = 255;
+ }
+ alphas >>= 16;
+ for (int i = 0; i < 16; i++) {
+ quint8 index = alphas & 0x07;
+ quint8 alpha = a[index];
+ QRgb rgb = rgbArr[i];
+ if (version == Four) // DXT4
+ rgbArr[i] = qRgba(qRed(rgb) * alpha / 0xff, qGreen(rgb) * alpha / 0xff, qBlue(rgb) * alpha / 0xff, alpha);
+ else if (version == Five) // DXT5
+ rgbArr[i] = qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), alpha);
+ alphas = alphas >> 3;
+ }
+}
+
+template <DXTVersions version>
+inline void setAlphaDXT(QRgb *rgbArr, quint64 alphas)
+{
+ Q_UNUSED(rgbArr);
+ Q_UNUSED(alphas);
+}
+
+template <>
+inline void setAlphaDXT<Two>(QRgb *rgbArr, quint64 alphas)
+{
+ setAlphaDXT32Helper<Two>(rgbArr, alphas);
+}
+
+template <>
+inline void setAlphaDXT<Three>(QRgb *rgbArr, quint64 alphas)
+{
+ setAlphaDXT32Helper<Three>(rgbArr, alphas);
+}
+
+template <>
+inline void setAlphaDXT<Four>(QRgb *rgbArr, quint64 alphas)
+{
+ setAlphaDXT45Helper<Four>(rgbArr, alphas);
+}
+
+template <>
+inline void setAlphaDXT<Five>(QRgb *rgbArr, quint64 alphas)
+{
+ setAlphaDXT45Helper<Five>(rgbArr, alphas);
+}
+
+template <>
+inline void setAlphaDXT<RXGB>(QRgb *rgbArr, quint64 alphas)
+{
+ setAlphaDXT45Helper<Five>(rgbArr, alphas);
+}
+
+static inline QRgb invertRXGBColors(QRgb pixel)
+{
+ return qRgb(qAlpha(pixel), qGreen(pixel), qBlue(pixel));
+}
+
+template <DXTVersions version>
+static QImage readDXT(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage::Format format = (version == Two || version == Four) ?
+ QImage::Format_ARGB32_Premultiplied : QImage::Format_ARGB32;
+
+ QImage image(width, height, format);
+
+ for (quint32 i = 0; i < height; i += 4) {
+ for (quint32 j = 0; j < width; j += 4) {
+ quint64 alpha = 0;
+ quint16 c0, c1;
+ quint32 table;
+ if (version != One)
+ s >> alpha;
+ s >> c0;
+ s >> c1;
+ s >> table;
+
+ QRgb arr[16];
+
+ DXTFillColors(arr, c0, c1, table, version == One && c0 <= c1);
+ setAlphaDXT<version>(arr, alpha);
+
+ const quint32 kMax = qMin<quint32>(4, height - i);
+ const quint32 lMax = qMin<quint32>(4, width - j);
+ for (quint32 k = 0; k < kMax; k++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(i + k));
+ for (quint32 l = 0; l < lMax; l++) {
+ QRgb pixel = arr[k * 4 + l];
+ if (version == RXGB)
+ pixel = invertRXGBColors(pixel);
+
+ line[j + l] = pixel;
+ }
+ }
+ }
+ }
+ return image;
+}
+
+static inline QImage readDXT1(QDataStream &s, quint32 width, quint32 height)
+{
+ return readDXT<One>(s, width, height);
+}
+
+static inline QImage readDXT2(QDataStream &s, quint32 width, quint32 height)
+{
+ return readDXT<Two>(s, width, height);
+}
+
+static inline QImage readDXT3(QDataStream &s, quint32 width, quint32 height)
+{
+ return readDXT<Three>(s, width, height);
+}
+
+static inline QImage readDXT4(QDataStream &s, quint32 width, quint32 height)
+{
+ return readDXT<Four>(s, width, height);
+}
+
+static inline QImage readDXT5(QDataStream &s, quint32 width, quint32 height)
+{
+ return readDXT<Five>(s, width, height);
+}
+
+static inline QImage readRXGB(QDataStream &s, quint32 width, quint32 height)
+{
+ return readDXT<RXGB>(s, width, height);
+}
+
+static QImage readATI2(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ for (quint32 i = 0; i < height; i += 4) {
+ for (quint32 j = 0; j < width; j += 4) {
+ quint64 alpha1;
+ quint64 alpha2;
+ s >> alpha1;
+ s >> alpha2;
+
+ QRgb arr[16];
+ memset(arr, 0, sizeof(QRgb) * 16);
+ setAlphaDXT<Five>(arr, alpha1);
+ for (int i = 0; i < 16; ++i) {
+ quint8 a = qAlpha(arr[i]);
+ arr[i] = qRgba(0, 0, a, 0);
+ }
+ setAlphaDXT<Five>(arr, alpha2);
+
+ const quint32 kMax = qMin<quint32>(4, height - i);
+ const quint32 lMax = qMin<quint32>(4, width - j);
+ for (quint32 k = 0; k < kMax; k++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(i + k));
+ for (quint32 l = 0; l < lMax; l++) {
+ QRgb pixel = arr[k * 4 + l];
+ const quint8 nx = qAlpha(pixel);
+ const quint8 ny = qBlue(pixel);
+
+ // TODO: formulas can be incorrect
+ const double fx = nx / 127.5 - 1.0;
+ const double fy = ny / 127.5 - 1.0;
+ const double fxfy = 1.0 - fx * fx - fy * fy;
+ const double fz = fxfy > 0 ? sqrt(fxfy) : -1.0;
+ const quint8 nz = quint8((fz + 1.0) * 127.5);
+
+ line[j + l] = qRgb(nx, ny, nz);
+ }
+ }
+ }
+ }
+ return image;
+}
+
+static QImage readUnsignedImage(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height, bool hasAlpha)
+{
+ quint32 flags = dds.pixelFormat.flags;
+
+ quint32 masks[ColorCount];
+ quint8 shifts[ColorCount];
+ quint8 bits[ColorCount];
+ masks[Red] = dds.pixelFormat.rBitMask;
+ masks[Green] = dds.pixelFormat.gBitMask;
+ masks[Blue] = dds.pixelFormat.bBitMask;
+ masks[Alpha] = hasAlpha ? dds.pixelFormat.aBitMask : 0;
+ for (int i = 0; i < ColorCount; ++i) {
+ shifts[i] = maskToShift(masks[i]);
+ bits[i] = maskLength(masks[i]);
+
+ // move mask to the left
+ if (bits[i] <= 8)
+ masks[i] = (masks[i] >> shifts[i]) << (8 - bits[i]);
+ }
+
+ const QImage::Format format = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
+
+ QImage image(width, height, format);
+
+ for (quint32 y = 0; y < height; y++) {
+ for (quint32 x = 0; x < width; x++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+
+ quint32 value = readValue(s, dds.pixelFormat.rgbBitCount);
+ quint8 colors[ColorCount];
+
+ for (int c = 0; c < ColorCount; ++c) {
+ if (bits[c] > 8) {
+ // truncate unneseccary bits
+ colors[c] = (value & masks[c]) >> shifts[c] >> (bits[c] - 8);
+ } else {
+ // move color to the left
+ quint8 color = value >> shifts[c] << (8 - bits[c]) & masks[c];
+ if (masks[c])
+ colors[c] = color * 0xff / masks[c];
+ else
+ colors[c] = 0;
+ }
+ }
+
+ if (flags & DDSPixelFormat::FlagLuminance)
+ line[x] = qRgba(colors[Red], colors[Red], colors[Red], colors[Alpha]);
+ else if (flags & DDSPixelFormat::FlagYUV)
+ line[x] = yuv2rgb(colors[Red], colors[Green], colors[Blue]);
+ else
+ line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
+ }
+ }
+
+ return image;
+}
+
+static double readFloat16(QDataStream &s)
+{
+ quint16 value;
+ s >> value;
+
+ double sign = (value & 0x8000) == 0x8000 ? -1.0 : 1.0;
+ qint8 exp = (value & 0x7C00) >> 10;
+ quint16 fraction = value & 0x3FF;
+
+ if (exp == 0)
+ return sign * qPow(2.0, -14.0) * fraction / 1024.0;
+ else
+ return sign * qPow(2.0, exp - 15) * (1 + fraction / 1024.0);
+}
+
+static inline float readFloat32(QDataStream &s)
+{
+ Q_ASSERT(sizeof(float) == 4);
+ float value;
+ // TODO: find better way to avoid setting precision each time
+ QDataStream::FloatingPointPrecision precision = s.floatingPointPrecision();
+ s.setFloatingPointPrecision(QDataStream::SinglePrecision);
+ s >> value;
+ s.setFloatingPointPrecision(precision);
+ return value;
+}
+
+static QImage readR16F(QDataStream &s, const quint32 width, const quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ quint8 r = readFloat16(s) * 255;
+ line[x] = qRgba(r, 0, 0, 0);
+ }
+ }
+
+ return image;
+}
+
+static QImage readRG16F(QDataStream &s, const quint32 width, const quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ quint8 r = readFloat16(s) * 255;
+ quint8 g = readFloat16(s) * 255;
+ line[x] = qRgba(r, g, 0, 0);
+ }
+ }
+
+ return image;
+}
+
+static QImage readARGB16F(QDataStream &s, const quint32 width, const quint32 height)
+{
+ QImage image(width, height, QImage::Format_ARGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ quint8 colors[ColorCount];
+ for (int c = 0; c < ColorCount; ++c)
+ colors[c] = readFloat16(s) * 255;
+
+ line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
+ }
+ }
+
+ return image;
+}
+
+static QImage readR32F(QDataStream &s, const quint32 width, const quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ quint8 r = readFloat32(s) * 255;
+ line[x] = qRgba(r, 0, 0, 0);
+ }
+ }
+
+ return image;
+}
+
+static QImage readRG32F(QDataStream &s, const quint32 width, const quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ quint8 r = readFloat32(s) * 255;
+ quint8 g = readFloat32(s) * 255;
+ line[x] = qRgba(r, g, 0, 0);
+ }
+ }
+
+ return image;
+}
+
+static QImage readARGB32F(QDataStream &s, const quint32 width, const quint32 height)
+{
+ QImage image(width, height, QImage::Format_ARGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ quint8 colors[ColorCount];
+ for (int c = 0; c < ColorCount; ++c)
+ colors[c] = readFloat32(s) * 255;
+ line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
+ }
+ }
+
+ return image;
+}
+
+static QImage readQ16W16V16U16(QDataStream &s, const quint32 width, const quint32 height)
+{
+ QImage image(width, height, QImage::Format_ARGB32);
+
+ quint8 colors[ColorCount];
+ qint16 tmp;
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ for (int i = 0; i < ColorCount; i++) {
+ s >> tmp;
+ colors[i] = (tmp + 0x7FFF) >> 8;
+ }
+ line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
+ }
+ }
+
+ return image;
+}
+
+// TODO: this seems incorrect
+static QImage readCxV8U8(QDataStream &s, const quint32 width, const quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ qint8 v, u;
+ s >> v >> u;
+
+ const quint8 vn = v + 128, un = u + 128;
+
+ const double vd = vn / 127.5 - 1.0, ud = un / 127.5 - 1.0;
+ const quint8 c = 255 * sqrt(1.0 - vd * vd - ud * ud);
+ line[x] = qRgb(vn, un, c);
+ }
+ }
+
+ return image;
+}
+
+static QImage readPaletteImage(QDataStream & s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_Indexed8);
+ for (int i = 0; i < 256; ++i) {
+ quint8 r, g, b, a;
+ s >> r >> g >> b >> a;
+ image.setColor(i, qRgba(r, g, b, a));
+ }
+
+ for (quint32 y = 0; y < height; y++) {
+ for (quint32 x = 0; x < width; x++) {
+ quint8 index;
+ s >> index;
+ image.setPixel(x, y, index);
+ }
+ }
+
+ return image;
+}
+
+static QImage readARGB16(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_ARGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ quint8 colors[ColorCount];
+ for (int i = 0; i < ColorCount; ++i) {
+ quint16 color;
+ s >> color;
+ colors[i] = quint8(color >> 8);
+ }
+ line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
+ }
+ }
+
+ return image;
+}
+
+static QImage readV8U8(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ qint8 v, u;
+ s >> v >> u;
+ line[x] = qRgb(v + 128, u + 128, 255);
+ }
+ }
+
+ return image;
+}
+
+static QImage readL6V5U5(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_ARGB32);
+
+ quint16 tmp;
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ s >> tmp;
+ quint8 r = qint8((tmp & 0x001f) >> 0) * 0xff/0x1f + 128;
+ quint8 g = qint8((tmp & 0x03e0) >> 5) * 0xff/0x1f + 128;
+ quint8 b = quint8((tmp & 0xfc00) >> 10) * 0xff/0x3f;
+ line[x] = qRgba(r, g, 0xff, b);
+ }
+ }
+ return image;
+}
+
+static QImage readX8L8V8U8(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_ARGB32);
+
+ quint8 a, l;
+ qint8 v, u;
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ s >> v >> u >> a >> l;
+ line[x] = qRgba(v + 128, u + 128, 255, a);
+ }
+ }
+
+ return image;
+}
+
+static QImage readQ8W8V8U8(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_ARGB32);
+
+ quint8 colors[ColorCount];
+ qint8 tmp;
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ for (int i = 0; i < ColorCount; i++) {
+ s >> tmp;
+ colors[i] = tmp + 128;
+ }
+ line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
+ }
+ }
+
+ return image;
+}
+
+static QImage readV16U16(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ qint16 v, u;
+ s >> v >> u;
+ v = (v + 0x8000) >> 8;
+ u = (u + 0x8000) >> 8;
+ line[x] = qRgb(v, u, 255);
+ }
+ }
+
+ return image;
+}
+
+static QImage readA2W10V10U10(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_ARGB32);
+
+ quint32 tmp;
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ s >> tmp;
+ quint8 r = qint8((tmp & 0x3ff00000) >> 20 >> 2) + 128;
+ quint8 g = qint8((tmp & 0x000ffc00) >> 10 >> 2) + 128;
+ quint8 b = qint8((tmp & 0x000003ff) >> 0 >> 2) + 128;
+ quint8 a = 0xff * ((tmp & 0xc0000000) >> 30) / 3;
+ // dunno why we should swap b and r here
+ line[x] = qRgba(b, g, r, a);
+ }
+ }
+
+ return image;
+}
+
+static QImage readUYVY(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ quint8 uyvy[4];
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width - 1; ) {
+ s >> uyvy[0] >> uyvy[1] >> uyvy[2] >> uyvy[3];
+ line[x++] = yuv2rgb(uyvy[1], uyvy[0], uyvy[2]);
+ line[x++] = yuv2rgb(uyvy[3], uyvy[0], uyvy[2]);
+ }
+ if (width % 2 == 1) {
+ s >> uyvy[0] >> uyvy[1] >> uyvy[2] >> uyvy[3];
+ line[width - 1] = yuv2rgb(uyvy[1], uyvy[0], uyvy[2]);
+ }
+ }
+
+ return image;
+}
+
+static QImage readR8G8B8G8(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+ quint8 rgbg[4];
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width - 1; ) {
+ s >> rgbg[1] >> rgbg[0] >> rgbg[3] >> rgbg[2];
+ line[x++] = qRgb(rgbg[0], rgbg[1], rgbg[2]);
+ line[x++] = qRgb(rgbg[0], rgbg[3], rgbg[2]);
+ }
+ if (width % 2 == 1) {
+ s >> rgbg[1] >> rgbg[0] >> rgbg[3] >> rgbg[2];
+ line[width - 1] = qRgb(rgbg[0], rgbg[1], rgbg[2]);
+ }
+ }
+
+ return image;
+}
+
+static QImage readYUY2(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ quint8 yuyv[4];
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width - 1; ) {
+ s >> yuyv[0] >> yuyv[1] >> yuyv[2] >> yuyv[3];
+ line[x++] = yuv2rgb(yuyv[0], yuyv[1], yuyv[3]);
+ line[x++] = yuv2rgb(yuyv[2], yuyv[1], yuyv[3]);
+ }
+ if (width % 2 == 1) {
+ s >> yuyv[0] >> yuyv[1] >> yuyv[2] >> yuyv[3];
+ line[width - 1] = yuv2rgb(yuyv[2], yuyv[1], yuyv[3]);
+ }
+ }
+
+ return image;
+}
+
+static QImage readG8R8G8B8(QDataStream &s, quint32 width, quint32 height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+ quint8 grgb[4];
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width - 1; ) {
+ s >> grgb[1] >> grgb[0] >> grgb[3] >> grgb[2];
+ line[x++] = qRgb(grgb[1], grgb[0], grgb[3]);
+ line[x++] = qRgb(grgb[1], grgb[2], grgb[3]);
+ }
+ if (width % 2 == 1) {
+ s >> grgb[1] >> grgb[0] >> grgb[3] >> grgb[2];
+ line[width - 1] = qRgb(grgb[1], grgb[0], grgb[3]);
+ }
+ }
+
+ return image;
+}
+
+static QImage readA2R10G10B10(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height)
+{
+ QImage image = readUnsignedImage(s, dds, width, height, true);
+ for (quint32 y = 0; y < height; y++) {
+ QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
+ for (quint32 x = 0; x < width; x++) {
+ QRgb pixel = image.pixel(x, y);
+ line[x] = qRgba(qBlue(pixel), qGreen(pixel), qRed(pixel), qAlpha(pixel));
+ }
+ }
+ return image;
+}
+
+static QImage readLayer(QDataStream & s, const DDSHeader & dds, const int format, quint32 width, quint32 height)
+{
+ if (width * height == 0)
+ return QImage();
+
+ switch (format) {
+ case FormatR8G8B8:
+ case FormatX8R8G8B8:
+ case FormatR5G6B5:
+ case FormatR3G3B2:
+ case FormatX1R5G5B5:
+ case FormatX4R4G4B4:
+ case FormatX8B8G8R8:
+ case FormatG16R16:
+ case FormatL8:
+ case FormatL16:
+ return readUnsignedImage(s, dds, width, height, false);
+ case FormatA8R8G8B8:
+ case FormatA1R5G5B5:
+ case FormatA4R4G4B4:
+ case FormatA8:
+ case FormatA8R3G3B2:
+ case FormatA8B8G8R8:
+ case FormatA8L8:
+ case FormatA4L4:
+ return readUnsignedImage(s, dds, width, height, true);
+ case FormatA2R10G10B10:
+ case FormatA2B10G10R10:
+ return readA2R10G10B10(s, dds, width, height);
+ case FormatP8:
+ return readPaletteImage(s, width, height);
+ case FormatA8P8:
+ break;
+ case FormatA16B16G16R16:
+ return readARGB16(s, width, height);
+ case FormatV8U8:
+ return readV8U8(s, width, height);
+ case FormatL6V5U5:
+ return readL6V5U5(s, width, height);
+ case FormatX8L8V8U8:
+ return readX8L8V8U8(s, width, height);
+ case FormatQ8W8V8U8:
+ return readQ8W8V8U8(s, width, height);
+ case FormatV16U16:
+ return readV16U16(s, width, height);
+ case FormatA2W10V10U10:
+ return readA2W10V10U10(s, width, height);
+ case FormatUYVY:
+ return readUYVY(s, width, height);
+ case FormatR8G8B8G8:
+ return readR8G8B8G8(s, width, height);
+ case FormatYUY2:
+ return readYUY2(s, width, height);
+ case FormatG8R8G8B8:
+ return readG8R8G8B8(s, width, height);
+ case FormatDXT1:
+ return readDXT1(s, width, height);
+ case FormatDXT2:
+ return readDXT2(s, width, height);
+ case FormatDXT3:
+ return readDXT3(s, width, height);
+ case FormatDXT4:
+ return readDXT4(s, width, height);
+ case FormatDXT5:
+ return readDXT5(s, width, height);
+ case FormatRXGB:
+ return readRXGB(s, width, height);
+ case FormatATI2:
+ return readATI2(s, width, height);
+ case FormatR16F:
+ return readR16F(s, width, height);
+ case FormatG16R16F:
+ return readRG16F(s, width, height);
+ case FormatA16B16G16R16F:
+ return readARGB16F(s, width, height);
+ case FormatR32F:
+ return readR32F(s, width, height);
+ case FormatG32R32F:
+ return readRG32F(s, width, height);
+ case FormatA32B32G32R32F:
+ return readARGB32F(s, width, height);
+ case FormatD16Lockable:
+ case FormatD32:
+ case FormatD15S1:
+ case FormatD24S8:
+ case FormatD24X8:
+ case FormatD24X4S4:
+ case FormatD16:
+ case FormatD32FLockable:
+ case FormatD24FS8:
+ case FormatD32Lockable:
+ case FormatS8Lockable:
+ case FormatVertexData:
+ case FormatIndex16:
+ case FormatIndex32:
+ break;
+ case FormatQ16W16V16U16:
+ return readQ16W16V16U16(s, width, height);
+ case FormatMulti2ARGB8:
+ break;
+ case FormatCxV8U8:
+ return readCxV8U8(s, width, height);
+ case FormatA1:
+ case FormatA2B10G10R10_XR_BIAS:
+ case FormatBinaryBuffer:
+ case FormatLast:
+ break;
+ }
+
+ return QImage();
+}
+
+static inline QImage readTexture(QDataStream & s, const DDSHeader & dds, const int format, const int mipmapLevel)
+{
+ quint32 width = dds.width / (1 << mipmapLevel);
+ quint32 height = dds.height / (1 << mipmapLevel);
+ return readLayer(s, dds, format, width, height);
+}
+
+static qint64 mipmapSize(const DDSHeader &dds, const int format, const int level)
+{
+ quint32 w = dds.width/(1 << level);
+ quint32 h = dds.height/(1 << level);
+
+ switch (format) {
+ case FormatR8G8B8:
+ case FormatX8R8G8B8:
+ case FormatR5G6B5:
+ case FormatX1R5G5B5:
+ case FormatX4R4G4B4:
+ case FormatX8B8G8R8:
+ case FormatG16R16:
+ case FormatL8:
+ case FormatL16:
+ return w * h * dds.pixelFormat.rgbBitCount / 8;
+ case FormatA8R8G8B8:
+ case FormatA1R5G5B5:
+ case FormatA4R4G4B4:
+ case FormatA8:
+ case FormatA8R3G3B2:
+ case FormatA2B10G10R10:
+ case FormatA8B8G8R8:
+ case FormatA2R10G10B10:
+ case FormatA8L8:
+ case FormatA4L4:
+ return w * h * dds.pixelFormat.rgbBitCount / 8;
+ case FormatP8:
+ return 256 + w * h * 8;
+ case FormatA16B16G16R16:
+ return w * h * 4 * 2;
+ case FormatA8P8:
+ break;
+ case FormatV8U8:
+ case FormatL6V5U5:
+ return w * h * 2;
+ case FormatX8L8V8U8:
+ case FormatQ8W8V8U8:
+ case FormatV16U16:
+ case FormatA2W10V10U10:
+ return w * h * 4;
+ case FormatUYVY:
+ case FormatR8G8B8G8:
+ case FormatYUY2:
+ case FormatG8R8G8B8:
+ return w * h * 2;
+ case FormatDXT1:
+ return ((w + 3)/4) * ((h + 3)/4) * 8;
+ case FormatDXT2:
+ case FormatDXT3:
+ case FormatDXT4:
+ case FormatDXT5:
+ return ((w + 3)/4) * ((h + 3)/4) * 16;
+ case FormatD16Lockable:
+ case FormatD32:
+ case FormatD15S1:
+ case FormatD24S8:
+ case FormatD24X8:
+ case FormatD24X4S4:
+ case FormatD16:
+ case FormatD32FLockable:
+ case FormatD24FS8:
+ case FormatD32Lockable:
+ case FormatS8Lockable:
+ case FormatVertexData:
+ case FormatIndex16:
+ case FormatIndex32:
+ break;
+ case FormatQ16W16V16U16:
+ return w * h * 4 * 2;
+ case FormatMulti2ARGB8:
+ break;
+ case FormatR16F:
+ return w * h * 1 * 2;
+ case FormatG16R16F:
+ return w * h * 2 * 2;
+ case FormatA16B16G16R16F:
+ return w * h * 4 * 2;
+ case FormatR32F:
+ return w * h * 1 * 4;
+ case FormatG32R32F:
+ return w * h * 2 * 4;
+ case FormatA32B32G32R32F:
+ return w * h * 4 * 4;
+ case FormatCxV8U8:
+ return w * h * 2;
+ case FormatA1:
+ case FormatA2B10G10R10_XR_BIAS:
+ case FormatBinaryBuffer:
+ case FormatLast:
+ break;
+ }
+
+ return 0;
+}
+
+static qint64 mipmapOffset(const DDSHeader &dds, const int format, const int level)
+{
+ qint64 result = 0;
+ for (int i = 0; i < level; ++i)
+ result += mipmapSize(dds, format, i);
+ return result;
+}
+
+static QImage readCubeMap(QDataStream & s, const DDSHeader & dds, const int fmt)
+{
+ QImage::Format format = hasAlpha(dds) ? QImage::Format_ARGB32 : QImage::Format_RGB32;
+ QImage image(4 * dds.width, 3 * dds.height, format);
+
+ image.fill(0);
+
+ for (int i = 0; i < 6; i++) {
+ if (!(dds.caps2 & faceFlags[i]))
+ continue; // Skip face.
+
+ const QImage face = readLayer(s, dds, fmt, dds.width, dds.height);
+
+ // Compute face offsets.
+ int offset_x = faceOffsets[i].x * dds.width;
+ int offset_y = faceOffsets[i].y * dds.height;
+
+ // Copy face on the image.
+ for (quint32 y = 0; y < dds.height; y++) {
+ const QRgb *src = reinterpret_cast<const QRgb *>(face.scanLine(y));
+ QRgb *dst = reinterpret_cast<QRgb *>(image.scanLine(y + offset_y)) + offset_x;
+ memcpy(dst, src, sizeof(QRgb) * dds.width);
+ }
+ }
+
+ return image;
+}
+
+QDDSHandler::QDDSHandler() :
+ m_currentImage(0),
+ m_headerCached(false)
+{
+}
+
+bool QDDSHandler::canRead() const
+{
+ if (canRead(device())) {
+ setFormat(QByteArrayLiteral("dds"));
+ return true;
+ }
+ return false;
+}
+
+bool QDDSHandler::read(QImage *outImage)
+{
+ if (!ensureHeaderCached() || device()->isSequential())
+ return false;
+
+ qint64 pos = headerSize + mipmapOffset(m_header, m_format, m_currentImage);
+ if (!device()->seek(pos))
+ return false;
+ QDataStream s(device());
+ s.setByteOrder(QDataStream::LittleEndian);
+
+ QImage image = isCubeMap(m_header) ?
+ readCubeMap(s, m_header, m_format) :
+ readTexture(s, m_header, m_format, m_currentImage);
+
+ bool ok = s.status() == QDataStream::Ok && !image.isNull();
+ if (ok)
+ *outImage = image;
+ return ok;
+}
+
+bool QDDSHandler::write(const QImage &outImage)
+{
+ QDataStream s( device() );
+ s.setByteOrder(QDataStream::LittleEndian);
+
+ // Filling header
+ DDSHeader dds;
+ // Filling header
+ dds.magic = ddsMagic;
+ dds.size = 124;
+ dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
+ DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat;
+ dds.height = outImage.height();
+ dds.width = outImage.width();
+ dds.pitchOrLinearSize = 128;
+ dds.depth = 0;
+ dds.mipMapCount = 0;
+ for (int i = 0; i < DDSHeader::ReservedCount; i++)
+ dds.reserved1[i] = 0;
+ dds.caps = DDSHeader::CapsTexture;
+ dds.caps2 = 0;
+ dds.caps3 = 0;
+ dds.caps4 = 0;
+ dds.reserved2 = 0;
+
+ // Filling pixelformat
+ dds.pixelFormat.size = 32;
+ dds.pixelFormat.flags = DDSPixelFormat::FlagAlphaPixels | DDSPixelFormat::FlagRGB;
+ dds.pixelFormat.fourCC = 0;
+ dds.pixelFormat.rgbBitCount = 32;
+ dds.pixelFormat.aBitMask = 0xff000000;
+ dds.pixelFormat.rBitMask = 0x00ff0000;
+ dds.pixelFormat.gBitMask = 0x0000ff00;
+ dds.pixelFormat.bBitMask = 0x000000ff;
+
+ s << dds;
+ for (int width = 0; width < outImage.width(); width++) {
+ for (int height = 0; height < outImage.height(); height++) {
+ QRgb pixel = outImage.pixel(height, width);;
+ quint32 color;
+ quint8 alpha = qAlpha(pixel);
+ quint8 red = qRed(pixel);
+ quint8 green = qGreen(pixel);
+ quint8 blue = qBlue(pixel);
+ color = (alpha << 24) + (red << 16) + (green << 8) + blue;
+ s << color;
+ }
+ }
+
+ return true;
+}
+
+int QDDSHandler::imageCount() const
+{
+ if (!ensureHeaderCached())
+ return 0;
+
+ return qMax<quint32>(1, m_header.mipMapCount);
+}
+
+bool QDDSHandler::jumpToImage(int imageNumber)
+{
+ if (imageNumber >= imageCount())
+ return false;
+
+ m_currentImage = imageNumber;
+ return true;
+}
+
+QByteArray QDDSHandler::name() const
+{
+ return "dds";
+}
+
+bool QDDSHandler::canRead(QIODevice *device)
+{
+ if (!device) {
+ qWarning() << "DDSHandler::canRead() called with no device";
+ return false;
+ }
+
+ if (device->isSequential())
+ return false;
+
+ return device->peek(4) == "DDS ";
+}
+
+bool QDDSHandler::ensureHeaderCached() const
+{
+ if (m_headerCached)
+ return true;
+
+ if (device()->isSequential()) {
+ qWarning() << "Sequential devices are not supported";
+ return false;
+ }
+
+ qint64 oldPos = device()->pos();
+ device()->seek(0);
+
+ QDDSHandler *that = const_cast<QDDSHandler *>(this);
+ QDataStream s(device());
+ s.setByteOrder(QDataStream::LittleEndian);
+ s >> that->m_header;
+ if (m_header.pixelFormat.fourCC == dx10Magic)
+ s >> that->m_header10;
+
+ device()->seek(oldPos);
+
+ if (s.status() != QDataStream::Ok)
+ return false;
+
+ if (!verifyHeader(m_header))
+ return false;
+
+ that->m_format = getFormat(m_header);
+
+ m_headerCached = true;
+ return true;
+}
+
+bool QDDSHandler::verifyHeader(const DDSHeader &dds) const
+{
+ quint32 flags = dds.flags;
+ quint32 requiredFlags = DDSHeader::FlagCaps | DDSHeader::FlagHeight
+ | DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat;
+ if ((flags & requiredFlags) != requiredFlags) {
+ qWarning() << "Wrong dds.flags - not all required flags present. "
+ "Actual flags :" << flags;
+ return false;
+ }
+
+ if (dds.size != ddsSize) {
+ qWarning() << "Wrong dds.size: actual =" << dds.size
+ << "expected =" << ddsSize;
+ return false;
+ }
+
+ if (dds.pixelFormat.size != pixelFormatSize) {
+ qWarning() << "Wrong dds.pixelFormat.size: actual =" << dds.pixelFormat.size
+ << "expected =" << pixelFormatSize;
+ return false;
+ }
+
+ if (dds.width > INT_MAX || dds.height > INT_MAX) {
+ qWarning() << "Can't read image with w/h bigger than INT_MAX";
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE