/////////////////////////////////////////////////////////////////////////////// // Name: tests/testimage.h // Purpose: Unit test helpers for dealing with wxImage. // Author: Vadim Zeitlin // Copyright: (c) 2011 Vadim Zeitlin // Licence: wxWindows licence /////////////////////////////////////////////////////////////////////////////// #ifndef _WX_TESTS_TESTIMAGE_H_ #define _WX_TESTS_TESTIMAGE_H_ #include "wx/image.h" namespace Catch { template <> struct StringMaker { static std::string convert(const wxImage& image) { return wxString::Format("image of size %d*%d with%s alpha", image.GetWidth(), image.GetHeight(), image.HasAlpha() ? "" : "out") .ToStdString(); } }; } class ImageRGBMatcher : public Catch::MatcherBase { public: ImageRGBMatcher(const wxImage& image, int tolerance, bool checkAlpha = false) : m_image(image) , m_tolerance(tolerance) , m_checkAlpha(checkAlpha) { } bool match(const wxImage& other) const wxOVERRIDE { if ( other.GetWidth() != m_image.GetWidth() ) return false; if ( other.GetHeight() != m_image.GetHeight() ) return false; if ( memcmp(other.GetData(), m_image.GetData(), other.GetWidth()*other.GetHeight()*3) == 0 ) { if ( m_checkAlpha ) { if ( !other.HasAlpha() && !m_image.HasAlpha() ) return true; if ( other.HasAlpha() && m_image.HasAlpha() && memcmp(other.GetAlpha(), m_image.GetAlpha(), other.GetWidth() * other.GetHeight()) == 0 ) return true; } else { return true; } } const unsigned char* d1 = m_image.GetData(); const unsigned char* d2 = other.GetData(); const unsigned char* a1 = m_image.GetAlpha(); const unsigned char* a2 = other.GetAlpha(); bool dispAlphaVal = a1 || a2; for ( int y = 0; y < m_image.GetHeight(); ++y ) { for ( int x = 0; x < m_image.GetWidth(); ++x ) { wxString a1txt = dispAlphaVal ? (a1 != NULL ? wxString::Format("%02x", *a1) : "--") : ""; wxString a2txt = dispAlphaVal ? (a2 != NULL ? wxString::Format("%02x", *a2) : "--") : ""; for ( int i = 0; i < 3; i++ ) { const unsigned char diff = d1[i] > d2[i] ? d1[i] - d2[i] : d2[i] - d1[i]; if ( diff > m_tolerance ) { m_diffDesc.Printf ( "first mismatch is at (%d, %d) which " "has value 0x%02x%02x%02x%s instead of the " "expected 0x%02x%02x%02x%s", x, y, d2[0], d2[1], d2[2], a2txt, d1[0], d1[1], d1[2], a1txt ); // Don't show all mismatches, there may be too many of them. return false; } } if ( m_checkAlpha ) { if ( a1 && a2 ) { const unsigned char diff = *a1 > *a2 ? *a1 - *a2 : *a2 - *a1; if ( diff > m_tolerance ) { m_diffDesc.Printf ( "first mismatch is at (%d, %d) which " "has value 0x%02x%02x%02x%02x instead of the " "expected 0x%02x%02x%02x%02x", x, y, d2[0], d2[1], d2[2], *a2, d1[0], d1[1], d1[2], *a1 ); // Don't show all mismatches, there may be too many of them. return false; } } else if ( a1txt != a2txt ) { m_diffDesc.Printf ( "first mismatch is at (%d, %d) which " "has value 0x%02x%02x%02x%s instead of the " "expected 0x%02x%02x%02x%s", x, y, d2[0], d2[1], d2[2], a2txt, d1[0], d1[1], d1[2], a1txt ); // Don't show all mismatches, there may be too many of them. return false; } } d1 += 3; d2 += 3; if ( a1 ) a1++; if ( a2 ) a2++; } } // We can only get here when the images are different AND we've not exited the // method from the loop. That implies the tolerance must have caused this. wxASSERT_MSG(m_tolerance > 0, "Unreachable without tolerance"); return true; } std::string describe() const wxOVERRIDE { std::string desc = "doesn't have the same RGB data as " + Catch::toString(m_image); if ( !m_diffDesc.empty() ) desc += + ": " + m_diffDesc.ToStdString(wxConvUTF8); return desc; } private: const wxImage m_image; const int m_tolerance; const bool m_checkAlpha; mutable wxString m_diffDesc; }; inline ImageRGBMatcher RGBSameAs(const wxImage& image) { return ImageRGBMatcher(image, 0, false); } inline ImageRGBMatcher RGBASameAs(const wxImage& image) { return ImageRGBMatcher(image, 0, true); } // Allows small differences (within given tolerance) for r, g, and b values. inline ImageRGBMatcher RGBSimilarTo(const wxImage& image, int tolerance) { return ImageRGBMatcher(image, tolerance, false); } inline ImageRGBMatcher RGBASimilarTo(const wxImage& image, int tolerance) { return ImageRGBMatcher(image, tolerance, true); } class ImageAlphaMatcher : public Catch::MatcherBase { public: ImageAlphaMatcher(unsigned char alpha) : m_alpha(alpha) { } bool match(const wxImage& other) const wxOVERRIDE { if (!other.HasAlpha()) { m_diffDesc = "no alpha data"; return false; } unsigned char center_alpha = *(other.GetAlpha() + (other.GetWidth() / 2) + (other.GetHeight() / 2 * other.GetWidth())); if (m_alpha != center_alpha) { m_diffDesc.Printf("got alpha %u", center_alpha); return false; } return true; } std::string describe() const wxOVERRIDE { std::string desc; if (!m_diffDesc.empty()) desc = m_diffDesc.ToStdString(wxConvUTF8); return desc; } private: const unsigned char m_alpha; mutable wxString m_diffDesc; }; inline ImageAlphaMatcher CenterAlphaPixelEquals(unsigned char alpha) { return ImageAlphaMatcher(alpha); } #endif // _WX_TESTS_TESTIMAGE_H_