]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/gifdecod.cpp
Make code reading BMP files more robust.
[wxWidgets.git] / src / common / gifdecod.cpp
index ad4395a84968b01d2606dca897d8fcd20606f3a8..b853ee97a987f11e9e4eee57b8292318e3927f11 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
-// Name:        gifdecod.cpp
+// Name:        src/common/gifdecod.cpp
 // Purpose:     wxGIFDecoder, GIF reader for wxImage and wxAnimation
 // Author:      Guillermo Rodriguez Garcia <guille@iies.es>
 // Version:     3.04
 // Purpose:     wxGIFDecoder, GIF reader for wxImage and wxAnimation
 // Author:      Guillermo Rodriguez Garcia <guille@iies.es>
 // Version:     3.04
@@ -8,27 +8,68 @@
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-#pragma implementation "gifdecod.h"
-#endif
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
-#  pragma hdrstop
+    #pragma hdrstop
 #endif
 
 #endif
 
+#if wxUSE_STREAMS && wxUSE_GIF
+
 #ifndef WX_PRECOMP
 #ifndef WX_PRECOMP
-#  include "wx/defs.h"
-#  include "wx/palette.h"
+    #include "wx/palette.h"
+    #include "wx/intl.h"
+    #include "wx/log.h"
 #endif
 
 #endif
 
-#if wxUSE_STREAMS && wxUSE_GIF
-
 #include <stdlib.h>
 #include <string.h>
 #include "wx/gifdecod.h"
 #include <stdlib.h>
 #include <string.h>
 #include "wx/gifdecod.h"
+#include "wx/scopedptr.h"
+#include "wx/scopeguard.h"
+
+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
+};
+
+#define GetFrame(n)     ((GIFImage*)m_frames[n])
+
+//---------------------------------------------------------------------------
+// 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
+    wxString comment;
+
+    wxDECLARE_NO_COPY_CLASS(GIFImage);
+};
+
+wxDECLARE_SCOPED_PTR(GIFImage, GIFImagePtr)
+wxDEFINE_SCOPED_PTR(GIFImage, GIFImagePtr)
 
 
 //---------------------------------------------------------------------------
 
 
 //---------------------------------------------------------------------------
@@ -41,32 +82,19 @@ GIFImage::GIFImage()
     left = 0;
     top = 0;
     transparent = 0;
     left = 0;
     top = 0;
     transparent = 0;
-    disposal = 0;
+    disposal = wxANIM_DONOTREMOVE;
     delay = -1;
     p = (unsigned char *) NULL;
     pal = (unsigned char *) NULL;
     delay = -1;
     p = (unsigned char *) NULL;
     pal = (unsigned char *) NULL;
-    next = (GIFImage *) NULL;
-    prev = (GIFImage *) NULL;
+    ncolours = 0;
 }
 
 //---------------------------------------------------------------------------
 // wxGIFDecoder constructor and destructor
 //---------------------------------------------------------------------------
 
 }
 
 //---------------------------------------------------------------------------
 // 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()
 }
 
 wxGIFDecoder::~wxGIFDecoder()
@@ -76,24 +104,17 @@ wxGIFDecoder::~wxGIFDecoder()
 
 void wxGIFDecoder::Destroy()
 {
 
 void wxGIFDecoder::Destroy()
 {
-    GIFImage *pimg, *paux;
-
-    pimg = m_pfirst;
-
-    while (pimg != NULL)
+    wxASSERT(m_nFrames==m_frames.GetCount());
+    for (unsigned int i=0; i<m_nFrames; i++)
     {
     {
-        paux = pimg->next;
-        free(pimg->p);
-        free(pimg->pal);
-        delete pimg;
-        pimg = paux;
+        GIFImage *f = (GIFImage*)m_frames[i];
+        free(f->p);
+        free(f->pal);
+        delete f;
     }
 
     }
 
-    m_pimage  = NULL;
-    m_pfirst  = NULL;
-    m_plast   = NULL;
-    m_image   = 0;
-    m_nimages = 0;
+    m_frames.Clear();
+    m_nFrames = 0;
 }
 
 
 }
 
 
@@ -103,30 +124,32 @@ void wxGIFDecoder::Destroy()
 
 // This function was designed by Vaclav Slavik
 
 
 // This function was designed by Vaclav Slavik
 
-bool wxGIFDecoder::ConvertToImage(wxImage *image) const
+bool wxGIFDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
 {
     unsigned char *src, *dst, *pal;
     unsigned long i;
     int      transparent;
 
 {
     unsigned char *src, *dst, *pal;
     unsigned long i;
     int      transparent;
 
-    /* just in case... */
+    // just in case...
     image->Destroy();
 
     image->Destroy();
 
-    /* create the image */
-    image->Create(GetWidth(), GetHeight());
+    // create the image
+    wxSize sz = GetFrameSize(frame);
+    image->Create(sz.GetWidth(), sz.GetHeight());
+    image->SetType(wxBITMAP_TYPE_GIF);
 
 
-    if (!image->Ok())
+    if (!image->IsOk())
         return false;
 
         return false;
 
-    pal = GetPalette();
-    src = GetData();
+    pal = GetPalette(frame);
+    src = GetData(frame);
     dst = image->GetData();
     dst = image->GetData();
-    transparent = GetTransparentColour();
+    transparent = GetTransparentColourIndex(frame);
 
 
-    /* set transparent colour mask */
+    // set transparent colour mask
     if (transparent != -1)
     {
     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) &&
         {
             if ((pal[3 * i + 0] == 255) &&
                 (pal[3 * i + 1] == 0) &&
@@ -146,31 +169,35 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const
         image->SetMask(false);
 
 #if wxUSE_PALETTE
         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];
-        }
+    unsigned char r[256];
+    unsigned char g[256];
+    unsigned char b[256];
 
 
-        image->SetPalette(wxPalette(256, r, g, b));
+    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
 
 #endif // wxUSE_PALETTE
 
-    /* copy image data */
-    for (i = 0; i < (GetWidth() * GetHeight()); i++, src++)
+    // copy image data
+    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];
     }
 
     {
         *(dst++) = pal[3 * (*src) + 0];
         *(dst++) = pal[3 * (*src) + 1];
         *(dst++) = pal[3 * (*src) + 2];
     }
 
+    wxString comment = GetFrame(frame)->comment;
+    if ( !comment.empty() )
+    {
+        image->SetOption(wxIMAGE_OPTION_GIF_COMMENT, comment);
+    }
+
     return true;
 }
 
     return true;
 }
 
@@ -181,115 +208,43 @@ bool wxGIFDecoder::ConvertToImage(wxImage *image) const
 
 // Get data for current frame
 
 
 // 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
-
-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); }
-
-
-//---------------------------------------------------------------------------
-// Functions to move through the animation
-//---------------------------------------------------------------------------
-
-bool wxGIFDecoder::GoFirstFrame()
+wxSize wxGIFDecoder::GetFrameSize(unsigned int 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(unsigned int 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(unsigned int 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(unsigned int 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(unsigned int frame) const
 {
 {
-    if (!IsAnimation())
-        return false;
-
-    if ((which >= 1) && (which <= m_nimages))
-    {
-        m_image = 1;
-        m_pimage = m_pfirst;
+    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]);
+}
 
 
-        while (m_image < which)
-        {
-            m_image++;
-            m_pimage = m_pimage->next;
-        }
+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); }
 
 
-        return true;
-    }
-    else
-        return false;
-}
 
 
 //---------------------------------------------------------------------------
 
 
 //---------------------------------------------------------------------------
@@ -299,23 +254,22 @@ bool wxGIFDecoder::GoFrame(int which)
 // getcode:
 //  Reads the next code from the file stream, with size 'bits'
 //
 // 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) */
+    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;
 
     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)
     {
     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)
         {
         if (m_restbyte == 0)
         {
-            m_restbyte = (unsigned char)m_f->GetC();
+            m_restbyte = stream.GetC();
 
             /* Some encoders are a bit broken: instead of issuing
              * an end-of-image symbol (ab_fin) they come up with
 
             /* Some encoders are a bit broken: instead of issuing
              * an end-of-image symbol (ab_fin) they come up with
@@ -328,9 +282,9 @@ int wxGIFDecoder::getcode(int bits, int ab_fin)
                 break;
             }
 
                 break;
             }
 
-            /* prefetch data */
-            m_f->Read((void *) m_buffer, m_restbyte);
-            if (m_f->LastRead() != m_restbyte)
+            // prefetch data
+            stream.Read((void *) m_buffer, m_restbyte);
+            if (stream.LastRead() != m_restbyte)
             {
                 code = ab_fin;
                 return code;
             {
                 code = ab_fin;
                 return code;
@@ -338,17 +292,17 @@ int wxGIFDecoder::getcode(int bits, int ab_fin)
             m_bufp = m_buffer;
         }
 
             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--;
 
         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;
     }
 
         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;
     m_restbits = (m_restbits - bits);
 
     return code;
@@ -360,23 +314,24 @@ int wxGIFDecoder::getcode(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)
 //  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)
-int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits)
+wxGIFErrorCode
+wxGIFDecoder::dgif(wxInputStream& stream, GIFImage *img, int interl, int bits)
 {
     static const int allocSize = 4096 + 1;
 {
     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;
     }
 
     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;
     }
 
     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;
     if (stack == NULL)
     {
         delete[] ab_prefix;
@@ -384,22 +339,22 @@ int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits)
         return wxGIF_MEMERR;
     }
 
         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;
 
 
     int code, readcode, lastcode, abcabca;
 
-    /* these won't change */
+    // these won't change
     ab_clr = (1 << bits);
     ab_fin = (1 << bits) + 1;
 
     ab_clr = (1 << bits);
     ab_fin = (1 << bits) + 1;
 
-    /* these will change through the decompression proccess */
+    // these will change through the decompression process
     ab_bits  = bits + 1;
     ab_free  = (1 << bits) + 2;
     ab_max   = (1 << ab_bits) - 1;
     ab_bits  = bits + 1;
     ab_free  = (1 << bits) + 2;
     ab_max   = (1 << ab_bits) - 1;
@@ -408,41 +363,41 @@ int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits)
     pass     = 1;
     pos = x = y = 0;
 
     pass     = 1;
     pos = x = y = 0;
 
-    /* reset decoder vars */
+    // reset decoder vars
     m_restbits = 0;
     m_restbyte = 0;
     m_lastbyte = 0;
 
     do
     {
     m_restbits = 0;
     m_restbyte = 0;
     m_lastbyte = 0;
 
     do
     {
-        /* get next code */
-        readcode = code = getcode(ab_bits, ab_fin);
+        // get next code
+        readcode = code = getcode(stream, ab_bits, ab_fin);
 
 
-        /* end of image? */
+        // end of image?
         if (code == ab_fin) break;
 
         if (code == ab_fin) break;
 
-        /* reset alphabet? */
+        // reset alphabet?
         if (code == ab_clr)
         {
         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;
 
             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;
         }
 
             continue;
         }
 
-        /* unknown code: special case (like in ABCABCA) */
+        // unknown code: special case (like in ABCABCA)
         if (code >= ab_free)
         {
         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];
         while (code > ab_clr)
         {
             stack[pos++] = ab_tail[code];
@@ -468,12 +423,31 @@ int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits)
             return wxGIF_INVFORMAT;
         }
 
             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)
         {
         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++;
             ab_prefix[ab_free] = lastcode;
             ab_tail[ab_free]   = code;
             ab_free++;
@@ -485,7 +459,7 @@ int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits)
             }
         }
 
             }
         }
 
-        /* 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];
         while (pos >= 0)
         {
             (img->p)[x + (y * (img->w))] = (char) stack[pos];
@@ -497,7 +471,7 @@ int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits)
 
                 if (interl)
                 {
 
                 if (interl)
                 {
-                    /* support for interlaced images */
+                    // support for interlaced images
                     switch (pass)
                     {
                         case 1: y += 8; break;
                     switch (pass)
                     {
                         case 1: y += 8; break;
@@ -554,7 +528,7 @@ int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits)
                 }
                 else
                 {
                 }
                 else
                 {
-                    /* non-interlaced */
+                    // non-interlaced
                     y++;
 /*
 Normally image decoding is finished when an End of Information code is
                     y++;
 /*
 Normally image decoding is finished when an End of Information code is
@@ -616,20 +590,18 @@ as an End of Information itself)
 // CanRead:
 //  Returns true if the file looks like a valid GIF, false otherwise.
 //
 // CanRead:
 //  Returns true if the file looks like a valid GIF, false otherwise.
 //
-bool wxGIFDecoder::CanRead()
+bool wxGIFDecoder::DoCanRead(wxInputStream &stream) const
 {
     unsigned char buf[3];
 
 {
     unsigned char buf[3];
 
-    if ( !m_f->Read(buf, WXSIZEOF(buf)) )
+    if ( !stream.Read(buf, WXSIZEOF(buf)) )
         return false;
 
         return false;
 
-    m_f->SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent);
-
     return memcmp(buf, "GIF", WXSIZEOF(buf)) == 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
 //  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
@@ -638,86 +610,90 @@ bool wxGIFDecoder::CanRead()
 //  (== 0) on success, or an error code if something fails (see
 //  header file for details)
 //
 //  (== 0) on success, or an error code if something fails (see
 //  header file for details)
 //
-int wxGIFDecoder::ReadGIF()
+wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
 {
 {
-    unsigned int ncolors;
-    int           bits, interl, transparent, disposal, i;
+    unsigned int  global_ncolors = 0;
+    int           bits, interl, i;
+    wxAnimationDisposal disposal;
     long          size;
     long          delay;
     unsigned char type = 0;
     unsigned char pal[768];
     unsigned char buf[16];
     long          size;
     long          delay;
     unsigned char type = 0;
     unsigned char pal[768];
     unsigned char buf[16];
-    GIFImage      **ppimg;
-    GIFImage      *pimg, *pprev;
+    bool anim = true;
 
 
-    /* check GIF signature */
-    if (!CanRead())
+    // check GIF signature
+    if (!CanRead(stream))
         return wxGIF_INVFORMAT;
 
         return wxGIF_INVFORMAT;
 
-    /* check for animated GIF support (ver. >= 89a) */
+    // check for animated GIF support (ver. >= 89a)
 
 
-    static const size_t headerSize = (3 + 3);
-    m_f->Read(buf, headerSize);
-    if (m_f->LastRead() != headerSize)
+    static const unsigned int headerSize = (3 + 3);
+    stream.Read(buf, headerSize);
+    if (stream.LastRead() != headerSize)
     {
         return wxGIF_INVFORMAT;
     }
 
     if (memcmp(buf + 3, "89a", 3) < 0)
     {
     {
         return wxGIF_INVFORMAT;
     }
 
     if (memcmp(buf + 3, "89a", 3) < 0)
     {
-        m_anim = false;
+        anim = false;
     }
 
     }
 
-    /* read logical screen descriptor block (LSDB) */
-    static const size_t lsdbSize = (2 + 2 + 1 + 1 + 1);
-    m_f->Read(buf, lsdbSize);
-    if (m_f->LastRead() != lsdbSize)
+    // read logical screen descriptor block (LSDB)
+    static const unsigned int lsdbSize = (2 + 2 + 1 + 1 + 1);
+    stream.Read(buf, lsdbSize);
+    if (stream.LastRead() != lsdbSize)
     {
         return wxGIF_INVFORMAT;
     }
 
     {
         return wxGIF_INVFORMAT;
     }
 
-    m_screenw = buf[0] + 256 * buf[1];
-    m_screenh = buf[2] + 256 * buf[3];
+    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 */
+    // load global color map if available
     if ((buf[4] & 0x80) == 0x80)
     {
     if ((buf[4] & 0x80) == 0x80)
     {
-        m_background = buf[5];
+        int backgroundColIndex = buf[5];
 
 
-        ncolors = 2 << (buf[4] & 0x07);
-        size_t numBytes = 3 * ncolors;
-        m_f->Read(pal, numBytes);
-        if (m_f->LastRead() != numBytes)
+        global_ncolors = 2 << (buf[4] & 0x07);
+        unsigned int numBytes = 3 * global_ncolors;
+        stream.Read(pal, numBytes);
+        if (stream.LastRead() != numBytes)
         {
             return wxGIF_INVFORMAT;
         }
         {
             return wxGIF_INVFORMAT;
         }
+
+        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;
+    // transparent colour, disposal method and delay default to unused
+    int transparent = -1;
+    disposal = wxANIM_UNSPECIFIED;
     delay = -1;
     delay = -1;
-
-    /* read images */
-    ppimg = &m_pfirst;
-    pprev = NULL;
-    pimg  = NULL;
+    wxString comment;
 
     bool done = false;
 
     bool done = false;
-
-    while(!done)
+    while (!done)
     {
     {
-        type = (unsigned char)m_f->GetC();
+        type = stream.GetC();
 
         /*
         If the end of file has been reached (or an error) and a ";"
 
         /*
         If the end of file has been reached (or an error) and a ";"
-        (0x3B) hasn't been encountered yet, exit the loop. (Without this
+        (GIF_MARKER_ENDOFDATA) 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.
         */
         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())
+        if (stream.Eof())// || !stream.IsOk())
         {
             /*
             type is set to some bogus value, so there's no
         {
             /*
             type is set to some bogus value, so there's no
@@ -726,211 +702,263 @@ int wxGIFDecoder::ReadGIF()
             break; // Alternative : "return wxGIF_INVFORMAT;"
         }
 
             break; // Alternative : "return wxGIF_INVFORMAT;"
         }
 
-        /* end of data? */
-        if (type == 0x3B)
-        {
-            done = true;
-        }
-        else
-        /* extension block? */
-        if (type == 0x21)
+        switch (type)
         {
         {
-            if (((unsigned char)m_f->GetC()) == 0xF9)
-            /* graphics control extension, parse it */
-            {
-                static const size_t gceSize = 6;
-                m_f->Read(buf, gceSize);
-                if (m_f->LastRead() != gceSize)
+            case GIF_MARKER_ENDOFDATA:
+                done = true;
+                break;
+            case GIF_MARKER_EXT:
+                switch (stream.GetC())
                 {
                 {
-                    Destroy();
-                    return wxGIF_INVFORMAT;
-                }
+                    case GIF_MARKER_EXT_GRAPHICS_CONTROL:
+                    {
+                        // graphics control extension, parse it
 
 
-                /* read delay and convert from 1/100 of a second to ms */
-                delay = 10 * (buf[2] + 256 * buf[3]);
+                        static const unsigned int gceSize = 6;
+                        stream.Read(buf, gceSize);
+                        if (stream.LastRead() != gceSize)
+                        {
+                            Destroy();
+                            return wxGIF_INVFORMAT;
+                        }
 
 
-                /* read transparent colour index, if used */
-                if (buf[1] & 0x01)
-                    transparent = buf[4];
+                        // read delay and convert from 1/100 of a second to ms
+                        delay = 10 * (buf[2] + 256 * buf[3]);
 
 
-                /* read disposal method */
-                disposal = ((buf[1] & 0x1C) >> 2) - 1;
-            }
-            else
-            /* other extension, skip */
-            {
-                while ((i = (unsigned char)m_f->GetC()) != 0)
-                {
-                    m_f->SeekI(i, wxFromCurrent);
-                    if (m_f->Eof())
+                        // read transparent colour index, if used
+                        transparent = buf[1] & 0x01 ? buf[4] : -1;
+
+                        // read disposal method
+                        disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1);
+                        break;
+                    }
+                    case GIF_MARKER_EXT_COMMENT:
                     {
                     {
-                        done = true;
+                        int len = stream.GetC();
+                        while (len)
+                        {
+                            if ( stream.Eof() )
+                            {
+                                done = true;
+                                break;
+                            }
+
+                            wxCharBuffer charbuf(len);
+                            stream.Read(charbuf.data(), len);
+                            if ( (int) stream.LastRead() != len )
+                            {
+                                done = true;
+                                break;
+                            }
+
+                            comment += wxConvertMB2WX(charbuf.data());
+
+                            len = stream.GetC();
+                        }
+
                         break;
                     }
                         break;
                     }
+                    default:
+                        // other extension, skip
+                        while ((i = stream.GetC()) != 0)
+                        {
+                            if (stream.Eof() || (stream.LastRead() == 0) ||
+                                stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
+                            {
+                                done = true;
+                                break;
+                            }
+                        }
+                        break;
                 }
                 }
-            }
-        }
-        else
-        /* image descriptor block? */
-        if (type == 0x2C)
-        {
-            /* allocate memory for IMAGEN struct */
-            pimg = (*ppimg) = new GIFImage();
-
-            if (pimg == NULL)
+                break;
+            case GIF_MARKER_SEP:
             {
             {
-                Destroy();
-                return wxGIF_MEMERR;
-            }
+                // allocate memory for IMAGEN struct
+                GIFImagePtr pimg(new GIFImage());
 
 
-            /* fill in the data */
-            static const size_t idbSize = (2 + 2 + 2 + 2 + 1);
-            m_f->Read(buf, idbSize);
-            if (m_f->LastRead() != idbSize)
-            {
-                Destroy();
-                return wxGIF_INVFORMAT;
-            }
+                wxScopeGuard guardDestroy = wxMakeObjGuard(*this, &wxGIFDecoder::Destroy);
 
 
-            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.get() )
+                    return wxGIF_MEMERR;
 
 
-            if (pimg->w == 0 || pimg->h == 0)
-            {
-                Destroy();
-                return wxGIF_INVFORMAT;
-            }
+                // fill in the data
+                static const unsigned int idbSize = (2 + 2 + 2 + 2 + 1);
+                stream.Read(buf, idbSize);
+                if (stream.LastRead() != idbSize)
+                    return wxGIF_INVFORMAT;
+
+                pimg->comment = comment;
+                comment.clear();
+                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 )
+                {
+                    // 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);
-            size = pimg->w * pimg->h;
+                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;
+                pimg->transparent = transparent;
+                pimg->disposal = disposal;
+                pimg->delay = delay;
 
 
-            /* allocate memory for image and palette */
-            pimg->p   = (unsigned char *) malloc((size_t)size);
-            pimg->pal = (unsigned char *) malloc(768);
+                // 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;
-            }
+                if ((!pimg->p) || (!pimg->pal))
+                    return wxGIF_MEMERR;
 
 
-            /* load local color map if available, else use global map */
-            if ((buf[8] & 0x80) == 0x80)
-            {
-                ncolors = 2 << (buf[8] & 0x07);
-                size_t numBytes = 3 * ncolors;
-                m_f->Read(pimg->pal, numBytes);
-                if (m_f->LastRead() != numBytes)
+                // load local color map if available, else use global map
+                if ((buf[8] & 0x80) == 0x80)
                 {
                 {
-                    Destroy();
-                    return wxGIF_INVFORMAT;
+                    unsigned int local_ncolors = 2 << (buf[8] & 0x07);
+                    unsigned int numBytes = 3 * local_ncolors;
+                    stream.Read(pimg->pal, numBytes);
+                    pimg->ncolours = local_ncolors;
+                    if (stream.LastRead() != numBytes)
+                        return wxGIF_INVFORMAT;
+                }
+                else
+                {
+                    memcpy(pimg->pal, pal, 768);
+                    pimg->ncolours = global_ncolors;
                 }
                 }
-            }
-            else
-            {
-                memcpy(pimg->pal, pal, 768);
-            }
 
 
-            /* get initial code size from first byte in raster data */
-            bits = (unsigned char)m_f->GetC();
+                // get initial code size from first byte in raster data
+                bits = stream.GetC();
+                if (bits == 0)
+                    return wxGIF_INVFORMAT;
 
 
-            /* decode image */
-            int result = dgif(pimg, interl, bits);
-            if (result != wxGIF_OK)
-            {
-                Destroy();
-                return result;
-            }
-            m_nimages++;
+                // decode image
+                wxGIFErrorCode result = dgif(stream, pimg.get(), interl, bits);
+                if (result != wxGIF_OK)
+                    return result;
 
 
-            /* if this is not an animated GIF, exit after first image */
-            if (!m_anim)
-                done = true;
+                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 (!anim)
+                    done = true;
+                break;
+            }
         }
     }
 
         }
     }
 
-    if (m_nimages == 0)
+    if (m_nFrames <= 0)
     {
         Destroy();
         return wxGIF_INVFORMAT;
     }
 
     {
         Destroy();
         return wxGIF_INVFORMAT;
     }
 
-    /* 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)
+    // try to read to the end of the stream
+    while (type != GIF_MARKER_ENDOFDATA)
     {
     {
-        if (!m_f->IsOk())
+        if (!stream.IsOk())
             return wxGIF_TRUNCATED;
 
             return wxGIF_TRUNCATED;
 
-        type = (unsigned char)m_f->GetC();
-
-        if (type == 0x21)
-        {
-            /* extension type */
-            (void) m_f->GetC();
+        type = stream.GetC();
 
 
-            /* skip all data */
-            while ((i = (unsigned char)m_f->GetC()) != 0)
-            {
-                m_f->SeekI(i, wxFromCurrent);
-            }
-        }
-        else if (type == 0x2C)
+        switch (type)
         {
         {
-            /* 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;
-            }
+            case GIF_MARKER_EXT:
+                // extension type
+                (void) stream.GetC();
 
 
-            /* local color map */
-            if ((buf[8] & 0x80) == 0x80)
+                // skip all data
+                while ((i = stream.GetC()) != 0)
+                {
+                    if (stream.Eof() || (stream.LastRead() == 0) ||
+                        stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
+                    {
+                        Destroy();
+                        return wxGIF_INVFORMAT;
+                    }
+                }
+                break;
+            case GIF_MARKER_SEP:
             {
             {
-                ncolors = 2 << (buf[8] & 0x07);
-                wxFileOffset pos = m_f->TellI();
-                wxFileOffset numBytes = 3 * ncolors;
-                m_f->SeekI(numBytes, wxFromCurrent);
-                if (m_f->TellI() != (pos + numBytes))
+                // image descriptor block
+                static const unsigned int idbSize = (2 + 2 + 2 + 2 + 1);
+                stream.Read(buf, idbSize);
+                if (stream.LastRead() != idbSize)
                 {
                     Destroy();
                     return wxGIF_INVFORMAT;
                 }
                 {
                     Destroy();
                     return wxGIF_INVFORMAT;
                 }
-            }
 
 
-            /* initial code size */
-            (void) m_f->GetC();
+                // local color map
+                if ((buf[8] & 0x80) == 0x80)
+                {
+                    unsigned int local_ncolors = 2 << (buf[8] & 0x07);
+                    wxFileOffset numBytes = 3 * local_ncolors;
+                    if (stream.SeekI(numBytes, wxFromCurrent) == wxInvalidOffset)
+                    {
+                        Destroy();
+                        return wxGIF_INVFORMAT;
+                    }
+                }
 
 
-            /* skip all data */
-            while ((i = (unsigned char)m_f->GetC()) != 0)
-            {
-                m_f->SeekI(i, wxFromCurrent);
+                // initial code size
+                (void) stream.GetC();
+                if (stream.Eof() || (stream.LastRead() == 0))
+                {
+                    Destroy();
+                    return wxGIF_INVFORMAT;
+                }
+
+                // skip all data
+                while ((i = stream.GetC()) != 0)
+                {
+                    if (stream.Eof() || (stream.LastRead() == 0) ||
+                        stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
+                    {
+                        Destroy();
+                        return wxGIF_INVFORMAT;
+                    }
+                }
+                break;
             }
             }
-        }
-        else if ((type != 0x3B) && (type != 00)) /* testing */
-        {
-            /* images are OK, but couldn't read to the end of the stream */
-            return wxGIF_TRUNCATED;
+            default:
+                if ((type != GIF_MARKER_ENDOFDATA) && (type != 00)) // testing
+                {
+                    // images are OK, but couldn't read to the end of the stream
+                    return wxGIF_TRUNCATED;
+                }
+                break;
         }
     }
 
         }
     }