X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1cda5c7e62be5b0a31cc964fabbcbca4367dd8cf..85ee88cd536cbf8b347e58f84b160b60204ed157:/src/common/gifdecod.cpp diff --git a/src/common/gifdecod.cpp b/src/common/gifdecod.cpp index 0b53e9c627..27669b8d2a 100644 --- a/src/common/gifdecod.cpp +++ b/src/common/gifdecod.cpp @@ -19,11 +19,15 @@ #ifndef WX_PRECOMP #include "wx/palette.h" + #include "wx/intl.h" + #include "wx/log.h" #endif #include #include #include "wx/gifdecod.h" +#include "wx/scopedptr.h" +#include "wx/scopeguard.h" @@ -38,20 +42,22 @@ 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) + 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 + + wxDECLARE_NO_COPY_CLASS(GIFImage); }; +wxDECLARE_SCOPED_PTR(GIFImage, GIFImagePtr) +wxDEFINE_SCOPED_PTR(GIFImage, GIFImagePtr) //--------------------------------------------------------------------------- @@ -87,7 +93,7 @@ wxGIFDecoder::~wxGIFDecoder() void wxGIFDecoder::Destroy() { wxASSERT(m_nFrames==m_frames.GetCount()); - for (size_t i=0; ip); @@ -106,18 +112,19 @@ void wxGIFDecoder::Destroy() // This function was designed by Vaclav Slavik -bool wxGIFDecoder::ConvertToImage(size_t frame, wxImage *image) const +bool wxGIFDecoder::ConvertToImage(unsigned int frame, wxImage *image) const { unsigned char *src, *dst, *pal; unsigned long i; int transparent; - /* just in case... */ + // just in case... image->Destroy(); - /* create the image */ + // create the image wxSize sz = GetFrameSize(frame); image->Create(sz.GetWidth(), sz.GetHeight()); + image->SetType(wxBITMAP_TYPE_GIF); if (!image->Ok()) return false; @@ -127,7 +134,7 @@ bool wxGIFDecoder::ConvertToImage(size_t frame, wxImage *image) const dst = image->GetData(); transparent = GetTransparentColourIndex(frame); - /* set transparent colour mask */ + // set transparent colour mask if (transparent != -1) { for (i = 0; i < GetNcolours(frame); i++) @@ -164,7 +171,7 @@ bool wxGIFDecoder::ConvertToImage(size_t frame, wxImage *image) const image->SetPalette(wxPalette(GetNcolours(frame), r, g, b)); #endif // wxUSE_PALETTE - /* copy image data */ + // copy image data unsigned long npixel = sz.GetWidth() * sz.GetHeight(); for (i = 0; i < npixel; i++, src++) { @@ -186,27 +193,27 @@ bool wxGIFDecoder::ConvertToImage(size_t frame, wxImage *image) const // Get data for current frame -wxSize wxGIFDecoder::GetFrameSize(size_t frame) const +wxSize wxGIFDecoder::GetFrameSize(unsigned int frame) const { return wxSize(GetFrame(frame)->w, GetFrame(frame)->h); } -wxPoint wxGIFDecoder::GetFramePosition(size_t frame) const +wxPoint wxGIFDecoder::GetFramePosition(unsigned int frame) const { return wxPoint(GetFrame(frame)->left, GetFrame(frame)->top); } -wxAnimationDisposal wxGIFDecoder::GetDisposalMethod(size_t frame) const +wxAnimationDisposal wxGIFDecoder::GetDisposalMethod(unsigned int frame) const { return GetFrame(frame)->disposal; } -long wxGIFDecoder::GetDelay(size_t frame) const +long wxGIFDecoder::GetDelay(unsigned int frame) const { return GetFrame(frame)->delay; } -wxColour wxGIFDecoder::GetTransparentColour(size_t frame) const +wxColour wxGIFDecoder::GetTransparentColour(unsigned int frame) const { unsigned char *pal = GetFrame(frame)->pal; int n = GetFrame(frame)->transparent; @@ -218,10 +225,10 @@ wxColour wxGIFDecoder::GetTransparentColour(size_t frame) const pal[n*3 + 2]); } -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); } +unsigned char* wxGIFDecoder::GetData(unsigned int frame) const { return (GetFrame(frame)->p); } +unsigned char* wxGIFDecoder::GetPalette(unsigned int frame) const { return (GetFrame(frame)->pal); } +unsigned int wxGIFDecoder::GetNcolours(unsigned int frame) const { return (GetFrame(frame)->ncolours); } +int wxGIFDecoder::GetTransparentColourIndex(unsigned int frame) const { return (GetFrame(frame)->transparent); } @@ -234,17 +241,17 @@ int wxGIFDecoder::GetTransparentColourIndex(size_t frame) const { return (GetFr // int wxGIFDecoder::getcode(wxInputStream& stream, int bits, int ab_fin) { - unsigned int mask; /* bit mask */ - unsigned int code; /* code (result) */ + unsigned int mask; // bit mask + unsigned int code; // code (result) - /* get remaining bits from last byte read */ + // get remaining bits from last byte read mask = (1 << bits) - 1; code = (m_lastbyte >> (8 - m_restbits)) & mask; - /* keep reading new bytes while needed */ + // keep reading new bytes while needed while (bits > m_restbits) { - /* if no bytes left in this block, read the next block */ + // if no bytes left in this block, read the next block if (m_restbyte == 0) { m_restbyte = (unsigned char)stream.GetC(); @@ -260,7 +267,7 @@ int wxGIFDecoder::getcode(wxInputStream& stream, int bits, int ab_fin) break; } - /* prefetch data */ + // prefetch data stream.Read((void *) m_buffer, m_restbyte); if (stream.LastRead() != m_restbyte) { @@ -270,17 +277,17 @@ int wxGIFDecoder::getcode(wxInputStream& stream, int bits, int ab_fin) m_bufp = m_buffer; } - /* read next byte and isolate the bits we need */ + // 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 */ + // adjust total number of bits extracted from the buffer m_restbits = m_restbits + 8; } - /* find number of bits remaining for next code */ + // find number of bits remaining for next code m_restbits = (m_restbits - bits); return code; @@ -292,23 +299,24 @@ int wxGIFDecoder::getcode(wxInputStream& stream, int bits, int ab_fin) // is 'bits'. Supports interlaced images (interl == 1). // 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) +wxGIFErrorCode +wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int interl, int bits) { static const int allocSize = 4096 + 1; - int *ab_prefix = new int[allocSize]; /* alphabet (prefixes) */ + int *ab_prefix = new int[allocSize]; // alphabet (prefixes) if (ab_prefix == NULL) { return wxGIF_MEMERR; } - int *ab_tail = new int[allocSize]; /* alphabet (tails) */ + 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 */ + int *stack = new int[allocSize]; // decompression stack if (stack == NULL) { delete[] ab_prefix; @@ -316,22 +324,22 @@ wxGIFErrorCode wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int inte 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 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; - /* these won't change */ + // these won't change ab_clr = (1 << bits); ab_fin = (1 << bits) + 1; - /* these will change through the decompression proccess */ + // these will change through the decompression proccess ab_bits = bits + 1; ab_free = (1 << bits) + 2; ab_max = (1 << ab_bits) - 1; @@ -340,41 +348,41 @@ wxGIFErrorCode wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int inte pass = 1; pos = x = y = 0; - /* reset decoder vars */ + // reset decoder vars m_restbits = 0; m_restbyte = 0; m_lastbyte = 0; do { - /* get next code */ + // get next code readcode = code = getcode(stream, ab_bits, ab_fin); - /* end of image? */ + // end of image? if (code == ab_fin) break; - /* reset alphabet? */ + // reset alphabet? if (code == ab_clr) { - /* reset main variables */ + // 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 */ + // skip to next code continue; } - /* unknown code: special case (like in ABCABCA) */ + // unknown code: special case (like in ABCABCA) if (code >= ab_free) { - code = lastcode; /* take last string */ - stack[pos++] = abcabca; /* add first character */ + code = lastcode; // take last string + stack[pos++] = abcabca; // add first character } - /* build the string for this code in the stack */ + // build the string for this code in the stack while (code > ab_clr) { stack[pos++] = ab_tail[code]; @@ -400,10 +408,10 @@ wxGIFErrorCode wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int inte return wxGIF_INVFORMAT; } - stack[pos] = code; /* push last code into the stack */ - abcabca = code; /* save for special case */ + 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) */ + // make new entry in alphabet (only if NOT just cleared) if (lastcode != -1) { // Normally, after the alphabet is full and can't grow any @@ -436,7 +444,7 @@ wxGIFErrorCode wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int inte } } - /* dump stack data to the image buffer */ + // dump stack data to the image buffer while (pos >= 0) { (img->p)[x + (y * (img->w))] = (char) stack[pos]; @@ -448,7 +456,7 @@ wxGIFErrorCode wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int inte if (interl) { - /* support for interlaced images */ + // support for interlaced images switch (pass) { case 1: y += 8; break; @@ -505,7 +513,7 @@ wxGIFErrorCode wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int inte } else { - /* non-interlaced */ + // non-interlaced y++; /* Normally image decoding is finished when an End of Information code is @@ -567,15 +575,13 @@ as an End of Information itself) // CanRead: // Returns true if the file looks like a valid GIF, false otherwise. // -bool wxGIFDecoder::CanRead(wxInputStream &stream) const +bool wxGIFDecoder::DoCanRead(wxInputStream &stream) const { unsigned char buf[3]; if ( !stream.Read(buf, WXSIZEOF(buf)) ) return false; - stream.SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent); - return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0; } @@ -592,7 +598,7 @@ bool wxGIFDecoder::CanRead(wxInputStream &stream) const wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) { unsigned int global_ncolors = 0; - int bits, interl, transparent, i; + int bits, interl, i; wxAnimationDisposal disposal; long size; long delay; @@ -601,13 +607,13 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) unsigned char buf[16]; bool anim = true; - /* check GIF signature */ + // check GIF signature if (!CanRead(stream)) return wxGIF_INVFORMAT; - /* check for animated GIF support (ver. >= 89a) */ + // check for animated GIF support (ver. >= 89a) - static const size_t headerSize = (3 + 3); + static const unsigned int headerSize = (3 + 3); stream.Read(buf, headerSize); if (stream.LastRead() != headerSize) { @@ -619,8 +625,8 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) anim = false; } - /* read logical screen descriptor block (LSDB) */ - static const size_t lsdbSize = (2 + 2 + 1 + 1 + 1); + // read logical screen descriptor block (LSDB) + static const unsigned int lsdbSize = (2 + 2 + 1 + 1 + 1); stream.Read(buf, lsdbSize); if (stream.LastRead() != lsdbSize) { @@ -630,18 +636,18 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) m_szAnimation.SetWidth( buf[0] + 256 * buf[1] ); m_szAnimation.SetHeight( buf[2] + 256 * buf[3] ); - if ((m_szAnimation.GetWidth() == 0) || (m_szAnimation.GetHeight() == 0)) + if (anim && ((m_szAnimation.GetWidth() == 0) || (m_szAnimation.GetHeight() == 0))) { return wxGIF_INVFORMAT; } - /* load global color map if available */ + // load global color map if available if ((buf[4] & 0x80) == 0x80) { int backgroundColIndex = buf[5]; global_ncolors = 2 << (buf[4] & 0x07); - size_t numBytes = 3 * global_ncolors; + unsigned int numBytes = 3 * global_ncolors; stream.Read(pal, numBytes); if (stream.LastRead() != numBytes) { @@ -653,8 +659,8 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) pal[backgroundColIndex*3 + 2]); } - /* transparent colour, disposal method and delay default to unused */ - transparent = -1; + // transparent colour, disposal method and delay default to unused + int transparent = -1; disposal = wxANIM_UNSPECIFIED; delay = -1; @@ -680,19 +686,19 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) break; // Alternative : "return wxGIF_INVFORMAT;" } - /* end of data? */ + // end of data? if (type == 0x3B) { done = true; } else - /* extension block? */ + // extension block? if (type == 0x21) { if (((unsigned char)stream.GetC()) == 0xF9) - /* graphics control extension, parse it */ + // graphics control extension, parse it { - static const size_t gceSize = 6; + static const unsigned int gceSize = 6; stream.Read(buf, gceSize); if (stream.LastRead() != gceSize) { @@ -700,51 +706,46 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) return wxGIF_INVFORMAT; } - /* read delay and convert from 1/100 of a second to ms */ + // 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 transparent colour index, if used + transparent = buf[1] & 0x01 ? buf[4] : -1; - /* read disposal method */ + // read disposal method disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1); } else - /* other extension, skip */ + // other extension, skip { while ((i = (unsigned char)stream.GetC()) != 0) { - if (stream.Eof() || (stream.LastRead() == 0)) + if (stream.Eof() || (stream.LastRead() == 0) || + stream.SeekI(i, wxFromCurrent) == wxInvalidOffset) { done = true; break; } - stream.SeekI(i, wxFromCurrent); } } } else - /* image descriptor block? */ + // image descriptor block? if (type == 0x2C) { - /* allocate memory for IMAGEN struct */ - GIFImage *pimg = new GIFImage(); + // allocate memory for IMAGEN struct + GIFImagePtr pimg(new GIFImage()); - if (pimg == NULL) - { - Destroy(); + wxScopeGuard guardDestroy = wxMakeObjGuard(*this, &wxGIFDecoder::Destroy); + + if ( !pimg.get() ) return wxGIF_MEMERR; - } - /* fill in the data */ - static const size_t idbSize = (2 + 2 + 2 + 2 + 1); + // fill in the data + static const unsigned int 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]; @@ -755,11 +756,29 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) pimg->w = buf[4] + 256 * buf[5]; pimg->h = buf[6] + 256 * buf[7]; - if ((pimg->w == 0) || (pimg->w > (unsigned int)m_szAnimation.GetWidth()) || - (pimg->h == 0) || (pimg->h > (unsigned int)m_szAnimation.GetHeight())) + if ( anim ) { - Destroy(); - return wxGIF_INVFORMAT; + // some GIF images specify incorrect animation size but we can + // still open them if we fix up the animation size, see #9465 + if ( m_nFrames == 0 ) + { + if ( pimg->w > (unsigned)m_szAnimation.x ) + m_szAnimation.x = pimg->w; + if ( pimg->h > (unsigned)m_szAnimation.y ) + m_szAnimation.y = pimg->h; + } + else // subsequent frames + { + // check that we have valid size + if ( (!pimg->w || pimg->w > (unsigned)m_szAnimation.x) || + (!pimg->h || pimg->h > (unsigned)m_szAnimation.y) ) + { + wxLogError(_("Incorrect GIF frame size (%u, %d) for " + "the frame #%u"), + pimg->w, pimg->h, m_nFrames); + return wxGIF_INVFORMAT; + } + } } interl = ((buf[8] & 0x40)? 1 : 0); @@ -769,28 +788,22 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) pimg->disposal = disposal; pimg->delay = delay; - /* allocate memory for image and palette */ - pimg->p = (unsigned char *) malloc((size_t)size); + // allocate memory for image and palette + pimg->p = (unsigned char *) malloc((unsigned int)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 */ + // load local color map if available, else use global map if ((buf[8] & 0x80) == 0x80) { unsigned int local_ncolors = 2 << (buf[8] & 0x07); - size_t numBytes = 3 * local_ncolors; + unsigned int numBytes = 3 * local_ncolors; stream.Read(pimg->pal, numBytes); pimg->ncolours = local_ncolors; if (stream.LastRead() != numBytes) - { - Destroy(); return wxGIF_INVFORMAT; - } } else { @@ -798,27 +811,23 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) pimg->ncolours = global_ncolors; } - /* get initial code size from first byte in raster data */ + // get initial code size from first byte in raster data bits = (unsigned char)stream.GetC(); if (bits == 0) - { - Destroy(); return wxGIF_INVFORMAT; - } - /* decode image */ - wxGIFErrorCode result = dgif(stream, pimg, interl, bits); + // decode image + wxGIFErrorCode result = dgif(stream, pimg.get(), interl, bits); if (result != wxGIF_OK) - { - Destroy(); return result; - } - /* add the image to our frame array */ - m_frames.Add((void*)pimg); + guardDestroy.Dismiss(); + + // add the image to our frame array + m_frames.Add(pimg.release()); m_nFrames++; - /* if this is not an animated GIF, exit after first image */ + // if this is not an animated GIF, exit after first image if (!anim) done = true; } @@ -830,7 +839,7 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) return wxGIF_INVFORMAT; } - /* try to read to the end of the stream */ + // try to read to the end of the stream while (type != 0x3B) { if (!stream.IsOk()) @@ -840,24 +849,24 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) if (type == 0x21) { - /* extension type */ + // extension type (void) stream.GetC(); - /* skip all data */ + // skip all data while ((i = (unsigned char)stream.GetC()) != 0) { - if (stream.Eof() || (stream.LastRead() == 0)) + if (stream.Eof() || (stream.LastRead() == 0) || + stream.SeekI(i, wxFromCurrent) == wxInvalidOffset) { 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); + // image descriptor block + static const unsigned int idbSize = (2 + 2 + 2 + 2 + 1); stream.Read(buf, idbSize); if (stream.LastRead() != idbSize) { @@ -865,15 +874,19 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) return wxGIF_INVFORMAT; } - /* local color map */ + // 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); + if (stream.SeekI(numBytes, wxFromCurrent) == wxInvalidOffset) + { + Destroy(); + return wxGIF_INVFORMAT; + } } - /* initial code size */ + // initial code size (void) stream.GetC(); if (stream.Eof() || (stream.LastRead() == 0)) { @@ -881,20 +894,20 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream) return wxGIF_INVFORMAT; } - /* skip all data */ + // skip all data while ((i = (unsigned char)stream.GetC()) != 0) { - if (stream.Eof() || (stream.LastRead() == 0)) + if (stream.Eof() || (stream.LastRead() == 0) || + stream.SeekI(i, wxFromCurrent) == wxInvalidOffset) { Destroy(); return wxGIF_INVFORMAT; } - stream.SeekI(i, wxFromCurrent); } } - else if ((type != 0x3B) && (type != 00)) /* testing */ + else if ((type != 0x3B) && (type != 00)) // testing { - /* images are OK, but couldn't read to the end of the stream */ + // images are OK, but couldn't read to the end of the stream return wxGIF_TRUNCATED; } }