/////////////////////////////////////////////////////////////////////////////// // Name: tests/graphics/clipper.cpp // Purpose: wxDCClipper unit tests // Author: Artur Wieczorek // Created: 2022-12-27 // Copyright: (c) 2022 wxWidgets development team /////////////////////////////////////////////////////////////////////////////// // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- #include "testprec.h" #include #include "wx/bitmap.h" #include "wx/dcclient.h" #include "wx/dcgraph.h" #include "wx/dcmemory.h" #include "wx/dcsvg.h" #include "wx/app.h" #include "wx/window.h" #include "testfile.h" #include "waitfor.h" static const wxSize s_dcSize(260, 300); static inline wxRect DeviceToLogical(wxDC& dc, const wxRect& r) { return wxRect(dc.DeviceToLogical(r.GetPosition()), dc.DeviceToLogicalRel(r.GetSize())); } static void NoTransform(wxDC& dc) { wxRect initClipBox; dc.GetClippingBox(initClipBox); { const wxRect r(10, 20, 30, 40); wxDCClipper clipper(dc, r); wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(r == clipBox); } wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(initClipBox == clipBox); } static void ExternalTransform(wxDC& dc, bool useTransformMatrix) { #if wxUSE_DC_TRANSFORM_MATRIX if ( useTransformMatrix && !dc.CanUseTransformMatrix() ) return; #endif // wxUSE_DC_TRANSFORM_MATRIX #if wxUSE_DC_TRANSFORM_MATRIX if ( useTransformMatrix ) { wxAffineMatrix2D m; m.Translate(40, 75); m.Scale(2.0, 3.0); dc.SetTransformMatrix(m); } else #endif // wxUSE_DC_TRANSFORM_MATRIX { dc.SetDeviceOrigin(10, 15); dc.SetUserScale(0.5, 1.5); dc.SetLogicalScale(4.0, 2.0); dc.SetLogicalOrigin(-15, -20); } wxRect initClipBox; dc.GetClippingBox(initClipBox); { const wxRect r(10, 20, 30, 40); wxDCClipper clipper(dc, r); wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(r == clipBox); } wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(initClipBox == clipBox); } static void InternalTransform(wxDC& dc, bool useTransformMatrix) { #if wxUSE_DC_TRANSFORM_MATRIX if ( useTransformMatrix && !dc.CanUseTransformMatrix() ) return; #endif // wxUSE_DC_TRANSFORM_MATRIX wxRect initClipBox; dc.GetClippingBox(initClipBox); { const wxRect r(10, 20, 30, 40); wxDCClipper clipper(dc, r); #if wxUSE_DC_TRANSFORM_MATRIX if ( useTransformMatrix ) { wxAffineMatrix2D m; m.Translate(40, 75); m.Scale(2.0, 3.0); dc.SetTransformMatrix(m); } else #endif // wxUSE_DC_TRANSFORM_MATRIX { dc.SetDeviceOrigin(10, 15); dc.SetUserScale(0.5, 1.5); dc.SetLogicalScale(4.0, 2.0); dc.SetLogicalOrigin(-15, -20); } wxRect clipExpected = DeviceToLogical(dc, r); wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(clipExpected == clipBox); } wxRect initClipExpected = DeviceToLogical(dc, initClipBox); wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(initClipExpected == clipBox); } static void SpecificClipping(wxDC& dc) { wxRect initClipBox; dc.GetClippingBox(initClipBox); { const wxRect r(10, 20, 30, 40); wxDCClipper clipper(dc, r); const wxRect r1(16, 25, 20, 30); dc.SetClippingRegion(r1); wxRect clipExpected = DeviceToLogical(dc, r1); wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(clipExpected == clipBox); } wxRect initClipExpected = DeviceToLogical(dc, initClipBox); wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(initClipExpected == clipBox); } static void InternalTransformSpecificClipping(wxDC& dc, bool useTransformMatrix) { #if wxUSE_DC_TRANSFORM_MATRIX if ( useTransformMatrix && !dc.CanUseTransformMatrix() ) return; #endif // wxUSE_DC_TRANSFORM_MATRIX wxRect initClipBox; dc.GetClippingBox(initClipBox); { const wxRect r(10, 20, 30, 40); wxDCClipper clipper(dc, r); const wxRect r1(16, 25, 20, 30); dc.SetClippingRegion(r1); #if wxUSE_DC_TRANSFORM_MATRIX if ( useTransformMatrix ) { wxAffineMatrix2D m; m.Translate(40, 75); m.Scale(2.0, 3.0); dc.SetTransformMatrix(m); } else #endif // wxUSE_DC_TRANSFORM_MATRIX { dc.SetDeviceOrigin(10, 15); dc.SetUserScale(0.5, 1.5); dc.SetLogicalScale(4.0, 2.0); dc.SetLogicalOrigin(-15, -20); } wxRect clipExpected = DeviceToLogical(dc, r1); wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(clipExpected == clipBox); } wxRect initClipExpected = DeviceToLogical(dc, initClipBox); wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(initClipExpected == clipBox); } static void NoTransformEmbeddedClip(wxDC& dc) { wxRect initClipBox; dc.GetClippingBox(initClipBox); { const wxRect r1(10, 20, 30, 40); wxDCClipper clipper1(dc, r1); { const wxRect r2(15, 25, 20, 30); wxDCClipper clipper2(dc, r2); wxRect clipBox2; dc.GetClippingBox(clipBox2); CHECK(r2 == clipBox2); } wxRect clipBox1; dc.GetClippingBox(clipBox1); CHECK(r1 == clipBox1); } wxRect clipBox; dc.GetClippingBox(clipBox); CHECK(initClipBox == clipBox); } static void DCAttributes(wxDC& dc) { // Check if wxDC atrributes left unchanged wxFont font = dc.GetFont().Bold().Smaller(); wxPen pen(*wxYELLOW, 2); wxBrush brush = *wxBLUE_BRUSH; wxDCFontChanger fontChanger(dc, font); wxDCPenChanger penChanger(dc,pen); wxDCBrushChanger brushChanger(dc, brush); wxCoord chWidth = dc.GetCharWidth(); wxCoord chHeight = dc.GetCharHeight(); wxFontMetrics fm = dc.GetFontMetrics(); { wxDCClipper clipper(dc, 10, 20, 30, 40); } CHECK(dc.GetFont() == font); CHECK(dc.GetPen() == pen); CHECK(dc.GetBrush() == brush); CHECK(dc.GetCharWidth() == chWidth); CHECK(dc.GetCharHeight() == chHeight); wxFontMetrics fm2 = dc.GetFontMetrics(); CHECK(fm2.ascent == fm.ascent); CHECK(fm2.averageWidth == fm.averageWidth); CHECK(fm2.descent == fm.descent); CHECK(fm2.externalLeading == fm.externalLeading); CHECK(fm2.height == fm.height); CHECK(fm2.internalLeading == fm.internalLeading); } TEST_CASE("ClipperTestCase::wxDC", "[clipper][dc]") { wxBitmap bmp(s_dcSize); wxMemoryDC dc(bmp); dc.SetBackground(*wxWHITE_BRUSH); dc.Clear(); SECTION("NoTransform") { NoTransform(dc); } SECTION("ExternalTransform 1") { ExternalTransform(dc, false); } SECTION("ExternalTransform 2") { ExternalTransform(dc, true); } SECTION("InternalTransform 1") { InternalTransform(dc, false); } SECTION("InternalTransform 2") { InternalTransform(dc, true); } SECTION("SpecificClipping") { SpecificClipping(dc); } SECTION("InternalTransformSpecificClipping 1") { InternalTransformSpecificClipping(dc, false); } SECTION("InternalTransformSpecificClipping 2") { InternalTransformSpecificClipping(dc, true); } SECTION("NoTransformEmbeddedClip") { NoTransformEmbeddedClip(dc); } SECTION("DCAttributes") { DCAttributes(dc); } } #if wxUSE_GRAPHICS_CONTEXT TEST_CASE("ClipperTestCase::wxGCDC", "[clipper][dc][gcdc]") { #ifdef __WXMSW__ int depth = GENERATE(24, 32); wxBitmap bmp(s_dcSize, depth); #else wxBitmap bmp(s_dcSize); #endif wxMemoryDC mdc(bmp); mdc.SetBackground(*wxWHITE_BRUSH); mdc.Clear(); wxGCDC dc(mdc); dc.GetGraphicsContext()->SetAntialiasMode(wxANTIALIAS_NONE); dc.GetGraphicsContext()->DisableOffset(); SECTION("NoTransform") { NoTransform(dc); } SECTION("ExternalTransform 1") { ExternalTransform(dc, false); } SECTION("ExternalTransform 2") { ExternalTransform(dc, true); } SECTION("InternalTransform 1") { InternalTransform(dc, false); } SECTION("InternalTransform 2") { InternalTransform(dc, true); } SECTION("SpecificClipping") { SpecificClipping(dc); } SECTION("InternalTransformSpecificClipping 1") { InternalTransformSpecificClipping(dc, false); } SECTION("InternalTransformSpecificClipping 2") { InternalTransformSpecificClipping(dc, true); } SECTION("NoTransformEmbeddedClip") { NoTransformEmbeddedClip(dc); } SECTION("DCAttributes") { DCAttributes(dc); } } #ifdef __WXMSW__ #if wxUSE_GRAPHICS_GDIPLUS TEST_CASE("ClipperTestCase::wxGCDC(GDI+)", "[clipper][dc][gcdc][gdiplus]") { int depth = GENERATE(24, 32); wxBitmap bmp(s_dcSize, depth); wxMemoryDC mdc(bmp); mdc.SetBackground(*wxWHITE_BRUSH); mdc.Clear(); wxGraphicsRenderer* rend = wxGraphicsRenderer::GetGDIPlusRenderer(); REQUIRE(rend); wxGraphicsContext* gc = rend->CreateContext(mdc); gc->SetAntialiasMode(wxANTIALIAS_NONE); gc->DisableOffset(); wxGCDC dc(gc); SECTION("NoTransform") { NoTransform(dc); } SECTION("ExternalTransform 1") { ExternalTransform(dc, false); } SECTION("ExternalTransform 2") { ExternalTransform(dc, true); } SECTION("InternalTransform 1") { InternalTransform(dc, false); } SECTION("InternalTransform 2") { InternalTransform(dc, true); } SECTION("SpecificClipping") { SpecificClipping(dc); } SECTION("InternalTransformSpecificClipping 1") { InternalTransformSpecificClipping(dc, false); } SECTION("InternalTransformSpecificClipping 2") { InternalTransformSpecificClipping(dc, true); } SECTION("NoTransformEmbeddedClip") { NoTransformEmbeddedClip(dc); } SECTION("DCAttributes") { DCAttributes(dc); } } #endif // wxUSE_GRAPHICS_GDIPLUS #if wxUSE_GRAPHICS_DIRECT2D TEST_CASE("ClipperTestCase::wxGCDC(Direct2D)", "[clipper][dc][gcdc][direct2d]") { if ( wxIsRunningUnderWine() ) { WARN("Skipping tests known to fail in Wine"); } else { int depth = GENERATE(24, 32); wxBitmap bmp(s_dcSize, depth); wxMemoryDC mdc(bmp); mdc.SetBackground(*wxWHITE_BRUSH); mdc.Clear(); wxGraphicsRenderer* rend = wxGraphicsRenderer::GetDirect2DRenderer(); REQUIRE(rend); wxGraphicsContext* gc = rend->CreateContext(mdc); gc->SetAntialiasMode(wxANTIALIAS_NONE); gc->DisableOffset(); wxGCDC dc(gc); SECTION("NoTransform") { NoTransform(dc); } SECTION("ExternalTransform 1") { ExternalTransform(dc, false); } SECTION("ExternalTransform 2") { ExternalTransform(dc, true); } SECTION("InternalTransform 1") { InternalTransform(dc, false); } SECTION("InternalTransform 2") { InternalTransform(dc, true); } SECTION("SpecificClipping") { SpecificClipping(dc); } SECTION("InternalTransformSpecificClipping 1") { InternalTransformSpecificClipping(dc, false); } SECTION("InternalTransformSpecificClipping 2") { InternalTransformSpecificClipping(dc, true); } SECTION("NoTransformEmbeddedClip") { NoTransformEmbeddedClip(dc); } SECTION("DCAttributes") { DCAttributes(dc); } } } #endif // wxUSE_GRAPHICS_DIRECT2D #endif // __WXMSW__ #if wxUSE_CAIRO TEST_CASE("ClipperTestCase::wxGCDC(Cairo)", "[clipper][dc][gcdc][cairo]") { #ifdef __WXMSW__ int depth = GENERATE(24, 32); wxBitmap bmp(s_dcSize, depth); #else wxBitmap bmp(s_dcSize); #endif wxMemoryDC mdc(bmp); mdc.SetBackground(*wxWHITE_BRUSH); mdc.Clear(); wxGraphicsRenderer* rend = wxGraphicsRenderer::GetCairoRenderer(); REQUIRE(rend); wxGraphicsContext* gc = rend->CreateContext(mdc); gc->SetAntialiasMode(wxANTIALIAS_NONE); gc->DisableOffset(); wxGCDC dc(gc); SECTION("NoTransform") { NoTransform(dc); } SECTION("ExternalTransform 1") { ExternalTransform(dc, false); } SECTION("ExternalTransform 2") { ExternalTransform(dc, true); } SECTION("InternalTransform 1") { InternalTransform(dc, false); } SECTION("InternalTransform 2") { InternalTransform(dc, true); } SECTION("SpecificClipping") { SpecificClipping(dc); } SECTION("InternalTransformSpecificClipping 1") { InternalTransformSpecificClipping(dc, false); } SECTION("InternalTransformSpecificClipping 2") { InternalTransformSpecificClipping(dc, true); } SECTION("NoTransformEmbeddedClip") { NoTransformEmbeddedClip(dc); } SECTION("DCAttributes") { DCAttributes(dc); } } #endif // wxUSE_CAIRO #endif // wxUSE_GRAPHICS_CONTEXT #if wxUSE_SVG TEST_CASE("ClipperTestCase::wxSVGFileDC", "[clipper][dc][svgdc]") { TestFile tf; wxSVGFileDC dc(tf.GetName(), s_dcSize.x, s_dcSize.y); dc.SetBackground(*wxWHITE_BRUSH); dc.Clear(); SECTION("NoTransform") { NoTransform(dc); } SECTION("ExternalTransform 1") { ExternalTransform(dc, false); } SECTION("ExternalTransform 2") { ExternalTransform(dc, true); } SECTION("InternalTransform 1") { InternalTransform(dc, false); } SECTION("InternalTransform 2") { InternalTransform(dc, true); } SECTION("SpecificClipping") { SpecificClipping(dc); } SECTION("InternalTransformSpecificClipping 1") { InternalTransformSpecificClipping(dc, false); } SECTION("InternalTransformSpecificClipping 2") { InternalTransformSpecificClipping(dc, true); } SECTION("NoTransformEmbeddedClip") { NoTransformEmbeddedClip(dc); } SECTION("DCAttributes") { DCAttributes(dc); } } #endif // wxUSE_SVG TEST_CASE("ClipperTestCase::wxPaintDC", "[clipper][dc][paintdc]") { // Ensure window is shown and large enough for testing wxTheApp->GetTopWindow()->Raise(); REQUIRE(wxTheApp->GetTopWindow()->IsShown()); wxSize winSize = wxTheApp->GetTopWindow()->GetSize(); winSize.x = wxMax(winSize.x, s_dcSize.x + 50); winSize.y = wxMax(winSize.y, s_dcSize.y + 50); wxTheApp->GetTopWindow()->SetSize(winSize); #if defined(__WXGTK__) // Under wxGTK we need to have two children (at least) because if there // is one child its paint area is set to fill the whole parent frame. std::unique_ptr w0(new wxWindow(wxTheApp->GetTopWindow(), wxID_ANY)); #endif // wxGTK std::unique_ptr win(new wxWindow(wxTheApp->GetTopWindow(), wxID_ANY, wxPoint(0, 0))); win->SetClientSize(s_dcSize); // Wait for the first paint event to be sure // that window really has its final size. wxWindow* testWin = win.get(); { WaitForPaint waitForPaint(testWin); testWin->Show(); waitForPaint.YieldUntilPainted(); } bool paintExecuted = false; testWin->Bind(wxEVT_PAINT, [=, &paintExecuted](wxPaintEvent&) { wxPaintDC dc(testWin); REQUIRE(dc.GetSize() == s_dcSize); dc.SetBackground(*wxWHITE_BRUSH); dc.Clear(); SECTION("NoTransform") { NoTransform(dc); } SECTION("ExternalTransform 1") { ExternalTransform(dc, false); } SECTION("ExternalTransform 2") { ExternalTransform(dc, true); } SECTION("InternalTransform 1") { InternalTransform(dc, false); } SECTION("InternalTransform 2") { InternalTransform(dc, true); } SECTION("SpecificClipping") { SpecificClipping(dc); } SECTION("InternalTransformSpecificClipping 1") { InternalTransformSpecificClipping(dc, false); } SECTION("InternalTransformSpecificClipping 2") { InternalTransformSpecificClipping(dc, true); } SECTION("NoTransformEmbeddedClip") { NoTransformEmbeddedClip(dc); } SECTION("DCAttributes") { DCAttributes(dc); } paintExecuted = true; }); testWin->Refresh(); testWin->Update(); // Wait for update to be done YieldForAWhile(); CHECK(paintExecuted == true); }