From 90106003d3c6f934a974b4f550df2679f04532ad Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 1 Oct 2019 01:13:42 +0200 Subject: [PATCH 1/9] Clear DC using white background by default in wxMSW Since 848f5e78a60bde49900159a77b218bb15c80382d the background wasn't cleared at all if the background brush hadn't been explicitly set prior to calling wxDC::Clear(). This was incompatible with the old behaviour and even managed to break our own print preview code, so it clearly wasn't a good idea to change Clear() like this. Instead, continue to clear the DC using white background by default, while still not doing anything if a transparent background brush had been explicitly set. This fixes print preview background under MSW. See #10273, #18371. --- src/msw/dc.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 9652af4ccf..5260a7085e 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -729,8 +729,23 @@ void wxMSWDCImpl::Clear() return; } + HBRUSH hbr; if ( !m_backgroundBrush.IsOk() ) + { + // By default, use the stock white brush for compatibility with the + // previous wx versions. + hbr = WHITE_BRUSH; + } + else if ( !m_backgroundBrush.IsTransparent() ) + { + hbr = GetHbrushOf(m_backgroundBrush); + } + else // Using transparent background brush. + { + // Clearing with transparent brush doesn't do anything, just as drawing + // with transparent pen doesn't. return; + } RECT rect; ::GetClipBox(GetHdc(), &rect); @@ -738,7 +753,7 @@ void wxMSWDCImpl::Clear() // to compensate rounding errors if DC is the subject // of complex transformation (is e.g. rotated). ::InflateRect(&rect, 1, 1); - ::FillRect(GetHdc(), &rect, GetHbrushOf(m_backgroundBrush)); + ::FillRect(GetHdc(), &rect, hbr); RealizeScaleAndOrigin(); } From f82eb24c54dcd52696b84e7f1848af306ea9656c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 1 Oct 2019 01:19:33 +0200 Subject: [PATCH 2/9] Remove useless code in wxGCDCImpl::SetBackground() No changes. --- src/common/dcgraph.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/common/dcgraph.cpp b/src/common/dcgraph.cpp index e9baee8b7c..8fb3b5a73d 100644 --- a/src/common/dcgraph.cpp +++ b/src/common/dcgraph.cpp @@ -543,8 +543,6 @@ void wxGCDCImpl::SetBrush( const wxBrush &brush ) void wxGCDCImpl::SetBackground( const wxBrush &brush ) { m_backgroundBrush = brush; - if (!m_backgroundBrush.IsOk()) - return; } void wxGCDCImpl::SetLogicalFunction( wxRasterOperationMode function ) From 771832ebbbfb1144f896eff21d1c868ee9f796f9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 1 Oct 2019 01:22:05 +0200 Subject: [PATCH 3/9] Clear background if background brush is invalid in wxGTK2 too We only should avoid clearing the background if the brush is transparent, not if it's not set. This refines 6549d4c3c5f5d3e3acc754a2b1dc04f4a0a336d1 See #10273. --- src/gtk/dcclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gtk/dcclient.cpp b/src/gtk/dcclient.cpp index c4ec6109cf..4ccd8ddc71 100644 --- a/src/gtk/dcclient.cpp +++ b/src/gtk/dcclient.cpp @@ -1508,7 +1508,7 @@ void wxWindowDCImpl::Clear() if (!m_gdkwindow) return; - if (!m_backgroundBrush.IsOk() || m_backgroundBrush.GetStyle() == wxBRUSHSTYLE_TRANSPARENT) + if (m_backgroundBrush.IsTransparent()) return; int width,height; From c8ca1df2cba490de35549a0f069c1e7e773cbf61 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 1 Oct 2019 01:23:10 +0200 Subject: [PATCH 4/9] Don't clear background with transparent brush in wxGCDC Bail out immediately if the background brush is transparent, for consistency with the other wxDC implementations. See #10273. --- src/common/dcgraph.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common/dcgraph.cpp b/src/common/dcgraph.cpp index 8fb3b5a73d..dc6d018fb2 100644 --- a/src/common/dcgraph.cpp +++ b/src/common/dcgraph.cpp @@ -1284,6 +1284,9 @@ void wxGCDCImpl::Clear(void) { wxCHECK_RET( IsOk(), wxT("wxGCDC(cg)::Clear - invalid DC") ); + if ( m_backgroundBrush.IsTransparent() ) + return; + if ( m_backgroundBrush.IsOk() ) { m_graphicContext->SetBrush( m_backgroundBrush ); From 1f8b73b0d8974e0e845e6f17718ed4611caf3112 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 1 Oct 2019 01:28:42 +0200 Subject: [PATCH 5/9] Clear background with white brush by default in wxGCDC This makes wxGCDC consistent with wxDC. This commit is best viewed ignoring whitespace. --- src/common/dcgraph.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/common/dcgraph.cpp b/src/common/dcgraph.cpp index dc6d018fb2..456dc3c1a9 100644 --- a/src/common/dcgraph.cpp +++ b/src/common/dcgraph.cpp @@ -1287,28 +1287,20 @@ void wxGCDCImpl::Clear(void) if ( m_backgroundBrush.IsTransparent() ) return; - if ( m_backgroundBrush.IsOk() ) - { - m_graphicContext->SetBrush( m_backgroundBrush ); - wxPen p = *wxTRANSPARENT_PEN; - m_graphicContext->SetPen( p ); - wxCompositionMode formerMode = m_graphicContext->GetCompositionMode(); - m_graphicContext->SetCompositionMode(wxCOMPOSITION_SOURCE); + m_graphicContext->SetBrush( m_backgroundBrush.IsOk() ? m_backgroundBrush + : *wxWHITE_BRUSH ); + wxPen p = *wxTRANSPARENT_PEN; + m_graphicContext->SetPen( p ); + wxCompositionMode formerMode = m_graphicContext->GetCompositionMode(); + m_graphicContext->SetCompositionMode(wxCOMPOSITION_SOURCE); - double x, y, w, h; - m_graphicContext->GetClipBox(&x, &y, &w, &h); - m_graphicContext->DrawRectangle(x, y, w, h); + double x, y, w, h; + m_graphicContext->GetClipBox(&x, &y, &w, &h); + m_graphicContext->DrawRectangle(x, y, w, h); - m_graphicContext->SetCompositionMode(formerMode); - m_graphicContext->SetPen( m_pen ); - m_graphicContext->SetBrush( m_brush ); - } - else - { - double x, y, w, h; - m_graphicContext->GetClipBox(&x, &y, &w, &h); - m_graphicContext->ClearRectangle(x, y, w, h); - } + m_graphicContext->SetCompositionMode(formerMode); + m_graphicContext->SetPen( m_pen ); + m_graphicContext->SetBrush( m_brush ); } void wxGCDCImpl::DoGetSize(int *width, int *height) const From 40d989f297d1221e744a537329762a1efbbed566 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 1 Oct 2019 01:21:06 +0200 Subject: [PATCH 6/9] Revert "Use white background when rendering print preview" This reverts commit 70f0900799b670107982d683cecf216453fc67fe. It is not necessary any more after fixes to wxDC::Clear() in the previous commits. --- src/common/prntbase.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/prntbase.cpp b/src/common/prntbase.cpp index 3ee6ee2144..dd52c006d8 100644 --- a/src/common/prntbase.cpp +++ b/src/common/prntbase.cpp @@ -2054,7 +2054,6 @@ bool wxPrintPreviewBase::RenderPageIntoBitmap(wxBitmap& bmp, int pageNum) { wxMemoryDC memoryDC; memoryDC.SelectObject(bmp); - memoryDC.SetBackground(*wxWHITE_BRUSH); memoryDC.Clear(); return RenderPageIntoDC(memoryDC, pageNum); From 1bcde69a73e835df8faa8b02c3ec838e0adb3dc4 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 2 Oct 2019 02:43:56 +0200 Subject: [PATCH 7/9] Use white in wxDC::Clear() if background brush is reset in wxGTK2 Although white background was used if the brush was never set, setting and resetting it nothing unexpectedly (and inconsistently with the other ports) resulted in using black background. Fix this by just resetting the brush to white if it's invalid, instead of not setting it at all. --- src/gtk/dcclient.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gtk/dcclient.cpp b/src/gtk/dcclient.cpp index 4ccd8ddc71..de7fef4706 100644 --- a/src/gtk/dcclient.cpp +++ b/src/gtk/dcclient.cpp @@ -1738,7 +1738,8 @@ void wxWindowDCImpl::SetBackground( const wxBrush &brush ) m_backgroundBrush = brush; - if (!m_backgroundBrush.IsOk()) return; + if (!m_backgroundBrush.IsOk()) + m_backgroundBrush = *wxWHITE_BRUSH; if (!m_gdkwindow) return; From 864cc1aa711a0ae1c788167912afb274bda3a0f9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 2 Oct 2019 02:39:16 +0200 Subject: [PATCH 8/9] Document that wxDC::Clear() uses white background by default This officially documents the historical behaviour of this method in wxMSW and wxGTK2. --- interface/wx/dc.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/wx/dc.h b/interface/wx/dc.h index 40e37cb34a..e7c94b31e9 100644 --- a/interface/wx/dc.h +++ b/interface/wx/dc.h @@ -261,6 +261,9 @@ public: Note that SetBackground() method must be used to set the brush used by Clear(), the brush used for filling the shapes set by SetBrush() is ignored by it. + + If no background brush was set, solid white brush is used to clear the + device context. */ void Clear(); From cf07dea50dc5527448c4250ed0a0a55406f216d0 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 2 Oct 2019 02:39:53 +0200 Subject: [PATCH 9/9] Add unit tests for wxDC::Clear() Verify that it behaves as documented, i.e. uses white if the background brush is not explicitly set. --- tests/graphics/bitmap.cpp | 116 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/tests/graphics/bitmap.cpp b/tests/graphics/bitmap.cpp index 0fbad0442c..0032007844 100644 --- a/tests/graphics/bitmap.cpp +++ b/tests/graphics/bitmap.cpp @@ -25,6 +25,8 @@ #include "wx/graphics.h" #endif // wxUSE_GRAPHICS_CONTEXT +#include "testimage.h" + #define ASSERT_EQUAL_RGB(c, r, g, b) \ CHECK( (int)r == (int)c.Red() ); \ CHECK( (int)g == (int)c.Green() ); \ @@ -915,4 +917,118 @@ TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][ ASSERT_EQUAL_RGB(p, maskClrBottomRight.Red(), maskClrBottomRight.Green(), maskClrBottomRight.Blue()); } } + +namespace Catch +{ + template <> + struct StringMaker + { + static std::string convert(const wxBitmap& bmp) + { + return wxString::Format("bitmap of size %d*%d", + bmp.GetWidth(), + bmp.GetHeight()).ToStdString(); + } + }; +} + +class BitmapColourMatcher : public Catch::MatcherBase +{ +public: + explicit BitmapColourMatcher(const wxColour& col) + : m_col(col) + { + } + + bool match(const wxBitmap& bmp) const wxOVERRIDE + { + const wxImage img(bmp.ConvertToImage()); + + const unsigned char* data = img.GetData(); + for ( int y = 0; y < img.GetHeight(); ++y ) + { + for ( int x = 0; x < img.GetWidth(); ++x, data += 3 ) + { + if ( wxColour(data[0], data[1], data[2]) != m_col ) + return false; + } + } + + return true; + } + + std::string describe() const wxOVERRIDE + { + return wxString::Format("doesn't have all %s pixels", + m_col.GetAsString()).ToStdString(); + } + +private: + const wxColour m_col; +}; + +inline BitmapColourMatcher AllPixelsAre(const wxColour& col) +{ + return BitmapColourMatcher(col); +} + +TEST_CASE("DC::Clear", "[bitmap][dc]") +{ + // Just some arbitrary pixel data. + static unsigned char data[] = + { + 0xff, 0, 0, + 0, 0xff, 0, + 0, 0, 0xff, + 0x7f, 0, 0x7f + }; + + const wxImage img(2, 2, data, true /* don't take ownership of data */); + + wxBitmap bmp(img); + + SECTION("Clearing uses white by default") + { + { + wxMemoryDC dc(bmp); + dc.Clear(); + } + + CHECK_THAT(bmp, AllPixelsAre(*wxWHITE)); + } + + SECTION("Clearing with specified brush works as expected") + { + { + wxMemoryDC dc(bmp); + dc.SetBackground(*wxRED_BRUSH); + dc.Clear(); + } + CHECK_THAT(bmp, AllPixelsAre(*wxRED)); + } + + SECTION("Clearing with transparent brush does nothing") + { + { + wxMemoryDC dc(bmp); + dc.SetBackground(*wxTRANSPARENT_BRUSH); + dc.Clear(); + } + + CHECK_THAT(bmp.ConvertToImage(), RGBSameAs(img)); + } + + SECTION("Clearing with invalid brush uses white too") + { + { + wxMemoryDC dc(bmp); + dc.SetBackground(*wxBLACK_BRUSH); + dc.SetBackground(wxBrush()); + dc.Clear(); + } + + CHECK_THAT(bmp, AllPixelsAre(*wxWHITE)); + } +} + #endif //wxHAS_RAW_BITMAP