X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5b4f8dd5c0fe1bf44fb7971c669b036d4ad6eebe..7c081bd21c8f2a6331eae678328f23e45224b917:/src/common/gifdecod.cpp diff --git a/src/common/gifdecod.cpp b/src/common/gifdecod.cpp index 35c13c01f7..3ca73d91a6 100644 --- a/src/common/gifdecod.cpp +++ b/src/common/gifdecod.cpp @@ -1,54 +1,82 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: gifdecod.cpp +// Name: src/common/gifdecod.cpp // Purpose: wxGIFDecoder, GIF reader for wxImage and wxAnimation // Author: Guillermo Rodriguez Garcia -// Version: 3.03 -// RCS-ID: +// Version: 3.04 +// RCS-ID: $Id$ // Copyright: (c) Guillermo Rodriguez Garcia // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "gifdecod.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ -# pragma hdrstop + #pragma hdrstop #endif +#if wxUSE_STREAMS && wxUSE_GIF + #ifndef WX_PRECOMP -# include "wx/setup.h" + #include "wx/palette.h" #endif -#if wxUSE_STREAMS - #include #include -#include "wx/defs.h" #include "wx/gifdecod.h" - + + + +//--------------------------------------------------------------------------- +// GIFImage +//--------------------------------------------------------------------------- + +// internal class for storing GIF image data +class GIFImage +{ +public: + // def ctor + GIFImage(); + + unsigned int w; /* width */ + unsigned int h; /* height */ + unsigned int left; /* x coord (in logical screen) */ + unsigned int top; /* y coord (in logical screen) */ + int transparent; /* transparent color index (-1 = none) */ + wxAnimationDisposal disposal; /* disposal method */ + long delay; /* delay in ms (-1 = unused) */ + unsigned char *p; /* bitmap */ + unsigned char *pal; /* palette */ + unsigned int ncolours; /* number of colours */ + + DECLARE_NO_COPY_CLASS(GIFImage) +}; + + + +//--------------------------------------------------------------------------- +// GIFImage constructor +//--------------------------------------------------------------------------- +GIFImage::GIFImage() +{ + w = 0; + h = 0; + left = 0; + top = 0; + transparent = 0; + disposal = wxANIM_DONOTREMOVE; + delay = -1; + p = (unsigned char *) NULL; + pal = (unsigned char *) NULL; + ncolours = 0; +} //--------------------------------------------------------------------------- // wxGIFDecoder constructor and destructor //--------------------------------------------------------------------------- -wxGIFDecoder::wxGIFDecoder(wxInputStream *s, bool anim) +wxGIFDecoder::wxGIFDecoder() { - m_f = s; - m_anim = anim; - - m_background = -1; - m_screenw = 0; - m_screenh = 0; - - m_pimage = NULL; - m_pfirst = NULL; - m_plast = NULL; - m_image = 0; - m_nimages = 0; } wxGIFDecoder::~wxGIFDecoder() @@ -58,18 +86,17 @@ wxGIFDecoder::~wxGIFDecoder() void wxGIFDecoder::Destroy() { - IMAGEN *pimg, *paux; - - pimg = m_pfirst; - - while (pimg != NULL) + wxASSERT(m_nFrames==m_frames.GetCount()); + for (size_t i=0; inext; - free(pimg->p); - free(pimg->pal); - free(pimg); - pimg = paux; + GIFImage *f = (GIFImage*)m_frames[i]; + free(f->p); + free(f->pal); + delete f; } + + m_frames.Clear(); + m_nFrames = 0; } @@ -79,7 +106,7 @@ void wxGIFDecoder::Destroy() // This function was designed by Vaclav Slavik -bool wxGIFDecoder::ConvertToImage(wxImage *image) const +bool wxGIFDecoder::ConvertToImage(size_t frame, wxImage *image) const { unsigned char *src, *dst, *pal; unsigned long i; @@ -89,20 +116,21 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const image->Destroy(); /* create the image */ - image->Create(GetWidth(), GetHeight()); + wxSize sz = GetFrameSize(frame); + image->Create(sz.GetWidth(), sz.GetHeight()); if (!image->Ok()) - return FALSE; + return false; - pal = GetPalette(); - src = GetData(); + pal = GetPalette(frame); + src = GetData(frame); dst = image->GetData(); - transparent = GetTransparentColour(); + transparent = GetTransparentColourIndex(frame); /* set transparent colour mask */ if (transparent != -1) { - for (i = 0; i < 256; i++) + for (i = 0; i < GetNcolours(frame); i++) { if ((pal[3 * i + 0] == 255) && (pal[3 * i + 1] == 0) && @@ -119,133 +147,82 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const image->SetMaskColour(255, 0, 255); } else - image->SetMask(FALSE); + image->SetMask(false); + +#if wxUSE_PALETTE + unsigned char r[256]; + unsigned char g[256]; + unsigned char b[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(GetNcolours(frame), r, g, b)); +#endif // wxUSE_PALETTE /* copy image data */ - for (i = 0; i < (GetWidth() * GetHeight()); i++, src++) + unsigned long npixel = sz.GetWidth() * sz.GetHeight(); + for (i = 0; i < npixel; i++, src++) { *(dst++) = pal[3 * (*src) + 0]; *(dst++) = pal[3 * (*src) + 1]; *(dst++) = pal[3 * (*src) + 2]; } - return TRUE; + return true; } - + //--------------------------------------------------------------------------- // Data accessors //--------------------------------------------------------------------------- -// Get data for current frame - -int wxGIFDecoder::GetFrameIndex() const { return m_image; } -unsigned char* wxGIFDecoder::GetData() const { return (m_pimage->p); } -unsigned char* wxGIFDecoder::GetPalette() const { return (m_pimage->pal); } -unsigned int wxGIFDecoder::GetWidth() const { return (m_pimage->w); } -unsigned int wxGIFDecoder::GetHeight() const { return (m_pimage->h); } -unsigned int wxGIFDecoder::GetTop() const { return (m_pimage->top); } -unsigned int wxGIFDecoder::GetLeft() const { return (m_pimage->left); } -int wxGIFDecoder::GetTransparentColour() const { return (m_pimage->transparent); } -int wxGIFDecoder::GetDisposalMethod() const { return (m_pimage->disposal); } -long wxGIFDecoder::GetDelay() const { return (m_pimage->delay); } - -// Get global data +#define GetFrame(n) ((GIFImage*)m_frames[n]) -unsigned int wxGIFDecoder::GetLogicalScreenWidth() const { return m_screenw; } -unsigned int wxGIFDecoder::GetLogicalScreenHeight() const { return m_screenh; } -int wxGIFDecoder::GetBackgroundColour() const { return m_background; } -int wxGIFDecoder::GetNumberOfFrames() const { return m_nimages; } -bool wxGIFDecoder::IsAnimation() const { return (m_nimages > 1); } +// Get data for current frame -//--------------------------------------------------------------------------- -// Functions to move through the animation -//--------------------------------------------------------------------------- - -bool wxGIFDecoder::GoFirstFrame() +wxSize wxGIFDecoder::GetFrameSize(size_t frame) const { - if (!IsAnimation()) - return FALSE; - - m_image = 1; - m_pimage = m_pfirst; - return TRUE; + return wxSize(GetFrame(frame)->w, GetFrame(frame)->h); } -bool wxGIFDecoder::GoLastFrame() +wxPoint wxGIFDecoder::GetFramePosition(size_t frame) const { - if (!IsAnimation()) - return FALSE; - - m_image = m_nimages; - m_pimage = m_plast; - return TRUE; + return wxPoint(GetFrame(frame)->left, GetFrame(frame)->top); } -bool wxGIFDecoder::GoNextFrame(bool cyclic) +wxAnimationDisposal wxGIFDecoder::GetDisposalMethod(size_t frame) const { - if (!IsAnimation()) - return FALSE; - - if ((m_image < m_nimages) || (cyclic)) - { - m_pimage = m_pimage->next; - m_image++; - - if (!m_pimage) - { - m_image = 1; - m_pimage = m_pfirst; - } - - return TRUE; - } - else - return FALSE; -} + return GetFrame(frame)->disposal; +} -bool wxGIFDecoder::GoPrevFrame(bool cyclic) +long wxGIFDecoder::GetDelay(size_t frame) const { - if (!IsAnimation()) - return FALSE; - - if ((m_image > 1) || (cyclic)) - { - m_pimage = m_pimage->prev; - m_image--; - - if (!m_pimage) - { - m_image = m_nimages; - m_pimage = m_plast; - } - - return TRUE; - } - else - return FALSE; + return GetFrame(frame)->delay; } -bool wxGIFDecoder::GoFrame(int which) +wxColour wxGIFDecoder::GetTransparentColour(size_t frame) const { - int i; + unsigned char *pal = GetFrame(frame)->pal; + int n = GetFrame(frame)->transparent; + if (n == -1) + return wxNullColour; + + return wxColour(pal[n*3 + 0], + pal[n*3 + 1], + pal[n*3 + 2]); +} - if (!IsAnimation()) - return FALSE; +unsigned char* wxGIFDecoder::GetData(size_t frame) const { return (GetFrame(frame)->p); } +unsigned char* wxGIFDecoder::GetPalette(size_t frame) const { return (GetFrame(frame)->pal); } +unsigned int wxGIFDecoder::GetNcolours(size_t frame) const { return (GetFrame(frame)->ncolours); } +int wxGIFDecoder::GetTransparentColourIndex(size_t frame) const { return (GetFrame(frame)->transparent); } - if ((which >= 1) && (which <= m_nimages)) - { - m_pimage = m_pfirst; - - for (i = 1; i < which; i++) - m_pimage = m_pimage->next; - - return TRUE; - } - else - return FALSE; -} //--------------------------------------------------------------------------- @@ -255,12 +232,11 @@ bool wxGIFDecoder::GoFrame(int which) // getcode: // Reads the next code from the file stream, with size 'bits' // -int wxGIFDecoder::getcode(int bits, int ab_fin) +int wxGIFDecoder::getcode(wxInputStream& stream, int bits, int ab_fin) { unsigned int mask; /* bit mask */ unsigned int code; /* code (result) */ - /* get remaining bits from last byte read */ mask = (1 << bits) - 1; code = (m_lastbyte >> (8 - m_restbits)) & mask; @@ -271,7 +247,7 @@ int wxGIFDecoder::getcode(int bits, int ab_fin) /* if no bytes left in this block, read the next block */ if (m_restbyte == 0) { - m_restbyte = (unsigned char)m_f->GetC(); + m_restbyte = (unsigned char)stream.GetC(); /* Some encoders are a bit broken: instead of issuing * an end-of-image symbol (ab_fin) they come up with @@ -283,10 +259,19 @@ int wxGIFDecoder::getcode(int bits, int ab_fin) code = ab_fin; break; } + + /* prefetch data */ + stream.Read((void *) m_buffer, m_restbyte); + if (stream.LastRead() != m_restbyte) + { + code = ab_fin; + return code; + } + m_bufp = m_buffer; } /* read next byte and isolate the bits we need */ - m_lastbyte = (unsigned char)m_f->GetC(); + m_lastbyte = (unsigned char) (*m_bufp++); mask = (1 << (bits - m_restbits)) - 1; code = code + ((m_lastbyte & mask) << m_restbits); m_restbyte--; @@ -294,7 +279,7 @@ int wxGIFDecoder::getcode(int bits, int ab_fin) /* 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); @@ -305,21 +290,40 @@ int wxGIFDecoder::getcode(int bits, int ab_fin) // dgif: // 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) +// Returns wxGIF_OK (== 0) on success, or an error code if something +// fails (see header file for details) +wxGIFErrorCode wxGIFDecoder::dgif(wxInputStream& stream, 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 */ + static const int allocSize = 4096 + 1; + int *ab_prefix = new int[allocSize]; /* alphabet (prefixes) */ + if (ab_prefix == NULL) + { + return wxGIF_MEMERR; + } + + int *ab_tail = new int[allocSize]; /* alphabet (tails) */ + if (ab_tail == NULL) + { + delete[] ab_prefix; + return wxGIF_MEMERR; + } + + int *stack = new int[allocSize]; /* decompression stack */ + if (stack == NULL) + { + delete[] ab_prefix; + delete[] ab_tail; + return wxGIF_MEMERR; + } + + 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,7 +340,7 @@ 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; @@ -344,7 +348,7 @@ int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) do { /* get next code */ - readcode = code = getcode(ab_bits, ab_fin); + readcode = code = getcode(stream, ab_bits, ab_fin); /* end of image? */ if (code == ab_fin) break; @@ -375,13 +379,52 @@ int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) { stack[pos++] = ab_tail[code]; code = ab_prefix[code]; + + // Don't overflow. This shouldn't happen with normal + // GIF files, the allocSize of 4096+1 is enough. This + // will only happen with badly formed GIFs. + if (pos >= allocSize) + { + delete[] ab_prefix; + delete[] ab_tail; + delete[] stack; + return wxGIF_INVFORMAT; + } } + + if (pos >= allocSize) + { + delete[] ab_prefix; + delete[] ab_tail; + delete[] stack; + return wxGIF_INVFORMAT; + } + 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) { + // Normally, after the alphabet is full and can't grow any + // further (ab_free == 4096), encoder should (must?) emit CLEAR + // to reset it. This checks whether we really got it, otherwise + // the GIF is damaged. + if (ab_free > ab_max) + { + delete[] ab_prefix; + delete[] ab_tail; + delete[] stack; + return wxGIF_INVFORMAT; + } + + // This assert seems unnecessary since the condition above + // eliminates the only case in which it went false. But I really + // don't like being forced to ask "Who in .text could have + // written there?!" And I wouldn't have been forced to ask if + // this line had already been here. + wxASSERT(ab_free < allocSize); + ab_prefix[ab_free] = lastcode; ab_tail[ab_free] = code; ab_free++; @@ -393,10 +436,11 @@ int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) } } - /* dump stack data to the buffer */ + /* dump stack data to the image buffer */ while (pos >= 0) { - (img->p)[x + (y * (img->w))] = (char)stack[pos--]; + (img->p)[x + (y * (img->w))] = (char) stack[pos]; + pos--; if (++x >= (img->w)) { @@ -412,13 +456,50 @@ int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) case 3: y += 4; break; case 4: y += 2; break; } - if (y >= (img->h)) + + /* loop until a valid y coordinate has been + found, Or if the maximum number of passes has + been reached, exit the loop, and stop image + decoding (At this point the image is successfully + decoded). + If we don't loop, but merely set y to some other + value, that new value might still be invalid depending + on the height of the image. This would cause out of + bounds writing. + */ + while (y >= (img->h)) { switch (++pass) { case 2: y = 4; break; case 3: y = 2; break; case 4: y = 1; break; + + default: + /* + It's possible we arrive here. For example this + happens when the image is interlaced, and the + height is 1. Looking at the above cases, the + lowest possible y is 1. While the only valid + one would be 0 for an image of height 1. So + 'eventually' the loop will arrive here. + This case makes sure this while loop is + exited, as well as the 2 other ones. + */ + + // Set y to a valid coordinate so the local + // while loop will be exited. (y = 0 always + // is >= img->h since if img->h == 0 the + // image is never decoded) + y = 0; + + // This will exit the other outer while loop + pos = -1; + + // This will halt image decoding. + code = ab_fin; + + break; } } } @@ -426,6 +507,46 @@ int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) { /* non-interlaced */ y++; +/* +Normally image decoding is finished when an End of Information code is +encountered (code == ab_fin) however some broken encoders write wrong +"block byte counts" (The first byte value after the "code size" byte), +being one value too high. It might very well be possible other variants +of this problem occur as well. The only sensible solution seems to +be to check for clipping. +Example of wrong encoding: +(1 * 1 B/W image, raster data stream follows in hex bytes) + +02 << B/W images have a code size of 2 +02 << Block byte count +44 << LZW packed +00 << Zero byte count (terminates data stream) + +Because the block byte count is 2, the zero byte count is used in the +decoding process, and decoding is continued after this byte. (While it +should signal an end of image) + +It should be: +02 +02 +44 +01 << When decoded this correctly includes the End of Information code +00 + +Or (Worse solution): +02 +01 +44 +00 +(The 44 doesn't include an End of Information code, but at least the +decoder correctly skips to 00 now after decoding, and signals this +as an End of Information itself) +*/ + if (y >= img->h) + { + code = ab_fin; + break; + } } } } @@ -435,96 +556,149 @@ int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) } while (code != ab_fin); - return 0; + delete [] ab_prefix ; + delete [] ab_tail ; + delete [] stack ; + + return wxGIF_OK; } // CanRead: -// Returns TRUE if the file looks like a valid GIF, FALSE otherwise. +// Returns true if the file looks like a valid GIF, false otherwise. // -bool wxGIFDecoder::CanRead() +bool wxGIFDecoder::CanRead(wxInputStream &stream) const { 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); + if ( !stream.Read(buf, WXSIZEOF(buf)) ) + return false; + + stream.SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent); - return (memcmp(buf, "GIF", 3) == 0); + return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0; } -// ReadGIF: +// LoadGIF: // Reads and decodes one or more GIF images, depending on whether // 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() +wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) { - int ncolors, bits, interl, transparent, disposal, i; + unsigned int global_ncolors = 0; + int bits, interl, transparent, i; + wxAnimationDisposal disposal; long size; long delay; - unsigned char type; + unsigned char type = 0; unsigned char pal[768]; unsigned char buf[16]; - IMAGEN **ppimg, *pimg, *pprev; + bool anim = true; /* check GIF signature */ - if (!CanRead()) - return E_FORMATO; + if (!CanRead(stream)) + return wxGIF_INVFORMAT; - /* check for and animated GIF support (ver. >= 89a) */ - m_f->SeekI(0, wxFromStart); - m_f->Read(buf, 6); + /* check for animated GIF support (ver. >= 89a) */ + + static const size_t headerSize = (3 + 3); + stream.Read(buf, headerSize); + if (stream.LastRead() != headerSize) + { + return wxGIF_INVFORMAT; + } if (memcmp(buf + 3, "89a", 3) < 0) - m_anim = FALSE; + { + anim = false; + } /* read logical screen descriptor block (LSDB) */ - m_f->Read(buf, 7); - m_screenw = buf[0] + 256 * buf[1]; - m_screenh = buf[2] + 256 * buf[3]; + static const size_t lsdbSize = (2 + 2 + 1 + 1 + 1); + stream.Read(buf, lsdbSize); + if (stream.LastRead() != lsdbSize) + { + return wxGIF_INVFORMAT; + } + + m_szAnimation.SetWidth( buf[0] + 256 * buf[1] ); + m_szAnimation.SetHeight( buf[2] + 256 * buf[3] ); + + if (anim && ((m_szAnimation.GetWidth() == 0) || (m_szAnimation.GetHeight() == 0))) + { + return wxGIF_INVFORMAT; + } /* load global color map if available */ if ((buf[4] & 0x80) == 0x80) { - m_background = buf[5]; + int backgroundColIndex = buf[5]; + + global_ncolors = 2 << (buf[4] & 0x07); + size_t numBytes = 3 * global_ncolors; + stream.Read(pal, numBytes); + if (stream.LastRead() != numBytes) + { + return wxGIF_INVFORMAT; + } - ncolors = 2 << (buf[4] & 0x07); - m_f->Read(pal, 3 * ncolors); + m_background.Set(pal[backgroundColIndex*3 + 0], + pal[backgroundColIndex*3 + 1], + pal[backgroundColIndex*3 + 2]); } /* transparent colour, disposal method and delay default to unused */ transparent = -1; - disposal = -1; + disposal = wxANIM_UNSPECIFIED; delay = -1; - /* read images */ - ppimg = &m_pfirst; - pprev = NULL; - pimg = NULL; - - while (1) + bool done = false; + while (!done) { - type = (unsigned char)m_f->GetC(); + type = (unsigned char)stream.GetC(); + + /* + If the end of file has been reached (or an error) and a ";" + (0x3B) hasn't been encountered yet, exit the loop. (Without this + check the while loop would loop endlessly.) Later on, in the next while + loop, the file will be treated as being truncated (But still + be decoded as far as possible). returning wxGIF_TRUNCATED is not + possible here since some init code is done after this loop. + */ + if (stream.Eof())// || !stream.IsOk()) + { + /* + type is set to some bogus value, so there's no + need to continue evaluating it. + */ + break; // Alternative : "return wxGIF_INVFORMAT;" + } /* end of data? */ if (type == 0x3B) - break; - + { + done = true; + } + else /* extension block? */ if (type == 0x21) { - if (((unsigned char)m_f->GetC()) == 0xF9) + if (((unsigned char)stream.GetC()) == 0xF9) /* graphics control extension, parse it */ { - m_f->Read(buf, 6); + static const size_t gceSize = 6; + stream.Read(buf, gceSize); + if (stream.LastRead() != gceSize) + { + Destroy(); + return wxGIF_INVFORMAT; + } /* read delay and convert from 1/100 of a second to ms */ delay = 10 * (buf[2] + 256 * buf[3]); @@ -534,96 +708,198 @@ int wxGIFDecoder::ReadGIF() transparent = buf[4]; /* read disposal method */ - disposal = (buf[1] & 0x1C) - 1; + disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1); } else /* other extension, skip */ - { - while ((i = (unsigned char)m_f->GetC()) != 0) + { + while ((i = (unsigned char)stream.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); + if (stream.Eof() || (stream.LastRead() == 0)) + { + done = true; + break; + } + stream.SeekI(i, wxFromCurrent); } } } - + else /* image descriptor block? */ if (type == 0x2C) { /* allocate memory for IMAGEN struct */ - pimg = (*ppimg) = (IMAGEN *) malloc(sizeof(IMAGEN)); + GIFImage *pimg = new GIFImage(); if (pimg == NULL) { Destroy(); - return E_MEMORIA; + return wxGIF_MEMERR; } /* fill in the data */ - m_f->Read(buf, 9); + static const size_t idbSize = (2 + 2 + 2 + 2 + 1); + stream.Read(buf, idbSize); + if (stream.LastRead() != idbSize) + { + Destroy(); + return wxGIF_INVFORMAT; + } + + 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]; + + if (anim && ((pimg->w == 0) || (pimg->w > (unsigned int)m_szAnimation.GetWidth()) || + (pimg->h == 0) || (pimg->h > (unsigned int)m_szAnimation.GetHeight()))) + { + Destroy(); + return wxGIF_INVFORMAT; + } + 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->p = (unsigned char *) malloc((size_t)size); pimg->pal = (unsigned char *) malloc(768); if ((!pimg->p) || (!pimg->pal)) { Destroy(); - return E_MEMORIA; + 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); + unsigned int local_ncolors = 2 << (buf[8] & 0x07); + size_t numBytes = 3 * local_ncolors; + stream.Read(pimg->pal, numBytes); + pimg->ncolours = local_ncolors; + if (stream.LastRead() != numBytes) + { + Destroy(); + return wxGIF_INVFORMAT; + } } else + { memcpy(pimg->pal, pal, 768); + pimg->ncolours = global_ncolors; + } /* get initial code size from first byte in raster data */ - bits = (unsigned char)m_f->GetC(); + bits = (unsigned char)stream.GetC(); + if (bits == 0) + { + Destroy(); + return wxGIF_INVFORMAT; + } /* decode image */ - dgif(pimg, interl, bits); + wxGIFErrorCode result = dgif(stream, pimg, interl, bits); + if (result != wxGIF_OK) + { + Destroy(); + return result; + } + + /* add the image to our frame array */ + m_frames.Add((void*)pimg); + m_nFrames++; - m_nimages++; + /* if this is not an animated GIF, exit after first image */ + if (!anim) + done = true; } + } - /* if we have one image and no animated GIF support, exit */ - if (m_nimages == 1 && !m_anim) - break; + if (m_nFrames <= 0) + { + Destroy(); + return wxGIF_INVFORMAT; } - /* finish successfully :-) */ - if (m_nimages != 0) + /* try to read to the end of the stream */ + while (type != 0x3B) { - m_image = 1; - m_plast = pimg; - m_pimage = m_pfirst; + if (!stream.IsOk()) + return wxGIF_TRUNCATED; + + type = (unsigned char)stream.GetC(); + + if (type == 0x21) + { + /* extension type */ + (void) stream.GetC(); + + /* skip all data */ + while ((i = (unsigned char)stream.GetC()) != 0) + { + if (stream.Eof() || (stream.LastRead() == 0)) + { + Destroy(); + return wxGIF_INVFORMAT; + } + stream.SeekI(i, wxFromCurrent); + } + } + else if (type == 0x2C) + { + /* image descriptor block */ + static const size_t idbSize = (2 + 2 + 2 + 2 + 1); + stream.Read(buf, idbSize); + if (stream.LastRead() != idbSize) + { + Destroy(); + return wxGIF_INVFORMAT; + } + + /* local color map */ + if ((buf[8] & 0x80) == 0x80) + { + unsigned int local_ncolors = 2 << (buf[8] & 0x07); + wxFileOffset numBytes = 3 * local_ncolors; + stream.SeekI(numBytes, wxFromCurrent); + } + + /* initial code size */ + (void) stream.GetC(); + if (stream.Eof() || (stream.LastRead() == 0)) + { + Destroy(); + return wxGIF_INVFORMAT; + } + + /* skip all data */ + while ((i = (unsigned char)stream.GetC()) != 0) + { + if (stream.Eof() || (stream.LastRead() == 0)) + { + Destroy(); + return wxGIF_INVFORMAT; + } + stream.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