summaryrefslogtreecommitdiffstats
path: root/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp')
-rw-r--r--src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp1262
1 files changed, 1262 insertions, 0 deletions
diff --git a/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp b/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp
new file mode 100644
index 00000000..29e75a89
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureBMP.cpp
@@ -0,0 +1,1262 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 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$
+**
+****************************************************************************/
+// ==========================================================
+// BMP Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Martin Weber (martweb@gmx.net)
+// - Herve Drolon (drolon@infonie.fr)
+// - Michal Novotny (michal@etc.cz)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "Qt3DSRenderLoadedTextureFreeImageCompat.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+static const BYTE RLE_COMMAND = 0;
+static const BYTE RLE_ENDOFLINE = 0;
+static const BYTE RLE_ENDOFBITMAP = 1;
+static const BYTE RLE_DELTA = 2;
+
+static const BYTE BI_RGB = 0;
+static const BYTE BI_RLE8 = 1;
+static const BYTE BI_RLE4 = 2;
+static const BYTE BI_BITFIELDS = 3;
+
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagBITMAPCOREHEADER
+{
+ DWORD bcSize;
+ WORD bcWidth;
+ WORD bcHeight;
+ WORD bcPlanes;
+ WORD bcBitCnt;
+} BITMAPCOREHEADER, *PBITMAPCOREHEADER;
+
+typedef struct tagBITMAPINFOOS2_1X_HEADER
+{
+ DWORD biSize;
+ WORD biWidth;
+ WORD biHeight;
+ WORD biPlanes;
+ WORD biBitCount;
+} BITMAPINFOOS2_1X_HEADER, *PBITMAPINFOOS2_1X_HEADER;
+
+typedef struct tagBITMAPFILEHEADER
+{
+ WORD bfType;
+ DWORD bfSize;
+ WORD bfReserved1;
+ WORD bfReserved2;
+ DWORD bfOffBits;
+} BITMAPFILEHEADER, *PBITMAPFILEHEADER;
+
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void SwapInfoHeader(BITMAPINFOHEADER *header)
+{
+ SwapLong(&header->biSize);
+ SwapLong((DWORD *)&header->biWidth);
+ SwapLong((DWORD *)&header->biHeight);
+ SwapShort(&header->biPlanes);
+ SwapShort(&header->biBitCount);
+ SwapLong(&header->biCompression);
+ SwapLong(&header->biSizeImage);
+ SwapLong((DWORD *)&header->biXPelsPerMeter);
+ SwapLong((DWORD *)&header->biYPelsPerMeter);
+ SwapLong(&header->biClrUsed);
+ SwapLong(&header->biClrImportant);
+}
+
+static void SwapCoreHeader(BITMAPCOREHEADER *header)
+{
+ SwapLong(&header->bcSize);
+ SwapShort(&header->bcWidth);
+ SwapShort(&header->bcHeight);
+ SwapShort(&header->bcPlanes);
+ SwapShort(&header->bcBitCnt);
+}
+
+static void SwapOS21XHeader(BITMAPINFOOS2_1X_HEADER *header)
+{
+ SwapLong(&header->biSize);
+ SwapShort(&header->biWidth);
+ SwapShort(&header->biHeight);
+ SwapShort(&header->biPlanes);
+ SwapShort(&header->biBitCount);
+}
+
+static void SwapFileHeader(BITMAPFILEHEADER *header)
+{
+ SwapShort(&header->bfType);
+ SwapLong(&header->bfSize);
+ SwapShort(&header->bfReserved1);
+ SwapShort(&header->bfReserved2);
+ SwapLong(&header->bfOffBits);
+}
+#endif
+
+// --------------------------------------------------------------------------
+
+/**
+Load uncompressed image pixels for 1-, 4-, 8-, 16-, 24- and 32-bit dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param dib Image to be loaded
+@param height Image height
+@param pitch Image pitch
+@param bit_count Image bit-depth (1-, 4-, 8-, 16-, 24- or 32-bit)
+*/
+static void LoadPixelData(FreeImageIO *io, fi_handle handle, FIBITMAP *dib, int height, int pitch,
+ int bit_count)
+{
+ (void)bit_count;
+ // Load pixel data
+ // NB: height can be < 0 for BMP data
+ if (height > 0) {
+ io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle);
+ } else {
+ int positiveHeight = abs(height);
+ for (int c = 0; c < positiveHeight; ++c) {
+ io->read_proc((void *)FreeImage_GetScanLine(dib, positiveHeight - c - 1), pitch, 1,
+ handle);
+ }
+ }
+
+// swap as needed
+#ifdef FREEIMAGE_BIGENDIAN
+ if (bit_count == 16) {
+ for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
+ for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ SwapShort(pixel);
+ pixel++;
+ }
+ }
+ }
+#endif
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ if (bit_count == 24 || bit_count == 32) {
+ for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *pixel = FreeImage_GetScanLine(dib, y);
+ for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ INPLACESWAP(pixel[0], pixel[2]);
+ pixel += (bit_count >> 3);
+ }
+ }
+ }
+#endif
+}
+
+/**
+Load image pixels for 4-bit RLE compressed dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param width Image width
+@param height Image height
+@param dib Image to be loaded
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL LoadPixelDataRLE4(FreeImageIO *io, fi_handle handle, int width, int height,
+ FIBITMAP *dib)
+{
+ int status_byte = 0;
+ BYTE second_byte = 0;
+ int bits = 0;
+
+ BYTE *pixels = NULL; // temporary 8-bit buffer
+
+ try {
+ height = abs(height);
+
+ pixels = (BYTE *)malloc(width * height * sizeof(BYTE));
+ if (!pixels)
+ throw(1);
+ memset(pixels, 0, width * height * sizeof(BYTE));
+
+ BYTE *q = pixels;
+ BYTE *end = pixels + height * width;
+
+ for (int scanline = 0; scanline < height;) {
+ if (q < pixels || q >= end) {
+ break;
+ }
+ if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ if (status_byte != 0) {
+ status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
+ // Encoded mode
+ if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ for (int i = 0; i < status_byte; i++) {
+ *q++ = (BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
+ }
+ bits += status_byte;
+ } else {
+ // Escape mode
+ if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ switch (status_byte) {
+ case RLE_ENDOFLINE: {
+ // End of line
+ bits = 0;
+ scanline++;
+ q = pixels + scanline * width;
+ } break;
+
+ case RLE_ENDOFBITMAP:
+ // End of bitmap
+ q = end;
+ break;
+
+ case RLE_DELTA: {
+ // read the delta values
+
+ BYTE delta_x = 0;
+ BYTE delta_y = 0;
+
+ if (io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ if (io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+
+ // apply them
+
+ bits += delta_x;
+ scanline += delta_y;
+ q = pixels + scanline * width + bits;
+ } break;
+
+ default: {
+ // Absolute mode
+ status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
+ for (int i = 0; i < status_byte; i++) {
+ if ((i & 0x01) == 0) {
+ if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ }
+ *q++ =
+ (BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
+ }
+ bits += status_byte;
+ // Read pad byte
+ if (((status_byte & 0x03) == 1) || ((status_byte & 0x03) == 2)) {
+ BYTE padding = 0;
+ if (io->read_proc(&padding, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ }
+ } break;
+ }
+ }
+ }
+
+ {
+ // Convert to 4-bit
+ for (int y = 0; y < height; y++) {
+ const BYTE *src = (BYTE *)pixels + y * width;
+ BYTE *dst = FreeImage_GetScanLine(dib, y);
+
+ BOOL hinibble = TRUE;
+
+ for (int cols = 0; cols < width; cols++) {
+ if (hinibble) {
+ dst[cols >> 1] = (src[cols] << 4);
+ } else {
+ dst[cols >> 1] |= src[cols];
+ }
+
+ hinibble = !hinibble;
+ }
+ }
+ }
+
+ free(pixels);
+
+ return TRUE;
+
+ } catch (int) {
+ if (pixels)
+ free(pixels);
+ return FALSE;
+ }
+}
+
+/**
+Load image pixels for 8-bit RLE compressed dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param width Image width
+@param height Image height
+@param dib Image to be loaded
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL LoadPixelDataRLE8(FreeImageIO *io, fi_handle handle, int width, int height,
+ FIBITMAP *dib)
+{
+ BYTE status_byte = 0;
+ BYTE second_byte = 0;
+ int scanline = 0;
+ int bits = 0;
+
+ for (;;) {
+ if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ switch (status_byte) {
+ case RLE_COMMAND:
+ if (io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ switch (status_byte) {
+ case RLE_ENDOFLINE:
+ bits = 0;
+ scanline++;
+ break;
+
+ case RLE_ENDOFBITMAP:
+ return TRUE;
+
+ case RLE_DELTA: {
+ // read the delta values
+
+ BYTE delta_x = 0;
+ BYTE delta_y = 0;
+
+ if (io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+ if (io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ // apply them
+
+ bits += delta_x;
+ scanline += delta_y;
+
+ break;
+ }
+
+ default: {
+ if (scanline >= abs(height)) {
+ return TRUE;
+ }
+
+ int count = MIN((int)status_byte, width - bits);
+
+ BYTE *sline = FreeImage_GetScanLine(dib, scanline);
+
+ if (io->read_proc((void *)(sline + bits), sizeof(BYTE) * count, 1, handle) != 1) {
+ return FALSE;
+ }
+
+ // align run length to even number of bytes
+
+ if ((status_byte & 1) == 1) {
+ if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+ }
+
+ bits += status_byte;
+
+ break;
+ }
+ }
+
+ break;
+
+ default: {
+ if (scanline >= abs(height)) {
+ return TRUE;
+ }
+
+ int count = MIN((int)status_byte, width - bits);
+
+ BYTE *sline = FreeImage_GetScanLine(dib, scanline);
+
+ if (io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ for (int i = 0; i < count; i++) {
+ *(sline + bits) = second_byte;
+
+ bits++;
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags,
+ unsigned bitmap_bits_offset)
+{
+ FIBITMAP *dib = NULL;
+ (void)flags;
+ try {
+ // load the info header
+
+ BITMAPINFOHEADER bih;
+
+ io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(&bih);
+#endif
+
+ // keep some general information about the bitmap
+
+ int used_colors = bih.biClrUsed;
+ int width = bih.biWidth;
+ int height = bih.biHeight; // WARNING: height can be < 0 => check each call using 'height'
+ // as a parameter
+ int alloc_height = abs(height);
+ int bit_count = bih.biBitCount;
+ int compression = bih.biCompression;
+ int pitch = CalculatePitch(CalculateLine(width, bit_count));
+
+ switch (bit_count) {
+ case 1:
+ case 4:
+ case 8: {
+ if ((used_colors <= 0) || (used_colors > CalculateUsedPaletteEntries(bit_count)))
+ used_colors = CalculateUsedPaletteEntries(bit_count);
+
+ // allocate enough memory to hold the bitmap (header, palette, pixels) and read the
+ // palette
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, io);
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // load the palette
+
+ io->read_proc(FreeImage_GetPalette(dib), used_colors * sizeof(RGBQUAD), 1, handle);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ for (int i = 0; i < used_colors; i++) {
+ INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
+ }
+#endif
+
+ // seek to the actual pixel data.
+ // this is needed because sometimes the palette is larger than the entries it contains
+ // predicts
+
+ if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ + (used_colors * sizeof(RGBQUAD))))
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read the pixel data
+
+ switch (compression) {
+ case BI_RGB:
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+ return dib;
+
+ case BI_RLE4:
+ if (LoadPixelDataRLE4(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE4 BMP data";
+ }
+ break;
+
+ case BI_RLE8:
+ if (LoadPixelDataRLE8(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE8 BMP data";
+ }
+ break;
+
+ default:
+ throw "compression type not supported";
+ }
+ } break; // 1-, 4-, 8-bit
+
+ case 16: {
+ if (bih.biCompression == BI_BITFIELDS) {
+ DWORD bitfields[3];
+
+ io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, bitfields[0], bitfields[1],
+ bitfields[2], io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI16_555_RED_MASK,
+ FI16_555_GREEN_MASK, FI16_555_BLUE_MASK, io);
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ } break; // 16-bit
+
+ case 24:
+ case 32: {
+ if (bih.biCompression == BI_BITFIELDS) {
+ DWORD bitfields[3];
+
+ io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, bitfields[0], bitfields[1],
+ bitfields[2], io);
+ } else {
+ if (bit_count == 32) {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ }
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ if (used_colors > 0) {
+ io->seek_proc(handle, used_colors * sizeof(RGBQUAD), SEEK_CUR);
+ } else if ((bih.biCompression != BI_BITFIELDS)
+ && (bitmap_bits_offset
+ > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) {
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+ }
+
+ // read in the bitmap bits
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ // check if the bitmap contains transparency, if so enable it in the header
+
+ return dib;
+ } break; // 24-, 32-bit
+ }
+ } catch (const char *message) {
+ if (dib) {
+ FreeImage_Unload(dib);
+ }
+ if (message) {
+ FreeImage_OutputMessageProc(s_format_id, message, io);
+ }
+ }
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags,
+ unsigned bitmap_bits_offset)
+{
+ FIBITMAP *dib = NULL;
+ (void)flags;
+ try {
+ // load the info header
+
+ BITMAPINFOHEADER bih;
+
+ io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(&bih);
+#endif
+
+ // keep some general information about the bitmap
+
+ int used_colors = bih.biClrUsed;
+ int width = bih.biWidth;
+ int height = bih.biHeight; // WARNING: height can be < 0 => check each read_proc using
+ // 'height' as a parameter
+ int alloc_height = abs(height);
+ int bit_count = bih.biBitCount;
+ int compression = bih.biCompression;
+ int pitch = CalculatePitch(CalculateLine(width, bit_count));
+
+ switch (bit_count) {
+ case 1:
+ case 4:
+ case 8: {
+ if ((used_colors <= 0) || (used_colors > CalculateUsedPaletteEntries(bit_count)))
+ used_colors = CalculateUsedPaletteEntries(bit_count);
+
+ // allocate enough memory to hold the bitmap (header, palette, pixels) and read the
+ // palette
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, io);
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // load the palette
+
+ io->seek_proc(handle, sizeof(BITMAPFILEHEADER) + bih.biSize, SEEK_SET);
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ for (int count = 0; count < used_colors; count++) {
+ FILE_BGR bgr;
+
+ io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
+
+ pal[count].rgbRed = bgr.r;
+ pal[count].rgbGreen = bgr.g;
+ pal[count].rgbBlue = bgr.b;
+ }
+
+ // seek to the actual pixel data.
+ // this is needed because sometimes the palette is larger than the entries it contains
+ // predicts
+
+ if (bitmap_bits_offset
+ > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3)))
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read the pixel data
+
+ switch (compression) {
+ case BI_RGB:
+ // load pixel data
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+ return dib;
+
+ case BI_RLE4:
+ if (LoadPixelDataRLE4(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE4 BMP data";
+ }
+ break;
+
+ case BI_RLE8:
+ if (LoadPixelDataRLE8(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE8 BMP data";
+ }
+ break;
+
+ default:
+ throw "compression type not supported";
+ }
+ }
+
+ case 16: {
+ if (bih.biCompression == 3) {
+ DWORD bitfields[3];
+
+ io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, bitfields[0], bitfields[1],
+ bitfields[2], io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI16_555_RED_MASK,
+ FI16_555_GREEN_MASK, FI16_555_BLUE_MASK, io);
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ if (bitmap_bits_offset
+ > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+ }
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+
+ case 24:
+ case 32: {
+ if (bit_count == 32) {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ if (bitmap_bits_offset
+ > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3)))
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read in the bitmap bits
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+ }
+ } catch (const char *message) {
+ if (dib)
+ FreeImage_Unload(dib);
+
+ FreeImage_OutputMessageProc(s_format_id, message, io);
+ }
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags,
+ unsigned bitmap_bits_offset)
+{
+ FIBITMAP *dib = NULL;
+ (void)flags;
+ try {
+ BITMAPINFOOS2_1X_HEADER bios2_1x;
+
+ io->read_proc(&bios2_1x, sizeof(BITMAPINFOOS2_1X_HEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapOS21XHeader(&bios2_1x);
+#endif
+ // keep some general information about the bitmap
+
+ int used_colors = 0;
+ int width = bios2_1x.biWidth;
+ int height = bios2_1x.biHeight; // WARNING: height can be < 0 => check each read_proc using
+ // 'height' as a parameter
+ int alloc_height = abs(height);
+ int bit_count = bios2_1x.biBitCount;
+ int pitch = CalculatePitch(CalculateLine(width, bit_count));
+
+ switch (bit_count) {
+ case 1:
+ case 4:
+ case 8: {
+ used_colors = CalculateUsedPaletteEntries(bit_count);
+
+ // allocate enough memory to hold the bitmap (header, palette, pixels) and read the
+ // palette
+
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, io);
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information to default values (72 dpi in english units)
+ FreeImage_SetDotsPerMeterX(dib, 2835);
+ FreeImage_SetDotsPerMeterY(dib, 2835);
+
+ // load the palette
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ for (int count = 0; count < used_colors; count++) {
+ FILE_BGR bgr;
+
+ io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
+
+ pal[count].rgbRed = bgr.r;
+ pal[count].rgbGreen = bgr.g;
+ pal[count].rgbBlue = bgr.b;
+ }
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read the pixel data
+
+ // load pixel data
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+
+ case 16: {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI16_555_RED_MASK,
+ FI16_555_GREEN_MASK, FI16_555_BLUE_MASK, io);
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information to default values (72 dpi in english units)
+ FreeImage_SetDotsPerMeterX(dib, 2835);
+ FreeImage_SetDotsPerMeterY(dib, 2835);
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+
+ case 24:
+ case 32: {
+ if (bit_count == 32) {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ } else {
+ dib = FreeImage_Allocate(width, alloc_height, bit_count, FI_RGBA_RED_MASK,
+ FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, io);
+ }
+
+ if (dib == NULL)
+ throw "DIB allocation failed";
+
+ // set resolution information to default values (72 dpi in english units)
+ FreeImage_SetDotsPerMeterX(dib, 2835);
+ FreeImage_SetDotsPerMeterY(dib, 2835);
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ // check if the bitmap contains transparency, if so enable it in the header
+
+ return dib;
+ }
+ }
+ } catch (const char *message) {
+ if (dib)
+ FreeImage_Unload(dib);
+
+ FreeImage_OutputMessageProc(s_format_id, message, io);
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+// ----------------------------------------------------------
+
+static FIBITMAP *DoLoadBMP(FreeImageIO *io, fi_handle handle, int flags)
+{
+ if (handle != NULL) {
+ BITMAPFILEHEADER bitmapfileheader;
+ DWORD type = 0;
+ BYTE magic[2];
+
+ // we use this offset value to make seemingly absolute seeks relative in the file
+
+ long offset_in_file = io->tell_proc(handle);
+
+ // read the magic
+
+ io->read_proc(&magic, sizeof(magic), 1, handle);
+
+ // compare the magic with the number we know
+
+ // somebody put a comment here explaining the purpose of this loop
+ while (memcmp(&magic, "BA", 2) == 0) {
+ io->read_proc(&bitmapfileheader.bfSize, sizeof(DWORD), 1, handle);
+ io->read_proc(&bitmapfileheader.bfReserved1, sizeof(WORD), 1, handle);
+ io->read_proc(&bitmapfileheader.bfReserved2, sizeof(WORD), 1, handle);
+ io->read_proc(&bitmapfileheader.bfOffBits, sizeof(DWORD), 1, handle);
+ io->read_proc(&magic, sizeof(magic), 1, handle);
+ }
+
+ // read the fileheader
+
+ io->seek_proc(handle, (0 - (int)sizeof(magic)), SEEK_CUR);
+ io->read_proc(&bitmapfileheader, (int)sizeof(BITMAPFILEHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapFileHeader(&bitmapfileheader);
+#endif
+
+ // read the first byte of the infoheader
+
+ io->read_proc(&type, sizeof(DWORD), 1, handle);
+ io->seek_proc(handle, 0 - (int)sizeof(DWORD), SEEK_CUR);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapLong(&type);
+#endif
+
+ // call the appropriate load function for the found bitmap type
+
+ if (type == 40)
+ return LoadWindowsBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+
+ if (type == 12)
+ return LoadOS21XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+
+ if (type <= 64)
+ return LoadOS22XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+
+ char buf[256];
+ sprintf(buf, "unknown bmp subtype with id %d", type);
+ FreeImage_OutputMessageProc(s_format_id, buf, io);
+ }
+
+ return NULL;
+}
+
+template <QT3DSU32 TBitWidth>
+struct SPaletteIndexer
+{
+ static inline uint32_t IndexOf(const uint8_t *inData, uint32_t inPos)
+ {
+ uint32_t divisor = 8 / TBitWidth;
+ uint32_t byte = inPos / divisor;
+ uint32_t modulus = inPos % divisor;
+ uint32_t shift = TBitWidth * modulus;
+ uint32_t mask = (1 << TBitWidth) - 1;
+ mask = mask << shift;
+ uint32_t byteData = inData[byte];
+ return (byteData & mask) >> shift;
+ }
+};
+
+template <>
+struct SPaletteIndexer<1>
+{
+ static inline uint32_t IndexOf(const uint8_t *inData, uint32_t inPos)
+ {
+ uint32_t byte = (inPos / 8);
+ uint32_t bit = 1 << (7 - (inPos % 8));
+ uint32_t byteData = inData[byte];
+ return (byteData & bit) ? 1 : 0;
+ }
+};
+
+template <>
+struct SPaletteIndexer<8>
+{
+ static inline uint32_t IndexOf(const uint8_t *inData, uint32_t inPos)
+ {
+ uint32_t byte = inPos;
+ uint32_t bit = 0xFF;
+ uint32_t byteData = inData[byte];
+ return byteData & bit;
+ }
+};
+
+static inline void assignQuad(uint8_t *dest, const RGBQUAD &quad)
+{
+ dest[0] = quad.rgbRed;
+ dest[1] = quad.rgbGreen;
+ dest[2] = quad.rgbBlue;
+}
+
+template <QT3DSU32 bitCount>
+inline void LoadPalettized(bool inFlipY, const RGBQUAD *palette, void *data, uint8_t *newData,
+ int width, int height, int components, int transparentIndex)
+{
+ const uint8_t *oldData = (const uint8_t *)data;
+ int pitch = CalculatePitch(CalculateLine(width, bitCount));
+ for (uint32_t h = 0; h < (uint32_t)height; ++h) {
+ uint32_t relHeight = h;
+ if (inFlipY)
+ relHeight = ((uint32_t)height) - h - 1;
+ for (uint32_t w = 0; w < (uint32_t)width; ++w) {
+ const uint8_t *dataLine = oldData + pitch * h;
+ uint32_t pos = width * relHeight + w;
+ uint32_t paletteIndex = SPaletteIndexer<bitCount>::IndexOf(dataLine, w);
+ const RGBQUAD &theQuad = palette[paletteIndex];
+ uint8_t *writePtr = newData + (pos * components);
+ assignQuad(writePtr, theQuad);
+ if (paletteIndex == (uint32_t)transparentIndex && components == 4) {
+ writePtr[3] = 0;
+ }
+ }
+ }
+}
+
+inline int firstHighBit(int data)
+{
+ if (data == 0)
+ return 0;
+ int idx = 0;
+ while ((data % 2) == 0) {
+ data = data >> 1;
+ ++idx;
+ }
+ return idx;
+}
+
+struct SMaskData
+{
+ uint32_t mask;
+ uint32_t shift;
+ uint32_t max;
+
+ SMaskData(int inMask)
+ {
+ mask = inMask;
+ shift = firstHighBit(mask);
+ max = mask >> shift;
+ }
+
+ inline uint8_t MapColor(uint32_t color) const
+ {
+ uint32_t intermediateValue = (color & mask) >> shift;
+ return (uint8_t)((intermediateValue * 255) / max);
+ }
+};
+
+template <QT3DSU32>
+struct ColorAccess
+{
+};
+
+template <>
+struct ColorAccess<16>
+{
+ static uint32_t GetPixelWidth() { return 2; }
+ static uint32_t GetColor(const char8_t *src)
+ {
+ return (uint32_t) * reinterpret_cast<const QT3DSU16 *>(src);
+ }
+};
+
+template <>
+struct ColorAccess<24>
+{
+ static uint32_t GetPixelWidth() { return 3; }
+ static uint32_t GetColor(const char8_t *src)
+ {
+ return (uint32_t)(*reinterpret_cast<const QT3DSU32 *>(src) & 0xFFFFFF);
+ }
+};
+
+template <>
+struct ColorAccess<32>
+{
+ static uint32_t GetPixelWidth() { return 4; }
+ static uint32_t GetColor(const char8_t *src)
+ {
+ return *reinterpret_cast<const uint32_t *>(src);
+ }
+};
+
+template <QT3DSU32 TBitCount>
+inline void LoadMasked(bool inFlipY, QT3DSI32 *inMasks, void *data, uint8_t *newData, int width,
+ int height)
+{
+ const char8_t *oldData = (const char8_t *)data;
+ SMaskData rMask(inMasks[0]);
+ SMaskData gMask(inMasks[1]);
+ SMaskData bMask(inMasks[2]);
+ for (int h = 0; h < height; ++h) {
+ int relHeight = h;
+ if (inFlipY)
+ relHeight = height - h - 1;
+ for (int w = 0; w < width; ++w) {
+ int pos = width * relHeight + w;
+ const char8_t *readPtr = oldData + (pos * ColorAccess<TBitCount>::GetPixelWidth());
+ uint8_t *writePtr = newData + (pos * 3);
+ uint32_t colorVal = ColorAccess<TBitCount>::GetColor(readPtr);
+ writePtr[0] = rMask.MapColor(colorVal);
+ writePtr[1] = gMask.MapColor(colorVal);
+ writePtr[2] = bMask.MapColor(colorVal);
+ }
+ }
+}
+
+void SLoadedTexture::FreeImagePostProcess(bool inFlipY)
+{
+ // We always convert 32 bit RGBA
+ if (m_ExtendedFormat != ExtendedTextureFormats::NoExtendedFormat) {
+
+ QT3DSU32 stride = 3 * width;
+ format = NVRenderTextureFormats::RGB8;
+ components = 3;
+ if (m_ExtendedFormat == ExtendedTextureFormats::Palettized && m_TransparentPaletteIndex > -1
+ && m_TransparentPaletteIndex < 256) {
+ stride = 4 * width;
+ components = 4;
+ format = NVRenderTextureFormats::RGBA8;
+ }
+ QT3DSU32 byteSize = height * stride;
+ uint8_t *newData =
+ (uint8_t *)m_Allocator.allocate(byteSize, "texture data", __FILE__, __LINE__);
+ if (format == NVRenderTextureFormats::RGBA8)
+ memSet(newData, 255, byteSize);
+ switch (m_ExtendedFormat) {
+ case ExtendedTextureFormats::Palettized: {
+ RGBQUAD *palette = (RGBQUAD *)m_Palette;
+ switch (m_BitCount) {
+ case 1:
+ LoadPalettized<1>(inFlipY, palette, data, newData, width, height, components,
+ m_TransparentPaletteIndex);
+ break;
+ case 2:
+ LoadPalettized<2>(inFlipY, palette, data, newData, width, height, components,
+ m_TransparentPaletteIndex);
+ break;
+ case 4:
+ LoadPalettized<4>(inFlipY, palette, data, newData, width, height, components,
+ m_TransparentPaletteIndex);
+ break;
+ case 8:
+ LoadPalettized<8>(inFlipY, palette, data, newData, width, height, components,
+ m_TransparentPaletteIndex);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ memSet(newData, 0, byteSize);
+ break;
+ }
+ } break;
+ case ExtendedTextureFormats::CustomRGB: {
+ switch (m_BitCount) {
+ case 16:
+ LoadMasked<16>(inFlipY, m_CustomMasks, data, newData, width, height);
+ break;
+ case 24:
+ LoadMasked<24>(inFlipY, m_CustomMasks, data, newData, width, height);
+ break;
+ case 32:
+ LoadMasked<32>(inFlipY, m_CustomMasks, data, newData, width, height);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ memSet(newData, 0, byteSize);
+ break;
+ }
+ } break;
+ default:
+ QT3DS_ASSERT(false);
+ memSet(newData, 0, byteSize);
+ break;
+ }
+ m_Allocator.deallocate(data);
+ if (m_Palette)
+ m_Allocator.deallocate(m_Palette);
+ data = newData;
+ m_Palette = NULL;
+ m_BitCount = 0;
+ this->dataSizeInBytes = byteSize;
+ m_ExtendedFormat = ExtendedTextureFormats::NoExtendedFormat;
+ }
+}
+
+SLoadedTexture *SLoadedTexture::LoadBMP(ISeekableIOStream &inStream, bool inFlipY,
+ NVFoundationBase &inFnd,
+ qt3ds::render::NVRenderContextType renderContextType)
+{
+ Q_UNUSED(renderContextType)
+ FreeImageIO theIO(inFnd.getAllocator(), inFnd);
+ SLoadedTexture *retval = DoLoadBMP(&theIO, &inStream, 0);
+ if (retval)
+ retval->FreeImagePostProcess(inFlipY);
+ return retval;
+}