summaryrefslogtreecommitdiff
path: root/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/PluginGIF.cpp')
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginGIF.cpp1406
1 files changed, 0 insertions, 1406 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp b/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp
deleted file mode 100644
index d9b430eac5..0000000000
--- a/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp
+++ /dev/null
@@ -1,1406 +0,0 @@
-// ==========================================================
-// GIF Loader and Writer
-//
-// Design and implementation by
-// - Ryan Rubley <ryan@lostreality.org>
-// - Raphaлl Gaquer <raphael.gaquer@alcer.com>
-// - Aaron Shumate <aaron@shumate.us>
-//
-// 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 "FreeImage.h"
-#include "Utilities.h"
-#include "../Metadata/FreeImageTag.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;
- std::vector<size_t> application_extension_offsets;
- std::vector<size_t> comment_extension_offsets;
- std::vector<size_t> graphic_control_extension_offsets;
- std::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
-
- std::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};
-
-// ==========================================================
-// Helpers Functions
-// ==========================================================
-
-static BOOL
-FreeImage_SetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, WORD id, FREE_IMAGE_MDTYPE type, DWORD count, DWORD length, const void *value)
-{
- BOOL bResult = FALSE;
- FITAG *tag = FreeImage_CreateTag();
- if(tag) {
- FreeImage_SetTagKey(tag, key);
- FreeImage_SetTagID(tag, id);
- FreeImage_SetTagType(tag, type);
- FreeImage_SetTagCount(tag, count);
- FreeImage_SetTagLength(tag, length);
- FreeImage_SetTagValue(tag, value);
- if(model == FIMD_ANIMATION) {
- TagLib& s = TagLib::instance();
- // get the tag description
- const char *description = s.getTagDescription(TagLib::ANIMATION, id);
- FreeImage_SetTagDescription(tag, description);
- }
- // store the tag
- bResult = FreeImage_SetMetadata(model, dib, key, tag);
- FreeImage_DeleteTag(tag);
- }
- return bResult;
-}
-
-static BOOL
-FreeImage_GetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FREE_IMAGE_MDTYPE type, FITAG **tag)
-{
- if( FreeImage_GetMetadata(model, dib, key, tag) ) {
- if( FreeImage_GetTagType(*tag) == type ) {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-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 = new(std::nothrow) 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(std::nothrow) BYTE[len];
- m_bufferRealSize = len;
- } else if( len > m_bufferRealSize ) {
- delete [] m_buffer;
- m_buffer = new(std::nothrow) 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_nextCode < 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 const char * DLL_CALLCONV
-Format() {
- return "GIF";
-}
-
-static const char * DLL_CALLCONV
-Description() {
- return "Graphics Interchange Format";
-}
-
-static const char * DLL_CALLCONV
-Extension() {
- return "gif";
-}
-
-static const char * DLL_CALLCONV
-RegExpr() {
- return "^GIF";
-}
-
-static const char * DLL_CALLCONV
-MimeType() {
- return "image/gif";
-}
-
-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 BOOL DLL_CALLCONV
-SupportsExportDepth(int depth) {
- return (depth == 1) ||
- (depth == 4) ||
- (depth == 8);
-}
-
-static BOOL DLL_CALLCONV
-SupportsExportType(FREE_IMAGE_TYPE type) {
- return (type == FIT_BITMAP) ? TRUE : FALSE;
-}
-
-// ----------------------------------------------------------
-
-static void *DLL_CALLCONV
-Open(FreeImageIO *io, fi_handle handle, BOOL read) {
- GIFinfo *info = new(std::nothrow) GIFinfo;
- if( info == NULL ) {
- return NULL;
- }
-
- // 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;
- if( read ) {
- try {
- //Header
- if( !Validate(io, handle) ) {
- throw FI_MSG_ERROR_MAGIC_NUMBER;
- }
- 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);
- delete info;
- return NULL;
- }
- } else {
- //Header
- io->write_proc((void *)"GIF89a", 6, 1, handle);
- }
-
- return info;
-}
-
-static void DLL_CALLCONV
-Close(FreeImageIO *io, fi_handle handle, void *data) {
- if( data == NULL ) {
- return;
- }
- GIFinfo *info = (GIFinfo *)data;
-
- if( !info->read ) {
- //Trailer
- BYTE b = GIF_BLOCK_TRAILER;
- io->write_proc(&b, 1, 1, handle);
- }
-
- delete info;
-}
-
-static int DLL_CALLCONV
-PageCount(FreeImageIO *io, fi_handle handle, void *data) {
- if( data == NULL ) {
- return 0;
- }
- GIFinfo *info = (GIFinfo *)data;
-
- return (int) info->image_descriptor_offsets.size();
-}
-
-static FIBITMAP * DLL_CALLCONV
-Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
- if( data == NULL ) {
- return NULL;
- }
- GIFinfo *info = (GIFinfo *)data;
-
- if( page == -1 ) {
- page = 0;
- }
- if( page < 0 || page >= (int)info->image_descriptor_offsets.size() ) {
- return NULL;
- }
-
- FIBITMAP *dib = NULL;
- try {
- bool have_transparent = false, no_local_palette = false, interlaced = false;
- int disposal_method = GIF_DISPOSAL_LEAVE, delay_time = 0, transparent_color = 0;
- WORD left, top, width, height;
- BYTE packed, b;
- WORD w;
-
- //playback pages to generate what the user would see for this frame
- if( (flags & GIF_PLAYBACK) == GIF_PLAYBACK ) {
- //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
- //set the background color with 0 alpha
- RGBQUAD background;
- if( info->global_color_table_offset != 0 && info->background_color < info->global_color_table_size ) {
- io->seek_proc(handle, (long)(info->global_color_table_offset + (info->background_color * 3)), SEEK_SET);
- io->read_proc(&background.rgbRed, 1, 1, handle);
- io->read_proc(&background.rgbGreen, 1, 1, handle);
- io->read_proc(&background.rgbBlue, 1, 1, handle);
- } else {
- background.rgbRed = 0;
- background.rgbGreen = 0;
- background.rgbBlue = 0;
- }
- background.rgbReserved = 0;
-
- //allocate entire logical area
- dib = FreeImage_Allocate(logicalwidth, logicalheight, 32);
- if( dib == NULL ) {
- throw FI_MSG_ERROR_DIB_MEMORY;
- }
-
- //fill with background color to start
- int x, y;
- RGBQUAD *scanline;
- for( y = 0; y < logicalheight; y++ ) {
- scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, y);
- for( x = 0; x < logicalwidth; x++ ) {
- *scanline++ = background;
- }
- }
-
- //cache some info about each of the pages so we can avoid decoding as many of them as possible
- std::vector<PageInfo> pageinfo;
- int start = page, end = page;
- while( start >= 0 ) {
- //Graphic Control Extension
- io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[start] + 1), SEEK_SET);
- io->read_proc(&packed, 1, 1, handle);
- have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
- disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
- //Image Descriptor
- io->seek_proc(handle, (long)(info->image_descriptor_offsets[start]), 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
-
- pageinfo.push_back(PageInfo(disposal_method, left, top, width, height));
-
- if( start != end ) {
- if( left == 0 && top == 0 && width == logicalwidth && height == logicalheight ) {
- if( disposal_method == GIF_DISPOSAL_BACKGROUND ) {
- pageinfo.pop_back();
- start++;
- break;
- } else if( disposal_method != GIF_DISPOSAL_PREVIOUS ) {
- if( !have_transparent ) {
- break;
- }
- }
- }
- }
- start--;
- }
- if( start < 0 ) {
- start = 0;
- }
-
- //draw each page into the logical area
- delay_time = 0;
- for( page = start; page <= end; page++ ) {
- PageInfo &info = pageinfo[end - page];
- //things we can skip having to decode
- if( page != end ) {
- if( info.disposal_method == GIF_DISPOSAL_PREVIOUS ) {
- continue;
- }
- if( info.disposal_method == GIF_DISPOSAL_BACKGROUND ) {
- for( y = 0; y < info.height; y++ ) {
- const int scanidx = logicalheight - (y + info.top) - 1;
- if ( scanidx < 0 ) {
- break; // If data is corrupt, don't calculate in invalid scanline
- }
- scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + info.left;
- for( x = 0; x < info.width; x++ ) {
- *scanline++ = background;
- }
- }
- continue;
- }
- }
-
- //decode page
- FIBITMAP *pagedib = Load(io, handle, page, GIF_LOAD256, data);
- if( pagedib != NULL ) {
- RGBQUAD *pal = FreeImage_GetPalette(pagedib);
- have_transparent = false;
- if( FreeImage_IsTransparent(pagedib) ) {
- int count = FreeImage_GetTransparencyCount(pagedib);
- BYTE *table = FreeImage_GetTransparencyTable(pagedib);
- for( int i = 0; i < count; i++ ) {
- if( table[i] == 0 ) {
- have_transparent = true;
- transparent_color = i;
- break;
- }
- }
- }
- //copy page data into logical buffer, with full alpha opaqueness
- for( y = 0; y < info.height; y++ ) {
- const int scanidx = logicalheight - (y + info.top) - 1;
- if ( scanidx < 0 ) {
- break; // If data is corrupt, don't calculate in invalid scanline
- }
- scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + info.left;
- BYTE *pageline = FreeImage_GetScanLine(pagedib, info.height - y - 1);
- for( x = 0; x < info.width; x++ ) {
- if( !have_transparent || *pageline != transparent_color ) {
- *scanline = pal[*pageline];
- scanline->rgbReserved = 255;
- }
- scanline++;
- pageline++;
- }
- }
- //copy frame time
- if( page == end ) {
- FITAG *tag;
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, pagedib, "FrameTime", FIDT_LONG, &tag) ) {
- delay_time = *(LONG *)FreeImage_GetTagValue(tag);
- }
- }
- FreeImage_Unload(pagedib);
- }
- }
-
- //setup frame time
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
- return dib;
- }
-
- //get the actual frame image data for a single frame
-
- //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( (flags & GIF_LOAD256) == 0 ) {
- 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);
- if( dib == NULL ) {
- throw FI_MSG_ERROR_DIB_MEMORY;
- }
-
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", ANIMTAG_FRAMELEFT, FIDT_SHORT, 1, 2, &left);
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", ANIMTAG_FRAMETOP, FIDT_SHORT, 1, 2, &top);
- b = no_local_palette ? 1 : 0;
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", ANIMTAG_NOLOCALPALETTE, FIDT_BYTE, 1, 1, &b);
- b = interlaced ? 1 : 0;
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", ANIMTAG_INTERLACED, FIDT_BYTE, 1, 1, &b);
-
- //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(std::nothrow) 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 ) {
- size_t 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
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", ANIMTAG_LOGICALWIDTH, FIDT_SHORT, 1, 2, &logicalwidth);
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", ANIMTAG_LOGICALHEIGHT, FIDT_SHORT, 1, 2, &logicalheight);
-
- //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++;
- }
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "GlobalPalette", ANIMTAG_GLOBALPALETTE, FIDT_PALETTE, info->global_color_table_size, info->global_color_table_size * 4, globalpalette);
- //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;
- }
- }
- }
- }
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Loop", ANIMTAG_LOOP, FIDT_LONG, 1, 4, &loop);
-
- // Comment Extension
- for (idx = 0; idx < info->comment_extension_offsets.size(); idx++) {
- io->seek_proc(handle, (long)info->comment_extension_offsets[idx], SEEK_SET);
- std::string comment;
- char buf[255];
- io->read_proc(&b, 1, 1, handle);
- while (b) {
- io->read_proc(buf, b, 1, handle);
- comment.append(buf, b);
- io->read_proc(&b, 1, 1, handle);
- }
- comment.append(1, '\0');
- sprintf(buf, "Comment%d", (int)idx);
- DWORD comment_size = (DWORD)comment.size();
- FreeImage_SetMetadataEx(FIMD_COMMENTS, dib, buf, 1, FIDT_ASCII, comment_size, comment_size, comment.c_str());
- }
- }
-
- //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;
- delay_time = w * 10; //convert cs to ms
- 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);
- }
- }
- }
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
- b = (BYTE)disposal_method;
- FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", ANIMTAG_DISPOSALMETHOD, FIDT_BYTE, 1, 1, &b);
-
- delete stringtable;
-
- } catch (const char *msg) {
- if( dib != NULL ) {
- FreeImage_Unload(dib);
- }
- FreeImage_OutputMessageProc(s_format_id, msg);
- return NULL;
- }
-
- return dib;
-}
-
-static BOOL DLL_CALLCONV
-Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
- if( data == NULL ) {
- return FALSE;
- }
- //GIFinfo *info = (GIFinfo *)data;
-
- if( page == -1 ) {
- page = 0;
- }
-
- try {
- BYTE packed, b;
- WORD w;
- FITAG *tag;
-
- int bpp = FreeImage_GetBPP(dib);
- if( bpp != 1 && bpp != 4 && bpp != 8 ) {
- throw "Only 1, 4, or 8 bpp images supported";
- }
-
- bool have_transparent = false, no_local_palette = false, interlaced = false;
- int disposal_method = GIF_DISPOSAL_BACKGROUND, delay_time = 100, transparent_color = 0;
- WORD left = 0, top = 0, width = (WORD)FreeImage_GetWidth(dib), height = (WORD)FreeImage_GetHeight(dib);
- WORD output_height = height;
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", FIDT_SHORT, &tag) ) {
- left = *(WORD *)FreeImage_GetTagValue(tag);
- }
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", FIDT_SHORT, &tag) ) {
- top = *(WORD *)FreeImage_GetTagValue(tag);
- }
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", FIDT_BYTE, &tag) ) {
- no_local_palette = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
- }
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", FIDT_BYTE, &tag) ) {
- interlaced = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
- }
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", FIDT_LONG, &tag) ) {
- delay_time = *(LONG *)FreeImage_GetTagValue(tag);
- }
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", FIDT_BYTE, &tag) ) {
- disposal_method = *(BYTE *)FreeImage_GetTagValue(tag);
- }
-
- RGBQUAD *pal = FreeImage_GetPalette(dib);
-#ifdef FREEIMAGE_BIGENDIAN
- SwapShort(&left);
- SwapShort(&top);
- SwapShort(&width);
- SwapShort(&height);
-#endif
-
- if( page == 0 ) {
- //gather some info
- WORD logicalwidth = width; // width has already been swapped...
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", FIDT_SHORT, &tag) ) {
- logicalwidth = *(WORD *)FreeImage_GetTagValue(tag);
-#ifdef FREEIMAGE_BIGENDIAN
- SwapShort(&logicalwidth);
-#endif
- }
- WORD logicalheight = height; // height has already been swapped...
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", FIDT_SHORT, &tag) ) {
- logicalheight = *(WORD *)FreeImage_GetTagValue(tag);
-#ifdef FREEIMAGE_BIGENDIAN
- SwapShort(&logicalheight);
-#endif
- }
- RGBQUAD *globalpalette = NULL;
- int globalpalette_size = 0;
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "GlobalPalette", FIDT_PALETTE, &tag) ) {
- globalpalette_size = FreeImage_GetTagCount(tag);
- if( globalpalette_size >= 2 ) {
- globalpalette = (RGBQUAD *)FreeImage_GetTagValue(tag);
- }
- }
-
- //Logical Screen Descriptor
- io->write_proc(&logicalwidth, 2, 1, handle);
- io->write_proc(&logicalheight, 2, 1, handle);
- packed = GIF_PACKED_LSD_COLORRES;
- b = 0;
- RGBQUAD background_color;
- if( globalpalette != NULL ) {
- packed |= GIF_PACKED_LSD_HAVEGCT;
- if( globalpalette_size < 4 ) {
- globalpalette_size = 2;
- packed |= 0 & GIF_PACKED_LSD_GCTSIZE;
- } else if( globalpalette_size < 8 ) {
- globalpalette_size = 4;
- packed |= 1 & GIF_PACKED_LSD_GCTSIZE;
- } else if( globalpalette_size < 16 ) {
- globalpalette_size = 8;
- packed |= 2 & GIF_PACKED_LSD_GCTSIZE;
- } else if( globalpalette_size < 32 ) {
- globalpalette_size = 16;
- packed |= 3 & GIF_PACKED_LSD_GCTSIZE;
- } else if( globalpalette_size < 64 ) {
- globalpalette_size = 32;
- packed |= 4 & GIF_PACKED_LSD_GCTSIZE;
- } else if( globalpalette_size < 128 ) {
- globalpalette_size = 64;
- packed |= 5 & GIF_PACKED_LSD_GCTSIZE;
- } else if( globalpalette_size < 256 ) {
- globalpalette_size = 128;
- packed |= 6 & GIF_PACKED_LSD_GCTSIZE;
- } else {
- globalpalette_size = 256;
- packed |= 7 & GIF_PACKED_LSD_GCTSIZE;
- }
- if( FreeImage_GetBackgroundColor(dib, &background_color) ) {
- for( int i = 0; i < globalpalette_size; i++ ) {
- if( background_color.rgbRed == globalpalette[i].rgbRed &&
- background_color.rgbGreen == globalpalette[i].rgbGreen &&
- background_color.rgbBlue == globalpalette[i].rgbBlue ) {
-
- b = (BYTE)i;
- break;
- }
- }
- }
- } else {
- packed |= (bpp - 1) & GIF_PACKED_LSD_GCTSIZE;
- }
- io->write_proc(&packed, 1, 1, handle);
- io->write_proc(&b, 1, 1, handle);
- b = 0;
- io->write_proc(&b, 1, 1, handle);
-
- //Global Color Table
- if( globalpalette != NULL ) {
- int i = 0;
- while( i < globalpalette_size ) {
- io->write_proc(&globalpalette[i].rgbRed, 1, 1, handle);
- io->write_proc(&globalpalette[i].rgbGreen, 1, 1, handle);
- io->write_proc(&globalpalette[i].rgbBlue, 1, 1, handle);
- i++;
- }
- }
-
- //Application Extension
- LONG loop = 0;
- if( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "Loop", FIDT_LONG, &tag) ) {
- loop = *(LONG *)FreeImage_GetTagValue(tag);
- }
- if( loop != 1 ) {
- //the Netscape extension is really "repeats" not "loops"
- if( loop > 1 ) loop--;
- if( loop > 0xFFFF ) loop = 0xFFFF;
- w = (WORD)loop;
-#ifdef FREEIMAGE_BIGENDIAN
- SwapShort(&w);
-#endif
- io->write_proc((void *)"\x21\xFF\x0BNETSCAPE2.0\x03\x01", 16, 1, handle);
- io->write_proc(&w, 2, 1, handle);
- b = 0;
- io->write_proc(&b, 1, 1, handle);
- }
-
- //Comment Extension
- FIMETADATA *mdhandle = NULL;
- FITAG *tag = NULL;
- mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag);
- if( mdhandle ) {
- do {
- if( FreeImage_GetTagType(tag) == FIDT_ASCII ) {
- int length = FreeImage_GetTagLength(tag) - 1;
- char *value = (char *)FreeImage_GetTagValue(tag);
- io->write_proc((void *)"\x21\xFE", 2, 1, handle);
- while( length > 0 ) {
- b = (BYTE)(length >= 255 ? 255 : length);
- io->write_proc(&b, 1, 1, handle);
- io->write_proc(value, b, 1, handle);
- value += b;
- length -= b;
- }
- b = 0;
- io->write_proc(&b, 1, 1, handle);
- }
- } while(FreeImage_FindNextMetadata(mdhandle, &tag));
-
- FreeImage_FindCloseMetadata(mdhandle);
- }
- }
-
- //Graphic Control Extension
- if( FreeImage_IsTransparent(dib) ) {
- int count = FreeImage_GetTransparencyCount(dib);
- BYTE *table = FreeImage_GetTransparencyTable(dib);
- for( int i = 0; i < count; i++ ) {
- if( table[i] == 0 ) {
- have_transparent = true;
- transparent_color = i;
- break;
- }
- }
- }
- io->write_proc((void *)"\x21\xF9\x04", 3, 1, handle);
- b = (BYTE)((disposal_method << 2) & GIF_PACKED_GCE_DISPOSAL);
- if( have_transparent ) b |= GIF_PACKED_GCE_HAVETRANS;
- io->write_proc(&b, 1, 1, handle);
- //Notes about delay time for GIFs:
- //IE5/IE6 have a minimum and default of 100ms
- //Mozilla/Firefox/Netscape 6+/Opera have a minimum of 20ms and a default of 100ms if <20ms is specified or the GCE is absent
- //Netscape 4 has a minimum of 10ms if 0ms is specified, but will use 0ms if the GCE is absent
- w = (WORD)(delay_time / 10); //convert ms to cs
-#ifdef FREEIMAGE_BIGENDIAN
- SwapShort(&w);
-#endif
- io->write_proc(&w, 2, 1, handle);
- b = (BYTE)transparent_color;
- io->write_proc(&b, 1, 1, handle);
- b = 0;
- io->write_proc(&b, 1, 1, handle);
-
- //Image Descriptor
- b = GIF_BLOCK_IMAGE_DESCRIPTOR;
- io->write_proc(&b, 1, 1, handle);
- io->write_proc(&left, 2, 1, handle);
- io->write_proc(&top, 2, 1, handle);
- io->write_proc(&width, 2, 1, handle);
- io->write_proc(&height, 2, 1, handle);
- packed = 0;
- if( !no_local_palette ) packed |= GIF_PACKED_ID_HAVELCT | ((bpp - 1) & GIF_PACKED_ID_LCTSIZE);
- if( interlaced ) packed |= GIF_PACKED_ID_INTERLACED;
- io->write_proc(&packed, 1, 1, handle);
-
- //Local Color Table
- if( !no_local_palette ) {
- int palsize = 1 << bpp;
- for( int i = 0; i < palsize; i++ ) {
- io->write_proc(&pal[i].rgbRed, 1, 1, handle);
- io->write_proc(&pal[i].rgbGreen, 1, 1, handle);
- io->write_proc(&pal[i].rgbBlue, 1, 1, handle);
- }
- }
-
-
- //LZW Minimum Code Size
- b = (BYTE)(bpp == 1 ? 2 : bpp);
- io->write_proc(&b, 1, 1, handle);
- StringTable *stringtable = new(std::nothrow) StringTable;
- stringtable->Initialize(b);
- stringtable->CompressStart(bpp, width);
-
- //Image Data Sub-blocks
- int y = 0, interlacepass = 0, line = FreeImage_GetLine(dib);
- BYTE buf[255], *bufptr = buf; //255 is the max sub-block length
- int size = sizeof(buf);
- b = sizeof(buf);
- while( y < output_height ) {
- memcpy(stringtable->FillInputBuffer(line), FreeImage_GetScanLine(dib, output_height - y - 1), line);
- while( stringtable->Compress(bufptr, &size) ) {
- bufptr += size;
- if( bufptr - buf == sizeof(buf) ) {
- io->write_proc(&b, 1, 1, handle);
- io->write_proc(buf, sizeof(buf), 1, handle);
- size = sizeof(buf);
- bufptr = buf;
- } else {
- size = (int)(sizeof(buf) - (bufptr - buf));
- }
- }
- if( interlaced ) {
- y += g_GifInterlaceIncrement[interlacepass];
- if( y >= output_height && ++interlacepass < GIF_INTERLACE_PASSES ) {
- y = g_GifInterlaceOffset[interlacepass];
- }
- } else {
- y++;
- }
- }
- size = (int)(bufptr - buf);
- BYTE last[4];
- w = (WORD)stringtable->CompressEnd(last);
- if( size + w >= sizeof(buf) ) {
- //one last full size sub-block
- io->write_proc(&b, 1, 1, handle);
- io->write_proc(buf, size, 1, handle);
- io->write_proc(last, sizeof(buf) - size, 1, handle);
- //and possibly a tiny additional sub-block
- b = (BYTE)(w - (sizeof(buf) - size));
- if( b > 0 ) {
- io->write_proc(&b, 1, 1, handle);
- io->write_proc(last + w - b, b, 1, handle);
- }
- } else {
- //last sub-block less than full size
- b = (BYTE)(size + w);
- io->write_proc(&b, 1, 1, handle);
- io->write_proc(buf, size, 1, handle);
- io->write_proc(last, w, 1, handle);
- }
-
- //Block Terminator
- b = 0;
- io->write_proc(&b, 1, 1, handle);
-
- delete stringtable;
-
- } catch (const char *msg) {
- FreeImage_OutputMessageProc(s_format_id, msg);
- return FALSE;
- }
-
- return TRUE;
-}
-
-// ==========================================================
-// Init
-// ==========================================================
-
-void DLL_CALLCONV
-InitGIF(Plugin *plugin, int format_id) {
- s_format_id = format_id;
-
- plugin->format_proc = Format;
- plugin->description_proc = Description;
- plugin->extension_proc = Extension;
- plugin->regexpr_proc = RegExpr;
- plugin->open_proc = Open;
- plugin->close_proc = Close;
- plugin->pagecount_proc = PageCount;
- plugin->pagecapability_proc = NULL;
- plugin->load_proc = Load;
- plugin->save_proc = Save;
- plugin->validate_proc = Validate;
- plugin->mime_proc = MimeType;
- plugin->supports_export_bpp_proc = SupportsExportDepth;
- plugin->supports_export_type_proc = SupportsExportType;
- plugin->supports_icc_profiles_proc = NULL;
-}