]> git.saurik.com Git - wxWidgets.git/commitdiff
Added support for reading comments from a GIF image.
authorDimitri Schoolwerth <dimitri.schoolwerth@gmail.com>
Wed, 2 Feb 2011 11:19:30 +0000 (11:19 +0000)
committerDimitri Schoolwerth <dimitri.schoolwerth@gmail.com>
Wed, 2 Feb 2011 11:19:30 +0000 (11:19 +0000)
Applied (modified) patch by troelsk. Changed comments (which are allowed per frame in an animated GIF) to be read using wxIMAGE_OPTION_GIF_COMMENT with wxImage.GetOption. Added unit tests for reading and writing GIF comments.

Closes #12843.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@66828 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
interface/wx/image.h
src/common/gifdecod.cpp
tests/image/image.cpp

index 31750ae2f1d85d35b9cf1fbb74e8470001441213..0fbb5c6b8c79f4a5a4280d7cbb07f8265a51c245 100644 (file)
@@ -424,6 +424,7 @@ All:
 - Added wxVersionInfo and various GetLibraryVersionInfo() functions (troelsk).
 - Added wxNumberFormatter for dealing with thousands separators.
 - Added wxIntegerValidator<> and wxFloatingPointValidator<> validators.
 - Added wxVersionInfo and various GetLibraryVersionInfo() functions (troelsk).
 - Added wxNumberFormatter for dealing with thousands separators.
 - Added wxIntegerValidator<> and wxFloatingPointValidator<> validators.
+- Added wxIMAGE_OPTION_GIF_COMMENT to read and write GIF comments (troelsk).
 
 Unix:
 
 
 Unix:
 
index e106e4306104c5e12fa73a9cc623ccd972524e52..105ee05f0ff4dd0f7b328c23c07c2e666e550c17 100644 (file)
@@ -1136,6 +1136,12 @@ public:
             the resulting PNG file. Use this option if your application produces
             images with small size variation.
 
             the resulting PNG file. Use this option if your application produces
             images with small size variation.
 
+        Options specific to wxGIFHandler:
+        @li @c wxIMAGE_OPTION_GIF_COMMENT: The comment text that is read from
+            or written to the GIF file. In an animated GIF each frame can have
+            its own comment. If there is only a comment in the first frame of
+            a GIF it will not be repeated in other frames.
+
         @param name
             The name of the option, case-insensitive.
         @return
         @param name
             The name of the option, case-insensitive.
         @return
index e8faf7d1dd2773858b43f2078a6fefa21802605c..36e8ed276edc93fc49f102767e635a021c0f7968 100644 (file)
@@ -40,6 +40,8 @@ enum
     GIF_MARKER_EXT_APP              = 0xFF
 };
 
     GIF_MARKER_EXT_APP              = 0xFF
 };
 
+#define GetFrame(n)     ((GIFImage*)m_frames[n])
+
 //---------------------------------------------------------------------------
 // GIFImage
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 // GIFImage
 //---------------------------------------------------------------------------
@@ -61,6 +63,7 @@ public:
     unsigned char *p;               // bitmap
     unsigned char *pal;             // palette
     unsigned int ncolours;          // number of colours
     unsigned char *p;               // bitmap
     unsigned char *pal;             // palette
     unsigned int ncolours;          // number of colours
+    wxString comment;
 
     wxDECLARE_NO_COPY_CLASS(GIFImage);
 };
 
     wxDECLARE_NO_COPY_CLASS(GIFImage);
 };
@@ -189,6 +192,12 @@ bool wxGIFDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
         *(dst++) = pal[3 * (*src) + 2];
     }
 
         *(dst++) = pal[3 * (*src) + 2];
     }
 
+    wxString comment = GetFrame(frame)->comment;
+    if ( !comment.empty() )
+    {
+        image->SetOption(wxIMAGE_OPTION_GIF_COMMENT, comment);
+    }
+
     return true;
 }
 
     return true;
 }
 
@@ -197,9 +206,6 @@ bool wxGIFDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
 // Data accessors
 //---------------------------------------------------------------------------
 
 // Data accessors
 //---------------------------------------------------------------------------
 
-#define GetFrame(n)     ((GIFImage*)m_frames[n])
-
-
 // Get data for current frame
 
 wxSize wxGIFDecoder::GetFrameSize(unsigned int frame) const
 // Get data for current frame
 
 wxSize wxGIFDecoder::GetFrameSize(unsigned int frame) const
@@ -672,6 +678,7 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
     int transparent = -1;
     disposal = wxANIM_UNSPECIFIED;
     delay = -1;
     int transparent = -1;
     disposal = wxANIM_UNSPECIFIED;
     delay = -1;
+    wxString comment;
 
     bool done = false;
     while (!done)
 
     bool done = false;
     while (!done)
@@ -701,38 +708,68 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
                 done = true;
                 break;
             case GIF_MARKER_EXT:
                 done = true;
                 break;
             case GIF_MARKER_EXT:
-                if (stream.GetC() == GIF_MARKER_EXT_GRAPHICS_CONTROL)
-                // graphics control extension, parse it
+                switch (stream.GetC())
                 {
                 {
-                    static const unsigned int gceSize = 6;
-                    stream.Read(buf, gceSize);
-                    if (stream.LastRead() != gceSize)
+                    case GIF_MARKER_EXT_GRAPHICS_CONTROL:
                     {
                     {
-                        Destroy();
-                        return wxGIF_INVFORMAT;
-                    }
+                        // 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
-                    transparent = buf[1] & 0x01 ? buf[4] : -1;
+                        // read delay and convert from 1/100 of a second to ms
+                        delay = 10 * (buf[2] + 256 * buf[3]);
 
 
-                    // read disposal method
-                    disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1);
-                }
-                else
-                // other extension, skip
-                {
-                    while ((i = stream.GetC()) != 0)
+                        // 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:
                     {
                     {
-                        if (stream.Eof() || (stream.LastRead() == 0) ||
-                            stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
+                        int len = stream.GetC();
+                        while (len)
                         {
                         {
-                            done = true;
-                            break;
+                            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;
                     }
                     }
+                    default:
+                        // other extension, skip
+                        while ((i = stream.GetC()) != 0)
+                        {
+                            if (stream.Eof() || (stream.LastRead() == 0) ||
+                                stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
+                            {
+                                done = true;
+                                break;
+                            }
+                        }
+                        break;
                 }
                 break;
             case GIF_MARKER_SEP:
                 }
                 break;
             case GIF_MARKER_SEP:
@@ -751,6 +788,8 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
                 if (stream.LastRead() != idbSize)
                     return wxGIF_INVFORMAT;
 
                 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[0] + 256 * buf[1];
                 pimg->top = buf[2] + 256 * buf[3];
     /*
index 241001fcaf4bc0f75dfedddbf55df765230df31b..958dd922a7a0326675401fbd1c466325f3c31d1e 100644 (file)
@@ -73,6 +73,7 @@ private:
         CPPUNIT_TEST( CompareSavedImage );
         CPPUNIT_TEST( SaveAnimatedGIF );
         CPPUNIT_TEST( ReadCorruptedTGA );
         CPPUNIT_TEST( CompareSavedImage );
         CPPUNIT_TEST( SaveAnimatedGIF );
         CPPUNIT_TEST( ReadCorruptedTGA );
+        CPPUNIT_TEST( GIFComment );
     CPPUNIT_TEST_SUITE_END();
 
     void LoadFromSocketStream();
     CPPUNIT_TEST_SUITE_END();
 
     void LoadFromSocketStream();
@@ -83,6 +84,7 @@ private:
     void CompareSavedImage();
     void SaveAnimatedGIF();
     void ReadCorruptedTGA();
     void CompareSavedImage();
     void SaveAnimatedGIF();
     void ReadCorruptedTGA();
+    void GIFComment();
 
     DECLARE_NO_COPY_CLASS(ImageTestCase)
 };
 
     DECLARE_NO_COPY_CLASS(ImageTestCase)
 };
@@ -1171,6 +1173,81 @@ void ImageTestCase::ReadCorruptedTGA()
     CPPUNIT_ASSERT( !tgaImage.LoadFile(memIn) );
 }
 
     CPPUNIT_ASSERT( !tgaImage.LoadFile(memIn) );
 }
 
+static void TestGIFComment(const wxString& comment)
+{
+    wxImage image("horse.gif");
+
+    image.SetOption(wxIMAGE_OPTION_GIF_COMMENT, comment);
+    wxMemoryOutputStream memOut;
+    CPPUNIT_ASSERT(image.SaveFile(memOut, wxBITMAP_TYPE_GIF));
+
+    wxMemoryInputStream memIn(memOut);
+    CPPUNIT_ASSERT( image.LoadFile(memIn) );
+
+    CPPUNIT_ASSERT_EQUAL(comment,
+        image.GetOption(wxIMAGE_OPTION_GIF_COMMENT));
+}
+
+void ImageTestCase::GIFComment()
+{
+    // Test reading a comment.
+    wxImage image("horse.gif");
+    CPPUNIT_ASSERT_EQUAL("  Imported from GRADATION image: gray",
+        image.GetOption(wxIMAGE_OPTION_GIF_COMMENT));
+
+
+    // Test writing a comment and reading it back.
+    TestGIFComment("Giving the GIF a gifted giraffe as a gift");
+
+
+    // Test writing and reading a comment again but with a long comment.
+    TestGIFComment(wxString(wxT('a'), 256)
+        + wxString(wxT('b'), 256)
+        + wxString(wxT('c'), 256));
+
+
+    // Test writing comments in an animated GIF and reading them back.
+    CPPUNIT_ASSERT( image.LoadFile("horse.gif") );
+
+    wxImageArray images;
+    int i;
+    for (i = 0; i < 4; ++i)
+    {
+        if (i)
+        {
+            images.Add( images[i-1].Rotate90() );
+            images[i].SetPalette(images[0].GetPalette());
+        }
+        else
+        {
+            images.Add(image);
+        }
+
+        images[i].SetOption(wxIMAGE_OPTION_GIF_COMMENT,
+            wxString::Format("GIF comment for frame #%d", i+1));
+
+    }
+
+
+    wxMemoryOutputStream memOut;
+    CPPUNIT_ASSERT( wxGIFHandler().SaveAnimation(images, &memOut) );
+
+    wxGIFHandler handler;
+    wxMemoryInputStream memIn(memOut);
+    CPPUNIT_ASSERT(memIn.IsOk());
+    const int imageCount = handler.GetImageCount(memIn);
+    for (i = 0; i < imageCount; ++i)
+    {
+        wxFileOffset pos = memIn.TellI();
+        CPPUNIT_ASSERT( handler.LoadFile(&image, memIn, true /*verbose?*/, i) );
+
+        CPPUNIT_ASSERT_EQUAL(
+            wxString::Format("GIF comment for frame #%d", i+1),
+            image.GetOption(wxIMAGE_OPTION_GIF_COMMENT));
+        memIn.SeekI(pos);
+    }
+}
+
 #endif //wxUSE_IMAGE
 
 
 #endif //wxUSE_IMAGE