X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5b4f8dd5c0fe1bf44fb7971c669b036d4ad6eebe..038072e2f1eaf2bf32d748a71234d767ebba8919:/src/common/gifdecod.cpp?ds=sidebyside diff --git a/src/common/gifdecod.cpp b/src/common/gifdecod.cpp index 35c13c01f7..78344a08c1 100644 --- a/src/common/gifdecod.cpp +++ b/src/common/gifdecod.cpp @@ -2,8 +2,8 @@ // Name: gifdecod.cpp // Purpose: wxGIFDecoder, GIF reader for wxImage and wxAnimation // Author: Guillermo Rodriguez Garcia <guille@iies.es> -// Version: 3.03 -// RCS-ID: +// Version: 3.04 +// RCS-ID: $Id$ // Copyright: (c) Guillermo Rodriguez Garcia // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -20,16 +20,15 @@ #endif #ifndef WX_PRECOMP -# include "wx/setup.h" +# include "wx/defs.h" #endif -#if wxUSE_STREAMS +#if wxUSE_STREAMS && wxUSE_GIF #include <stdlib.h> #include <string.h> -#include "wx/defs.h" #include "wx/gifdecod.h" - + //--------------------------------------------------------------------------- // wxGIFDecoder constructor and destructor @@ -37,7 +36,7 @@ wxGIFDecoder::wxGIFDecoder(wxInputStream *s, bool anim) { - m_f = s; + m_f = s; m_anim = anim; m_background = -1; @@ -58,18 +57,24 @@ wxGIFDecoder::~wxGIFDecoder() void wxGIFDecoder::Destroy() { - IMAGEN *pimg, *paux; + GIFImage *pimg, *paux; pimg = m_pfirst; while (pimg != NULL) { - paux = pimg->next; - free(pimg->p); - free(pimg->pal); - free(pimg); - pimg = paux; + paux = pimg->next; + free(pimg->p); + free(pimg->pal); + delete pimg; + pimg = paux; } + + m_pimage = NULL; + m_pfirst = NULL; + m_plast = NULL; + m_image = 0; + m_nimages = 0; } @@ -92,7 +97,7 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const image->Create(GetWidth(), GetHeight()); if (!image->Ok()) - return FALSE; + return FALSE; pal = GetPalette(); src = GetData(); @@ -102,37 +107,52 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const /* set transparent colour mask */ if (transparent != -1) { - for (i = 0; i < 256; i++) - { - if ((pal[3 * i + 0] == 255) && - (pal[3 * i + 1] == 0) && - (pal[3 * i + 2] == 255)) - { - pal[3 * i + 2] = 254; - } - } - - pal[3 * transparent + 0] = 255, - pal[3 * transparent + 1] = 0, - pal[3 * transparent + 2] = 255; - - image->SetMaskColour(255, 0, 255); + for (i = 0; i < 256; i++) + { + if ((pal[3 * i + 0] == 255) && + (pal[3 * i + 1] == 0) && + (pal[3 * i + 2] == 255)) + { + pal[3 * i + 2] = 254; + } + } + + pal[3 * transparent + 0] = 255, + pal[3 * transparent + 1] = 0, + pal[3 * transparent + 2] = 255; + + image->SetMaskColour(255, 0, 255); } else - image->SetMask(FALSE); + image->SetMask(FALSE); + + if (pal) + { + unsigned char* r = new unsigned char[256]; + unsigned char* g = new unsigned char[256]; + unsigned char* b = new unsigned char[256]; + for (i = 0; i < 256; i++) + { + r[i] = pal[3*i + 0]; + g[i] = pal[3*i + 1]; + b[i] = pal[3*i + 2]; + } + image->SetPalette(wxPalette(256, r, g, b)); + delete[] r; delete[] g; delete[] b; + } /* copy image data */ for (i = 0; i < (GetWidth() * GetHeight()); i++, src++) { - *(dst++) = pal[3 * (*src) + 0]; - *(dst++) = pal[3 * (*src) + 1]; - *(dst++) = pal[3 * (*src) + 2]; + *(dst++) = pal[3 * (*src) + 0]; + *(dst++) = pal[3 * (*src) + 1]; + *(dst++) = pal[3 * (*src) + 2]; } return TRUE; } - + //--------------------------------------------------------------------------- // Data accessors //--------------------------------------------------------------------------- @@ -166,7 +186,7 @@ bool wxGIFDecoder::IsAnimation() const { return (m_nimages > 1); } bool wxGIFDecoder::GoFirstFrame() { if (!IsAnimation()) - return FALSE; + return FALSE; m_image = 1; m_pimage = m_pfirst; @@ -176,7 +196,7 @@ bool wxGIFDecoder::GoFirstFrame() bool wxGIFDecoder::GoLastFrame() { if (!IsAnimation()) - return FALSE; + return FALSE; m_image = m_nimages; m_pimage = m_plast; @@ -186,45 +206,45 @@ bool wxGIFDecoder::GoLastFrame() bool wxGIFDecoder::GoNextFrame(bool cyclic) { if (!IsAnimation()) - return FALSE; + return FALSE; if ((m_image < m_nimages) || (cyclic)) { - m_pimage = m_pimage->next; - m_image++; + m_pimage = m_pimage->next; + m_image++; - if (!m_pimage) - { - m_image = 1; - m_pimage = m_pfirst; - } + if (!m_pimage) + { + m_image = 1; + m_pimage = m_pfirst; + } - return TRUE; + return TRUE; } else - return FALSE; -} + return FALSE; +} bool wxGIFDecoder::GoPrevFrame(bool cyclic) { if (!IsAnimation()) - return FALSE; + return FALSE; if ((m_image > 1) || (cyclic)) { - m_pimage = m_pimage->prev; - m_image--; + m_pimage = m_pimage->prev; + m_image--; - if (!m_pimage) - { - m_image = m_nimages; - m_pimage = m_plast; - } + if (!m_pimage) + { + m_image = m_nimages; + m_pimage = m_plast; + } - return TRUE; + return TRUE; } else - return FALSE; + return FALSE; } bool wxGIFDecoder::GoFrame(int which) @@ -232,19 +252,19 @@ bool wxGIFDecoder::GoFrame(int which) int i; if (!IsAnimation()) - return FALSE; + return FALSE; if ((which >= 1) && (which <= m_nimages)) { - m_pimage = m_pfirst; + m_pimage = m_pfirst; - for (i = 1; i < which; i++) - m_pimage = m_pimage->next; + for (i = 1; i < which; i++) + m_pimage = m_pimage->next; - return TRUE; + return TRUE; } else - return FALSE; + return FALSE; } @@ -268,33 +288,37 @@ int wxGIFDecoder::getcode(int bits, int ab_fin) /* keep reading new bytes while needed */ while (bits > m_restbits) { - /* if no bytes left in this block, read the next block */ - if (m_restbyte == 0) - { - m_restbyte = (unsigned char)m_f->GetC(); - - /* Some encoders are a bit broken: instead of issuing - * an end-of-image symbol (ab_fin) they come up with - * a zero-length subblock!! We catch this here so - * that the decoder sees an ab_fin code. - */ - if (m_restbyte == 0) - { - code = ab_fin; - break; - } - } - - /* read next byte and isolate the bits we need */ - m_lastbyte = (unsigned char)m_f->GetC(); - mask = (1 << (bits - m_restbits)) - 1; - code = code + ((m_lastbyte & mask) << m_restbits); - m_restbyte--; - - /* adjust total number of bits extracted from the buffer */ - m_restbits = m_restbits + 8; + /* if no bytes left in this block, read the next block */ + if (m_restbyte == 0) + { + m_restbyte = (unsigned char)m_f->GetC(); + + /* Some encoders are a bit broken: instead of issuing + * an end-of-image symbol (ab_fin) they come up with + * a zero-length subblock!! We catch this here so + * that the decoder sees an ab_fin code. + */ + if (m_restbyte == 0) + { + code = ab_fin; + break; + } + + /* prefetch data */ + m_f->Read((void *) m_buffer, m_restbyte); + m_bufp = m_buffer; + } + + /* read next byte and isolate the bits we need */ + m_lastbyte = (unsigned char) (*m_bufp++); + mask = (1 << (bits - m_restbits)) - 1; + code = code + ((m_lastbyte & mask) << m_restbits); + m_restbyte--; + + /* adjust total number of bits extracted from the buffer */ + m_restbits = m_restbits + 8; } - + /* find number of bits remaining for next code */ m_restbits = (m_restbits - bits); @@ -306,20 +330,19 @@ int wxGIFDecoder::getcode(int bits, int ab_fin) // GIF decoding function. The initial code size (aka root size) // is 'bits'. Supports interlaced images (interl == 1). // -int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) +int wxGIFDecoder::dgif(GIFImage *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 */ - unsigned int x, y; /* position in image buffer */ + int *ab_prefix = new int[4096]; /* alphabet (prefixes) */ + int *ab_tail = new int[4096]; /* alphabet (tails) */ + int *stack = new int[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 */ + unsigned int x, y; /* position in image buffer */ int code, readcode, lastcode, abcabca; @@ -336,105 +359,109 @@ int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) pass = 1; pos = x = y = 0; - /* reset static globals */ + /* reset decoder vars */ m_restbits = 0; m_restbyte = 0; m_lastbyte = 0; do { - /* get next code */ - readcode = code = getcode(ab_bits, ab_fin); - - /* end of image? */ - if (code == ab_fin) break; - - /* reset alphabet? */ - if (code == ab_clr) - { - /* 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; - } - - /* unknown code: special case (like in ABCABCA) */ - if (code >= ab_free) - { - code = lastcode; /* take last string */ - stack[pos++] = abcabca; /* add first character */ - } - - /* build the string for this code in the stack */ - while (code > ab_clr) - { - stack[pos++] = ab_tail[code]; - code = ab_prefix[code]; - } - 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) - { - ab_prefix[ab_free] = lastcode; - ab_tail[ab_free] = code; - ab_free++; - - if ((ab_free > ab_max) && (ab_bits < 12)) - { - ab_bits++; - ab_max = (1 << ab_bits) - 1; - } - } - - /* dump stack data to the buffer */ - while (pos >= 0) - { - (img->p)[x + (y * (img->w))] = (char)stack[pos--]; - - if (++x >= (img->w)) - { - x = 0; - - 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 - { - /* non-interlaced */ - y++; - } - } - } - - pos = 0; - lastcode = readcode; + /* get next code */ + readcode = code = getcode(ab_bits, ab_fin); + + /* end of image? */ + if (code == ab_fin) break; + + /* reset alphabet? */ + if (code == ab_clr) + { + /* 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; + } + + /* unknown code: special case (like in ABCABCA) */ + if (code >= ab_free) + { + code = lastcode; /* take last string */ + stack[pos++] = abcabca; /* add first character */ + } + + /* build the string for this code in the stack */ + while (code > ab_clr) + { + stack[pos++] = ab_tail[code]; + code = ab_prefix[code]; + } + 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) + { + ab_prefix[ab_free] = lastcode; + ab_tail[ab_free] = code; + ab_free++; + + if ((ab_free > ab_max) && (ab_bits < 12)) + { + ab_bits++; + ab_max = (1 << ab_bits) - 1; + } + } + + /* dump stack data to the buffer */ + while (pos >= 0) + { + (img->p)[x + (y * (img->w))] = (char)stack[pos--]; + + if (++x >= (img->w)) + { + x = 0; + + 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 + { + /* non-interlaced */ + y++; + } + } + } + + pos = 0; + lastcode = readcode; } while (code != ab_fin); + delete [] ab_prefix ; + delete [] ab_tail ; + delete [] stack ; + return 0; } @@ -445,12 +472,9 @@ int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) bool wxGIFDecoder::CanRead() { unsigned char buf[3]; - off_t pos; - pos = m_f->TellI(); - m_f->SeekI(0, wxFromStart); m_f->Read(buf, 3); - m_f->SeekI(pos, wxFromStart); + m_f->SeekI(-3, wxFromCurrent); return (memcmp(buf, "GIF", 3) == 0); } @@ -461,30 +485,30 @@ bool wxGIFDecoder::CanRead() // animated GIF support is enabled. Can read GIFs with any bit // size (color depth), but the output images are always expanded // to 8 bits per pixel. Also, the image palettes always contain -// 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 header file). +// 256 colors, although some of them may be unused. Returns wxGIF_OK +// (== 0) on success, or an error code if something fails (see +// header file for details) // int wxGIFDecoder::ReadGIF() { int ncolors, bits, interl, transparent, disposal, i; long size; long delay; - unsigned char type; + unsigned char type = 0; unsigned char pal[768]; unsigned char buf[16]; - IMAGEN **ppimg, *pimg, *pprev; + GIFImage **ppimg; + GIFImage *pimg, *pprev; /* check GIF signature */ if (!CanRead()) - return E_FORMATO; + return wxGIF_INVFORMAT; - /* check for and animated GIF support (ver. >= 89a) */ - m_f->SeekI(0, wxFromStart); + /* check for animated GIF support (ver. >= 89a) */ m_f->Read(buf, 6); if (memcmp(buf + 3, "89a", 3) < 0) - m_anim = FALSE; + m_anim = FALSE; /* read logical screen descriptor block (LSDB) */ m_f->Read(buf, 7); @@ -494,10 +518,10 @@ int wxGIFDecoder::ReadGIF() /* load global color map if available */ if ((buf[4] & 0x80) == 0x80) { - m_background = buf[5]; + m_background = buf[5]; - ncolors = 2 << (buf[4] & 0x07); - m_f->Read(pal, 3 * ncolors); + ncolors = 2 << (buf[4] & 0x07); + m_f->Read(pal, 3 * ncolors); } /* transparent colour, disposal method and delay default to unused */ @@ -510,120 +534,164 @@ int wxGIFDecoder::ReadGIF() pprev = NULL; pimg = NULL; - while (1) + bool done = FALSE; + + while(!done) { - type = (unsigned char)m_f->GetC(); - - /* end of data? */ - if (type == 0x3B) - break; - - /* extension block? */ - if (type == 0x21) - { - if (((unsigned char)m_f->GetC()) == 0xF9) - /* graphics control extension, parse it */ - { - m_f->Read(buf, 6); - - /* read delay and convert from 1/100 of a second to ms */ - delay = 10 * (buf[2] + 256 * buf[3]); - - /* read transparent colour index, if used */ - if (buf[1] & 0x01) - transparent = buf[4]; - - /* read disposal method */ - disposal = (buf[1] & 0x1C) - 1; - } - else - /* other extension, skip */ - { - while ((i = (unsigned char)m_f->GetC()) != 0) - { - /* This line should not be neccessary! - * Some images are not loaded correctly - * without it. A bug in wxStream? - * Yes. Fixed now. - */ - // m_f->SeekI(m_f->TellI(), wxFromStart); - - m_f->SeekI(i, wxFromCurrent); - } - } - } - - /* image descriptor block? */ - if (type == 0x2C) - { - /* allocate memory for IMAGEN struct */ - pimg = (*ppimg) = (IMAGEN *) malloc(sizeof(IMAGEN)); - - if (pimg == NULL) - { - Destroy(); - return E_MEMORIA; - } - - /* fill in the data */ - m_f->Read(buf, 9); - pimg->left = buf[4] + 256 * buf[5]; - pimg->top = buf[4] + 256 * buf[5]; - pimg->w = buf[4] + 256 * buf[5]; - pimg->h = buf[6] + 256 * buf[7]; - interl = ((buf[8] & 0x40)? 1 : 0); - size = pimg->w * pimg->h; - - pimg->transparent = transparent; - pimg->disposal = disposal; - pimg->delay = delay; - pimg->next = NULL; - pimg->prev = pprev; - pprev = pimg; - ppimg = &pimg->next; - - /* allocate memory for image and palette */ - pimg->p = (unsigned char *) malloc(size); - pimg->pal = (unsigned char *) malloc(768); - - if ((!pimg->p) || (!pimg->pal)) - { - Destroy(); - return E_MEMORIA; - } - - /* load local color map if available, else use global map */ - if ((buf[8] & 0x80) == 0x80) - { - ncolors = 2 << (buf[8] & 0x07); - m_f->Read(pimg->pal, 3 * ncolors); - } - else - memcpy(pimg->pal, pal, 768); - - /* get initial code size from first byte in raster data */ - bits = (unsigned char)m_f->GetC(); - - /* decode image */ - dgif(pimg, interl, bits); - - m_nimages++; - } - - /* if we have one image and no animated GIF support, exit */ - if (m_nimages == 1 && !m_anim) - break; + type = (unsigned char)m_f->GetC(); + + /* end of data? */ + if (type == 0x3B) + { + done = TRUE; + } + else + /* extension block? */ + if (type == 0x21) + { + if (((unsigned char)m_f->GetC()) == 0xF9) + /* graphics control extension, parse it */ + { + m_f->Read(buf, 6); + + /* read delay and convert from 1/100 of a second to ms */ + delay = 10 * (buf[2] + 256 * buf[3]); + + /* read transparent colour index, if used */ + if (buf[1] & 0x01) + transparent = buf[4]; + + /* read disposal method */ + disposal = (buf[1] & 0x1C) - 1; + } + else + /* other extension, skip */ + { + while ((i = (unsigned char)m_f->GetC()) != 0) + { + m_f->SeekI(i, wxFromCurrent); + } + } + } + else + /* image descriptor block? */ + if (type == 0x2C) + { + /* allocate memory for IMAGEN struct */ + pimg = (*ppimg) = new GIFImage(); + + if (pimg == NULL) + { + Destroy(); + return wxGIF_MEMERR; + } + + /* fill in the data */ + m_f->Read(buf, 9); + pimg->left = buf[0] + 256 * buf[1]; + pimg->top = buf[2] + 256 * buf[3]; +/* + pimg->left = buf[4] + 256 * buf[5]; + pimg->top = buf[4] + 256 * buf[5]; +*/ + pimg->w = buf[4] + 256 * buf[5]; + pimg->h = buf[6] + 256 * buf[7]; + interl = ((buf[8] & 0x40)? 1 : 0); + size = pimg->w * pimg->h; + + pimg->transparent = transparent; + pimg->disposal = disposal; + pimg->delay = delay; + pimg->next = NULL; + pimg->prev = pprev; + pprev = pimg; + ppimg = &pimg->next; + + /* allocate memory for image and palette */ + pimg->p = (unsigned char *) malloc((size_t)size); + pimg->pal = (unsigned char *) malloc(768); + + if ((!pimg->p) || (!pimg->pal)) + { + Destroy(); + return wxGIF_MEMERR; + } + + /* load local color map if available, else use global map */ + if ((buf[8] & 0x80) == 0x80) + { + ncolors = 2 << (buf[8] & 0x07); + m_f->Read(pimg->pal, 3 * ncolors); + } + else + memcpy(pimg->pal, pal, 768); + + /* get initial code size from first byte in raster data */ + bits = (unsigned char)m_f->GetC(); + + /* decode image */ + dgif(pimg, interl, bits); + m_nimages++; + + /* if this is not an animated GIF, exit after first image */ + if (!m_anim) + done = TRUE; + } } - /* finish successfully :-) */ + /* setup image pointers */ if (m_nimages != 0) { - m_image = 1; - m_plast = pimg; - m_pimage = m_pfirst; + m_image = 1; + m_plast = pimg; + m_pimage = m_pfirst; + } + + /* try to read to the end of the stream */ + while (type != 0x3B) + { + type = (unsigned char)m_f->GetC(); + + if (type == 0x21) + { + /* extension type */ + (void) m_f->GetC(); + + /* skip all data */ + while ((i = (unsigned char)m_f->GetC()) != 0) + { + m_f->SeekI(i, wxFromCurrent); + } + } + else if (type == 0x2C) + { + /* image descriptor block */ + m_f->Read(buf, 9); + + /* local color map */ + if ((buf[8] & 0x80) == 0x80) + { + ncolors = 2 << (buf[8] & 0x07); + m_f->SeekI(3 * ncolors, wxFromCurrent); + } + + /* initial code size */ + (void) m_f->GetC(); + + /* skip all data */ + while ((i = (unsigned char)m_f->GetC()) != 0) + { + m_f->SeekI(i, wxFromCurrent); + } + } + else if ((type != 0x3B) && (type != 00)) /* testing */ + { + /* images are OK, but couldn't read to the end of the stream */ + return wxGIF_TRUNCATED; + } } - return E_OK; + return wxGIF_OK; } -#endif // wxUSE_STREAM +#endif // wxUSE_STREAMS && wxUSE_GIF