/////////////////////////////////////////////////////////////////////////////// // Name: tests/image/image.cpp // Purpose: Test wxImage // Author: Francesco Montorsi // Created: 2009-05-31 // RCS-ID: $Id$ // Copyright: (c) 2009 Francesco Montorsi // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- #include "testprec.h" #if wxUSE_IMAGE #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #endif // WX_PRECOMP #include "wx/anidecod.h" // wxImageArray #include "wx/image.h" #include "wx/palette.h" #include "wx/url.h" #include "wx/log.h" #include "wx/mstream.h" #include "wx/zstream.h" #include "wx/wfstream.h" struct testData { const char* file; wxBitmapType type; unsigned bitDepth; } g_testfiles[] = { { "horse.ico", wxBITMAP_TYPE_ICO, 4 }, { "horse.xpm", wxBITMAP_TYPE_XPM, 8 }, { "horse.png", wxBITMAP_TYPE_PNG, 24 }, { "horse.ani", wxBITMAP_TYPE_ANI, 24 }, { "horse.bmp", wxBITMAP_TYPE_BMP, 8 }, { "horse.cur", wxBITMAP_TYPE_CUR, 1 }, { "horse.gif", wxBITMAP_TYPE_GIF, 8 }, { "horse.jpg", wxBITMAP_TYPE_JPEG, 24 }, { "horse.pcx", wxBITMAP_TYPE_PCX, 8 }, { "horse.pnm", wxBITMAP_TYPE_PNM, 24 }, { "horse.tga", wxBITMAP_TYPE_TGA, 8 }, { "horse.tif", wxBITMAP_TYPE_TIF, 8 } }; // ---------------------------------------------------------------------------- // test class // ---------------------------------------------------------------------------- class ImageTestCase : public CppUnit::TestCase { public: ImageTestCase(); ~ImageTestCase(); private: CPPUNIT_TEST_SUITE( ImageTestCase ); CPPUNIT_TEST( LoadFromSocketStream ); CPPUNIT_TEST( LoadFromZipStream ); CPPUNIT_TEST( LoadFromFile ); CPPUNIT_TEST( SizeImage ); CPPUNIT_TEST( CompareLoadedImage ); CPPUNIT_TEST( CompareSavedImage ); CPPUNIT_TEST( SaveAnimatedGIF ); CPPUNIT_TEST( ReadCorruptedTGA ); CPPUNIT_TEST( GIFComment ); CPPUNIT_TEST_SUITE_END(); void LoadFromSocketStream(); void LoadFromZipStream(); void LoadFromFile(); void SizeImage(); void CompareLoadedImage(); void CompareSavedImage(); void SaveAnimatedGIF(); void ReadCorruptedTGA(); void GIFComment(); DECLARE_NO_COPY_CLASS(ImageTestCase) }; CPPUNIT_TEST_SUITE_REGISTRATION( ImageTestCase ); CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ImageTestCase, "ImageTestCase" ); ImageTestCase::ImageTestCase() { wxSocketBase::Initialize(); // the formats we're going to test: wxImage::AddHandler(new wxICOHandler); wxImage::AddHandler(new wxXPMHandler); wxImage::AddHandler(new wxPNGHandler); wxImage::AddHandler(new wxANIHandler); wxImage::AddHandler(new wxBMPHandler); wxImage::AddHandler(new wxCURHandler); wxImage::AddHandler(new wxGIFHandler); wxImage::AddHandler(new wxJPEGHandler); wxImage::AddHandler(new wxPCXHandler); wxImage::AddHandler(new wxPNMHandler); wxImage::AddHandler(new wxTGAHandler); wxImage::AddHandler(new wxTIFFHandler); } ImageTestCase::~ImageTestCase() { wxSocketBase::Shutdown(); } void ImageTestCase::LoadFromFile() { wxImage img; for (unsigned int i=0; iIsOk() ); wxImage img; // NOTE: it's important to inform wxImage about the type of the image being // loaded otherwise it will try to autodetect the format, but that // requires a seekable stream! WX_ASSERT_MESSAGE ( ("Loading image from \"%s\" failed.", testData[i].url), img.LoadFile(*in_stream, testData[i].type) ); delete in_stream; } } void ImageTestCase::LoadFromZipStream() { for (unsigned int i=0; iGetSize() ); unsigned bitsPerPixel = testPalette ? 8 : (testAlpha ? 32 : 24); WX_ASSERT_MESSAGE ( ("Compare test '%s (%d-bit)' for saving failed", handler.GetExtension(), bitsPerPixel), memcmp(actual.GetData(), expected->GetData(), expected->GetWidth() * expected->GetHeight() * 3) == 0 ); #if wxUSE_PALETTE CPPUNIT_ASSERT(actual.HasPalette() == (testPalette || type == wxBITMAP_TYPE_XPM)); #endif CPPUNIT_ASSERT( actual.HasAlpha() == testAlpha); if (!testAlpha) { return; } WX_ASSERT_MESSAGE ( ("Compare alpha test '%s' for saving failed", handler.GetExtension()), memcmp(actual.GetAlpha(), expected->GetAlpha(), expected->GetWidth() * expected->GetHeight()) == 0 ); } void ImageTestCase::CompareSavedImage() { // FIXME-VC6: Pre-declare the loop variables for compatibility with // pre-standard compilers such as MSVC6 that don't implement proper scope // for the variables declared in the for loops. int i, x, y; wxImage expected24("horse.png"); CPPUNIT_ASSERT( expected24.IsOk() ); CPPUNIT_ASSERT( !expected24.HasAlpha() ); wxImage expected8 = expected24.ConvertToGreyscale(); #if wxUSE_PALETTE unsigned char greys[256]; for (i = 0; i < 256; ++i) { greys[i] = i; } wxPalette palette(256, greys, greys, greys); expected8.SetPalette(palette); #endif // #if wxUSE_PALETTE expected8.SetOption(wxIMAGE_OPTION_BMP_FORMAT, wxBMP_8BPP_PALETTE); // Create an image with alpha based on the loaded image wxImage expected32(expected24); expected32.SetAlpha(); int width = expected32.GetWidth(); int height = expected32.GetHeight(); for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { expected32.SetAlpha(x, y, (x*y) & wxIMAGE_ALPHA_OPAQUE); } } const wxList& list = wxImage::GetHandlers(); for ( wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext() ) { wxImageHandler *handler = (wxImageHandler *) node->GetData(); #if wxUSE_PALETTE CompareImage(*handler, expected8, wxIMAGE_HAVE_PALETTE); #endif CompareImage(*handler, expected24); CompareImage(*handler, expected32, wxIMAGE_HAVE_ALPHA); } expected8.LoadFile("horse.gif"); CPPUNIT_ASSERT( expected8.IsOk() ); #if wxUSE_PALETTE CPPUNIT_ASSERT( expected8.HasPalette() ); #endif // #if wxUSE_PALETTE expected8.SetAlpha(); width = expected8.GetWidth(); height = expected8.GetHeight(); for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { expected8.SetAlpha(x, y, (x*y) & wxIMAGE_ALPHA_OPAQUE); } } /* Explicitly make known we want a palettised PNG. If we don't then this particular image gets saved as a true colour image because there's an alpha channel present and the PNG saver prefers to keep the alpha over saving as a palettised image that has alpha converted to a mask. */ expected8.SetOption(wxIMAGE_OPTION_PNG_FORMAT, wxPNG_TYPE_PALETTE); /* The image contains 256 indexed colours and needs another palette entry for storing the transparency index. This results in wanting 257 palette entries but that amount is not supported by PNG, as such this image should not contain a palette (but still have alpha) and be stored as a true colour image instead. */ CompareImage(*wxImage::FindHandler(wxBITMAP_TYPE_PNG), expected8, wxIMAGE_HAVE_ALPHA); #if wxUSE_PALETTE /* Now do the same test again but remove one (random) palette entry. This should result in saving the PNG with a palette. */ unsigned char red[256], green[256], blue[256]; const wxPalette& pal = expected8.GetPalette(); const int paletteCount = pal.GetColoursCount(); for (i = 0; i < paletteCount; ++i) { expected8.GetPalette().GetRGB(i, &red[i], &green[i], &blue[i]); } wxPalette newPal(paletteCount - 1, red, green, blue); expected8.Replace( red[paletteCount-1], green[paletteCount-1], blue[paletteCount-1], red[paletteCount-2], green[paletteCount-2], blue[paletteCount-2]); expected8.SetPalette(newPal); wxImage ref8 = expected8; /* Convert the alpha channel to a mask like the PNG saver does. Also convert the colour used for transparency from 1,0,0 to 2,0,0. The latter gets done by the PNG loader in search of an unused colour to use for transparency (this should be fixed). */ ref8.ConvertAlphaToMask(); ref8.Replace(1, 0, 0, 2, 0, 0); CompareImage(*wxImage::FindHandler(wxBITMAP_TYPE_PNG), expected8, wxIMAGE_HAVE_PALETTE, &ref8); #endif } void ImageTestCase::SaveAnimatedGIF() { #if wxUSE_PALETTE wxImage image("horse.gif"); CPPUNIT_ASSERT( image.IsOk() ); wxImageArray images; images.Add(image); int i; for (i = 0; i < 4-1; ++i) { images.Add( images[i].Rotate90() ); images[i+1].SetPalette(images[0].GetPalette()); } wxMemoryOutputStream memOut; CPPUNIT_ASSERT( wxGIFHandler().SaveAnimation(images, &memOut) ); wxGIFHandler handler; wxMemoryInputStream memIn(memOut); CPPUNIT_ASSERT(memIn.IsOk()); const int imageCount = handler.GetImageCount(memIn); CPPUNIT_ASSERT_EQUAL(4, imageCount); for (i = 0; i < imageCount; ++i) { wxFileOffset pos = memIn.TellI(); CPPUNIT_ASSERT( handler.LoadFile(&image, memIn, true, i) ); memIn.SeekI(pos); WX_ASSERT_MESSAGE ( ("Compare test for GIF frame number %d failed", i), memcmp(image.GetData(), images[i].GetData(), images[i].GetWidth() * images[i].GetHeight() * 3) == 0 ); } #endif // #if wxUSE_PALETTE } void ImageTestCase::ReadCorruptedTGA() { static unsigned char corruptTGA[18+1+3] = { 0, 0, 10, // RLE compressed image. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, // Width is 1. 1, 0, // Height is 1. 24, // Bits per pixel. 0, 0xff, // Run length (repeat next pixel 127+1 times). 0xff, 0xff, 0xff // One 24-bit pixel. }; wxMemoryInputStream memIn(corruptTGA, WXSIZEOF(corruptTGA)); CPPUNIT_ASSERT(memIn.IsOk()); wxImage tgaImage; CPPUNIT_ASSERT( !tgaImage.LoadFile(memIn) ); /* Instead of repeating a pixel 127+1 times, now tell it there will follow 127+1 uncompressed pixels (while we only should have 1 in total). */ corruptTGA[18] = 0x7f; 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 /* TODO: add lots of more tests to wxImage functions */