Added support for reading comments from a GIF image.
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
This commit is contained in:
@@ -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 wxIMAGE_OPTION_GIF_COMMENT to read and write GIF comments (troelsk).
|
||||
|
||||
Unix:
|
||||
|
||||
|
@@ -1136,6 +1136,12 @@ public:
|
||||
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
|
||||
|
@@ -40,6 +40,8 @@ enum
|
||||
GIF_MARKER_EXT_APP = 0xFF
|
||||
};
|
||||
|
||||
#define GetFrame(n) ((GIFImage*)m_frames[n])
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// GIFImage
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -61,6 +63,7 @@ public:
|
||||
unsigned char *p; // bitmap
|
||||
unsigned char *pal; // palette
|
||||
unsigned int ncolours; // number of colours
|
||||
wxString comment;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(GIFImage);
|
||||
};
|
||||
@@ -189,6 +192,12 @@ bool wxGIFDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
|
||||
*(dst++) = pal[3 * (*src) + 2];
|
||||
}
|
||||
|
||||
wxString comment = GetFrame(frame)->comment;
|
||||
if ( !comment.empty() )
|
||||
{
|
||||
image->SetOption(wxIMAGE_OPTION_GIF_COMMENT, comment);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -197,9 +206,6 @@ bool wxGIFDecoder::ConvertToImage(unsigned int frame, wxImage *image) const
|
||||
// Data accessors
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#define GetFrame(n) ((GIFImage*)m_frames[n])
|
||||
|
||||
|
||||
// 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;
|
||||
wxString comment;
|
||||
|
||||
bool done = false;
|
||||
while (!done)
|
||||
@@ -701,9 +708,12 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
|
||||
done = true;
|
||||
break;
|
||||
case GIF_MARKER_EXT:
|
||||
if (stream.GetC() == GIF_MARKER_EXT_GRAPHICS_CONTROL)
|
||||
// graphics control extension, parse it
|
||||
switch (stream.GetC())
|
||||
{
|
||||
case GIF_MARKER_EXT_GRAPHICS_CONTROL:
|
||||
{
|
||||
// graphics control extension, parse it
|
||||
|
||||
static const unsigned int gceSize = 6;
|
||||
stream.Read(buf, gceSize);
|
||||
if (stream.LastRead() != gceSize)
|
||||
@@ -720,10 +730,36 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
|
||||
|
||||
// read disposal method
|
||||
disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1);
|
||||
break;
|
||||
}
|
||||
else
|
||||
// other extension, skip
|
||||
case GIF_MARKER_EXT_COMMENT:
|
||||
{
|
||||
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;
|
||||
}
|
||||
default:
|
||||
// other extension, skip
|
||||
while ((i = stream.GetC()) != 0)
|
||||
{
|
||||
if (stream.Eof() || (stream.LastRead() == 0) ||
|
||||
@@ -733,6 +769,7 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GIF_MARKER_SEP:
|
||||
@@ -751,6 +788,8 @@ wxGIFErrorCode wxGIFDecoder::LoadGIF(wxInputStream& stream)
|
||||
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];
|
||||
/*
|
||||
|
@@ -73,6 +73,7 @@ private:
|
||||
CPPUNIT_TEST( CompareSavedImage );
|
||||
CPPUNIT_TEST( SaveAnimatedGIF );
|
||||
CPPUNIT_TEST( ReadCorruptedTGA );
|
||||
CPPUNIT_TEST( GIFComment );
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
void LoadFromSocketStream();
|
||||
@@ -83,6 +84,7 @@ private:
|
||||
void CompareSavedImage();
|
||||
void SaveAnimatedGIF();
|
||||
void ReadCorruptedTGA();
|
||||
void GIFComment();
|
||||
|
||||
DECLARE_NO_COPY_CLASS(ImageTestCase)
|
||||
};
|
||||
@@ -1171,6 +1173,81 @@ void ImageTestCase::ReadCorruptedTGA()
|
||||
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
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user