wxWidgets/tests/graphics/clippingbox.cpp
2016-08-21 21:23:01 +02:00

1927 lines
61 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: tests/graphics/clippingbox.cpp
// Purpose: clipping box unit tests
// Author: Artur Wieczorek
// Created: 2016-06-29
// Copyright: (c) 2016 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "testprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include "wx/bitmap.h"
#include "wx/dcmemory.h"
#if wxUSE_GRAPHICS_CONTEXT
#include "wx/dcgraph.h"
#endif // wxUSE_GRAPHICS_CONTEXT
// ----------------------------------------------------------------------------
// test class
// ----------------------------------------------------------------------------
static const wxSize s_dcSize(100, 120);
static const wxColour s_bgColour(*wxWHITE); // colour to draw outside clipping box
static const wxColour s_fgColour(*wxGREEN); // colour to draw inside clipping box
#if wxUSE_IMAGE
static bool CompareImageFuzzy(const wxImage& img1, const wxImage& img2, int posTolerance = 0)
{
// For each (x,y) pixel in the image1 we check
// if there is a pixel of the same value in the square
// area (x-d..x+d, y-d..y+d) around the pixel (x,y)
// in the image2. If not, we consider pixels
// and images as different.
if ( img1.GetWidth() != img2.GetWidth() )
return false;
if ( img1.GetHeight() != img2.GetHeight() )
return false;
const int w = img1.GetWidth();
const int h = img1.GetHeight();
for ( int y1 = 0; y1 < h; y1++ )
{
int y2min = wxMax(y1 - posTolerance, 0);
int y2max = wxMin(y1 + posTolerance, h);
for( int x1 = 0; x1 < w; x1++ )
{
int x2min = wxMax(x1 - posTolerance, 0);
int x2max = wxMin(x1 + posTolerance, w);
unsigned char r1 = img1.GetRed(x1, y1);
unsigned char g1 = img1.GetGreen(x1, y1);
unsigned char b1 = img1.GetBlue(x1, y1);
bool pix2Found = false;
for( int y2 = y2min; y2 <= y2max && !pix2Found; y2++ )
for( int x2 = x2min; x2 <= x2max; x2++ )
{
if( r1 == img2.GetRed(x2, y2) &&
g1 == img2.GetGreen(x2, y2) &&
b1 == img2.GetBlue(x2, y2))
{
pix2Found = true;
break;
}
}
if ( !pix2Found )
return false;
}
}
return true;
}
#endif // wxUSE_IMAGE
class ClippingBoxTestCaseBase : public CppUnit::TestCase
{
public:
ClippingBoxTestCaseBase()
{
m_bmp.Create(s_dcSize);
}
virtual ~ClippingBoxTestCaseBase()
{
m_bmp = wxNullBitmap;
}
protected:
void CheckBoxPosition(int cur_x, int cur_y, int cur_w, int cur_h,
int act_x, int act_y, int act_w, int act_h,
int posTolerance = 0);
void CheckClipRect(int x, int y, int width, int height);
void CheckClipWithBitmap(const wxBitmap& bmpRef, int posTolerance = 0);
wxBitmap m_bmp;
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseBase);
};
// ====================
// wxDC / wxGCDC tests
// ====================
class ClippingBoxTestCaseDCBase : public ClippingBoxTestCaseBase
{
public:
ClippingBoxTestCaseDCBase()
{
m_dc = NULL;
}
virtual ~ClippingBoxTestCaseDCBase()
{
}
virtual void setUp() { wxASSERT( m_dc ); }
virtual wxDC* GetDC(wxMemoryDC* dc) = 0;
protected:
void CheckClipPos(int x, int y, int width, int height, int posTolerance = 0);
void CheckClipBox(int x, int y, int width, int height);
void CheckClipShape(const wxBitmap& bmpRef, int posTolerance);
void InitialState();
void InitialStateWithTransformedDC();
void InitialStateWithRotatedDC();
void OneRegion();
void OneLargeRegion();
void OneOuterRegion();
void OneRegionNegDim();
void OneRegionAndReset();
void OneRegionAndEmpty();
void OneRegionWithTransformedDC();
void TwoRegionsOverlapping();
void TwoRegionsOverlappingNegDim();
void TwoRegionsNonOverlapping();
void TwoRegionsNonOverlappingNegDim();
void OneDevRegion();
void OneLargeDevRegion();
void OneOuterDevRegion();
void OneDevRegionNegDim();
void OneDevRegionNonRect();
void OneDevRegionAndReset();
void OneDevRegionAndEmpty();
void TwoDevRegionsOverlapping();
void TwoDevRegionsOverlappingNegDim();
void TwoDevRegionsNonOverlapping();
void TwoDevRegionsNonOverlappingNegDim();
virtual void FlushDC() = 0;
wxDC* m_dc;
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseDCBase);
};
// ===========
// wxDC tests
// ===========
class ClippingBoxTestCaseDC : public ClippingBoxTestCaseDCBase
{
public:
ClippingBoxTestCaseDC()
{
m_mdc.SelectObject(m_bmp);
m_dc = &m_mdc;
}
virtual ~ClippingBoxTestCaseDC()
{
m_mdc.SelectObject(wxNullBitmap);
}
virtual void setUp() wxOVERRIDE
{
m_mdc.DestroyClippingRegion();
wxBrush bgBrush(s_bgColour, wxBRUSHSTYLE_SOLID);
m_mdc.SetBackground(bgBrush);
m_mdc.Clear();
}
virtual wxDC* GetDC(wxMemoryDC* dc) wxOVERRIDE
{
return dc;
}
protected:
virtual void FlushDC() wxOVERRIDE {}
private:
CPPUNIT_TEST_SUITE( ClippingBoxTestCaseDC );
CPPUNIT_TEST( InitialState );
CPPUNIT_TEST( InitialStateWithTransformedDC );
CPPUNIT_TEST( InitialStateWithRotatedDC );
CPPUNIT_TEST( OneRegion );
CPPUNIT_TEST( OneLargeRegion );
CPPUNIT_TEST( OneOuterRegion );
CPPUNIT_TEST( OneRegionNegDim );
CPPUNIT_TEST( OneRegionAndReset );
CPPUNIT_TEST( OneRegionAndEmpty );
CPPUNIT_TEST( OneRegionWithTransformedDC );
CPPUNIT_TEST( TwoRegionsOverlapping );
CPPUNIT_TEST( TwoRegionsOverlappingNegDim );
CPPUNIT_TEST( TwoRegionsNonOverlapping );
CPPUNIT_TEST( TwoRegionsNonOverlappingNegDim );
CPPUNIT_TEST( OneDevRegion);
CPPUNIT_TEST( OneLargeDevRegion );
CPPUNIT_TEST( OneOuterDevRegion );
CPPUNIT_TEST( OneDevRegionNegDim );
CPPUNIT_TEST( OneDevRegionNonRect );
CPPUNIT_TEST( OneDevRegionAndReset);
CPPUNIT_TEST( OneDevRegionAndEmpty );
CPPUNIT_TEST( TwoDevRegionsOverlapping );
CPPUNIT_TEST( TwoDevRegionsOverlappingNegDim);
CPPUNIT_TEST( TwoDevRegionsNonOverlapping );
CPPUNIT_TEST( TwoDevRegionsNonOverlappingNegDim );
CPPUNIT_TEST_SUITE_END();
protected:
wxMemoryDC m_mdc;
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseDC);
};
// For GTK+ 3 wxDC is equivalent to wxGCDC
// and hence doesn't need to be tested individually.
#if !defined(__WXGTK3__)
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( ClippingBoxTestCaseDC );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ClippingBoxTestCaseDC, "ClippingBoxTestCaseDC" );
#endif // !__WXGTK3__
#if wxUSE_GRAPHICS_CONTEXT
// =============
// wxGCDC tests
// =============
class ClippingBoxTestCaseGCDC : public ClippingBoxTestCaseDC
{
public:
ClippingBoxTestCaseGCDC()
{
m_gcdc = new wxGCDC(m_mdc);
m_dc = m_gcdc;
}
virtual ~ClippingBoxTestCaseGCDC()
{
delete m_gcdc;
}
virtual void setUp() wxOVERRIDE
{
CPPUNIT_ASSERT_MESSAGE("Invalid wxGCDC", m_gcdc);
wxGraphicsContext* ctx = m_gcdc->GetGraphicsContext();
ctx->SetAntialiasMode(wxANTIALIAS_NONE);
ctx->DisableOffset();
m_gcdc->DestroyClippingRegion();
wxBrush bgBrush(s_bgColour, wxBRUSHSTYLE_SOLID);
m_gcdc->SetBackground(bgBrush);
m_gcdc->Clear();
}
virtual wxDC* GetDC(wxMemoryDC* dc) wxOVERRIDE
{
wxGraphicsRenderer* rend = m_gcdc->GetGraphicsContext()->GetRenderer();
wxGraphicsContext* ctx = rend->CreateContext(*dc);
ctx->SetAntialiasMode(wxANTIALIAS_NONE);
ctx->DisableOffset();
return new wxGCDC(ctx);
}
protected:
virtual void FlushDC() wxOVERRIDE
{
m_gcdc->GetGraphicsContext()->Flush();
}
void InitialStateWithRotatedGCForDC();
private:
CPPUNIT_TEST_SUITE( ClippingBoxTestCaseGCDC );
CPPUNIT_TEST( InitialState );
CPPUNIT_TEST( InitialStateWithTransformedDC );
CPPUNIT_TEST( InitialStateWithRotatedDC );
CPPUNIT_TEST( InitialStateWithRotatedGCForDC );
CPPUNIT_TEST( OneRegion );
CPPUNIT_TEST( OneLargeRegion );
CPPUNIT_TEST( OneOuterRegion );
CPPUNIT_TEST( OneRegionNegDim );
CPPUNIT_TEST( OneRegionAndReset );
CPPUNIT_TEST( OneRegionAndEmpty );
CPPUNIT_TEST( OneRegionWithTransformedDC );
CPPUNIT_TEST( TwoRegionsOverlapping );
CPPUNIT_TEST( TwoRegionsOverlappingNegDim );
CPPUNIT_TEST( TwoRegionsNonOverlapping );
CPPUNIT_TEST( TwoRegionsNonOverlappingNegDim );
CPPUNIT_TEST( OneDevRegion);
CPPUNIT_TEST( OneLargeDevRegion );
CPPUNIT_TEST( OneOuterDevRegion );
CPPUNIT_TEST( OneDevRegionNegDim );
CPPUNIT_TEST( OneDevRegionNonRect );
CPPUNIT_TEST( OneDevRegionAndReset);
CPPUNIT_TEST( OneDevRegionAndEmpty );
CPPUNIT_TEST( TwoDevRegionsOverlapping );
CPPUNIT_TEST( TwoDevRegionsOverlappingNegDim);
CPPUNIT_TEST( TwoDevRegionsNonOverlapping );
CPPUNIT_TEST( TwoDevRegionsNonOverlappingNegDim );
CPPUNIT_TEST_SUITE_END();
protected:
wxGCDC* m_gcdc;
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseGCDC);
};
// For MSW we have individual test cases for each graphics renderer
// so we don't need to test wxGCDC with default renderer.
#ifndef __WXMSW__
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( ClippingBoxTestCaseGCDC );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ClippingBoxTestCaseGCDC, "ClippingBoxTestCaseGCDC" );
#endif // !__WXMSW__
#ifdef __WXMSW__
// GDI+ and Direct2D are available only under MSW.
#if wxUSE_GRAPHICS_GDIPLUS
class ClippingBoxTestCaseGCDCGDIPlus : public ClippingBoxTestCaseGCDC
{
public:
ClippingBoxTestCaseGCDCGDIPlus()
{
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetGDIPlusRenderer();
wxGraphicsContext* ctx = rend->CreateContext(m_mdc);
m_gcdc->SetGraphicsContext(ctx);
}
virtual ~ClippingBoxTestCaseGCDCGDIPlus() {}
private:
CPPUNIT_TEST_SUITE( ClippingBoxTestCaseGCDCGDIPlus );
CPPUNIT_TEST( InitialState );
CPPUNIT_TEST( InitialStateWithTransformedDC );
CPPUNIT_TEST( InitialStateWithRotatedDC );
CPPUNIT_TEST( InitialStateWithRotatedGCForDC );
CPPUNIT_TEST( OneRegion );
CPPUNIT_TEST( OneLargeRegion );
CPPUNIT_TEST( OneOuterRegion );
CPPUNIT_TEST( OneRegionNegDim );
CPPUNIT_TEST( OneRegionAndReset );
CPPUNIT_TEST( OneRegionAndEmpty );
CPPUNIT_TEST( OneRegionWithTransformedDC );
CPPUNIT_TEST( TwoRegionsOverlapping );
CPPUNIT_TEST( TwoRegionsOverlappingNegDim );
CPPUNIT_TEST( TwoRegionsNonOverlapping );
CPPUNIT_TEST( TwoRegionsNonOverlappingNegDim );
CPPUNIT_TEST( OneDevRegion);
CPPUNIT_TEST( OneLargeDevRegion );
CPPUNIT_TEST( OneOuterDevRegion );
CPPUNIT_TEST( OneDevRegionNegDim );
CPPUNIT_TEST( OneDevRegionNonRect );
CPPUNIT_TEST( OneDevRegionAndReset);
CPPUNIT_TEST( OneDevRegionAndEmpty );
CPPUNIT_TEST( TwoDevRegionsOverlapping );
CPPUNIT_TEST( TwoDevRegionsOverlappingNegDim);
CPPUNIT_TEST( TwoDevRegionsNonOverlapping );
CPPUNIT_TEST( TwoDevRegionsNonOverlappingNegDim );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseGCDCGDIPlus);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( ClippingBoxTestCaseGCDCGDIPlus );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ClippingBoxTestCaseGCDCGDIPlus, "ClippingBoxTestCaseGCDCGDIPlus" );
#endif // wxUSE_GRAPHICS_GDIPLUS
#if wxUSE_GRAPHICS_DIRECT2D
class ClippingBoxTestCaseGCDCDirect2D : public ClippingBoxTestCaseGCDC
{
public:
ClippingBoxTestCaseGCDCDirect2D()
{
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetDirect2DRenderer();
wxGraphicsContext* ctx = rend->CreateContext(m_mdc);
m_gcdc->SetGraphicsContext(ctx);
}
virtual ~ClippingBoxTestCaseGCDCDirect2D() {}
virtual void FlushDC()
{
// Apparently, flushing native Direct2D renderer
// is not enough to update underlying DC (bitmap)
// and therefore we have to destroy the renderer
// to do so.
ClippingBoxTestCaseGCDC::FlushDC();
m_gcdc->SetGraphicsContext(NULL);
}
private:
CPPUNIT_TEST_SUITE( ClippingBoxTestCaseGCDCDirect2D );
CPPUNIT_TEST( InitialState );
CPPUNIT_TEST( InitialStateWithTransformedDC );
CPPUNIT_TEST( InitialStateWithRotatedDC );
CPPUNIT_TEST( InitialStateWithRotatedGCForDC );
CPPUNIT_TEST( OneRegion );
CPPUNIT_TEST( OneLargeRegion );
CPPUNIT_TEST( OneOuterRegion );
CPPUNIT_TEST( OneRegionNegDim );
CPPUNIT_TEST( OneRegionAndReset );
CPPUNIT_TEST( OneRegionAndEmpty );
CPPUNIT_TEST( OneRegionWithTransformedDC );
CPPUNIT_TEST( TwoRegionsOverlapping );
CPPUNIT_TEST( TwoRegionsOverlappingNegDim );
CPPUNIT_TEST( TwoRegionsNonOverlapping );
CPPUNIT_TEST( TwoRegionsNonOverlappingNegDim );
CPPUNIT_TEST( OneDevRegion);
CPPUNIT_TEST( OneLargeDevRegion );
CPPUNIT_TEST( OneOuterDevRegion );
CPPUNIT_TEST( OneDevRegionNegDim );
CPPUNIT_TEST( OneDevRegionNonRect );
CPPUNIT_TEST( OneDevRegionAndReset);
CPPUNIT_TEST( OneDevRegionAndEmpty );
CPPUNIT_TEST( TwoDevRegionsOverlapping );
CPPUNIT_TEST( TwoDevRegionsOverlappingNegDim);
CPPUNIT_TEST( TwoDevRegionsNonOverlapping );
CPPUNIT_TEST( TwoDevRegionsNonOverlappingNegDim );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseGCDCDirect2D);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( ClippingBoxTestCaseGCDCDirect2D );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ClippingBoxTestCaseGCDCDirect2D, "ClippingBoxTestCaseGCDCDirect2D" );
#endif // wxUSE_GRAPHICS_DIRECT2D
#endif // __WXMSW__
#if wxUSE_CAIRO
class ClippingBoxTestCaseGCDCCairo : public ClippingBoxTestCaseGCDC
{
public:
ClippingBoxTestCaseGCDCCairo()
{
wxGraphicsRenderer* rend = wxGraphicsRenderer::GetCairoRenderer();
wxGraphicsContext* ctx = rend->CreateContext(m_mdc);
m_gcdc->SetGraphicsContext(ctx);
}
virtual ~ClippingBoxTestCaseGCDCCairo() {}
private:
CPPUNIT_TEST_SUITE( ClippingBoxTestCaseGCDCCairo );
CPPUNIT_TEST( InitialState );
CPPUNIT_TEST( InitialStateWithTransformedDC );
CPPUNIT_TEST( InitialStateWithRotatedDC );
CPPUNIT_TEST( InitialStateWithRotatedGCForDC );
CPPUNIT_TEST( OneRegion );
CPPUNIT_TEST( OneLargeRegion );
CPPUNIT_TEST( OneOuterRegion );
CPPUNIT_TEST( OneRegionNegDim );
CPPUNIT_TEST( OneRegionAndReset );
CPPUNIT_TEST( OneRegionAndEmpty );
CPPUNIT_TEST( OneRegionWithTransformedDC );
CPPUNIT_TEST( TwoRegionsOverlapping );
CPPUNIT_TEST( TwoRegionsOverlappingNegDim );
CPPUNIT_TEST( TwoRegionsNonOverlapping );
CPPUNIT_TEST( TwoRegionsNonOverlappingNegDim );
CPPUNIT_TEST( OneDevRegion);
CPPUNIT_TEST( OneLargeDevRegion );
CPPUNIT_TEST( OneOuterDevRegion );
CPPUNIT_TEST( OneDevRegionNegDim );
CPPUNIT_TEST( OneDevRegionNonRect );
CPPUNIT_TEST( OneDevRegionAndReset);
CPPUNIT_TEST( OneDevRegionAndEmpty );
CPPUNIT_TEST( TwoDevRegionsOverlapping );
CPPUNIT_TEST( TwoDevRegionsOverlappingNegDim);
CPPUNIT_TEST( TwoDevRegionsNonOverlapping );
CPPUNIT_TEST( TwoDevRegionsNonOverlappingNegDim );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseGCDCCairo);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( ClippingBoxTestCaseGCDCCairo );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ClippingBoxTestCaseGCDCCairo, "ClippingBoxTestCaseGCDCCairo" );
#endif // wxUSE_CAIRO
#endif // wxUSE_GRAPHICS_CONTEXT
// Helper class to hold rectangle
// which size is guaranteed to be >= 0
class wxClipRect : public wxRect
{
public:
wxClipRect(int xx, int yy, int w, int h, int boundWidth = 1)
{
if ( w < 0 )
{
w = -w;
xx -= (w - boundWidth);
}
if ( h < 0 )
{
h = -h;
yy -= (h - boundWidth);
}
x = xx;
y = yy;
width = w;
height = h;
}
};
// ===== Implementation =====
static bool inline IsCoordEqual(int pos1, int pos2, int posTolerance = 0)
{
return abs(pos1 - pos2) <= posTolerance;
}
void ClippingBoxTestCaseBase::CheckBoxPosition(int cur_x, int cur_y, int cur_w, int cur_h,
int x, int y, int w, int h,
int posTolerance)
{
wxString msgPos;
if ( !IsCoordEqual(x, cur_x, posTolerance) ||
!IsCoordEqual(y, cur_y, posTolerance) )
{
msgPos =
wxString::Format(wxS("Invalid position: Actual: (%i, %i) Expected: (%i, %i)"),
cur_x, cur_y, x, y);
}
wxString msgDim;
if ( !IsCoordEqual(w, cur_w, 2*posTolerance) ||
!IsCoordEqual(h, cur_h, 2*posTolerance) )
{
msgDim =
wxString::Format(wxS("Invalid dimension: Actual: %i x %i Expected: %i x %i"),
cur_w, cur_h, w, h);
}
wxString msg;
if ( !msgPos.empty() )
{
msg = msgPos;
if ( !msgDim.empty() )
{
msg += wxS("\n- ");
msg += msgDim;
}
}
else if ( !msgDim.empty() )
{
msg = msgDim;
}
if( !msg.empty() )
{
wxCharBuffer buffer = msg.ToUTF8();
CPPUNIT_FAIL( buffer.data() );
}
}
void ClippingBoxTestCaseBase::CheckClipRect(int x, int y, int width, int height)
{
#if wxUSE_IMAGE
// We check whether diagonal corners of the clipping box are actually
// drawn at the edge of the clipping region.
wxImage img = m_bmp.ConvertToImage();
wxString msg;
wxPoint corners[4];
corners[0] = wxPoint(x, y); // top-left corner
corners[1] = wxPoint(x+width-1, y); // top-right corner
corners[2] = wxPoint(x, y+height-1); // bottom-left corner
corners[3] = wxPoint(x+width-1, y+height-1); // bottom-right corner
// Check area near every corner
for ( int c = 0; c < WXSIZEOF(corners); c++ )
{
int ymin = corners[c].y-1;
int xmin = corners[c].x-1;
int ymax = corners[c].y+1;
int xmax = corners[c].x+1;
ymin = wxMin(wxMax(ymin, 0), s_dcSize.GetHeight()-1);
xmin = wxMin(wxMax(xmin, 0), s_dcSize.GetWidth()-1);
ymax = wxMin(wxMax(ymax, 0), s_dcSize.GetHeight()-1);
xmax = wxMin(wxMax(xmax, 0), s_dcSize.GetWidth()-1);
for( int py = ymin; py <= ymax; py++ )
for( int px = xmin; px <= xmax; px++ )
{
wxColour c;
unsigned char r = img.GetRed(px, py);
unsigned char g = img.GetGreen(px, py);
unsigned char b = img.GetBlue(px, py);
c.Set(r, g, b);
wxString msgColour;
if ( px >= x && px <= x + (width-1) &&
py >= y && py <= y + (height-1) )
{
// Pixel inside the box.
if ( c != s_fgColour )
{
msgColour =
wxString::Format(wxS("Invalid colour drawn at (%i, %i): Actual: %s Expected: %s"),
px, py, c.GetAsString().mbc_str(), s_fgColour.GetAsString().mbc_str());
}
}
else
{
// Pixel outside the box.
if ( c != s_bgColour )
{
msgColour =
wxString::Format(wxS("Invalid colour drawn at (%i, %i): Actual: %s Expected: %s"),
px, py, c.GetAsString().mbc_str(), s_bgColour.GetAsString().mbc_str());
}
}
if ( !msgColour.empty() )
{
if ( !msg.empty() )
{
msg += wxS("\n- ");
msg += msgColour;
}
else
{
msg = msgColour;
}
}
}
}
if( !msg.empty() )
{
wxCharBuffer buffer = msg.ToUTF8();
CPPUNIT_FAIL( buffer.data() );
}
#endif // wxUSE_IMAGE
}
void ClippingBoxTestCaseBase::CheckClipWithBitmap(const wxBitmap& bmpRef, int posTolerance)
{
#if wxUSE_IMAGE
wxImage img = m_bmp.ConvertToImage();
// If reference bitmap is given then it has to be
// compared with current bitmap.
if ( bmpRef.IsOk() )
{
wxImage imgRef = bmpRef.ConvertToImage();
// Figures created by clipping and drawing procedures
// can be slightly different (shifted by few pixels) due
// to the different algorithms they can use so we need
// to perform a "fuzzy" comparison of the images,
// tolerating some drift of the pixels.
if ( !CompareImageFuzzy(img, imgRef, posTolerance) )
CPPUNIT_FAIL( "Invalid shape of the clipping region" );
}
#endif // wxUSE_IMAGE
}
void ClippingBoxTestCaseDCBase::CheckClipPos(int x, int y, int width, int height, int posTolerance)
{
// Check clipping box boundaries.
int clipX, clipY, clipW, clipH;
m_dc->GetClippingBox(&clipX, &clipY, &clipW, &clipH);
CheckBoxPosition(clipX, clipY, clipW, clipH, x, y, width, height, posTolerance);
}
void ClippingBoxTestCaseDCBase::CheckClipBox(int x, int y, int width, int height)
{
// Check clipping box boundaries.
int clipX, clipY, clipW, clipH;
m_dc->GetClippingBox(&clipX, &clipY, &clipW, &clipH);
CheckBoxPosition(clipX, clipY, clipW, clipH, x, y, width, height);
// We will examine pixels directly in the underlying bitmap
// so we need to get device coordinates of examined area.
x = m_dc->LogicalToDeviceX(x);
y = m_dc->LogicalToDeviceY(y);
width = m_dc->LogicalToDeviceXRel(width);
height = m_dc->LogicalToDeviceYRel(height);
// Update wxDC contents.
FlushDC();
#if wxUSE_IMAGE
// We check whether diagonal corners
// of the rectangular clipping box are actually
// drawn at the edge of the clipping region.
CheckClipRect(x, y, width, height);
#endif // wxUSE_IMAGE
}
void ClippingBoxTestCaseDCBase::CheckClipShape(const wxBitmap& bmpRef, int posTolerance)
{
// Update wxDC contents.
FlushDC();
#if wxUSE_IMAGE
// Figures created by clipping and drawing procedures
// can be slightly different (shifted by few pixels) due
// to the different algorithms they can use so we need
// to accept some shift of pixels.
CheckClipWithBitmap(bmpRef, posTolerance);
#endif // wxUSE_IMAGE
}
// Actual tests
void ClippingBoxTestCaseDCBase::InitialState()
{
// Initial clipping box should be the same as the entire DC surface.
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
}
void ClippingBoxTestCaseDCBase::InitialStateWithTransformedDC()
{
// Initial clipping box with transformed DC.
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(m_dc->DeviceToLogicalX(0),
m_dc->DeviceToLogicalY(0),
m_dc->DeviceToLogicalXRel(s_dcSize.GetWidth()),
m_dc->DeviceToLogicalYRel(s_dcSize.GetHeight()));
}
void ClippingBoxTestCaseDCBase::InitialStateWithRotatedDC()
{
#if wxUSE_DC_TRANSFORM_MATRIX
// Initial clipping box with rotated DC.
if ( m_dc->CanUseTransformMatrix() )
{
wxAffineMatrix2D m = m_dc->GetTransformMatrix();
m.Rotate(6*M_PI/180.0);
m_dc->SetTransformMatrix(m);
}
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
#endif // wxUSE_DC_TRANSFORM_MATRIX
}
void ClippingBoxTestCaseDCBase::OneRegion()
{
// Setting one clipping region inside DC area.
const int x = 10;
const int y = 20;
const int w = 80;
const int h = 75;
m_dc->SetClippingRegion(x, y, w, h);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(x, y, w, h);
}
void ClippingBoxTestCaseDCBase::OneLargeRegion()
{
// Setting one clipping region larger then DC surface.
// Final clipping box should be limited to the DC extents.
m_dc->SetClippingRegion(-10, -20,
s_dcSize.GetWidth()+30, s_dcSize.GetHeight()+50);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
}
void ClippingBoxTestCaseDCBase::OneOuterRegion()
{
// Setting one clipping region entirely outside DC surface.
// Final clipping box should be empty.
m_dc->SetClippingRegion(-100, -80, 20, 40);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseDCBase::OneRegionNegDim()
{
// Setting one clipping region with negative sizes values.
// Final clipping box should have standard positive size values.
const int x = 10;
const int y = 20;
const int w = -80;
const int h = -75;
wxClipRect r1(x, y, w, h);
wxRect r2(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
wxRect r = r1.Intersect(r2);
wxASSERT( !r.IsEmpty() );
m_dc->SetClippingRegion(x, y, w, h);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(r.GetLeft(), r.GetTop(),
r.GetWidth(), r.GetHeight());
}
void ClippingBoxTestCaseDCBase::OneRegionAndReset()
{
// Setting one clipping region and next destroy it.
// Final clipping box should be the same as DC surface.
m_dc->SetClippingRegion(10, 20, 80, 75);
m_dc->DestroyClippingRegion();
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
}
void ClippingBoxTestCaseDCBase::OneRegionAndEmpty()
{
// Setting one clipping region and next an empty box.
// Final clipping box should empty.
m_dc->SetClippingRegion(10, 20, 80, 75);
m_dc->SetClippingRegion(0, 0, 0, 0);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseDCBase::OneRegionWithTransformedDC()
{
// Setting one clipping region inside DC area
// with applied some transformations.
wxRect r1(-10, -20, 80, 75);
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
m_dc->SetClippingRegion(r1);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
wxRect r2(m_dc->DeviceToLogicalX(0),
m_dc->DeviceToLogicalY(0),
m_dc->DeviceToLogicalXRel(s_dcSize.GetWidth()),
m_dc->DeviceToLogicalYRel(s_dcSize.GetHeight()));
wxRect r = r1.Intersect(r2);
wxASSERT( !r.IsEmpty() );
CheckClipBox(r.GetLeft(),
r.GetTop(),
r.GetWidth(),
r.GetHeight());
}
void ClippingBoxTestCaseDCBase::TwoRegionsOverlapping()
{
// Setting one clipping region and next another region (partially overlapping).
// Final clipping box should be an intersection of these two boxes.
wxRect r1(10, 20, 80, 75);
wxRect r2(50, 60, 50, 40);
wxRect r = r1.Intersect(r2);
wxASSERT( !r.IsEmpty() );
m_dc->SetClippingRegion(r1);
m_dc->SetClippingRegion(r2);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(r.GetLeft(), r.GetTop(),
r.GetWidth(), r.GetHeight());
}
void ClippingBoxTestCaseDCBase::TwoRegionsOverlappingNegDim()
{
// Setting one clipping region with negative size values
// and next another region (partially overlapping).
// Final clipping box should be an intersection of these two boxes
// with positive size values.
const int x1 = 90;
const int y1 = 95;
const int w1 = -80;
const int h1 = -75;
const int x2 = 50;
const int y2 = 60;
const int w2 = 50;
const int h2 = 40;
wxClipRect r1(x1, y1, w1, h1);
wxRect r2(x2, y2, w2, h2);
wxRect r = r1.Intersect(r2);
m_dc->SetClippingRegion(x1, y1, w1, h1);
m_dc->SetClippingRegion(x2, y2, w2, h2);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(r.GetLeft(), r.GetTop(),
r.GetWidth(), r.GetHeight());
}
void ClippingBoxTestCaseDCBase::TwoRegionsNonOverlapping()
{
// Setting one clipping region and next another region (non-overlapping).
// Final clipping box should be empty.
wxRect r1(10, 20, 30, 30);
wxRect r2(50, 60, 50, 40);
wxASSERT( !r1.Intersects(r2) );
m_dc->SetClippingRegion(r1);
m_dc->SetClippingRegion(r2);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseDCBase::TwoRegionsNonOverlappingNegDim()
{
// Setting one clipping region with negative size values
// and next another region (non-overlapping).
// Final clipping box should be empty.
const int x1 = 10;
const int y1 = 20;
const int w1 = -80;
const int h1 = -75;
const int x2 = 50;
const int y2 = 60;
const int w2 = 50;
const int h2 = 40;
wxClipRect r1(x1, y1, w1, h1);
wxRect r2(x2, y2, w2, h2);
wxASSERT( !r1.Intersects(r2) );
m_dc->SetClippingRegion(x1, y1, w1, h1);
m_dc->SetClippingRegion(x2, y2, w2, h2);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseDCBase::OneDevRegion()
{
// Setting one clipping region in device coordinates
// inside transformed DC area.
const int x = 10;
const int y = 21;
const int w = 80;
const int h = 75;
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg(x, y, w, h);
m_dc->SetDeviceClippingRegion(reg);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(m_dc->DeviceToLogicalX(x),
m_dc->DeviceToLogicalY(y),
m_dc->DeviceToLogicalXRel(w),
m_dc->DeviceToLogicalYRel(h));
}
void ClippingBoxTestCaseDCBase::OneLargeDevRegion()
{
// Setting one clipping region in device coordinates larger
// then transformed DC surface.
// Final clipping box should be limited to the DC extents.
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg(-10, -20, s_dcSize.GetWidth()+30, s_dcSize.GetHeight()+50);
m_dc->SetDeviceClippingRegion(reg);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(m_dc->DeviceToLogicalX(0),
m_dc->DeviceToLogicalY(0),
m_dc->DeviceToLogicalXRel(s_dcSize.GetWidth()),
m_dc->DeviceToLogicalYRel(s_dcSize.GetHeight()));
}
void ClippingBoxTestCaseDCBase::OneOuterDevRegion()
{
// Setting one clipping region in device coordinates
// entirely outside transformed DC surface.
// Final clipping box should be empty.
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg(200, 80, 20, 40);
m_dc->SetDeviceClippingRegion(reg);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseDCBase::OneDevRegionNegDim()
{
// Setting one clipping region in device coordinates
// with negative sizes values.
// Final clipping box should have standard positive size values.
const int x = 19;
const int y = 23;
const int w = -80;
const int h = -75;
wxClipRect r1(x, y, w, h);
wxRect r2(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
wxRect r = r1.Intersect(r2);
wxASSERT( !r.IsEmpty() );
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg(x, y, w, h);
m_dc->SetDeviceClippingRegion(reg);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(m_dc->DeviceToLogicalX(r.GetLeft()),
m_dc->DeviceToLogicalY(r.GetTop()),
m_dc->DeviceToLogicalXRel(r.GetWidth()),
m_dc->DeviceToLogicalYRel(r.GetHeight()));
}
void ClippingBoxTestCaseDCBase::OneDevRegionNonRect()
{
// Setting one triangular clipping region in device coordinates.
const wxPoint poly[3] =
{
wxPoint(3, 5),
wxPoint(68, 18),
wxPoint(40, 72)
};
// Expected clipping box in device coordinates.
const int clipX = 4;
const int clipY = 6;
const int clipW = 64;
const int clipH = 66;
// Draw image with reference triangle.
wxBitmap bmpRef(s_dcSize);
wxMemoryDC* memDC = new wxMemoryDC(bmpRef);
wxDC* dcRef = GetDC(memDC);
dcRef->SetBackground(wxBrush(s_bgColour, wxBRUSHSTYLE_SOLID));
dcRef->Clear();
dcRef->SetBrush(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
dcRef->SetPen(wxPen(s_fgColour));
dcRef->DrawPolygon(WXSIZEOF(poly), poly);
delete dcRef;
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion r(WXSIZEOF(poly), poly);
m_dc->SetDeviceClippingRegion(r);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
// Check clipping box parameters and compare
// filled in clipping region with reference triangle.
// Triangles created by clipping and drawing procedures
// can be slightly different (shifted by few pixels) due
// to the different algorithms used for different operations
// so we need to perform a "fuzzy" comparison of the images,
// tolerating some drift of the pixels.
CheckClipPos(m_dc->DeviceToLogicalX(clipX),
m_dc->DeviceToLogicalY(clipY),
m_dc->DeviceToLogicalXRel(clipW),
m_dc->DeviceToLogicalYRel(clipH), 1);
CheckClipShape(bmpRef, 1);
}
void ClippingBoxTestCaseDCBase::OneDevRegionAndReset()
{
// Setting one clipping region in device coordinates
// and next destroy it.
// Final clipping box should be the same as DC surface.
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg(10, 20, 80, 75);
m_dc->SetDeviceClippingRegion(reg);
m_dc->DestroyClippingRegion();
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(m_dc->DeviceToLogicalX(0),
m_dc->DeviceToLogicalY(0),
m_dc->DeviceToLogicalXRel(s_dcSize.GetWidth()),
m_dc->DeviceToLogicalYRel(s_dcSize.GetHeight()));
}
void ClippingBoxTestCaseDCBase::OneDevRegionAndEmpty()
{
// Setting one clipping region in device coordinates
// and next an empty region.
// Final clipping box should empty.
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg1(10, 20, 80, 75);
m_dc->SetDeviceClippingRegion(reg1);
wxRegion reg2(0, 0, 0, 0);
m_dc->SetDeviceClippingRegion(reg2);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseDCBase::TwoDevRegionsOverlapping()
{
// Setting one clipping region in device coordinates
// and next another region in device coordinates (partially overlapping).
// Final clipping box should be an intersection of these two regions.
wxRect r1(30, 39, 40, 30);
wxRect r2(60, 51, 40, 30);
wxRect r = r1.Intersect(r2);
wxASSERT( !r.IsEmpty() );
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg1(r1);
m_dc->SetDeviceClippingRegion(reg1);
wxRegion reg2(r2);
m_dc->SetDeviceClippingRegion(reg2);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(m_dc->DeviceToLogicalX(r.GetLeft()),
m_dc->DeviceToLogicalY(r.GetTop()),
m_dc->DeviceToLogicalXRel(r.GetWidth()),
m_dc->DeviceToLogicalYRel(r.GetHeight()));
}
void ClippingBoxTestCaseDCBase::TwoDevRegionsOverlappingNegDim()
{
// Setting one clipping region in device coordinates with negative size values
// and next another region in device coordinates (partially overlapping).
// Final clipping box should be an intersection of these two regions
// with positive size values.
const int x1 = 31;
const int y1 = 20;
const int w1 = -80;
const int h1 = -75;
const int x2 = 20;
const int y2 = 6;
const int w2 = 40;
const int h2 = 30;
wxClipRect r1(x1, y1, w1, h1);
wxRect r2(x2, y2, w2, h2);
wxRect r = r1.Intersect(r2);
wxASSERT( !r.IsEmpty() );
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg1(x1, y1, w1, h1);
m_dc->SetDeviceClippingRegion(reg1);
wxRegion reg2(x2, y2, w2, h2);
m_dc->SetDeviceClippingRegion(reg2);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(m_dc->DeviceToLogicalX(r.GetLeft()),
m_dc->DeviceToLogicalY(r.GetTop()),
m_dc->DeviceToLogicalXRel(r.GetWidth()),
m_dc->DeviceToLogicalYRel(r.GetHeight()));
}
void ClippingBoxTestCaseDCBase::TwoDevRegionsNonOverlapping()
{
// Setting one clipping region in device coordinates
// and next another region in device coordinates (non-overlapping).
// Final clipping box should be empty.
wxRect r1(10, 20, 30, 30);
wxRect r2(50, 60, 50, 40);
wxASSERT( !r1.Intersects(r2) );
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg1(r1);
m_dc->SetDeviceClippingRegion(reg1);
wxRegion reg2(r2);
m_dc->SetDeviceClippingRegion(reg2);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseDCBase::TwoDevRegionsNonOverlappingNegDim()
{
// Setting one clipping region in device coordinates with negative size values
// and next another region (non-overlapping).
// Final clipping box should be empty.
const int x1 = 10;
const int y1 = 20;
const int w1 = -80;
const int h1 = -75;
const int x2 = 50;
const int y2 = 60;
const int w2 = 50;
const int h2 = 40;
wxClipRect r1(x1, y1, w1, h1);
wxRect r2(x2, y2, w2, h2);
wxASSERT( !r1.Intersects(r2) );
m_dc->SetDeviceOrigin(10, 15);
m_dc->SetUserScale(0.5, 1.5);
m_dc->SetLogicalScale(4.0, 2.0);
m_dc->SetLogicalOrigin(-15, -20);
wxRegion reg1(x1, y1, w1, h1);
m_dc->SetDeviceClippingRegion(reg1);
wxRegion reg2(x2, y2, w2, h2);
m_dc->SetDeviceClippingRegion(reg2);
m_dc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_dc->Clear();
CheckClipBox(0, 0, 0, 0);
}
// Tests specific to wxGCDC
#if wxUSE_GRAPHICS_CONTEXT
void ClippingBoxTestCaseGCDC::InitialStateWithRotatedGCForDC()
{
// Initial wxGCDC clipping box with rotated wxGraphicsContext.
wxGraphicsContext* gc = m_gcdc->GetGraphicsContext();
gc->Rotate(6*M_PI/180.0);
m_gcdc->SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_gcdc->Clear();
CheckClipBox(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
}
#endif // wxUSE_GRAPHICS_CONTEXT
// ========================
// wxGraphicsContext tests
// ========================
#if wxUSE_GRAPHICS_CONTEXT
class ClippingBoxTestCaseGCBase : public ClippingBoxTestCaseBase
{
public:
ClippingBoxTestCaseGCBase()
{
m_rend = NULL;
m_gc = NULL;
m_mdc.SelectObject(m_bmp);
}
virtual ~ClippingBoxTestCaseGCBase()
{
m_mdc.SelectObject(wxNullBitmap);
delete m_gc;
}
virtual void setUp() wxOVERRIDE
{
m_mdc.DestroyClippingRegion();
wxBrush bgBrush(s_bgColour, wxBRUSHSTYLE_SOLID);
m_mdc.SetBackground(bgBrush);
m_mdc.Clear();
CPPUNIT_ASSERT_MESSAGE("Invalid renderer", m_rend);
m_gc = m_rend->CreateContext(m_mdc);
m_gc->SetAntialiasMode(wxANTIALIAS_NONE);
m_gc->DisableOffset();
m_gc->ResetClip();
}
protected:
virtual void FlushGC()
{
m_gc->Flush();
}
void CheckClipPos(int x, int y, int width, int height, int posTolerance = 0);
void CheckClipBox(int x, int y, int width, int height);
void CheckClipShape(const wxBitmap& bmpRef, int posTolerance);
void Clear();
void InitialState();
void InitialStateWithTransformedGC();
void OneRegion();
void OneLargeRegion();
void OneOuterRegion();
void OneRegionNegDim();
void OneRegionAndReset();
void OneRegionAndEmpty();
void OneRegionWithTransformedGC();
void OneRegionWithRotatedGC();
void TwoRegionsOverlapping();
void TwoRegionsOverlappingNegDim();
void TwoRegionsNonOverlapping();
void TwoRegionsNonOverlappingNegDim();
void RegionsAndPushPopState();
protected:
wxGraphicsRenderer* m_rend;
wxGraphicsContext* m_gc;
wxMemoryDC m_mdc;
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseGCBase);
};
#ifdef __WXMSW__
// GDI+ and Direct2D are available only under MSW.
#if wxUSE_GRAPHICS_GDIPLUS
class ClippingBoxTestCaseGCGDIPlus : public ClippingBoxTestCaseGCBase
{
public:
ClippingBoxTestCaseGCGDIPlus()
{
m_rend = wxGraphicsRenderer::GetGDIPlusRenderer();
}
virtual ~ClippingBoxTestCaseGCGDIPlus() {}
private:
CPPUNIT_TEST_SUITE( ClippingBoxTestCaseGCGDIPlus );
CPPUNIT_TEST( InitialState );
CPPUNIT_TEST( InitialStateWithTransformedGC );
CPPUNIT_TEST( OneRegion );
CPPUNIT_TEST( OneLargeRegion );
CPPUNIT_TEST( OneOuterRegion );
CPPUNIT_TEST( OneRegionNegDim );
CPPUNIT_TEST( OneRegionAndReset );
CPPUNIT_TEST( OneRegionAndEmpty );
CPPUNIT_TEST( OneRegionWithTransformedGC );
CPPUNIT_TEST( OneRegionWithRotatedGC );
CPPUNIT_TEST( TwoRegionsOverlapping );
CPPUNIT_TEST( TwoRegionsOverlappingNegDim );
CPPUNIT_TEST( TwoRegionsNonOverlapping );
CPPUNIT_TEST( TwoRegionsNonOverlappingNegDim );
CPPUNIT_TEST( RegionsAndPushPopState );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseGCGDIPlus);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( ClippingBoxTestCaseGCGDIPlus );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ClippingBoxTestCaseGCGDIPlus, "ClippingBoxTestCaseGCGDIPlus" );
#endif // wxUSE_GRAPHICS_GDIPLUS
#if wxUSE_GRAPHICS_DIRECT2D
class ClippingBoxTestCaseGCDirect2D : public ClippingBoxTestCaseGCBase
{
public:
ClippingBoxTestCaseGCDirect2D()
{
m_rend = wxGraphicsRenderer::GetDirect2DRenderer();
}
virtual ~ClippingBoxTestCaseGCDirect2D() {}
virtual void FlushGC()
{
// Apparently, flushing native Direct2D renderer
// is not enough to update underlying DC (bitmap)
// and therefore we have to destroy the renderer
// to do so.
ClippingBoxTestCaseGCBase::FlushGC();
delete m_gc;
m_gc = NULL;
}
private:
CPPUNIT_TEST_SUITE( ClippingBoxTestCaseGCDirect2D );
CPPUNIT_TEST( InitialState );
CPPUNIT_TEST( InitialStateWithTransformedGC );
CPPUNIT_TEST( OneRegion );
CPPUNIT_TEST( OneLargeRegion );
CPPUNIT_TEST( OneOuterRegion );
CPPUNIT_TEST( OneRegionNegDim );
CPPUNIT_TEST( OneRegionAndReset );
CPPUNIT_TEST( OneRegionAndEmpty );
CPPUNIT_TEST( OneRegionWithTransformedGC );
CPPUNIT_TEST( OneRegionWithRotatedGC );
CPPUNIT_TEST( TwoRegionsOverlapping );
CPPUNIT_TEST( TwoRegionsOverlappingNegDim );
CPPUNIT_TEST( TwoRegionsNonOverlapping );
CPPUNIT_TEST( TwoRegionsNonOverlappingNegDim );
CPPUNIT_TEST( RegionsAndPushPopState );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseGCDirect2D);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( ClippingBoxTestCaseGCDirect2D );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ClippingBoxTestCaseGCDirect2D, "ClippingBoxTestCaseGCDirect2D" );
#endif // wxUSE_GRAPHICS_DIRECT2D
#endif // __WXMSW__
#if wxUSE_CAIRO
class ClippingBoxTestCaseGCCairo : public ClippingBoxTestCaseGCBase
{
public:
ClippingBoxTestCaseGCCairo()
{
m_rend = wxGraphicsRenderer::GetCairoRenderer();
}
virtual ~ClippingBoxTestCaseGCCairo() {}
private:
CPPUNIT_TEST_SUITE( ClippingBoxTestCaseGCCairo );
CPPUNIT_TEST( InitialState );
CPPUNIT_TEST( InitialStateWithTransformedGC );
CPPUNIT_TEST( OneRegion );
CPPUNIT_TEST( OneLargeRegion );
CPPUNIT_TEST( OneOuterRegion );
CPPUNIT_TEST( OneRegionNegDim );
CPPUNIT_TEST( OneRegionAndReset );
CPPUNIT_TEST( OneRegionAndEmpty );
CPPUNIT_TEST( OneRegionWithTransformedGC );
CPPUNIT_TEST( OneRegionWithRotatedGC );
CPPUNIT_TEST( TwoRegionsOverlapping );
CPPUNIT_TEST( TwoRegionsOverlappingNegDim );
CPPUNIT_TEST( TwoRegionsNonOverlapping );
CPPUNIT_TEST( TwoRegionsNonOverlappingNegDim );
CPPUNIT_TEST( RegionsAndPushPopState );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseGCCairo);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( ClippingBoxTestCaseGCCairo );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ClippingBoxTestCaseGCCairo, "ClippingBoxTestCaseGCCairo" );
#endif // wxUSE_CAIRO
#ifdef __WXOSX__
class ClippingBoxTestCaseGCCoreGraphics : public ClippingBoxTestCaseGCBase
{
public:
ClippingBoxTestCaseGCCoreGraphics()
{
m_rend = wxGraphicsRenderer::GetDefaultRenderer();
}
virtual ~ClippingBoxTestCaseGCCoreGraphics() {}
private:
CPPUNIT_TEST_SUITE( ClippingBoxTestCaseGCCoreGraphics );
CPPUNIT_TEST( InitialState );
CPPUNIT_TEST( InitialStateWithTransformedGC );
CPPUNIT_TEST( OneRegion );
CPPUNIT_TEST( OneLargeRegion );
CPPUNIT_TEST( OneOuterRegion );
CPPUNIT_TEST( OneRegionNegDim );
CPPUNIT_TEST( OneRegionAndReset );
CPPUNIT_TEST( OneRegionAndEmpty );
CPPUNIT_TEST( OneRegionWithTransformedGC );
CPPUNIT_TEST( OneRegionWithRotatedGC );
CPPUNIT_TEST( TwoRegionsOverlapping );
CPPUNIT_TEST( TwoRegionsOverlappingNegDim );
CPPUNIT_TEST( TwoRegionsNonOverlapping );
CPPUNIT_TEST( TwoRegionsNonOverlappingNegDim );
CPPUNIT_TEST( RegionsAndPushPopState );
CPPUNIT_TEST_SUITE_END();
protected:
wxDECLARE_NO_COPY_CLASS(ClippingBoxTestCaseGCCoreGraphics);
};
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION( ClippingBoxTestCaseGCCoreGraphics );
// also include in it's own registry so that these tests can be run alone
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ClippingBoxTestCaseGCCoreGraphics, "ClippingBoxTestCaseGCCoreGraphics" );
#endif // __WXOSX__
void ClippingBoxTestCaseGCBase::CheckClipPos(int x, int y, int width, int height, int posTolerance)
{
// Check clipping box boundaries.
double clipX, clipY, clipW, clipH;
m_gc->GetClipBox(&clipX, &clipY, &clipW, &clipH);
CheckBoxPosition(wxRound(clipX), wxRound(clipY), wxRound(clipW), wxRound(clipH),
x, y, width, height, posTolerance);
}
void ClippingBoxTestCaseGCBase::CheckClipBox(int x, int y, int width, int height)
{
// Check clipping box boundaries.
double clipX, clipY, clipW, clipH;
m_gc->GetClipBox(&clipX, &clipY, &clipW, &clipH);
CheckBoxPosition(wxRound(clipX), wxRound(clipY), wxRound(clipW), wxRound(clipH),
x, y, width, height);
// We will examine pixels directly in the underlying bitmap
// so we need to get device coordinates of examined area.
wxGraphicsMatrix m = m_gc->GetTransform();
double xdev = x;
double ydev = y;
m.TransformPoint(&xdev, &ydev);
double wdev = width;
double hdev = height;
m.TransformDistance(&wdev, &hdev);
// Update wxGraphicsContext contents.
FlushGC();
#if wxUSE_IMAGE
// We check whether diagonal corners
// of the rectangular clipping box are actually
// drawn at the edge of the clipping region.
CheckClipRect(wxRound(xdev), wxRound(ydev), wxRound(wdev), wxRound(hdev));
#endif // wxUSE_IMAGE
}
void ClippingBoxTestCaseGCBase::CheckClipShape(const wxBitmap& bmpRef, int posTolerance)
{
// Update wxGraphicsContext contents.
FlushGC();
#if wxUSE_IMAGE
// Figures created by clipping and drawing procedures
// can be slightly different (shifted by few pixels) due
// to the different algorithms they can use so we need
// to accept some shift of pixels.
CheckClipWithBitmap(bmpRef, posTolerance);
#endif // wxUSE_IMAGE
}
void ClippingBoxTestCaseGCBase::Clear()
{
double x, y, w, h;
m_gc->GetClipBox(&x, &y, &w, &h);
m_gc->SetBrush(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_gc->SetPen(*wxTRANSPARENT_PEN);
m_gc->SetCompositionMode(wxCOMPOSITION_SOURCE);
m_gc->DrawRectangle(x, y, w, h);
}
// Actual tests
void ClippingBoxTestCaseGCBase::InitialState()
{
// Initial clipping box should be the same as the entire GC surface.
Clear();
CheckClipBox(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
}
void ClippingBoxTestCaseGCBase::InitialStateWithTransformedGC()
{
// Initial clipping box with transformed GC.
wxGraphicsMatrix m = m_gc->CreateMatrix();
m.Translate(10, 15);
m.Scale(1/2.0, 1/3.0);
m.Translate(20, 30);
m_gc->SetTransform(m);
Clear();
m.Invert();
double x = 0;
double y = 0;
m.TransformPoint(&x, &y);
double w = s_dcSize.GetWidth();
double h = s_dcSize.GetHeight();
m.TransformDistance(&w, &h);
CheckClipBox(wxRound(x), wxRound(y), wxRound(w), wxRound(h));
}
void ClippingBoxTestCaseGCBase::OneRegion()
{
// Setting one clipping region inside DC area.
const int x = 10;
const int y = 20;
const int w = 80;
const int h = 75;
m_gc->Clip(x, y, w, h);
Clear();
CheckClipBox(x, y, w, h);
}
void ClippingBoxTestCaseGCBase::OneLargeRegion()
{
// Setting one clipping region larger then GC surface.
// Final clipping box should be limited to the GC extents.
m_gc->Clip(-10, -20,
s_dcSize.GetWidth()+30, s_dcSize.GetHeight()+50);
Clear();
CheckClipBox(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
}
void ClippingBoxTestCaseGCBase::OneOuterRegion()
{
// Setting one clipping region entirely outside GC surface.
// Final clipping box should be empty.
m_gc->Clip(-100, -80, 20, 40);
Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseGCBase::OneRegionNegDim()
{
// Setting one clipping region with negative sizes values.
// Final clipping box should have standard positive size values.
const int x = 10;
const int y = 20;
const int w = -80;
const int h = -75;
wxClipRect r1(x, y, w, h, 0);
wxRect r2(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
wxRect r = r1.Intersect(r2);
wxASSERT( !r.IsEmpty() );
m_gc->Clip(x, y, w, h);
Clear();
CheckClipBox(r.GetLeft(), r.GetTop(),
r.GetWidth(), r.GetHeight());
}
void ClippingBoxTestCaseGCBase::OneRegionAndReset()
{
// Setting one clipping region and next destroy it.
// Final clipping box should be the same as GC surface.
m_gc->Clip(10, 20, 80, 75);
m_gc->ResetClip();
Clear();
CheckClipBox(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
}
void ClippingBoxTestCaseGCBase::OneRegionAndEmpty()
{
// Setting one clipping region and next an empty box.
// Final clipping box should empty.
m_gc->Clip(10, 20, 80, 75);
m_gc->Clip(0, 0, 0, 0);
Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseGCBase::OneRegionWithTransformedGC()
{
// Setting one clipping region inside GC area
// with applied some transformations.
wxRect r1(-10, -21, 80, 75);
wxGraphicsMatrix m = m_gc->CreateMatrix();
m.Translate(10, 15);
m.Scale(1/2.0, 1/3.0);
m.Translate(20, 30);
m_gc->SetTransform(m);
m_gc->Clip(r1.x, r1.y, r1.width, r1.height);
Clear();
m.Invert();
double x = 0;
double y = 0;
m.TransformPoint(&x, &y);
double w = s_dcSize.GetWidth();
double h = s_dcSize.GetHeight();
m.TransformDistance(&w, &h);
wxRect r2(wxRound(x), wxRound(y), wxRound(w), wxRound(h));
wxRect r = r1.Intersect(r2);
wxASSERT( !r.IsEmpty() );
CheckClipBox(r.GetLeft(), r.GetTop(),
r.GetWidth(), r.GetHeight());
}
void ClippingBoxTestCaseGCBase::OneRegionWithRotatedGC()
{
// Setting one rectangular clipping region for rotated graphics context.
const double rotAngle = 1.0*M_PI/180.0;
const int rectX = 16;
const int rectY = 14;
const int rectW = 60;
const int rectH = 55;
// Set clipping region for rotated wxGC.
m_gc->Rotate(rotAngle);
m_gc->Clip(rectX, rectY, rectW, rectH);
// Fill in clipping region.
Clear();
// Draw reference image with rotated rectangle which
// should look the same as rectangle drawn with Clip().
wxBitmap bmpRef(s_dcSize);
{
wxMemoryDC memDC(bmpRef);
wxGraphicsContext* gcRef = m_rend->CreateContext(memDC);
gcRef->SetAntialiasMode(wxANTIALIAS_NONE);
gcRef->DisableOffset();
gcRef->SetBrush(wxBrush(s_bgColour, wxBRUSHSTYLE_SOLID));
gcRef->SetPen(*wxTRANSPARENT_PEN);
gcRef->DrawRectangle(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
gcRef->Rotate(rotAngle);
gcRef->SetBrush(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
gcRef->SetPen(wxPen(s_fgColour));
gcRef->DrawRectangle(rectX, rectY, rectW, rectH);
delete gcRef;
}
// Compare filled in clipping region with reference rectangle.
// Rotated rectangles created by clipping and drawing procedures
// can be slightly different (shifted by few pixels) due
// to the different algorithms used for different operations
// so we need to perform a "fuzzy" comparison of the images,
// tolerating some drift of the pixels.
CheckClipShape(bmpRef, 1);
}
void ClippingBoxTestCaseGCBase::TwoRegionsOverlapping()
{
// Setting one clipping region and next another region (partially overlapping).
// Final clipping box should be an intersection of these two boxes.
wxRect r1(10, 20, 80, 75);
wxRect r2(50, 60, 50, 40);
wxRect r = r1.Intersect(r2);
wxASSERT( !r.IsEmpty() );
m_gc->Clip(r1.x, r1.y, r1.width, r1.height);
m_gc->Clip(r2.x, r2.y, r2.width, r2.height);
Clear();
CheckClipBox(r.GetLeft(), r.GetTop(),
r.GetWidth(), r.GetHeight());
}
void ClippingBoxTestCaseGCBase::TwoRegionsOverlappingNegDim()
{
// Setting one clipping region with negative size values
// and next another region (partially overlapping).
// Final clipping box should be an intersection of these two boxes
// with positive size values.
const int x1 = 90;
const int y1 = 95;
const int w1 = -80;
const int h1 = -75;
const int x2 = 50;
const int y2 = 60;
const int w2 = 50;
const int h2 = 40;
wxClipRect r1(x1, y1, w1, h1, 0);
wxRect r2(x2, y2, w2, h2);
wxRect r = r1.Intersect(r2);
m_gc->Clip(x1, y1, w1, h1);
m_gc->Clip(x2, y2, w2, h2);
Clear();
CheckClipBox(r.GetLeft(), r.GetTop(),
r.GetWidth(), r.GetHeight());
}
void ClippingBoxTestCaseGCBase::TwoRegionsNonOverlapping()
{
// Setting one clipping region and next another region (non-overlapping).
// Final clipping box should be empty.
wxRect r1(10, 20, 30, 30);
wxRect r2(50, 60, 50, 40);
wxASSERT( !r1.Intersects(r2) );
m_gc->Clip(r1.x, r1.y, r1.width, r1.height);
m_gc->Clip(r2.x, r2.y, r2.width, r2.height);
Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseGCBase::TwoRegionsNonOverlappingNegDim()
{
// Setting one clipping region with negative size values
// and next another region (non-overlapping).
// Final clipping box should be empty.
const int x1 = 10;
const int y1 = 20;
const int w1 = -80;
const int h1 = -75;
const int x2 = 50;
const int y2 = 60;
const int w2 = 50;
const int h2 = 40;
wxClipRect r1(x1, y1, w1, h1, 0);
wxRect r2(x2, y2, w2, h2);
wxASSERT( !r1.Intersects(r2) );
m_gc->Clip(x1, y1, w1, h1);
m_gc->Clip(x2, y2, w2, h2);
Clear();
CheckClipBox(0, 0, 0, 0);
}
void ClippingBoxTestCaseGCBase::RegionsAndPushPopState()
{
// Setting muliple rectangular clipping regions
// for transformed wxGC and store/restore them.
// Get rectangle of the entire drawing area.
double x, y, w, h;
m_gc->GetClipBox(&x, &y, &w, &h);
// Set clipping regions and store/restore them.
m_gc->SetBrush(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
m_gc->SetPen(wxPen(s_fgColour));
m_gc->Translate(5, 5);
m_gc->Rotate(5*M_PI/180);
m_gc->Clip(20, 15, 50, 45);
m_gc->PushState();
m_gc->Rotate(5*M_PI/180);
m_gc->ResetClip();
m_gc->Clip(10, 5, 60, 15);
m_gc->PushState();
m_gc->Rotate(-15*M_PI/180);
m_gc->ResetClip();
m_gc->Clip(5, 10, 30, 35);
m_gc->DrawRectangle(x, y, w, h);
m_gc->PopState();
m_gc->DrawRectangle(x, y, w, h);
m_gc->PopState();
m_gc->DrawRectangle(x, y, w, h);
// Draw reference image with rotated rectangles which
// should look the same as rectangles drawn with Clip().
wxBitmap bmpRef(s_dcSize);
{
wxMemoryDC memDC(bmpRef);
wxGraphicsContext* gcRef = m_rend->CreateContext(memDC);
gcRef->SetAntialiasMode(wxANTIALIAS_NONE);
gcRef->DisableOffset();
gcRef->SetBrush(wxBrush(s_bgColour, wxBRUSHSTYLE_SOLID));
gcRef->SetPen(*wxTRANSPARENT_PEN);
gcRef->DrawRectangle(0, 0, s_dcSize.GetWidth(), s_dcSize.GetHeight());
gcRef->SetBrush(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
gcRef->SetPen(wxPen(s_fgColour));
gcRef->Translate(5, 5);
gcRef->Rotate(5*M_PI/180);
gcRef->PushState();
gcRef->Rotate(5*M_PI/180);
gcRef->PushState();
gcRef->Rotate(-15*M_PI/180);
gcRef->DrawRectangle(5, 10, 30, 35);
gcRef->PopState();
gcRef->DrawRectangle(10, 5, 60, 15);
gcRef->PopState();
gcRef->DrawRectangle(20, 15, 50, 45);
delete gcRef;
}
// Compare filled in clipping regions with reference image.
// Rotated rectangles created by clipping and drawing procedures
// can be slightly different (shifted by few pixels) due
// to the different algorithms used for different operations
// so we need to perform a "fuzzy" comparison of the images,
// tolerating some drift of the pixels.
CheckClipShape(bmpRef, 1);
}
#endif // wxUSE_GRAPHICS_CONTEXT