diff options
Diffstat (limited to 'src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp')
-rw-r--r-- | src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp | 851 |
1 files changed, 851 insertions, 0 deletions
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp new file mode 100644 index 0000000..ece4d3a --- /dev/null +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTextureGIF.cpp @@ -0,0 +1,851 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +// ========================================================== +// GIF Loader and Writer +// +// Design and implementation by +// - Ryan Rubley <ryan@lostreality.org> +// - Raphael Gaquer <raphael.gaquer@alcer.com> +// +// 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! +// ========================================================== +#ifdef _MSC_VER +#pragma warning(disable : 4786) // identifier was truncated to 'number' characters +#endif + +#include "Qt3DSRenderLoadedTextureFreeImageCompat.h" +#include "EASTL/vector.h" +#include "EASTL/string.h" +// ========================================================== +// Metadata declarations +// ========================================================== + +#define GIF_DISPOSAL_UNSPECIFIED 0 +#define GIF_DISPOSAL_LEAVE 1 +#define GIF_DISPOSAL_BACKGROUND 2 +#define GIF_DISPOSAL_PREVIOUS 3 + +// ========================================================== +// Constant/Typedef declarations +// ========================================================== + +struct GIFinfo +{ + BOOL read; + // only really used when reading + size_t global_color_table_offset; + int global_color_table_size; + BYTE background_color; + eastl::vector<size_t> application_extension_offsets; + eastl::vector<size_t> comment_extension_offsets; + eastl::vector<size_t> graphic_control_extension_offsets; + eastl::vector<size_t> image_descriptor_offsets; + + GIFinfo() + : read(0) + , global_color_table_offset(0) + , global_color_table_size(0) + , background_color(0) + { + } +}; + +struct PageInfo +{ + PageInfo(int d, int l, int t, int w, int h) + { + disposal_method = d; + left = (WORD)l; + top = (WORD)t; + width = (WORD)w; + height = (WORD)h; + } + int disposal_method; + WORD left, top, width, height; +}; + +// GIF defines a max of 12 bits per code +#define MAX_LZW_CODE 4096 + +class StringTable +{ +public: + StringTable(); + ~StringTable(); + void Initialize(int minCodeSize); + BYTE *FillInputBuffer(int len); + void CompressStart(int bpp, int width); + int CompressEnd(BYTE *buf); // 0-4 bytes + bool Compress(BYTE *buf, int *len); + bool Decompress(BYTE *buf, int *len); + void Done(void); + +protected: + bool m_done; + + int m_minCodeSize, m_clearCode, m_endCode, m_nextCode; + + int m_bpp, m_slack; // Compressor information + + int m_prefix; // Compressor state variable + int m_codeSize, m_codeMask; // Compressor/Decompressor state variables + int m_oldCode; // Decompressor state variable + int m_partial, m_partialSize; // Compressor/Decompressor bit buffer + + int firstPixelPassed; // A specific flag that indicates if the first pixel + // of the whole image had already been read + + eastl::string m_strings[MAX_LZW_CODE]; // This is what is really the "string table" data for the + // Decompressor + int *m_strmap; + + // input buffer + BYTE *m_buffer; + int m_bufferSize, m_bufferRealSize, m_bufferPos, m_bufferShift; + + void ClearCompressorTable(void); + void ClearDecompressorTable(void); +}; + +#define GIF_PACKED_LSD_HAVEGCT 0x80 +#define GIF_PACKED_LSD_COLORRES 0x70 +#define GIF_PACKED_LSD_GCTSORTED 0x08 +#define GIF_PACKED_LSD_GCTSIZE 0x07 +#define GIF_PACKED_ID_HAVELCT 0x80 +#define GIF_PACKED_ID_INTERLACED 0x40 +#define GIF_PACKED_ID_LCTSORTED 0x20 +#define GIF_PACKED_ID_RESERVED 0x18 +#define GIF_PACKED_ID_LCTSIZE 0x07 +#define GIF_PACKED_GCE_RESERVED 0xE0 +#define GIF_PACKED_GCE_DISPOSAL 0x1C +#define GIF_PACKED_GCE_WAITINPUT 0x02 +#define GIF_PACKED_GCE_HAVETRANS 0x01 + +#define GIF_BLOCK_IMAGE_DESCRIPTOR 0x2C +#define GIF_BLOCK_EXTENSION 0x21 +#define GIF_BLOCK_TRAILER 0x3B + +#define GIF_EXT_PLAINTEXT 0x01 +#define GIF_EXT_GRAPHIC_CONTROL 0xF9 +#define GIF_EXT_COMMENT 0xFE +#define GIF_EXT_APPLICATION 0xFF + +#define GIF_INTERLACE_PASSES 4 +static int g_GifInterlaceOffset[GIF_INTERLACE_PASSES] = { 0, 4, 2, 1 }; +static int g_GifInterlaceIncrement[GIF_INTERLACE_PASSES] = { 8, 8, 4, 2 }; + +StringTable::StringTable() +{ + m_buffer = NULL; + firstPixelPassed = 0; // Still no pixel read + // Maximum number of entries in the map is MAX_LZW_CODE * 256 + // (aka 2**12 * 2**8 => a 20 bits key) + // This Map could be optmized to only handle MAX_LZW_CODE * 2**(m_bpp) + m_strmap = (int *)new int[1 << 20]; +} + +StringTable::~StringTable() +{ + if (m_buffer != NULL) { + delete[] m_buffer; + } + if (m_strmap != NULL) { + delete[] m_strmap; + m_strmap = NULL; + } +} + +void StringTable::Initialize(int minCodeSize) +{ + m_done = false; + + m_bpp = 8; + m_minCodeSize = minCodeSize; + m_clearCode = 1 << m_minCodeSize; + if (m_clearCode > MAX_LZW_CODE) { + m_clearCode = MAX_LZW_CODE; + } + m_endCode = m_clearCode + 1; + + m_partial = 0; + m_partialSize = 0; + + m_bufferSize = 0; + ClearCompressorTable(); + ClearDecompressorTable(); +} + +BYTE *StringTable::FillInputBuffer(int len) +{ + if (m_buffer == NULL) { + m_buffer = new BYTE[len]; + m_bufferRealSize = len; + } else if (len > m_bufferRealSize) { + delete[] m_buffer; + m_buffer = new BYTE[len]; + m_bufferRealSize = len; + } + m_bufferSize = len; + m_bufferPos = 0; + m_bufferShift = 8 - m_bpp; + return m_buffer; +} + +void StringTable::CompressStart(int bpp, int width) +{ + m_bpp = bpp; + m_slack = (8 - ((width * bpp) % 8)) % 8; + + m_partial |= m_clearCode << m_partialSize; + m_partialSize += m_codeSize; + ClearCompressorTable(); +} + +int StringTable::CompressEnd(BYTE *buf) +{ + int len = 0; + + // output code for remaining prefix + m_partial |= m_prefix << m_partialSize; + m_partialSize += m_codeSize; + while (m_partialSize >= 8) { + *buf++ = (BYTE)m_partial; + m_partial >>= 8; + m_partialSize -= 8; + len++; + } + + // add the end of information code and flush the entire buffer out + m_partial |= m_endCode << m_partialSize; + m_partialSize += m_codeSize; + while (m_partialSize > 0) { + *buf++ = (BYTE)m_partial; + m_partial >>= 8; + m_partialSize -= 8; + len++; + } + + // most this can be is 4 bytes. 7 bits in m_partial to start + 12 for the + // last code + 12 for the end code = 31 bits total. + return len; +} + +bool StringTable::Compress(BYTE *buf, int *len) +{ + if (m_bufferSize == 0 || m_done) { + return false; + } + + int mask = (1 << m_bpp) - 1; + BYTE *bufpos = buf; + while (m_bufferPos < m_bufferSize) { + // get the current pixel value + char ch = (char)((m_buffer[m_bufferPos] >> m_bufferShift) & mask); + + // The next prefix is : + // <the previous LZW code (on 12 bits << 8)> | <the code of the current pixel (on 8 bits)> + int nextprefix = (((m_prefix) << 8) & 0xFFF00) + (ch & 0x000FF); + if (firstPixelPassed) { + + if (m_strmap[nextprefix] > 0) { + m_prefix = m_strmap[nextprefix]; + } else { + m_partial |= m_prefix << m_partialSize; + m_partialSize += m_codeSize; + // grab full bytes for the output buffer + while (m_partialSize >= 8 && bufpos - buf < *len) { + *bufpos++ = (BYTE)m_partial; + m_partial >>= 8; + m_partialSize -= 8; + } + + // add the code to the "table map" + m_strmap[nextprefix] = m_nextCode; + + // increment the next highest valid code, increase the code size + if (m_nextCode == (1 << m_codeSize)) { + m_codeSize++; + } + m_nextCode++; + + // if we're out of codes, restart the string table + if (m_nextCode == MAX_LZW_CODE) { + m_partial |= m_clearCode << m_partialSize; + m_partialSize += m_codeSize; + ClearCompressorTable(); + } + + // Only keep the 8 lowest bits (prevent problems with "negative chars") + m_prefix = ch & 0x000FF; + } + + // increment to the next pixel + if (m_bufferShift > 0 + && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack)) { + m_bufferShift -= m_bpp; + } else { + m_bufferPos++; + m_bufferShift = 8 - m_bpp; + } + + // jump out here if the output buffer is full + if (bufpos - buf == *len) { + return true; + } + + } else { + // Specific behavior for the first pixel of the whole image + + firstPixelPassed = 1; + // Only keep the 8 lowest bits (prevent problems with "negative chars") + m_prefix = ch & 0x000FF; + + // increment to the next pixel + if (m_bufferShift > 0 + && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack)) { + m_bufferShift -= m_bpp; + } else { + m_bufferPos++; + m_bufferShift = 8 - m_bpp; + } + + // jump out here if the output buffer is full + if (bufpos - buf == *len) { + return true; + } + } + } + + m_bufferSize = 0; + *len = (int)(bufpos - buf); + + return true; +} + +bool StringTable::Decompress(BYTE *buf, int *len) +{ + if (m_bufferSize == 0 || m_done) { + return false; + } + + BYTE *bufpos = buf; + for (; m_bufferPos < m_bufferSize; m_bufferPos++) { + m_partial |= (int)m_buffer[m_bufferPos] << m_partialSize; + m_partialSize += 8; + while (m_partialSize >= m_codeSize) { + int code = m_partial & m_codeMask; + m_partial >>= m_codeSize; + m_partialSize -= m_codeSize; + + if (code > m_nextCode || (m_nextCode == MAX_LZW_CODE && code != m_clearCode) + || code == m_endCode) { + m_done = true; + *len = (int)(bufpos - buf); + return true; + } + if (code == m_clearCode) { + ClearDecompressorTable(); + continue; + } + + // add new string to string table, if not the first pass since a clear code + if (m_oldCode != MAX_LZW_CODE) { + m_strings[m_nextCode] = + m_strings[m_oldCode] + m_strings[code == m_nextCode ? m_oldCode : code][0]; + } + + if ((int)m_strings[code].size() > *len - (bufpos - buf)) { + // out of space, stuff the code back in for next time + m_partial <<= m_codeSize; + m_partialSize += m_codeSize; + m_partial |= code; + m_bufferPos++; + *len = (int)(bufpos - buf); + return true; + } + + // output the string into the buffer + memcpy(bufpos, m_strings[code].data(), m_strings[code].size()); + bufpos += m_strings[code].size(); + + // increment the next highest valid code, add a bit to the mask if we need to increase + // the code size + if (m_oldCode != MAX_LZW_CODE && m_nextCode < MAX_LZW_CODE) { + if (++m_nextCode < MAX_LZW_CODE) { + if ((m_nextCode & m_codeMask) == 0) { + m_codeSize++; + m_codeMask |= m_nextCode; + } + } + } + + m_oldCode = code; + } + } + + m_bufferSize = 0; + *len = (int)(bufpos - buf); + + return true; +} + +void StringTable::Done(void) +{ + m_done = true; +} + +void StringTable::ClearCompressorTable(void) +{ + if (m_strmap) { + memset(m_strmap, 0xFF, sizeof(unsigned int) * (1 << 20)); + } + m_nextCode = m_endCode + 1; + + m_prefix = 0; + m_codeSize = m_minCodeSize + 1; +} + +void StringTable::ClearDecompressorTable(void) +{ + for (int i = 0; i < m_clearCode; i++) { + m_strings[i].resize(1); + m_strings[i][0] = (char)i; + } + m_nextCode = m_endCode + 1; + + m_codeSize = m_minCodeSize + 1; + m_codeMask = (1 << m_codeSize) - 1; + m_oldCode = MAX_LZW_CODE; +} + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ========================================================== +// Plugin Implementation +// ========================================================== + +static BOOL DLL_CALLCONV Validate(FreeImageIO *io, fi_handle handle) +{ + char buf[6]; + if (io->read_proc(buf, 6, 1, handle) < 1) { + return FALSE; + } + + BOOL bResult = FALSE; + if (!strncmp(buf, "GIF", 3)) { + if (buf[3] >= '0' && buf[3] <= '9' && buf[4] >= '0' && buf[4] <= '9' && buf[5] >= 'a' + && buf[5] <= 'z') { + bResult = TRUE; + } + } + + io->seek_proc(handle, -6, SEEK_CUR); + + return bResult; +} + +// ---------------------------------------------------------- + +static void *DLL_CALLCONV Open(FreeImageIO *io, fi_handle handle) +{ + GIFinfo *info = new GIFinfo; + if (info == NULL) { + return NULL; + } + BOOL read = TRUE; + + // 25/02/2008 MDA: Not safe to memset GIFinfo structure with VS 2008 (safe iterators), + // perform initialization in constructor instead. + // memset(info, 0, sizeof(GIFinfo)); + + info->read = read; + try { + // Header + if (!Validate(io, handle)) { + throw "Not a GIF file"; + } + io->seek_proc(handle, 6, SEEK_CUR); + + // Logical Screen Descriptor + io->seek_proc(handle, 4, SEEK_CUR); + BYTE packed; + if (io->read_proc(&packed, 1, 1, handle) < 1) { + throw "EOF reading Logical Screen Descriptor"; + } + if (io->read_proc(&info->background_color, 1, 1, handle) < 1) { + throw "EOF reading Logical Screen Descriptor"; + } + io->seek_proc(handle, 1, SEEK_CUR); + + // Global Color Table + if (packed & GIF_PACKED_LSD_HAVEGCT) { + info->global_color_table_offset = io->tell_proc(handle); + info->global_color_table_size = 2 << (packed & GIF_PACKED_LSD_GCTSIZE); + io->seek_proc(handle, 3 * info->global_color_table_size, SEEK_CUR); + } + + // Scan through all the rest of the blocks, saving offsets + size_t gce_offset = 0; + BYTE block = 0; + while (block != GIF_BLOCK_TRAILER) { + if (io->read_proc(&block, 1, 1, handle) < 1) { + throw "EOF reading blocks"; + } + if (block == GIF_BLOCK_IMAGE_DESCRIPTOR) { + info->image_descriptor_offsets.push_back(io->tell_proc(handle)); + // GCE may be 0, meaning no GCE preceded this ID + info->graphic_control_extension_offsets.push_back(gce_offset); + gce_offset = 0; + + io->seek_proc(handle, 8, SEEK_CUR); + if (io->read_proc(&packed, 1, 1, handle) < 1) { + throw "EOF reading Image Descriptor"; + } + + // Local Color Table + if (packed & GIF_PACKED_ID_HAVELCT) { + io->seek_proc(handle, 3 * (2 << (packed & GIF_PACKED_ID_LCTSIZE)), SEEK_CUR); + } + + // LZW Minimum Code Size + io->seek_proc(handle, 1, SEEK_CUR); + } else if (block == GIF_BLOCK_EXTENSION) { + BYTE ext; + if (io->read_proc(&ext, 1, 1, handle) < 1) { + throw "EOF reading extension"; + } + + if (ext == GIF_EXT_GRAPHIC_CONTROL) { + // overwrite previous offset if more than one GCE found before an ID + gce_offset = io->tell_proc(handle); + } else if (ext == GIF_EXT_COMMENT) { + info->comment_extension_offsets.push_back(io->tell_proc(handle)); + } else if (ext == GIF_EXT_APPLICATION) { + info->application_extension_offsets.push_back(io->tell_proc(handle)); + } + } else if (block == GIF_BLOCK_TRAILER) { + continue; + } else { + throw "Invalid GIF block found"; + } + + // Data Sub-blocks + BYTE len; + if (io->read_proc(&len, 1, 1, handle) < 1) { + throw "EOF reading sub-block"; + } + while (len != 0) { + io->seek_proc(handle, len, SEEK_CUR); + if (io->read_proc(&len, 1, 1, handle) < 1) { + throw "EOF reading sub-block"; + } + } + } + } catch (const char *msg) { + FreeImage_OutputMessageProc(s_format_id, msg, io); + delete info; + return NULL; + } + + return info; +} + +static FIBITMAP *DLL_CALLCONV DoLoadGIF(FreeImageIO *io, fi_handle handle, int flags, void *data) +{ + if (data == NULL) { + return NULL; + } + (void)flags; + GIFinfo *info = (GIFinfo *)data; + + int page = 0; + FIBITMAP *dib = NULL; + try { + bool have_transparent = false, no_local_palette = false, interlaced = false; + int disposal_method = GIF_DISPOSAL_LEAVE, transparent_color = 0; + WORD left, top, width, height; + BYTE packed, b; + WORD w; + + // Image Descriptor + io->seek_proc(handle, (long)info->image_descriptor_offsets[page], SEEK_SET); + io->read_proc(&left, 2, 1, handle); + io->read_proc(&top, 2, 1, handle); + io->read_proc(&width, 2, 1, handle); + io->read_proc(&height, 2, 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&left); + SwapShort(&top); + SwapShort(&width); + SwapShort(&height); +#endif + io->read_proc(&packed, 1, 1, handle); + interlaced = (packed & GIF_PACKED_ID_INTERLACED) ? true : false; + no_local_palette = (packed & GIF_PACKED_ID_HAVELCT) ? false : true; + + int bpp = 8; + if (!no_local_palette) { + int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE); + if (size <= 2) + bpp = 1; + else if (size <= 16) + bpp = 4; + } else if (info->global_color_table_offset != 0) { + if (info->global_color_table_size <= 2) + bpp = 1; + else if (info->global_color_table_size <= 16) + bpp = 4; + } + dib = FreeImage_Allocate(width, height, bpp, io); + if (dib == NULL) { + throw "DIB allocated failed"; + } + + // Palette + RGBQUAD *pal = FreeImage_GetPalette(dib); + if (!no_local_palette) { + int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE); + + int i = 0; + while (i < size) { + io->read_proc(&pal[i].rgbRed, 1, 1, handle); + io->read_proc(&pal[i].rgbGreen, 1, 1, handle); + io->read_proc(&pal[i].rgbBlue, 1, 1, handle); + i++; + } + } else if (info->global_color_table_offset != 0) { + long pos = io->tell_proc(handle); + io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET); + + int i = 0; + while (i < info->global_color_table_size) { + io->read_proc(&pal[i].rgbRed, 1, 1, handle); + io->read_proc(&pal[i].rgbGreen, 1, 1, handle); + io->read_proc(&pal[i].rgbBlue, 1, 1, handle); + i++; + } + + io->seek_proc(handle, pos, SEEK_SET); + } else { + // its legal to have no palette, but we're going to generate *something* + for (int i = 0; i < 256; i++) { + pal[i].rgbRed = (BYTE)i; + pal[i].rgbGreen = (BYTE)i; + pal[i].rgbBlue = (BYTE)i; + } + } + + // LZW Minimum Code Size + io->read_proc(&b, 1, 1, handle); + StringTable *stringtable = new StringTable; + stringtable->Initialize(b); + + // Image Data Sub-blocks + int x = 0, xpos = 0, y = 0, shift = 8 - bpp, mask = (1 << bpp) - 1, interlacepass = 0; + BYTE *scanline = FreeImage_GetScanLine(dib, height - 1); + BYTE buf[4096]; + io->read_proc(&b, 1, 1, handle); + while (b) { + io->read_proc(stringtable->FillInputBuffer(b), b, 1, handle); + int size = sizeof(buf); + while (stringtable->Decompress(buf, &size)) { + for (int i = 0; i < size; i++) { + scanline[xpos] |= (buf[i] & mask) << shift; + if (shift > 0) { + shift -= bpp; + } else { + xpos++; + shift = 8 - bpp; + } + if (++x >= width) { + if (interlaced) { + y += g_GifInterlaceIncrement[interlacepass]; + if (y >= height && ++interlacepass < GIF_INTERLACE_PASSES) { + y = g_GifInterlaceOffset[interlacepass]; + } + } else { + y++; + } + if (y >= height) { + stringtable->Done(); + break; + } + x = xpos = 0; + shift = 8 - bpp; + scanline = FreeImage_GetScanLine(dib, height - y - 1); + } + } + size = sizeof(buf); + } + io->read_proc(&b, 1, 1, handle); + } + + if (page == 0) { + QT3DSU32 idx; + + // Logical Screen Descriptor + io->seek_proc(handle, 6, SEEK_SET); + WORD logicalwidth, logicalheight; + io->read_proc(&logicalwidth, 2, 1, handle); + io->read_proc(&logicalheight, 2, 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&logicalwidth); + SwapShort(&logicalheight); +#endif + + // Global Color Table + if (info->global_color_table_offset != 0) { + RGBQUAD globalpalette[256]; + io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET); + int i = 0; + while (i < info->global_color_table_size) { + io->read_proc(&globalpalette[i].rgbRed, 1, 1, handle); + io->read_proc(&globalpalette[i].rgbGreen, 1, 1, handle); + io->read_proc(&globalpalette[i].rgbBlue, 1, 1, handle); + globalpalette[i].rgbReserved = 0; + i++; + } + // background color + if (info->background_color < info->global_color_table_size) { + FreeImage_SetBackgroundColor(dib, &globalpalette[info->background_color]); + } + } + + // Application Extension + LONG loop = 1; // If no AE with a loop count is found, the default must be 1 + for (idx = 0; idx < info->application_extension_offsets.size(); idx++) { + io->seek_proc(handle, (long)info->application_extension_offsets[idx], SEEK_SET); + io->read_proc(&b, 1, 1, handle); + if (b == 11) { // All AEs start with an 11 byte sub-block to determine what type of + // AE it is + char buf[11]; + io->read_proc(buf, 11, 1, handle); + if (!memcmp(buf, "NETSCAPE2.0", 11) + || !memcmp(buf, "ANIMEXTS1.0", + 11)) { // Not everybody recognizes ANIMEXTS1.0 but it is valid + io->read_proc(&b, 1, 1, handle); + if (b == 3) { // we're supposed to have a 3 byte sub-block now + io->read_proc(&b, 1, 1, + handle); // this should be 0x01 but isn't really important + io->read_proc(&w, 2, 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&w); +#endif + loop = w; + if (loop > 0) + loop++; + break; + } + } + } + } + } + + // Graphic Control Extension + if (info->graphic_control_extension_offsets[page] != 0) { + io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[page] + 1), + SEEK_SET); + io->read_proc(&packed, 1, 1, handle); + io->read_proc(&w, 2, 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapShort(&w); +#endif + io->read_proc(&b, 1, 1, handle); + have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false; + disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2; + + transparent_color = b; + if (have_transparent) { + int size = 1 << bpp; + if (transparent_color <= size) { + BYTE table[256]; + memset(table, 0xFF, size); + table[transparent_color] = 0; + FreeImage_SetTransparencyTable(dib, table, size); + dib->m_TransparentPaletteIndex = b; + } + } + } + b = (BYTE)disposal_method; + + delete stringtable; + + } catch (const char *msg) { + if (dib != NULL) { + FreeImage_Unload(dib); + } + FreeImage_OutputMessageProc(s_format_id, msg, io); + return NULL; + } + + return dib; +} + +static void DLL_CALLCONV Close(void *data) +{ + if (data == NULL) { + return; + } + GIFinfo *info = (GIFinfo *)data; + delete info; +} + +SLoadedTexture *SLoadedTexture::LoadGIF(ISeekableIOStream &inStream, bool inFlipY, + NVFoundationBase &inFnd, + qt3ds::render::NVRenderContextType renderContextType) +{ + Q_UNUSED(renderContextType) + FreeImageIO theIO(inFnd.getAllocator(), inFnd); + void *gifData = Open(&theIO, &inStream); + if (gifData) { + SLoadedTexture *retval = DoLoadGIF(&theIO, &inStream, 0, gifData); + Close(gifData); + if (retval) + retval->FreeImagePostProcess(inFlipY); + return retval; + } + return NULL; +} |