Merge branches 'svgdc-clip' and 'fix-dc-clipper-restore'

Various clipping-related fixes.

See https://github.com/wxWidgets/wxWidgets/pull/835

See https://github.com/wxWidgets/wxWidgets/pull/836
This commit is contained in:
Vadim Zeitlin
2018-06-21 16:05:59 +02:00
15 changed files with 213 additions and 87 deletions

View File

@@ -446,21 +446,22 @@ public:
// NB: this function works with device coordinates, not the logical ones!
virtual void DoSetDeviceClippingRegion(const wxRegion& region) = 0;
virtual void DoGetClippingBox(wxCoord *x, wxCoord *y,
wxCoord *w, wxCoord *h) const
{
int dcWidth, dcHeight;
DoGetSize(&dcWidth, &dcHeight);
// Method used to implement wxDC::GetClippingBox().
//
// Default implementation returns values stored in m_clip[XY][12] member
// variables, so this method doesn't need to be overridden if they're kept
// up to date.
virtual bool DoGetClippingRect(wxRect& rect) const;
if ( x )
*x = m_clipping ? m_clipX1 : DeviceToLogicalX(0);
if ( y )
*y = m_clipping ? m_clipY1 : DeviceToLogicalY(0);
if ( w )
*w = m_clipping ? m_clipX2 - m_clipX1 : DeviceToLogicalXRel(dcWidth);
if ( h )
*h = m_clipping ? m_clipY2 - m_clipY1 : DeviceToLogicalYRel(dcHeight);
}
#if WXWIN_COMPATIBILITY_3_0
// This method is kept for backwards compatibility but shouldn't be used
// nor overridden in the new code, implement DoGetClippingRect() above
// instead.
wxDEPRECATED_BUT_USED_INTERNALLY(
virtual void DoGetClippingBox(wxCoord *x, wxCoord *y,
wxCoord *w, wxCoord *h) const
);
#endif // WXWIN_COMPATIBILITY_3_0
virtual void DestroyClippingRegion() { ResetClipping(); }
@@ -669,7 +670,10 @@ private:
wxDC *m_owner;
protected:
// unset clipping variables (after clipping region was destroyed)
// This method exists for backwards compatibility only (while it's not
// documented, there are derived classes using it outside wxWidgets
// itself), don't use it in any new code and just call wxDCImpl version of
// DestroyClippingRegion() to reset the clipping information instead.
void ResetClipping()
{
m_clipping = false;
@@ -736,6 +740,9 @@ protected:
#endif // wxUSE_PALETTE
private:
// Return the full DC area in logical coordinates.
wxRect GetLogicalArea() const;
wxDECLARE_ABSTRACT_CLASS(wxDCImpl);
};
@@ -959,10 +966,22 @@ public:
void DestroyClippingRegion()
{ m_pimpl->DestroyClippingRegion(); }
void GetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) const
{ m_pimpl->DoGetClippingBox(x, y, w, h); }
void GetClippingBox(wxRect& rect) const
{ m_pimpl->DoGetClippingBox(&rect.x, &rect.y, &rect.width, &rect.height); }
bool GetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) const
{
wxRect r;
const bool clipping = m_pimpl->DoGetClippingRect(r);
if ( x )
*x = r.x;
if ( y )
*y = r.y;
if ( w )
*w = r.width;
if ( h )
*h = r.height;
return clipping;
}
bool GetClippingBox(wxRect& rect) const
{ return m_pimpl->DoGetClippingRect(rect); }
// coordinates conversions and transforms
@@ -1437,30 +1456,35 @@ class WXDLLIMPEXP_CORE wxDCClipper
public:
wxDCClipper(wxDC& dc, const wxRegion& r) : m_dc(dc)
{
dc.GetClippingBox(m_oldClipRect);
dc.SetClippingRegion(r.GetBox());
Init(r.GetBox());
}
wxDCClipper(wxDC& dc, const wxRect& r) : m_dc(dc)
{
dc.GetClippingBox(m_oldClipRect);
dc.SetClippingRegion(r.x, r.y, r.width, r.height);
Init(r);
}
wxDCClipper(wxDC& dc, wxCoord x, wxCoord y, wxCoord w, wxCoord h) : m_dc(dc)
{
dc.GetClippingBox(m_oldClipRect);
dc.SetClippingRegion(x, y, w, h);
Init(wxRect(x, y, w, h));
}
~wxDCClipper()
{
m_dc.DestroyClippingRegion();
if ( !m_oldClipRect.IsEmpty() )
if ( m_restoreOld )
m_dc.SetClippingRegion(m_oldClipRect);
}
private:
// Common part of all ctors.
void Init(const wxRect& r)
{
m_restoreOld = m_dc.GetClippingBox(m_oldClipRect);
m_dc.SetClippingRegion(r);
}
wxDC& m_dc;
wxRect m_oldClipRect;
bool m_restoreOld;
wxDECLARE_NO_COPY_CLASS(wxDCClipper);
};

View File

@@ -195,8 +195,7 @@ public:
virtual void DoSetDeviceClippingRegion(const wxRegion& region) wxOVERRIDE;
virtual void DoSetClippingRegion(wxCoord x, wxCoord y,
wxCoord width, wxCoord height) wxOVERRIDE;
virtual void DoGetClippingBox(wxCoord *x, wxCoord *y,
wxCoord *w, wxCoord *h) const wxOVERRIDE;
virtual bool DoGetClippingRect(wxRect& rect) const wxOVERRIDE;
virtual void DoGetTextExtent(const wxString& string,
wxCoord *x, wxCoord *y,

View File

@@ -86,23 +86,11 @@ public:
virtual wxCoord GetCharHeight() const wxOVERRIDE;
virtual wxCoord GetCharWidth() const wxOVERRIDE;
virtual void SetClippingRegion(wxCoord x, wxCoord y,
wxCoord w, wxCoord h)
{
DoSetClippingRegion(x, y, w, h);
}
virtual void SetPalette(const wxPalette& WXUNUSED(palette)) wxOVERRIDE
{
wxFAIL_MSG(wxT("wxSVGFILEDC::SetPalette not implemented"));
}
virtual void GetClippingBox(wxCoord *x, wxCoord *y,
wxCoord *w, wxCoord *h)
{
DoGetClippingBox(x, y, w, h);
}
virtual void SetLogicalFunction(wxRasterOperationMode WXUNUSED(function)) wxOVERRIDE
{
wxFAIL_MSG(wxT("wxSVGFILEDC::SetLogicalFunction Call not implemented"));

View File

@@ -71,7 +71,7 @@ public:
virtual bool DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const wxOVERRIDE;
virtual void DoSetClippingRegion( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) wxOVERRIDE;
virtual void DoSetDeviceClippingRegion( const wxRegion &region ) wxOVERRIDE;
virtual void DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) const wxOVERRIDE;
virtual bool DoGetClippingRect(wxRect& rect) const wxOVERRIDE;
virtual wxCoord GetCharWidth() const wxOVERRIDE;
virtual wxCoord GetCharHeight() const wxOVERRIDE;

View File

@@ -243,8 +243,7 @@ public:
virtual void DoSetClippingRegion(wxCoord x, wxCoord y,
wxCoord width, wxCoord height) wxOVERRIDE;
virtual void DoSetDeviceClippingRegion(const wxRegion& region) wxOVERRIDE;
virtual void DoGetClippingBox(wxCoord *x, wxCoord *y,
wxCoord *w, wxCoord *h) const wxOVERRIDE;
virtual bool DoGetClippingRect(wxRect& rect) const wxOVERRIDE;
virtual void DoGetSizeMM(int* width, int* height) const wxOVERRIDE;

View File

@@ -764,8 +764,30 @@ public:
@remarks
Clipping region is given in logical coordinates.
@param x If non-@NULL, filled in with the logical horizontal coordinate
of the top left corner of the clipping region if the function
returns true or 0 otherwise.
@param y If non-@NULL, filled in with the logical vertical coordinate
of the top left corner of the clipping region if the function
returns true or 0 otherwise.
@param width If non-@NULL, filled in with the width of the clipping
region if the function returns true or the device context width
otherwise.
@param height If non-@NULL, filled in with the height of the clipping
region if the function returns true or the device context height
otherwise.
@return @true if there is a clipping region or @false if there is no
active clipping region (note that this return value is available
only since wxWidgets 3.1.2, this function didn't return anything in
the previous versions).
*/
void GetClippingBox(wxCoord *x, wxCoord *y, wxCoord *width, wxCoord *height) const;
bool GetClippingBox(wxCoord *x, wxCoord *y, wxCoord *width, wxCoord *height) const;
/**
@overload
*/
bool GetClippingBox(wxRect& rect) const;
/**
Sets the clipping region for this device context to the intersection of

View File

@@ -353,9 +353,6 @@ wxDCImpl::wxDCImpl( wxDC *owner )
(double)wxGetDisplaySizeMM().GetWidth();
m_mm_to_pix_y = (double)wxGetDisplaySize().GetHeight() /
(double)wxGetDisplaySizeMM().GetHeight();
ResetBoundingBox();
ResetClipping();
}
wxDCImpl::~wxDCImpl()
@@ -406,6 +403,65 @@ void wxDCImpl::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
}
}
wxRect wxDCImpl::GetLogicalArea() const
{
const wxSize size = GetSize();
return wxRect(DeviceToLogicalX(0),
DeviceToLogicalY(0),
DeviceToLogicalXRel(size.x),
DeviceToLogicalYRel(size.y));
}
bool wxDCImpl::DoGetClippingRect(wxRect& rect) const
{
#if WXWIN_COMPATIBILITY_3_0
// Call the old function for compatibility.
DoGetClippingBox(&rect.x, &rect.y, &rect.width, &rect.height);
if ( rect != wxRect(-1, -1, 0, 0) )
{
// Custom overridden version of DoGetClippingBox() was called, we need
// to check if there is an actual clipping region or not. Normally the
// function is supposed to return the whole DC area (in logical
// coordinates) in this case, but also check that the clipping region
// is not empty because some implementations seem to do this instead.
return !rect.IsEmpty() && rect != GetLogicalArea();
}
#endif // WXWIN_COMPATIBILITY_3_0
if ( m_clipping )
{
rect = wxRect(m_clipX1,
m_clipY1,
m_clipX2 - m_clipX1,
m_clipY2 - m_clipY1);
return true;
}
else // No active clipping region.
{
rect = GetLogicalArea();
return false;
}
}
#if WXWIN_COMPATIBILITY_3_0
void wxDCImpl::DoGetClippingBox(wxCoord *x, wxCoord *y,
wxCoord *w, wxCoord *h) const
{
// Dummy implementation just to allow DoGetClippingRect() above to
// determine if this version was called or not.
if ( x )
*x = -1;
if ( y )
*y = -1;
if ( w )
*w = 0;
if ( h )
*h = 0;
}
#endif // WXWIN_COMPATIBILITY_3_0
// ----------------------------------------------------------------------------
// coordinate conversions and transforms
// ----------------------------------------------------------------------------
@@ -1318,12 +1374,12 @@ void wxDC::GetDeviceOrigin(long *x, long *y) const
void wxDC::GetClippingBox(long *x, long *y, long *w, long *h) const
{
wxCoord xx,yy,ww,hh;
m_pimpl->DoGetClippingBox(&xx, &yy, &ww, &hh);
if (x) *x = xx;
if (y) *y = yy;
if (w) *w = ww;
if (h) *h = hh;
wxRect r;
m_pimpl->DoGetClippingRect(r);
if (x) *x = r.x;
if (y) *y = r.y;
if (w) *w = r.width;
if (h) *h = r.height;
}
void wxDC::DrawObject(wxDrawObject* drawobject)

View File

@@ -282,6 +282,17 @@ void wxGCDCImpl::UpdateClipBox()
double x, y, w, h;
m_graphicContext->GetClipBox(&x, &y, &w, &h);
// We shouldn't reset m_clipping if the clipping region that we set happens
// to be empty (e.g. because its intersection with the previous clipping
// region was empty), but we should set it to true if we do have a valid
// clipping region and it was false which may happen if the clipping region
// set from the outside of wxWidgets code.
if ( !m_clipping )
{
if ( w != 0. && h != 0. )
m_clipping = true;
}
m_clipX1 = wxRound(x);
m_clipY1 = wxRound(y);
m_clipX2 = wxRound(x+w);
@@ -289,9 +300,9 @@ void wxGCDCImpl::UpdateClipBox()
m_isClipBoxValid = true;
}
void wxGCDCImpl::DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) const
bool wxGCDCImpl::DoGetClippingRect(wxRect& rect) const
{
wxCHECK_RET( IsOk(), wxS("wxGCDC::DoGetClippingRegion - invalid GC") );
wxCHECK_MSG( IsOk(), false, wxS("wxGCDC::DoGetClippingRegion - invalid GC") );
// Check if we should retrieve the clipping region possibly not set
// by SetClippingRegion() but modified by application: this can
// happen when we're associated with an existing graphics context using
@@ -303,14 +314,7 @@ void wxGCDCImpl::DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h
self->UpdateClipBox();
}
if ( x )
*x = m_clipX1;
if ( y )
*y = m_clipY1;
if ( w )
*w = m_clipX2 - m_clipX1;
if ( h )
*h = m_clipY2 - m_clipY1;
return wxDCImpl::DoGetClippingRect(rect);
}
void wxGCDCImpl::DoSetClippingRegion( wxCoord x, wxCoord y, wxCoord w, wxCoord h )
@@ -376,7 +380,7 @@ void wxGCDCImpl::DestroyClippingRegion()
m_graphicContext->SetPen( m_pen );
m_graphicContext->SetBrush( m_brush );
ResetClipping();
wxDCImpl::DestroyClippingRegion();
m_isClipBoxValid = false;
}

View File

@@ -934,6 +934,9 @@ void wxSVGFileDCImpl::DoSetClippingRegion(int x, int y, int width, int height)
m_clipUniqueId++;
m_clipNestingLevel++;
// Update the base class m_clip[XY][12] fields too.
wxDCImpl::DoSetClippingRegion(x, y, width, height);
}
void wxSVGFileDCImpl::DestroyClippingRegion()
@@ -958,6 +961,9 @@ void wxSVGFileDCImpl::DestroyClippingRegion()
DoStartNewGraphics();
m_clipUniqueId = 0;
// Also update the base class clipping region information.
wxDCImpl::DestroyClippingRegion();
}
void wxSVGFileDCImpl::DoGetTextExtent(const wxString& string, wxCoord *w, wxCoord *h, wxCoord *descent, wxCoord *externalLeading, const wxFont *font) const

View File

@@ -115,7 +115,7 @@ void wxDFBDCImpl::DestroyClippingRegion()
m_surface->SetClip(NULL);
ResetClipping();
wxDCImpl::DestroyClippingRegion();
}
// ---------------------------------------------------------------------------

View File

@@ -1914,9 +1914,9 @@ void wxWindowDCImpl::UpdateClipBox()
m_isClipBoxValid = true;
}
void wxWindowDCImpl::DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) const
bool wxWindowDCImpl::DoGetClippingRect(wxRect& rect) const
{
wxCHECK_RET( IsOk(), wxS("invalid window dc") );
wxCHECK_MSG( IsOk(), false, wxS("invalid window dc") );
// Check if we should try to retrieve the clipping region possibly not set
// by our SetClippingRegion() but preset or modified by application: this
@@ -1928,14 +1928,7 @@ void wxWindowDCImpl::DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoor
self->UpdateClipBox();
}
if ( x )
*x = m_clipX1;
if ( y )
*y = m_clipY1;
if ( w )
*w = m_clipX2 - m_clipX1;
if ( h )
*h = m_clipY2 - m_clipY1;
return wxGTKDCImpl::DoGetClippingRect(rect);
}
void wxWindowDCImpl::DoSetClippingRegion( wxCoord x, wxCoord y, wxCoord width, wxCoord height )

View File

@@ -574,8 +574,7 @@ void wxMSWDCImpl::UpdateClipBox()
m_isClipBoxValid = true;
}
void
wxMSWDCImpl::DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) const
bool wxMSWDCImpl::DoGetClippingRect(wxRect& rect) const
{
// check if we should try to retrieve the clipping region possibly not set
// by our SetClippingRegion() but preset or modified by Windows: this
@@ -589,14 +588,22 @@ wxMSWDCImpl::DoGetClippingBox(wxCoord *x, wxCoord *y, wxCoord *w, wxCoord *h) co
self->UpdateClipBox();
}
if ( x )
*x = m_clipX1;
if ( y )
*y = m_clipY1;
if ( w )
*w = m_clipX2 - m_clipX1;
if ( h )
*h = m_clipY2 - m_clipY1;
// Unfortunately we can't just call wxDCImpl::DoGetClippingRect() here
// because it wouldn't return the correct result if there is no clipping
// region and this DC has a world transform applied to it, as the base
// class version doesn't know anything about world transforms and wouldn't
// apply it to GetLogicalArea() that it returns in this case.
//
// We could solve this by overriding GetLogicalArea() in wxMSW and using
// DPtoLP() to perform the conversion correctly ourselves, but it's even
// simpler to just use the value returned by our UpdateClipBox() which is
// already correct in any case, so just do this instead.
rect = wxRect(m_clipX1,
m_clipY1,
m_clipX2 - m_clipX1,
m_clipY2 - m_clipY1);
return m_clipping;
}
// common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()

View File

@@ -302,7 +302,7 @@ wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) :
InitDC();
// the HDC can have a clipping box (which we didn't set), make sure our
// DoGetClippingBox() checks for it
// DoGetClippingRect() checks for it
m_clipping = true;
}

View File

@@ -454,7 +454,7 @@ void wxQtDCImpl::DoSetDeviceClippingRegion(const wxRegion& region)
void wxQtDCImpl::DestroyClippingRegion()
{
ResetClipping();
wxDCImpl::DestroyClippingRegion();
m_clippingRegion->Clear();
if (m_qtPainter->isActive())

View File

@@ -21,7 +21,9 @@
#if wxUSE_GRAPHICS_CONTEXT
#include "wx/dcgraph.h"
#endif // wxUSE_GRAPHICS_CONTEXT
#include "wx/dcsvg.h"
#include "testfile.h"
// ----------------------------------------------------------------------------
// test class
@@ -2122,3 +2124,29 @@ void ClippingBoxTestCaseGCBase::RegionsAndPushPopState()
}
#endif // wxUSE_GRAPHICS_CONTEXT
#if wxUSE_SVG
// We can't reuse the existing tests for wxSVGFileDC as we can't check its
// output, but we can still at least check the behaviour of GetClippingBox().
TEST_CASE("ClippingBoxTestCaseSVGDC", "[clip][svgdc]")
{
TestFile tf;
wxSVGFileDC dc(tf.GetName(), s_dcSize.x, s_dcSize.y);
wxRect rect;
dc.GetClippingBox(rect);
CHECK( rect == wxRect(s_dcSize) );
const wxRect rectClip(10, 20, 80, 75);
dc.SetClippingRegion(rectClip);
dc.GetClippingBox(rect);
CHECK( rect == rectClip );
dc.DestroyClippingRegion();
dc.GetClippingBox(rect);
CHECK( rect == wxRect(s_dcSize) );
}
#endif // wxUSE_SVG