X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b59bf2db65896d128d72016c201dc1df9ff6c94e..769c3372a2bd68c45d1a4afc8462d3b7939decb1:/src/common/imaggif.cpp diff --git a/src/common/imaggif.cpp b/src/common/imaggif.cpp index ddcce30a7f..52f9cf14c4 100644 --- a/src/common/imaggif.cpp +++ b/src/common/imaggif.cpp @@ -1,454 +1,829 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: imaggif.cpp +// Name: src/common/imaggif.cpp // Purpose: wxGIFHandler -// Author: Guillermo Rodriguez Garcia -// wxWindows adaptation by Vaclav Slavik +// Author: Vaclav Slavik, Guillermo Rodriguez Garcia, Gershon Elber, Troels K +// RCS-ID: $Id$ +// Copyright: (c) 1999-2011 Vaclav Slavik, Guillermo Rodriguez Garcia, Gershon Elber, Troels K // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "imaggif.h" -#endif - // For compilers that support precompilation, includes "wx.h". -#include +#include "wx/wxprec.h" #ifdef __BORLANDC__ -#pragma hdrstop + #pragma hdrstop #endif -#include +#if wxUSE_IMAGE && wxUSE_GIF + +#ifndef WX_PRECOMP + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/palette.h" + #include "wx/utils.h" +#endif -#include -#include -#include +#include "wx/imaggif.h" +#include "wx/gifdecod.h" +#include "wx/stream.h" +#include "wx/anidecod.h" // wxImageArray -#include +#define GIF89_HDR "GIF89a" +#define NETSCAPE_LOOP "NETSCAPE2.0" + +// see members.aol.com/royalef/gifabout.htm +// members.aol.com/royalef/gif89a.txt + +enum +{ + GIF_MARKER_EXT = '!', // 0x21 + GIF_MARKER_SEP = ',', // 0x2C + GIF_MARKER_ENDOFDATA = ';', // 0x3B -/* + GIF_MARKER_EXT_GRAPHICS_CONTROL = 0xF9, + GIF_MARKER_EXT_COMMENT = 0xFE, + GIF_MARKER_EXT_APP = 0xFF +}; -FOLLOWING CODE IS BY G.R.G. : -(except wxInputStream stuff) +#define LZ_MAX_CODE 4095 // Biggest code possible in 12 bits. +#define FLUSH_OUTPUT 4096 // Impossible code, to signal flush. +#define FIRST_CODE 4097 // Impossible code, to signal first. -*/ +#define HT_SIZE 8192 // 12bits = 4096 or twice as big! +#define HT_KEY_MASK 0x1FFF // 13bits keys -/************************************************************************ - READGIF.H - Leer un archivo GIF de 8 bits - ------------------------------------------------------------------------ - Tratamiento Digital de la Imagen - ------------------------------------------------------------------------ - Guillermo Rodriguez Garcia - +#define HT_GET_KEY(l) (l >> 12) +#define HT_GET_CODE(l) (l & 0x0FFF) +#define HT_PUT_KEY(l) (l << 12) +#define HT_PUT_CODE(l) (l & 0x0FFF) - Version: 2.0 -*************************************************************************/ +struct wxRGB +{ + wxUint8 red; + wxUint8 green; + wxUint8 blue; +}; -typedef struct +struct GifHashTableType { - int w; /* width */ - int h; /* height */ - unsigned char *p; /* bitmap */ - unsigned char *pal; /* palette */ -} IMAGEN; + wxUint32 HTable[HT_SIZE]; +}; +IMPLEMENT_DYNAMIC_CLASS(wxGIFHandler,wxImageHandler) -/************************************************************************ - READGIF.C - Lee un archivo GIF de 256 colores - ------------------------------------------------------------------------ - Tratamiento Digital de la Imagen - ------------------------------------------------------------------------ - Guillermo Rodriguez Garcia - +//---------------------------------------------------------------------------- +// Forward declarations +//---------------------------------------------------------------------------- - Version: 2.0 -*************************************************************************/ +static int wxGIFHandler_KeyItem(unsigned long item); +#if wxUSE_STREAMS -#include -#include -#include +static int wxGIFHandler_BitSize(int n); +#if wxUSE_PALETTE +static bool wxGIFHandler_GetPalette(const wxImage& image, + wxRGB *pal, int *palCount, int *mask_index); +#endif +static +int wxGIFHandler_PaletteFind(const wxRGB& clr, const wxRGB *array, int count); + +static bool wxGIFHandler_Write(wxOutputStream *, const void *buf, size_t len); +static bool wxGIFHandler_WriteByte(wxOutputStream *, wxUint8); +static bool wxGIFHandler_WriteWord(wxOutputStream *, wxUint16); +static bool wxGIFHandler_WriteHeader(wxOutputStream *, int width, int height, + bool loop, const wxRGB *pal, int palCount); +static bool wxGIFHandler_WriteRect(wxOutputStream *, int width, int height); +#if wxUSE_PALETTE +static bool wxGIFHandler_WriteTerm(wxOutputStream *); +#endif +static bool wxGIFHandler_WriteZero(wxOutputStream *); +static bool wxGIFHandler_WritePalette(wxOutputStream *, + const wxRGB *pal, size_t palCount, int bpp); +static bool wxGIFHandler_WriteControl(wxOutputStream *, + int maskIndex, int delayMilliSecs); +static bool wxGIFHandler_WriteComment(wxOutputStream *, const wxString&); +static bool wxGIFHandler_WriteLoop(wxOutputStream *); -/* error codes */ -#define E_OK 0 /* everything was OK */ -#define E_ARCHIVO -1 /* error opening file */ -#define E_FORMATO -2 /* error in gif header */ -#define E_MEMORIA -3 /* error allocating memory */ +static bool wxGIFHandler_BufferedOutput(wxOutputStream *, wxUint8 *buf, int c); +#endif // wxUSE_STREAMS +//----------------------------------------------------------------------------- +// wxGIFHandler +//----------------------------------------------------------------------------- -/* This class binding is by VS, so all bugs in it are mine ;-) */ +#if wxUSE_STREAMS -class gifDecoder +bool wxGIFHandler::LoadFile(wxImage *image, wxInputStream& stream, + bool verbose, int index) { - private: - /* globals */ - int restbits; /* remaining valid bits */ - unsigned int restbyte; /* remaining bytes in this block */ - unsigned int lastbyte; /* last byte read */ + wxGIFDecoder *decod; + wxGIFErrorCode error; + bool ok = true; - unsigned char* file; /* input file in memory */ - unsigned int file_pos, file_size; /* position & size in it */ +// image->Destroy(); + decod = new wxGIFDecoder(); + error = decod->LoadGIF(stream); - int fgetc(); - void fread(void *ptr, size_t size, size_t nmemb); - void fseekcur(int rel_pos); + if ((error != wxGIF_OK) && (error != wxGIF_TRUNCATED)) + { + if (verbose) + { + switch (error) + { + case wxGIF_INVFORMAT: + wxLogError(_("GIF: error in GIF image format.")); + break; + case wxGIF_MEMERR: + wxLogError(_("GIF: not enough memory.")); + break; + default: + wxLogError(_("GIF: unknown error!!!")); + break; + } + } + delete decod; + return false; + } - public: - gifDecoder(unsigned char* mem, int sz) {file = mem; file_pos = 0; file_size = sz;} - int getcode(int bits); - int dgif(IMAGEN *img, int interl, int bits); - int readgif(IMAGEN *img); -}; + if ((error == wxGIF_TRUNCATED) && verbose) + { + wxLogError(_("GIF: data stream seems to be truncated.")); + // go on; image data is OK + } + if (ok) + { + ok = decod->ConvertToImage(index != -1 ? (size_t)index : 0, image); + } + else + { + wxLogError(_("GIF: Invalid gif index.")); + } -int gifDecoder::fgetc() -{ - if (file_pos < file_size) return file[file_pos++]; - else return EOF; + delete decod; + + return ok; } -void gifDecoder::fread(void *ptr, size_t size, size_t nmemb) +bool wxGIFHandler::SaveFile(wxImage *image, + wxOutputStream& stream, bool verbose) { - int todo = size * nmemb; - if (todo + file_pos > file_size) todo = file_size - file_pos; - memcpy(ptr, file + file_pos, todo); - file_pos += todo; +#if wxUSE_PALETTE + wxRGB pal[256]; + int palCount; + int maskIndex; + + return wxGIFHandler_GetPalette(*image, pal, &palCount, &maskIndex) + && DoSaveFile(*image, &stream, verbose, true /*first?*/, 0, + false /*loop?*/, pal, palCount, maskIndex) + && wxGIFHandler_WriteTerm(&stream); +#else + wxUnusedVar(image); + wxUnusedVar(stream); + wxUnusedVar(verbose); + return false; +#endif } -void gifDecoder::fseekcur(int rel_pos) +bool wxGIFHandler::DoCanRead( wxInputStream& stream ) { - file_pos += rel_pos; + wxGIFDecoder decod; + return decod.CanRead(stream); + // it's ok to modify the stream position here } -/* getcode: - * Reads the next code from the file, with size 'bits' - * v2.0 - changed to support 'bits' values < 8 - */ -int gifDecoder::getcode(int bits) +int wxGIFHandler::DoGetImageCount( wxInputStream& stream ) { - unsigned int mask; /* bit mask */ - unsigned int code; /* code (result) */ + wxGIFDecoder decod; + wxGIFErrorCode error = decod.LoadGIF(stream); + if ( (error != wxGIF_OK) && (error != wxGIF_TRUNCATED) ) + return -1; + // NOTE: this function modifies the current stream position but it's ok + // (see wxImageHandler::GetImageCount) - /* get remaining bits from last byte read */ - mask = (1 << bits) - 1; - code = (lastbyte >> (8 - restbits)) & mask; + return decod.GetFrameCount(); +} - /* keep reading new bytes until needed */ - while (bits > restbits) +bool wxGIFHandler::DoSaveFile(const wxImage& image, wxOutputStream *stream, + bool WXUNUSED(verbose), bool first, int delayMilliSecs, bool loop, + const wxRGB *pal, int palCount, int maskIndex) +{ + const unsigned long colorcount = image.CountColours(256+1); + bool ok = colorcount && (colorcount <= 256); + if (!ok) { - /* if no bytes left in this block, read the next block */ - if (restbyte == 0) - restbyte = fgetc(); + return false; + } - /* read next byte and isolate the bits we need */ - lastbyte = fgetc(); - mask = (1 << (bits - restbits)) - 1; - code = code + ((lastbyte & mask) << restbits); - restbyte--; + int width = image.GetWidth(); + int height = image.GetHeight(); + int width_even = width + ((width % 2) ? 1 : 0); - /* adjust total number of bits extracted from the buffer */ - restbits = restbits + 8; + if (first) + { + ok = wxGIFHandler_WriteHeader(stream, width, height, loop, + pal, palCount); } - /* find number of bits reamining for next code */ - restbits = (restbits - bits); + ok = ok + && wxGIFHandler_WriteComment(stream, + image.GetOption(wxIMAGE_OPTION_GIF_COMMENT)) + && wxGIFHandler_WriteControl(stream, maskIndex, delayMilliSecs) + && wxGIFHandler_WriteByte(stream, GIF_MARKER_SEP) + && wxGIFHandler_WriteRect(stream, width, height); - return code; -} + // local palette + if (first) + { + // we already saved the (global) palette + ok = ok && wxGIFHandler_WriteZero(stream); + } + else + { + const int bpp = wxGIFHandler_BitSize(palCount); + wxUint8 b; -/* dgif: - * GIF decoding function. The initial code size (aka root size) - * is 'bits'. Supports interlaced images (interl == 1). - */ -int gifDecoder::dgif(IMAGEN *img, int interl, int bits) -{ - int ab_prefix[4096]; /* alphabet (prefixes) */ - int ab_tail[4096]; /* alphabet (tails) */ - int stack[4096]; /* decompression stack */ - - int ab_clr; /* clear code */ - int ab_fin; /* end of info code */ - int ab_bits; /* actual symbol width, in bits */ - int ab_free; /* first free position in alphabet */ - int ab_max; /* last possible character in alphabet */ - int pass; /* pass number in interlaced images */ - int pos; /* index into decompresion stack */ - int x, y; /* position in image buffer */ - - int code, readcode, lastcode, abcabca; - - /* these won't change */ - ab_clr = (1 << bits); - ab_fin = (1 << bits) + 1; - - /* these will change through the decompression proccess */ - ab_bits = bits + 1; - ab_free = (1 << bits) + 2; - ab_max = (1 << ab_bits) - 1; - lastcode = -1; - abcabca = -1; - pass = 1; - pos = x = y = 0; - - /* reset static globals */ - restbits = 0; - restbyte = 0; - lastbyte = 0; + b = 0x80; + b |=(bpp - 1) << 5; + b |=(bpp - 1); + b &=~0x40; // clear interlaced - do + ok = ok && wxGIFHandler_WriteByte(stream, b) + && wxGIFHandler_WritePalette(stream, pal, palCount, bpp); + } + + if (!ok) { - /* get next code */ - readcode = code = getcode(ab_bits); + return false; + } - /* end of image? */ - if (code == ab_fin) break; + if (!InitHashTable()) + { + wxLogError(_("Couldn't initialize GIF hash table.")); + return false; + } - /* reset alphabet? */ - if (code == ab_clr) + const wxUint8 *src = image.GetData(); + wxUint8 *eightBitData = new wxUint8[width]; + + SetupCompress(stream, 8); + + m_pixelCount = height * width_even; + for (int y = 0; y < height; y++) + { + m_pixelCount -= width_even; + for (int x = 0; x < width; x++) { - /* reset main variables */ - ab_bits = bits + 1; - ab_free = (1 << bits) + 2; - ab_max = (1 << ab_bits) - 1; - lastcode = -1; - abcabca = -1; - - /* skip to next code */ - continue; + wxRGB rgb; + rgb.red = src[0]; + rgb.green = src[1]; + rgb.blue = src[2]; + int index = wxGIFHandler_PaletteFind(rgb, pal, palCount); + wxASSERT(index != wxNOT_FOUND); + eightBitData[x] = (wxUint8)index; + src+=3; } - /* unknown code: special case (like in ABCABCA) */ - if (code >= ab_free) + ok = CompressLine(stream, eightBitData, width); + if (!ok) { - code = lastcode; /* take last string */ - stack[pos++] = abcabca; /* add first character */ + break; } + } + + delete [] eightBitData; + + wxDELETE(m_hashTable); - /* build the string for this code in the stack */ - while (code > ab_clr) + return ok; +} + +bool wxGIFHandler::SaveAnimation(const wxImageArray& images, + wxOutputStream *stream, bool verbose, int delayMilliSecs) +{ +#if wxUSE_PALETTE + bool ok = true; + size_t i; + + wxSize size(0,0); + for (i = 0; (i < images.GetCount()) && ok; i++) + { + const wxImage& image = images.Item(i); + wxSize temp(image.GetWidth(), image.GetHeight()); + ok = ok && image.HasPalette(); + if (i) { - stack[pos++] = ab_tail[code]; - code = ab_prefix[code]; + ok = ok && (size == temp); } - stack[pos] = code; /* push last code into the stack */ - abcabca = code; /* save for special case */ - - /* make new entry in alphabet (only if NOT just cleared) */ - if (lastcode != -1) + else { - ab_prefix[ab_free] = lastcode; - ab_tail[ab_free] = code; - ab_free++; + size = temp; + } + } + + for (i = 0; (i < images.GetCount()) && ok; i++) + { + const wxImage& image = images.Item(i); + + wxRGB pal[256]; + int palCount; + int maskIndex; + + ok = wxGIFHandler_GetPalette(image, pal, &palCount, &maskIndex) + && DoSaveFile(image, stream, verbose, i == 0 /*first?*/, delayMilliSecs, + true /*loop?*/, pal, palCount, maskIndex); + } + + return ok && wxGIFHandler_WriteTerm(stream); +#else + wxUnusedVar(images); + wxUnusedVar(stream); + wxUnusedVar(verbose); + wxUnusedVar(delayMilliSecs); - if ((ab_free > ab_max) && (ab_bits < 12)) + return false; +#endif +} + +bool wxGIFHandler::CompressOutput(wxOutputStream *stream, int code) +{ + if (code == FLUSH_OUTPUT) + { + while (m_crntShiftState > 0) + { + // Get rid of what is left in DWord, and flush it. + if (!wxGIFHandler_BufferedOutput(stream, m_LZBuf, + m_crntShiftDWord & 0xff)) { - ab_bits++; - ab_max = (1 << ab_bits) - 1; + return false; } + m_crntShiftDWord >>= 8; + m_crntShiftState -= 8; } - - /* dump stack data to the buffer */ - while (pos >= 0) + m_crntShiftState = 0; // For next time. + if (!wxGIFHandler_BufferedOutput(stream, m_LZBuf, FLUSH_OUTPUT)) { - (img->p)[x + (y * (img->w))] = (char)stack[pos--]; + return false; + } + } + else + { + m_crntShiftDWord |= ((long) code) << m_crntShiftState; + m_crntShiftState += m_runningBits; + while (m_crntShiftState >= 8) + { + // Dump out full bytes: + if (!wxGIFHandler_BufferedOutput(stream, m_LZBuf, + m_crntShiftDWord & 0xff)) + { + return false; + } + m_crntShiftDWord >>= 8; + m_crntShiftState -= 8; + } + } + + // If code can't fit into RunningBits bits, must raise its size. Note + // however that codes above LZ_MAX_CODE are used for special signaling. + if ( (m_runningCode >= m_maxCode1) && (code <= LZ_MAX_CODE)) + { + m_maxCode1 = 1 << ++m_runningBits; + } + return true; +} + +bool wxGIFHandler::SetupCompress(wxOutputStream *stream, int bpp) +{ + m_LZBuf[0] = 0; // Nothing was output yet. + m_clearCode = (1 << bpp); + m_EOFCode = m_clearCode + 1; + m_runningCode = m_EOFCode + 1; + m_runningBits = bpp + 1; // Number of bits per code. + m_maxCode1 = 1 << m_runningBits; // Max. code + 1. + m_crntCode = FIRST_CODE; // Signal that this is first one! + m_crntShiftState = 0; // No information in CrntShiftDWord. + m_crntShiftDWord = 0; + + // Clear hash table and send Clear to make sure the decoder does the same. + ClearHashTable(); + + return wxGIFHandler_WriteByte(stream, (wxUint8)bpp) + && CompressOutput(stream, m_clearCode); +} - if (++x >= (img->w)) +bool wxGIFHandler::CompressLine(wxOutputStream *stream, + const wxUint8 *line, int lineLen) +{ + int i = 0, crntCode, newCode; + unsigned long newKey; + wxUint8 pixel; + if (m_crntCode == FIRST_CODE) // It's first time! + crntCode = line[i++]; + else + crntCode = m_crntCode; // Get last code in compression. + + while (i < lineLen) + { + // Decode lineLen items. + pixel = line[i++]; // Get next pixel from stream. + // Form a new unique key to search hash table for the code combines + // crntCode as Prefix string with Pixel as postfix char. + newKey = (((unsigned long) crntCode) << 8) + pixel; + if ((newCode = ExistsHashTable(newKey)) >= 0) + { + // This Key is already there, or the string is old one, so + // simply take new code as our crntCode: + crntCode = newCode; + } + else + { + // Put it in hash table, output the prefix code, and make our + // crntCode equal to Pixel. + if (!CompressOutput(stream, crntCode)) { - x = 0; + return false; + } - if (interl) - { - /* support for interlaced images */ - switch (pass) - { - case 1: y += 8; break; - case 2: y += 8; break; - case 3: y += 4; break; - case 4: y += 2; break; - } - if (y >= (img->h)) - { - switch (++pass) - { - case 2: y = 4; break; - case 3: y = 2; break; - case 4: y = 1; break; - } - } - } - else + crntCode = pixel; + + // If however the HashTable is full, we send a clear first and + // Clear the hash table. + if (m_runningCode >= LZ_MAX_CODE) + { + // Time to do some clearance: + if (!CompressOutput(stream, m_clearCode)) { - /* non-interlaced */ - y++; + return false; } + + m_runningCode = m_EOFCode + 1; + m_runningBits = 8 + 1; + m_maxCode1 = 1 << m_runningBits; + ClearHashTable(); } + else + { + // Put this unique key with its relative Code in hash table: + InsertHashTable(newKey, m_runningCode++); + } + } + } + // Preserve the current state of the compression algorithm: + m_crntCode = crntCode; + if (m_pixelCount == 0) + { + // We are done - output last Code and flush output buffers: + if (!CompressOutput(stream, crntCode) + || !CompressOutput(stream, m_EOFCode) + || !CompressOutput(stream, FLUSH_OUTPUT)) + { + return false; } + } - pos = 0; - lastcode = readcode; + return true; +} + +#endif // wxUSE_STREAMS + +bool wxGIFHandler::InitHashTable() +{ + if (!m_hashTable) + { + m_hashTable = new GifHashTableType(); + } + + if (!m_hashTable) + { + return false; } - while (code != ab_fin); - return 0; + ClearHashTable(); + + return true; +} + +void wxGIFHandler::ClearHashTable() +{ + int index = HT_SIZE; + wxUint32 *HTable = m_hashTable->HTable; + + while (--index>=0) + { + HTable[index] = 0xfffffffful; + } } -/* readgif: - * Reads a GIF image from the file with filename 'nombre' in the - * IMAGEN structure pointed by 'img'. Can read GIFs with any bit - * size (color depth), but the output image is always expanded - * to 8 bits per pixel. Also, the image palette always contains - * 256 colors, although some of them may be unused. Returns E_OK - * (== 0) on success, or an error code if something fails. Error - * codes are E_ARCHIVO, E_FORMATO, E_MEMORIA (see above). - */ -int gifDecoder::readgif(IMAGEN *img) +void wxGIFHandler::InsertHashTable(unsigned long key, int code) { - int size, ncolors, bits, interl, i; - unsigned char pal[768]; - unsigned char buf[16]; + int hKey = wxGIFHandler_KeyItem(key); + wxUint32 *HTable = m_hashTable->HTable; + while (HT_GET_KEY(HTable[hKey]) != 0xFFFFFL) + { + hKey = (hKey + 1) & HT_KEY_MASK; + } + HTable[hKey] = HT_PUT_KEY(key) | HT_PUT_CODE(code); +} - /* read header and logical screen descriptor block (LSDB) */ - fread(buf, 1, 13); - /* check GIF signature */ - if (memcmp(buf, "GIF", 3) != 0) return E_FORMATO; +int wxGIFHandler::ExistsHashTable(unsigned long key) +{ + int hKey = wxGIFHandler_KeyItem(key); + wxUint32 *HTable = m_hashTable->HTable, HTKey; - /* load global color map if available */ - if ((buf[10] & 0x80) == 0x80) + while ((HTKey = HT_GET_KEY(HTable[hKey])) != 0xFFFFFL) { - ncolors = 2 << (buf[10] & 0x07); - fread(pal, 1, 3 * ncolors); + if (key == HTKey) + { + return HT_GET_CODE(HTable[hKey]); + } + hKey = (hKey + 1) & HT_KEY_MASK; } + return -1; +} + +// --------------------------------------------------------------------------- +// implementation of global private functions +// --------------------------------------------------------------------------- - /* skip extensions */ - while (fgetc() == 0x21) /* separator */ +int wxGIFHandler_KeyItem(unsigned long item) +{ + return ((item >> 12) ^ item) & HT_KEY_MASK; +} + +#if wxUSE_STREAMS + +int wxGIFHandler_BitSize(int n) +{ + int i; + for (i = 1; i <= 8; i++) { - fgetc(); /* function code */ + if ((1 << i) >= n) + { + break; + } + } + return i; +} - while ((i = fgetc()) != 0) /* byte count */ - fseekcur(i); +#if wxUSE_PALETTE +bool wxGIFHandler_GetPalette(const wxImage& image, + wxRGB *pal, int *pPalCount, int *pMaskIndex) +{ + if (!image.HasPalette()) + { + return false; } - /* read image descriptor block (IDB) */ - fread(buf, 1, 9); - img->w = buf[4] + 256 * buf[5]; + const wxPalette& palette = image.GetPalette(); + int palCount = palette.GetColoursCount(); - img->h = buf[6] + 256 * buf[7]; - size = img->w * img->h; - interl = ((buf[8] & 0x40)? 1 : 0); + for (int i = 0; i < palCount; ++i) + { + if (!palette.GetRGB(i, &pal[i].red, &pal[i].green, &pal[i].blue)) + { + break; + } + } + if (image.HasMask()) + { + wxRGB mask; - /* load local color map if available */ - if ((buf[8] & 0x80) == 0x80) + mask.red = image.GetMaskRed(); + mask.green = image.GetMaskGreen(); + mask.blue = image.GetMaskBlue(); + *pMaskIndex = wxGIFHandler_PaletteFind(mask, pal, palCount); + if ( (*pMaskIndex == wxNOT_FOUND) && (palCount < 256)) + { + *pMaskIndex = palCount; + pal[palCount++] = mask; + } + } + else { - ncolors = 2 << (buf[8] & 0x07); - fread(pal, 1, 3 * ncolors); + *pMaskIndex = wxNOT_FOUND; } + *pPalCount = palCount; - /* get initial code size from first byte in raster data */ - bits = fgetc(); + return true; +} +#endif // wxUSE_PALETTE - /* allocate memory for image and palette */ - if ((img->p = (unsigned char*) malloc(size)) == NULL) return E_MEMORIA; - if ((img->pal = (unsigned char*) malloc(768)) == NULL) return E_MEMORIA; +int wxGIFHandler_PaletteFind(const wxRGB& clr, const wxRGB *array, int count) +{ + for (int i = 0; i < count; i++) + { + if ( (clr.red == array[i].red) + && (clr.green == array[i].green) + && (clr.blue == array[i].blue)) + { + return i; + } + } - /* shift palette to fit VGA 6-bit format */ - for (i = 0; i < 768; i++) - (img->pal)[i] = (unsigned char)pal[i] /* >> 2 not needed under wxWin */; + return wxNOT_FOUND; +} - /* decode GIF */ - dgif(img, interl, bits); +bool wxGIFHandler_Write(wxOutputStream *stream, const void *buf, size_t len) +{ + return (len == stream->Write(buf, len).LastWrite()); +} - /* finish successfully :-) */ - return E_OK; +bool wxGIFHandler_WriteByte(wxOutputStream *stream, wxUint8 byte) +{ + return wxGIFHandler_Write(stream, &byte, sizeof(byte)); } -/* +bool wxGIFHandler_WriteWord(wxOutputStream *stream, wxUint16 word) +{ + wxUint8 buf[2]; -FOLLOWING CODE IS BY V.S. : + buf[0] = word & 0xff; + buf[1] = (word >> 8) & 0xff; + return wxGIFHandler_Write(stream, &buf, sizeof(buf)); +} -*/ +bool wxGIFHandler_WriteHeader(wxOutputStream *stream, int width, int height, + bool loop, const wxRGB *pal, int palCount) +{ + const int bpp = wxGIFHandler_BitSize(palCount); + wxUint8 buf[3]; + + bool ok = wxGIFHandler_Write(stream, GIF89_HDR, sizeof(GIF89_HDR)-1) + && wxGIFHandler_WriteWord(stream, (wxUint16) width) + && wxGIFHandler_WriteWord(stream, (wxUint16) height); + + buf[0] = 0x80; + buf[0] |=(bpp - 1) << 5; + buf[0] |=(bpp - 1); + buf[1] = 0; // background color == entry 0 + buf[2] = 0; // aspect ratio 1:1 + ok = ok && wxGIFHandler_Write(stream, buf, sizeof(buf)) + && wxGIFHandler_WritePalette(stream, pal, palCount, bpp); + + if (loop) + { + ok = ok && wxGIFHandler_WriteLoop(stream); + } -//----------------------------------------------------------------------------- -// wxGIFHandler -//----------------------------------------------------------------------------- + return ok; +} -IMPLEMENT_DYNAMIC_CLASS(wxGIFHandler,wxImageHandler) +bool wxGIFHandler_WriteRect(wxOutputStream *stream, int width, int height) +{ + return wxGIFHandler_WriteWord(stream, 0) // left + && wxGIFHandler_WriteWord(stream, 0) // top + && wxGIFHandler_WriteWord(stream, (wxUint16) width) + && wxGIFHandler_WriteWord(stream, (wxUint16) height); +} -bool wxGIFHandler::LoadFile( wxImage *image, wxInputStream& stream ) +#if wxUSE_PALETTE +bool wxGIFHandler_WriteTerm(wxOutputStream *stream) { - unsigned char *ptr, *src, *pal; - int file_size; - unsigned char* file_con; - IMAGEN igif; - int i; - gifDecoder *decod; + return wxGIFHandler_WriteByte(stream, GIF_MARKER_ENDOFDATA); +} +#endif - image->Destroy(); +bool wxGIFHandler_WriteZero(wxOutputStream *stream) +{ + return wxGIFHandler_WriteByte(stream, 0); +} - file_size = stream.StreamSize(); - file_con = (unsigned char*) malloc(file_size); - stream.Read(file_con, file_size); - decod = new gifDecoder(file_con, file_size); +bool wxGIFHandler_WritePalette(wxOutputStream *stream, + const wxRGB *array, size_t count, int bpp) +{ + wxUint8 buf[3]; + for (int i = 0; (i < (1 << bpp)); i++) + { + if (i < (int)count) + { + buf[0] = array[i].red; + buf[1] = array[i].green; + buf[2] = array[i].blue; + } + else + { + buf[0] = buf[1] = buf[2] = 0; + } - if (decod -> readgif(&igif) != E_OK) { - wxLogDebug("Error reading GIF"); - free(file_con); - delete decod; - return FALSE; + if ( !wxGIFHandler_Write(stream, buf, sizeof(buf)) ) + { + return false; + } } - free(file_con); - delete decod; - image->Create(igif.w, igif.h); - if (!image->Ok()) { - free(igif.pal); - free(igif.p); - return FALSE; + return true; +} + +bool wxGIFHandler_WriteControl(wxOutputStream *stream, + int maskIndex, int delayMilliSecs) +{ + wxUint8 buf[8]; + + buf[0] = GIF_MARKER_EXT; // extension marker + buf[1] = GIF_MARKER_EXT_GRAPHICS_CONTROL; + buf[2] = 4; // length of block + buf[3] = (maskIndex != wxNOT_FOUND) ? 1 : 0; // has transparency + buf[4] = delayMilliSecs / 10; // delay time + buf[5] = 0; + buf[6] = (maskIndex != wxNOT_FOUND) ? (wxUint8) maskIndex : 0; + buf[7] = 0; + return wxGIFHandler_Write(stream, buf, sizeof(buf)); +} + +bool wxGIFHandler_WriteComment(wxOutputStream *stream, const wxString& comment) +{ + if ( comment.empty() ) + { + return true; } - image->SetMask(FALSE); - ptr = image->GetData(); - src = igif.p; - pal = igif.pal; - for (i = 0; i < igif.w * igif.h; i++, src++) { - *(ptr++) = pal[3 * (*src) + 0]; - *(ptr++) = pal[3 * (*src) + 1]; - *(ptr++) = pal[3 * (*src) + 2]; + // Write comment header. + wxUint8 buf[2]; + buf[0] = GIF_MARKER_EXT; + buf[1] = GIF_MARKER_EXT_COMMENT; + if ( !wxGIFHandler_Write(stream, buf, sizeof(buf)) ) + { + return false; } - free(igif.pal); - free(igif.p); - return TRUE; + /* + If comment is longer than 255 bytes write it in blocks of maximum 255 + bytes each. + */ + wxCharBuffer text( comment.mb_str() ); + + size_t pos = 0, fullLength = text.length(); + + do + { + size_t blockLength = wxMin(fullLength - pos, 255); + + if ( !wxGIFHandler_WriteByte(stream, (wxUint8) blockLength) + || !wxGIFHandler_Write(stream, &text.data()[pos], blockLength) ) + { + return false; + } + + pos += blockLength; + }while (pos < fullLength); + + + // Write comment footer. + return wxGIFHandler_WriteZero(stream); } -bool wxGIFHandler::SaveFile( wxImage *image, wxOutputStream& stream ) +bool wxGIFHandler_WriteLoop(wxOutputStream *stream) { - wxLogDebug("wxGIFHandler is read-only!!"); - return FALSE; + wxUint8 buf[4]; + const int loopcount = 0; // infinite + + buf[0] = GIF_MARKER_EXT; + buf[1] = GIF_MARKER_EXT_APP; + buf[2] = 0x0B; + bool ok = wxGIFHandler_Write(stream, buf, 3) + && wxGIFHandler_Write(stream, NETSCAPE_LOOP, sizeof(NETSCAPE_LOOP)-1); + + buf[0] = 3; + buf[1] = 1; + buf[2] = loopcount & 0xFF; + buf[3] = loopcount >> 8; + + return ok && wxGIFHandler_Write(stream, buf, 4) + && wxGIFHandler_WriteZero(stream); } -//////////////////// Module: - -/* We haven't yet decided to go for the wxModule approach or - * explicit handler-adding: assuming the latter for now -- JACS - -class wxGIFModule : public wxModule +bool wxGIFHandler_BufferedOutput(wxOutputStream *stream, wxUint8 *buf, int c) { - DECLARE_DYNAMIC_CLASS(wxGIFModule) + bool ok = true; - public: - virtual bool OnInit() + if (c == FLUSH_OUTPUT) + { + // Flush everything out. + if (buf[0]) { - wxImage::AddHandler(new wxGIFHandler); - return TRUE; + ok = wxGIFHandler_Write(stream, buf, buf[0]+1); } - virtual void OnExit() {} -}; - -IMPLEMENT_DYNAMIC_CLASS(wxGIFModule, wxModule) + // Mark end of compressed data, by an empty block (see GIF doc): + wxGIFHandler_WriteZero(stream); + } + else + { + if (buf[0] == 255) + { + // Dump out this buffer - it is full: + ok = wxGIFHandler_Write(stream, buf, buf[0] + 1); + buf[0] = 0; + } + buf[++buf[0]] = c; + } -*/ + return ok; +} +#endif // wxUSE_STREAMS +#endif // wxUSE_IMAGE && wxUSE_GIF