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:
Dimitri Schoolwerth
2011-02-02 11:19:30 +00:00
parent 41857332b6
commit 15f345aad3
4 changed files with 152 additions and 29 deletions

View 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 wxIMAGE_OPTION_GIF_COMMENT to read and write GIF comments (troelsk).
Unix:

View File

@@ -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

View File

@@ -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,38 +708,68 @@ 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())
{
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]);
// read transparent colour index, if used
transparent = buf[1] & 0x01 ? buf[4] : -1;
// read disposal method
disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1);
}
else
// other extension, skip
{
while ((i = stream.GetC()) != 0)
{
if (stream.Eof() || (stream.LastRead() == 0) ||
stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
static const unsigned int gceSize = 6;
stream.Read(buf, gceSize);
if (stream.LastRead() != gceSize)
{
done = true;
break;
Destroy();
return wxGIF_INVFORMAT;
}
// 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
transparent = buf[1] & 0x01 ? buf[4] : -1;
// read disposal method
disposal = (wxAnimationDisposal)(((buf[1] & 0x1C) >> 2) - 1);
break;
}
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) ||
stream.SeekI(i, wxFromCurrent) == wxInvalidOffset)
{
done = true;
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];
/*

View File

@@ -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