summaryrefslogtreecommitdiffstats
path: root/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp')
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp695
1 files changed, 695 insertions, 0 deletions
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp
new file mode 100644
index 0000000..19d41d1
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureDDS.cpp
@@ -0,0 +1,695 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSRenderLoadedTextureDDS.h"
+#include "Qt3DSRenderLoadedTextureFreeImageCompat.h"
+
+using namespace qt3ds::render;
+
+namespace qt3ds {
+namespace render {
+
+ static int s_exception_string;
+
+ //================================================================================
+ // DXT data-layout structure definitions.
+ typedef struct
+ {
+ QT3DSU16 col0; // 16-bit 565 interpolant endpoints
+ QT3DSU16 col1;
+ QT3DSU8 row[4]; // 4x4 * 2bpp color-index == 4 bytes.
+ } DXTColBlock;
+
+ typedef struct
+ {
+ QT3DSU16 row[4]; // 4x4 * 4bpp alpha == 8 bytes. (pure 4-bit alpha values)
+ } DXT3AlphaBlock;
+
+ typedef struct
+ {
+ QT3DSU8 alpha0; // 8-bit alpha interpolant endpoints
+ QT3DSU8 alpha1;
+ QT3DSU8 row[6]; // 4x4 * 3bpp alpha-index == 48bits == 6 bytes.
+ } DXT5AlphaBlock;
+
+ typedef struct
+ {
+ QT3DSU8 red;
+ QT3DSU8 green;
+ QT3DSU8 blue;
+ QT3DSU8 alpha;
+ } Color8888;
+
+//================================================================================
+// Various DDS file defines
+
+#define DDSD_CAPS 0x00000001l
+#define DDSD_HEIGHT 0x00000002l
+#define DDSD_WIDTH 0x00000004l
+#define DDSD_PIXELFORMAT 0x00001000l
+#define DDS_ALPHAPIXELS 0x00000001l
+#define DDS_FOURCC 0x00000004l
+#define DDS_PITCH 0x00000008l
+#define DDS_COMPLEX 0x00000008l
+#define DDS_RGB 0x00000040l
+#define DDS_TEXTURE 0x00001000l
+#define DDS_MIPMAPCOUNT 0x00020000l
+#define DDS_LINEARSIZE 0x00080000l
+#define DDS_VOLUME 0x00200000l
+#define DDS_MIPMAP 0x00400000l
+#define DDS_DEPTH 0x00800000l
+
+#define DDS_CUBEMAP 0x00000200L
+#define DDS_CUBEMAP_POSITIVEX 0x00000400L
+#define DDS_CUBEMAP_NEGATIVEX 0x00000800L
+#define DDS_CUBEMAP_POSITIVEY 0x00001000L
+#define DDS_CUBEMAP_NEGATIVEY 0x00002000L
+#define DDS_CUBEMAP_POSITIVEZ 0x00004000L
+#define DDS_CUBEMAP_NEGATIVEZ 0x00008000L
+
+#define FOURCC_DXT1 0x31545844 //(MAKEFOURCC('D','X','T','1'))
+#define FOURCC_DXT3 0x33545844 //(MAKEFOURCC('D','X','T','3'))
+#define FOURCC_DXT5 0x35545844 //(MAKEFOURCC('D','X','T','5'))
+
+#define DDS_MAGIC_FLIPPED 0x0F7166ED
+
+ //================================================================================
+ // DDS file format structures.
+ typedef struct _DDS_PIXELFORMAT
+ {
+ QT3DSU32 dwSize;
+ QT3DSU32 dwFlags;
+ QT3DSU32 dwFourCC;
+ QT3DSU32 dwRGBBitCount;
+ QT3DSU32 dwRBitMask;
+ QT3DSU32 dwGBitMask;
+ QT3DSU32 dwBBitMask;
+ QT3DSU32 dwABitMask;
+ } DDS_PIXELFORMAT;
+
+ typedef struct _DDS_HEADER
+ {
+ QT3DSU32 dwSize;
+ QT3DSU32 dwFlags;
+ QT3DSU32 dwHeight;
+ QT3DSU32 dwWidth;
+ QT3DSU32 dwPitchOrLinearSize;
+ QT3DSU32 dwDepth;
+ QT3DSU32 dwMipMapCount;
+ QT3DSU32 dwReserved1[11];
+ DDS_PIXELFORMAT ddspf;
+ QT3DSU32 dwCaps1;
+ QT3DSU32 dwCaps2;
+ QT3DSU32 dwReserved2[3];
+ } DDS_HEADER;
+
+ //================================================================================
+ // helper functions
+ //================================================================================
+
+ // helper macros.
+ static inline void NvSwapChar(QT3DSU8 &a, QT3DSU8 &b)
+ {
+ QT3DSU8 tmp;
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ static inline void NvSwapShort(QT3DSU16 &a, QT3DSU16 &b)
+ {
+ QT3DSU16 tmp;
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ //================================================================================
+ //================================================================================
+ static void flip_blocks_dxtc1(DXTColBlock *line, QT3DSI32 numBlocks)
+ {
+ DXTColBlock *curblock = line;
+ QT3DSI32 i;
+
+ for (i = 0; i < numBlocks; i++) {
+ NvSwapChar(curblock->row[0], curblock->row[3]);
+ NvSwapChar(curblock->row[1], curblock->row[2]);
+ curblock++;
+ }
+ }
+
+ //================================================================================
+ //================================================================================
+ static void flip_blocks_dxtc3(DXTColBlock *line, QT3DSI32 numBlocks)
+ {
+ DXTColBlock *curblock = line;
+ DXT3AlphaBlock *alphablock;
+ QT3DSI32 i;
+
+ for (i = 0; i < numBlocks; i++) {
+ alphablock = (DXT3AlphaBlock *)curblock;
+
+ NvSwapShort(alphablock->row[0], alphablock->row[3]);
+ NvSwapShort(alphablock->row[1], alphablock->row[2]);
+ curblock++;
+
+ NvSwapChar(curblock->row[0], curblock->row[3]);
+ NvSwapChar(curblock->row[1], curblock->row[2]);
+ curblock++;
+ }
+ }
+
+ static void flip_dxt5_alpha(DXT5AlphaBlock *block)
+ {
+ QT3DSI8 gBits[4][4];
+
+ const QT3DSU32 mask = 0x00000007; // bits = 00 00 01 11
+ QT3DSU32 bits = 0;
+ memcpy(&bits, &block->row[0], sizeof(QT3DSI8) * 3);
+
+ gBits[0][0] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[0][1] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[0][2] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[0][3] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[1][0] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[1][1] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[1][2] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[1][3] = (QT3DSI8)(bits & mask);
+
+ bits = 0;
+ memcpy(&bits, &block->row[3], sizeof(QT3DSI8) * 3);
+
+ gBits[2][0] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[2][1] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[2][2] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[2][3] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[3][0] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[3][1] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[3][2] = (QT3DSI8)(bits & mask);
+ bits >>= 3;
+ gBits[3][3] = (QT3DSI8)(bits & mask);
+
+ bits = (gBits[3][0] << 0) | (gBits[3][1] << 3) | (gBits[3][2] << 6) | (gBits[3][3] << 9)
+ | (gBits[2][0] << 12) | (gBits[2][1] << 15) | (gBits[2][2] << 18) | (gBits[2][3] << 21);
+ memcpy(&block->row[0], &bits, 3);
+
+ bits = (gBits[1][0] << 0) | (gBits[1][1] << 3) | (gBits[1][2] << 6) | (gBits[1][3] << 9)
+ | (gBits[0][0] << 12) | (gBits[0][1] << 15) | (gBits[0][2] << 18) | (gBits[0][3] << 21);
+ memcpy(&block->row[3], &bits, 3);
+ }
+
+ static void flip_blocks_dxtc5(DXTColBlock *line, QT3DSI32 numBlocks)
+ {
+ DXTColBlock *curblock = line;
+ DXT5AlphaBlock *alphablock;
+ QT3DSI32 i;
+
+ for (i = 0; i < numBlocks; i++) {
+ alphablock = (DXT5AlphaBlock *)curblock;
+
+ flip_dxt5_alpha(alphablock);
+ curblock++;
+
+ NvSwapChar(curblock->row[0], curblock->row[3]);
+ NvSwapChar(curblock->row[1], curblock->row[2]);
+ curblock++;
+ }
+ }
+
+ static void flip_data_vertical(FreeImageIO *io, QT3DSI8 *image, QT3DSI32 width, QT3DSI32 height,
+ Qt3DSDDSImage *info)
+ {
+ if (info->compressed) {
+ QT3DSI32 linesize, j;
+ DXTColBlock *top;
+ DXTColBlock *bottom;
+ QT3DSI8 *tmp;
+ void (*flipblocks)(DXTColBlock *, QT3DSI32) = NULL;
+ QT3DSI32 xblocks = width / 4;
+ QT3DSI32 yblocks = height / 4;
+ QT3DSI32 blocksize;
+
+ switch (info->format) {
+ case qt3ds::render::NVRenderTextureFormats::RGBA_DXT1:
+ blocksize = 8;
+ flipblocks = &flip_blocks_dxtc1;
+ break;
+ case qt3ds::render::NVRenderTextureFormats::RGBA_DXT3:
+ blocksize = 16;
+ flipblocks = &flip_blocks_dxtc3;
+ break;
+ case qt3ds::render::NVRenderTextureFormats::RGBA_DXT5:
+ blocksize = 16;
+ flipblocks = &flip_blocks_dxtc5;
+ break;
+ default:
+ return;
+ }
+
+ linesize = xblocks * blocksize;
+ tmp = (QT3DSI8 *)QT3DS_ALLOC(io->m_Allocator, linesize, "flip_data_vertical compressed");
+
+ for (j = 0; j < (yblocks >> 1); j++) {
+ top = (DXTColBlock *)(void *)(image + j * linesize);
+ bottom = (DXTColBlock *)(void *)(image + (((yblocks - j) - 1) * linesize));
+
+ (*flipblocks)(top, xblocks);
+ (*flipblocks)(bottom, xblocks);
+
+ memcpy(tmp, bottom, linesize);
+ memcpy(bottom, top, linesize);
+ memcpy(top, tmp, linesize);
+ }
+
+ // Catch the middle row of blocks if there is one
+ // The loop above will skip the middle row
+ if (yblocks & 0x01) {
+ DXTColBlock *middle = (DXTColBlock *)(void *)(image + (yblocks >> 1) * linesize);
+ (*flipblocks)(middle, xblocks);
+ }
+
+ QT3DS_FREE(io->m_Allocator, tmp);
+ } else {
+ QT3DSI32 linesize = width * info->bytesPerPixel;
+ QT3DSI32 j;
+ QT3DSI8 *top;
+ QT3DSI8 *bottom;
+ QT3DSI8 *tmp;
+
+ // much simpler - just compute the line length and swap each row
+ tmp = (QT3DSI8 *)QT3DS_ALLOC(io->m_Allocator, linesize, "flip_data_vertical");
+ ;
+
+ for (j = 0; j < (height >> 1); j++) {
+ top = (QT3DSI8 *)(image + j * linesize);
+ bottom = (QT3DSI8 *)(image + (((height - j) - 1) * linesize));
+
+ memcpy(tmp, bottom, linesize);
+ memcpy(bottom, top, linesize);
+ memcpy(top, tmp, linesize);
+ }
+
+ QT3DS_FREE(io->m_Allocator, tmp);
+ }
+ }
+
+ static QT3DSI32 size_image(QT3DSI32 width, QT3DSI32 height, const Qt3DSDDSImage *image)
+ {
+ if (image->compressed) {
+ return ((width + 3) / 4) * ((height + 3) / 4)
+ * (image->format == qt3ds::render::NVRenderTextureFormats::RGBA_DXT1 ? 8 : 16);
+ } else {
+ return width * height * image->bytesPerPixel;
+ }
+ }
+
+ static QT3DSI32 total_image_data_size(Qt3DSDDSImage *image)
+ {
+ QT3DSI32 i, j, index = 0, size = 0, w, h;
+ QT3DSI32 cubeCount = image->cubemap ? 6 : 1;
+
+ for (j = 0; j < cubeCount; j++) {
+ w = image->width;
+ h = image->height;
+
+ for (i = 0; i < image->numMipmaps; i++) // account for base plus each mip
+ {
+ image->size[index] = size_image(w, h, image);
+ image->mipwidth[index] = w;
+ image->mipheight[index] = h;
+ size += image->size[index];
+ if (w != 1) {
+ w >>= 1;
+ }
+ if (h != 1) {
+ h >>= 1;
+ }
+
+ index++;
+ }
+ }
+
+ return (size);
+ }
+
+ void *Qt3DSDDSAllocDataBlock(FreeImageIO *io, Qt3DSDDSImage *image)
+ {
+ if (image) {
+ QT3DSI32 i;
+ QT3DSI32 size = total_image_data_size(image);
+ image->dataBlock =
+ QT3DS_ALLOC(io->m_Allocator, size,
+ "Qt3DSDDSAllocDataBlock"); // no need to calloc, as we fill every bit...
+ if (image->dataBlock == NULL) {
+ return NULL;
+ }
+
+ image->data[0] = image->dataBlock;
+
+ QT3DSI32 planes = image->numMipmaps * (image->cubemap ? 6 : 1);
+
+ for (i = 1; i < planes; i++) // account for base plus each mip
+ {
+ image->data[i] =
+ (void *)(((size_t)(image->data[i - 1])) + (size_t)image->size[i - 1]);
+ }
+
+ return (image->dataBlock); // in case caller wants to sanity check...
+ }
+ return (NULL);
+ }
+
+ static FIBITMAP *DoLoadDDS(FreeImageIO *io, IInStream &inStream, QT3DSI32 flipVertical)
+ {
+ FIBITMAP *dib = NULL;
+ DDS_HEADER ddsh;
+ QT3DSI8 filecode[4];
+ Qt3DSDDSImage *image = NULL;
+ bool needsBGRASwap = false;
+ ;
+ bool isAllreadyFlipped = false;
+
+ try {
+ // check file code
+ inStream.Read(filecode, 4);
+ if (memcmp(filecode, "DDS ", 4)) {
+ throw "Invalid DDS file";
+ }
+
+ image = (Qt3DSDDSImage *)QT3DS_ALLOC(io->m_Allocator, sizeof(Qt3DSDDSImage), "DoLoadDDS");
+ if (image == NULL) {
+ throw "Qt3DSDDSImage allocation failed";
+ }
+ memset(image, 0, sizeof(Qt3DSDDSImage));
+
+ // read in DDS header
+ inStream.Read(&ddsh, 1);
+
+ // check if image is a cubempap
+ if (ddsh.dwCaps2 & DDS_CUBEMAP) {
+ const QT3DSI32 allFaces = DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_POSITIVEY
+ | DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEX | DDS_CUBEMAP_NEGATIVEY
+ | DDS_CUBEMAP_NEGATIVEZ;
+
+ if ((ddsh.dwCaps2 & allFaces) != allFaces) {
+ throw "Not all cubemap faces defined - not supported";
+ }
+
+ image->cubemap = 1;
+ } else {
+ image->cubemap = 0;
+ }
+
+ // check if image is a volume texture
+ if ((ddsh.dwCaps2 & DDS_VOLUME) && (ddsh.dwDepth > 0)) {
+ throw "Volume textures not supported";
+ }
+
+ // allocated the memory for the structure we return
+ dib = QT3DS_NEW(io->m_Allocator, SLoadedTexture)(io->m_Allocator);
+ if (dib == NULL) {
+ throw "DIB allocation failed";
+ }
+
+ // figure out what the image format is
+ if (ddsh.ddspf.dwFlags & DDS_FOURCC) {
+ switch (ddsh.ddspf.dwFourCC) {
+ case FOURCC_DXT1:
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT1;
+ image->components = 3;
+ image->compressed = 1;
+ image->alpha = 0; // Ugh - for backwards compatibility
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGB_DXT1;
+ break;
+ case FOURCC_DXT3:
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT3;
+ image->components = 4;
+ image->compressed = 1;
+ image->alpha = 1;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT3;
+ break;
+ case FOURCC_DXT5:
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT5;
+ image->components = 4;
+ image->compressed = 1;
+ image->alpha = 1;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGBA_DXT5;
+ break;
+ default:
+ throw "Unsupported FOURCC code";
+ }
+ } else {
+ // Check for a supported pixel format
+ if ((ddsh.ddspf.dwRGBBitCount == 32) && (ddsh.ddspf.dwRBitMask == 0x000000FF)
+ && (ddsh.ddspf.dwGBitMask == 0x0000FF00)
+ && (ddsh.ddspf.dwBBitMask == 0x00FF0000)
+ && (ddsh.ddspf.dwABitMask == 0xFF000000)) {
+ // We support D3D's A8B8G8R8, which is actually RGBA in linear
+ // memory, equivalent to GL's RGBA
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ image->components = 4;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 4;
+ image->alpha = 1;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 32) && (ddsh.ddspf.dwRBitMask == 0x00FF0000)
+ && (ddsh.ddspf.dwGBitMask == 0x0000FF00)
+ && (ddsh.ddspf.dwBBitMask == 0x000000FF)
+ && (ddsh.ddspf.dwABitMask == 0xFF000000)) {
+ // We support D3D's A8R8G8B8, which is actually BGRA in linear
+ // memory, need to be
+ image->format = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ image->components = 4;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 4;
+ image->alpha = 1;
+ image->compressed = 0;
+ needsBGRASwap = true;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGBA8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 16) && (ddsh.ddspf.dwRBitMask == 0x0000F800)
+ && (ddsh.ddspf.dwGBitMask == 0x000007E0)
+ && (ddsh.ddspf.dwBBitMask == 0x0000001F)
+ && (ddsh.ddspf.dwABitMask == 0x00000000)) {
+ // We support D3D's R5G6B5, which is actually RGB in linear
+ // memory. It is equivalent to GL's GL_UNSIGNED_SHORT_5_6_5
+ image->format = qt3ds::render::NVRenderTextureFormats::RGB8;
+ image->components = 3;
+ image->alpha = 0;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU16;
+ image->bytesPerPixel = 2;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::RGB8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 8) && (ddsh.ddspf.dwRBitMask == 0x00000000)
+ && (ddsh.ddspf.dwGBitMask == 0x00000000)
+ && (ddsh.ddspf.dwBBitMask == 0x00000000)
+ && (ddsh.ddspf.dwABitMask == 0x000000FF)) {
+ // We support D3D's A8
+ image->format = qt3ds::render::NVRenderTextureFormats::Alpha8;
+ image->components = 1;
+ image->alpha = 1;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 1;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::Alpha8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 8) && (ddsh.ddspf.dwRBitMask == 0x000000FF)
+ && (ddsh.ddspf.dwGBitMask == 0x00000000)
+ && (ddsh.ddspf.dwBBitMask == 0x00000000)
+ && (ddsh.ddspf.dwABitMask == 0x00000000)) {
+ // We support D3D's L8 (flagged as 8 bits of red only)
+ image->format = qt3ds::render::NVRenderTextureFormats::Luminance8;
+ image->components = 1;
+ image->alpha = 0;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 1;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::Luminance8;
+ } else if ((ddsh.ddspf.dwRGBBitCount == 16) && (ddsh.ddspf.dwRBitMask == 0x000000FF)
+ && (ddsh.ddspf.dwGBitMask == 0x00000000)
+ && (ddsh.ddspf.dwBBitMask == 0x00000000)
+ && (ddsh.ddspf.dwABitMask == 0x0000FF00)) {
+ // We support D3D's A8L8 (flagged as 8 bits of red and 8 bits of alpha)
+ image->format = qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8;
+ image->components = 2;
+ image->alpha = 1;
+ image->componentFormat = qt3ds::render::NVRenderComponentTypes::QT3DSU8;
+ image->bytesPerPixel = 2;
+ image->compressed = 0;
+ dib->format = qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8;
+ } else {
+ throw "not a DXTC or supported RGB(A) format image";
+ }
+ }
+
+ // detect flagging to indicate this texture was stored in a y-inverted fashion
+ if (!(ddsh.dwFlags & DDS_LINEARSIZE)) {
+ if (ddsh.dwPitchOrLinearSize == DDS_MAGIC_FLIPPED) {
+ isAllreadyFlipped = true;
+ }
+ }
+
+ flipVertical = (isAllreadyFlipped != (flipVertical ? true : false)) ? 1 : 0;
+
+ // store primary surface width/height/numMipmaps
+ image->width = ddsh.dwWidth;
+ image->height = ddsh.dwHeight;
+ image->numMipmaps = ddsh.dwFlags & DDS_MIPMAPCOUNT ? ddsh.dwMipMapCount : 1;
+
+ if (image->numMipmaps > QT3DS_DDS_MAX_MIPMAPS) {
+ throw "Too many mipmaps: max 16";
+ }
+
+ // allocate the meta datablock for all mip storage.
+ Qt3DSDDSAllocDataBlock(io, image);
+ if (image->dataBlock == NULL) {
+ throw "Failed to allocate memory for image data storage";
+ }
+
+ dib->width = image->width;
+ dib->height = image->height;
+ dib->dds = image;
+
+ QT3DSI32 faces = image->cubemap ? 6 : 1;
+
+ QT3DSI32 index = 0;
+ for (QT3DSI32 j = 0; j < faces; j++) {
+ // load all surfaces for the image
+ QT3DSI32 width = image->width;
+ QT3DSI32 height = image->height;
+
+ for (QT3DSI32 i = 0; i < image->numMipmaps; i++) {
+ // Get the size, read in the data.
+ inStream.Read(
+ NVDataRef<QT3DSU8>((QT3DSU8 *)image->data[index], (QT3DSU32)image->size[index]));
+
+ // Flip in Y for OpenGL if needed
+ if (flipVertical)
+ flip_data_vertical(io, (QT3DSI8 *)image->data[index], width, height, image);
+
+ // shrink to next power of 2
+ width >>= 1;
+ height >>= 1;
+
+ if (!width)
+ width = 1;
+
+ if (!height)
+ height = 1;
+
+ // make sure DXT isn't <4 on a side...
+ if (image->compressed) {
+ if (width < 4)
+ width = 4;
+ if (height < 4)
+ height = 4;
+ }
+
+ index++;
+ }
+ }
+
+ if (needsBGRASwap) {
+ QT3DSI32 index = 0;
+ QT3DSI32 k;
+
+ for (k = 0; k < faces; k++) {
+ QT3DSI32 width = image->width;
+ QT3DSI32 height = image->height;
+
+ for (QT3DSI32 i = 0; i < image->numMipmaps; i++) {
+ QT3DSI8 *data = (QT3DSI8 *)(image->data[index]);
+ QT3DSI32 pixels = width * height;
+ QT3DSI32 j;
+
+ for (j = 0; j < pixels; j++) {
+ QT3DSI8 temp = data[0];
+ data[0] = data[2];
+ data[2] = temp;
+
+ data += 4;
+ }
+
+ // shrink to next power of 2
+ width >>= 1;
+ height >>= 1;
+
+ if (!width)
+ width = 1;
+
+ if (!height)
+ height = 1;
+
+ index++;
+ }
+ }
+ }
+ } catch (const char *message) {
+ if (image) {
+ if (image->dataBlock)
+ QT3DS_FREE(io->m_Allocator, image->dataBlock);
+
+ QT3DS_FREE(io->m_Allocator, image);
+ }
+ if (dib) {
+ FreeImage_Unload(dib);
+ }
+ if (message) {
+ FreeImage_OutputMessageProc(s_exception_string, message, io);
+ }
+ }
+
+ return dib;
+ }
+
+ SLoadedTexture *SLoadedTexture::LoadDDS(IInStream &inStream, QT3DSI32 flipVertical,
+ NVFoundationBase &inFnd,
+ qt3ds::render::NVRenderContextType renderContextType)
+ {
+ Q_UNUSED(renderContextType)
+ FreeImageIO theIO(inFnd.getAllocator(), inFnd);
+ SLoadedTexture *retval = DoLoadDDS(&theIO, inStream, flipVertical);
+
+ return retval;
+ }
+}
+}