X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/2ce0a6e2d67c73ef32534345a422c2782f7dc091..baed10770806ddfd58eefce328d581bc0235f4fc:/src/common/gifdecod.cpp diff --git a/src/common/gifdecod.cpp b/src/common/gifdecod.cpp index 1ca8c471ce..db32b8fa25 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 -// Version: 3.03 -// RCS-ID: +// Version: 3.04 +// RCS-ID: $Id$ // Copyright: (c) Guillermo Rodriguez Garcia // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -30,13 +30,31 @@ #include "wx/gifdecod.h" +//--------------------------------------------------------------------------- +// GIFImage constructor +//--------------------------------------------------------------------------- +GIFImage::GIFImage() +{ + w = 0; + h = 0; + left = 0; + top = 0; + transparent = 0; + disposal = 0; + delay = -1; + p = (unsigned char *) NULL; + pal = (unsigned char *) NULL; + next = (GIFImage *) NULL; + prev = (GIFImage *) NULL; +} + //--------------------------------------------------------------------------- // wxGIFDecoder constructor and destructor //--------------------------------------------------------------------------- wxGIFDecoder::wxGIFDecoder(wxInputStream *s, bool anim) { - m_f = s; + m_f = s; m_anim = anim; m_background = -1; @@ -57,7 +75,7 @@ wxGIFDecoder::~wxGIFDecoder() void wxGIFDecoder::Destroy() { - IMAGEN *pimg, *paux; + GIFImage *pimg, *paux; pimg = m_pfirst; @@ -66,9 +84,15 @@ void wxGIFDecoder::Destroy() paux = pimg->next; free(pimg->p); free(pimg->pal); - free(pimg); + delete pimg; pimg = paux; } + + m_pimage = NULL; + m_pfirst = NULL; + m_plast = NULL; + m_image = 0; + m_nimages = 0; } @@ -120,6 +144,24 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const else image->SetMask(FALSE); +#if wxUSE_PALETTE + if (pal) + { + 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(256, r, g, b)); + } +#endif // wxUSE_PALETTE + /* copy image data */ for (i = 0; i < (GetWidth() * GetHeight()); i++, src++) { @@ -282,10 +324,19 @@ int wxGIFDecoder::getcode(int bits, int ab_fin) code = ab_fin; break; } + + /* prefetch data */ + m_f->Read((void *) m_buffer, m_restbyte); + if (m_f->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--; @@ -304,21 +355,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) +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 */ + 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; @@ -335,7 +405,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; @@ -374,6 +444,17 @@ 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; + } } stack[pos] = code; /* push last code into the stack */ abcabca = code; /* save for special case */ @@ -395,6 +476,14 @@ int wxGIFDecoder::dgif(IMAGEN *img, int interl, int bits) /* dump stack data to the buffer */ while (pos >= 0) { + if (pos >= allocSize) + { + delete[] ab_prefix; + delete[] ab_tail; + delete[] stack; + return wxGIF_INVFORMAT; + } + (img->p)[x + (y * (img->w))] = (char)stack[pos--]; if (++x >= (img->w)) @@ -425,6 +514,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; + } } } } @@ -434,7 +563,11 @@ 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; } @@ -444,12 +577,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); } @@ -460,33 +590,48 @@ 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; + unsigned int ncolors; + int 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); - m_f->Read(buf, 6); + /* check for animated GIF support (ver. >= 89a) */ + + static const size_t headerSize = (3 + 3); + m_f->Read(buf, headerSize); + if (m_f->LastRead() != headerSize) + { + return wxGIF_INVFORMAT; + } if (memcmp(buf + 3, "89a", 3) < 0) + { m_anim = FALSE; + } /* read logical screen descriptor block (LSDB) */ - m_f->Read(buf, 7); + static const size_t lsdbSize = (2 + 2 + 1 + 1 + 1); + m_f->Read(buf, lsdbSize); + if (m_f->LastRead() != lsdbSize) + { + return wxGIF_INVFORMAT; + } + m_screenw = buf[0] + 256 * buf[1]; m_screenh = buf[2] + 256 * buf[3]; @@ -496,7 +641,12 @@ int wxGIFDecoder::ReadGIF() m_background = buf[5]; ncolors = 2 << (buf[4] & 0x07); - m_f->Read(pal, 3 * ncolors); + size_t numBytes = 3 * ncolors; + m_f->Read(pal, numBytes); + if (m_f->LastRead() != numBytes) + { + return wxGIF_INVFORMAT; + } } /* transparent colour, disposal method and delay default to unused */ @@ -509,27 +659,48 @@ int wxGIFDecoder::ReadGIF() pprev = NULL; pimg = NULL; -#if defined(__VISAGECPP__) -// VA just can't stand while(1) - bool bOs2var = TRUE; - while(bOs2var) -#else - while (1) -#endif + bool done = FALSE; + + while(!done) { type = (unsigned char)m_f->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 (m_f->Eof())// || !m_f->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) /* graphics control extension, parse it */ { - m_f->Read(buf, 6); + static const size_t gceSize = 6; + m_f->Read(buf, gceSize); + if (m_f->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]); @@ -546,36 +717,52 @@ int wxGIFDecoder::ReadGIF() { 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); + if (m_f->Eof()) + { + done = TRUE; + break; + } } } } - + else /* image descriptor block? */ if (type == 0x2C) { /* allocate memory for IMAGEN struct */ - pimg = (*ppimg) = (IMAGEN *) malloc(sizeof(IMAGEN)); + pimg = (*ppimg) = 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); + m_f->Read(buf, idbSize); + if (m_f->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 (pimg->w == 0 || pimg->h == 0) + { + Destroy(); + return wxGIF_INVFORMAT; + } + interl = ((buf[8] & 0x40)? 1 : 0); size = pimg->w * pimg->h; @@ -588,47 +775,122 @@ int wxGIFDecoder::ReadGIF() 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); + size_t numBytes = 3 * ncolors; + m_f->Read(pimg->pal, numBytes); + if (m_f->LastRead() != numBytes) + { + Destroy(); + return wxGIF_INVFORMAT; + } } 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); - + int result = dgif(pimg, interl, bits); + if (result != wxGIF_OK) + { + Destroy(); + return result; + } m_nimages++; + + /* if this is not an animated GIF, exit after first image */ + if (!m_anim) + done = TRUE; } + } - /* if we have one image and no animated GIF support, exit */ - if (m_nimages == 1 && !m_anim) - break; + if (m_nimages == 0) + { + Destroy(); + return wxGIF_INVFORMAT; } - /* finish successfully :-) */ - if (m_nimages != 0) + /* setup image pointers */ + m_image = 1; + m_plast = pimg; + m_pimage = m_pfirst; + + /* try to read to the end of the stream */ + while (type != 0x3B) { - m_image = 1; - m_plast = pimg; - m_pimage = m_pfirst; + if (!m_f->IsOk()) + return wxGIF_TRUNCATED; + + 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 */ + static const size_t idbSize = (2 + 2 + 2 + 2 + 1); + m_f->Read(buf, idbSize); + if (m_f->LastRead() != idbSize) + { + Destroy(); + return wxGIF_INVFORMAT; + } + + /* local color map */ + if ((buf[8] & 0x80) == 0x80) + { + ncolors = 2 << (buf[8] & 0x07); + off_t pos = m_f->TellI(); + off_t numBytes = 3 * ncolors; + m_f->SeekI(numBytes, wxFromCurrent); + if (m_f->TellI() != (pos + numBytes)) + { + Destroy(); + return wxGIF_INVFORMAT; + } + } + + /* 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_STREAMS && wxUSE_GIF