From 614e38dbb25075f8865b09d658b39ef23990ea08 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 24 Oct 2012 23:40:41 +0000 Subject: [PATCH] Implement clipping in wxSVGFileDC. Support setting the clipping region and add update the documentation and the sample accordingly. Closes #14462. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@72762 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + include/wx/dcsvg.h | 23 +++++++++------ interface/wx/dcsvg.h | 43 +++++++++++++++++++++++---- samples/svg/svgtest.cpp | 65 +++++++++++++++++++++++++++++++++++++++-- src/common/dcsvg.cpp | 65 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 179 insertions(+), 18 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 812427cf1a..88dbc74e33 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -579,6 +579,7 @@ All (GUI): - Add missing styles support to wxWindow XRC hanlder (Steffen Olszewski). - Allow specifying all wxFlexGridSizer parameters in XRC (Steffen Olszewski). - Close wxLogWindow automatically if it's the last remaining top level window. +- Implement clipping for wxSVGFileDC (Steve Benbow). wxGTK: diff --git a/include/wx/dcsvg.h b/include/wx/dcsvg.h index dc541ae799..d1b26c1e20 100644 --- a/include/wx/dcsvg.h +++ b/include/wx/dcsvg.h @@ -54,10 +54,7 @@ public: wxFAIL_MSG(wxT("wxSVGFILEDC::Clear() Call not implemented \nNot sensible for an output file?")); } - virtual void DestroyClippingRegion() - { - wxFAIL_MSG(wxT("wxSVGFILEDC::void Call not yet implemented")); - } + virtual void DestroyClippingRegion(); virtual wxCoord GetCharHeight() const; virtual wxCoord GetCharWidth() const; @@ -175,10 +172,7 @@ private: wxFAIL_MSG(wxT("wxSVGFILEDC::DoSetDeviceClippingRegion not yet implemented")); } - virtual void DoSetClippingRegion( int WXUNUSED(x), int WXUNUSED(y), int WXUNUSED(width), int WXUNUSED(height) ) - { - wxFAIL_MSG(wxT("wxSVGFILEDC::DoSetClippingRegion not yet implemented")); - } + virtual void DoSetClippingRegion(int x, int y, int width, int height); virtual void DoGetSizeMM( int *width, int *height ) const; @@ -193,6 +187,10 @@ private: // new one for the last pen/brush change. void NewGraphicsIfNeeded(); + // Open a new graphics group setting up all the attributes according to + // their current values in wxDC. + void DoStartNewGraphics(); + wxFileOutputStream *m_outfile; wxString m_filename; int m_sub_images; // number of png format images we have @@ -201,7 +199,14 @@ private: int m_width, m_height; double m_dpi; -private: + // The clipping nesting level is incremented by every call to + // SetClippingRegion() and reset when DestroyClippingRegion() is called. + size_t m_clipNestingLevel; + + // Unique ID for every clipping graphics group: this is simply always + // incremented in each SetClippingRegion() call. + size_t m_clipUniqueId; + DECLARE_ABSTRACT_CLASS(wxSVGFileDCImpl) }; diff --git a/interface/wx/dcsvg.h b/interface/wx/dcsvg.h index 5138aead6b..d0a593b46b 100644 --- a/interface/wx/dcsvg.h +++ b/interface/wx/dcsvg.h @@ -67,21 +67,52 @@ public: */ void SetLogicalFunction(wxRasterOperationMode function); + /** + Sets the clipping region for this device context to the intersection of + the given region described by the parameters of this method and the previously + set clipping region. + Clipping is implemented in the SVG output using SVG group elements (), with + nested group elements being used to represent clipping region intersections when + two or more calls are made to SetClippingRegion(). + */ + void SetClippingRegion(wxCoord x, wxCoord y, wxCoord width, + wxCoord height); + + /** + This is an overloaded member function, provided for convenience. It differs from the + above function only in what argument(s) it accepts. + */ + void SetClippingRegion(const wxPoint& pt, const wxSize& sz); + + /** + This is an overloaded member function, provided for convenience. It differs from the + above function only in what argument(s) it accepts. + */ + void SetClippingRegion(const wxRect& rect); + + /** + This function is not implemented in this DC class. + It could be implemented in future if a GetPoints() function were made available on wxRegion. + */ + void SetClippingRegion(const wxRegion& region); + + /** + Destroys the current clipping region so that none of the DC is clipped. + Since intersections arising from sequential calls to SetClippingRegion are represented + with nested SVG group elements (), all such groups are closed when + DestroyClippingRegion is called. + */ + void DestroyClippingRegion(); + //@{ /** Functions not implemented in this DC class. */ void CrossHair(wxCoord x, wxCoord y); - void DestroyClippingRegion(); bool FloodFill(wxCoord x, wxCoord y, const wxColour& colour, wxFloodFillStyle style = wxFLOOD_SURFACE); void GetClippingBox(wxCoord *x, wxCoord *y, wxCoord *width, wxCoord *height) const; bool GetPixel(wxCoord x, wxCoord y, wxColour* colour) const; - void SetClippingRegion(wxCoord x, wxCoord y, wxCoord width, - wxCoord height); - void SetClippingRegion(const wxPoint& pt, const wxSize& sz); - void SetClippingRegion(const wxRect& rect); - void SetClippingRegion(const wxRegion& region); void SetPalette(const wxPalette& palette); bool StartDoc(const wxString& message); //@} diff --git a/samples/svg/svgtest.cpp b/samples/svg/svgtest.cpp index ba6561bd06..bcd3339105 100644 --- a/samples/svg/svgtest.cpp +++ b/samples/svg/svgtest.cpp @@ -323,7 +323,7 @@ MyCanvas::MyCanvas(MyChild *parent, const wxPoint& pos, const wxSize& size) SetBackgroundColour(wxColour(wxT("WHITE"))); m_child = parent; - m_index = m_child->GetFrame()->GetCountOfChildren() % 8; + m_index = m_child->GetFrame()->GetCountOfChildren() % 9; } // Define the repainting behaviour @@ -496,6 +496,68 @@ void MyCanvas::OnDraw(wxDC& dc) break; case 7: + dc.SetTextForeground(wxT("RED")); + dc.DrawText(wxT("Red = Clipping Off"), 30, 5); + dc.SetTextForeground(wxT("GREEN")); + dc.DrawText(wxT("Green = Clipping On"), 30, 25); + + dc.SetTextForeground(wxT("BLACK")); + + dc.SetPen(*wxRED_PEN); + dc.SetBrush (wxBrush (wxT("SALMON"),wxBRUSHSTYLE_TRANSPARENT)); + dc.DrawCheckMark ( 80,50,75,75); + dc.DrawRectangle ( 80,50,75,75); + + dc.SetPen(*wxGREEN_PEN); + + // Clipped checkmarks + dc.DrawRectangle(180,50,75,75); + dc.SetClippingRegion(180,50,75,75); // x,y,width,height version + dc.DrawCheckMark ( 180,50,75,75); + dc.DestroyClippingRegion(); + + dc.DrawRectangle(wxRect(80,150,75,75)); + dc.SetClippingRegion(wxPoint(80,150),wxSize(75,75)); // pt,size version + dc.DrawCheckMark ( 80,150,75,75); + dc.DestroyClippingRegion(); + + dc.DrawRectangle(wxRect(180,150,75,75)); + dc.SetClippingRegion(wxRect(180,150,75,75)); // rect version + dc.DrawCheckMark ( 180,150,75,75); + dc.DestroyClippingRegion(); + + dc.DrawRectangle(wxRect( 80,250,50,65)); + dc.DrawRectangle(wxRect(105,260,50,65)); + dc.SetClippingRegion(wxRect( 80,250,50,65)); // second call to SetClippingRegion + dc.SetClippingRegion(wxRect(105,260,50,65)); // forms intersection with previous + dc.DrawCheckMark(80,250,75,75); + dc.DestroyClippingRegion(); // only one call to destroy (there's no stack) + + /* + ** Clipping by wxRegion not implemented for SVG. Should be + ** possible, but need to access points that define the wxRegion + ** from inside DoSetDeviceClippingRegion() and wxRegion does not + ** implement anything like getPoints(). + points[0].x = 180; points[0].y = 250; + points[1].x = 255; points[1].y = 250; + points[2].x = 180; points[2].y = 325; + points[3].x = 255; points[3].y = 325; + points[4].x = 180; points[4].y = 250; + + dc.DrawLines (5, points); + wxRegion reg = wxRegion(5,points); + + dc.SetClippingRegion(reg); + dc.DrawCheckMark ( 180,250,75,75); + dc.DestroyClippingRegion(); + */ + +#if wxUSE_STATUSBAR + s = wxT("Clipping region"); +#endif // wxUSE_STATUSBAR + break; + + case 8: wxString txtStr; wxCoord txtX, txtY, txtW, txtH, txtDescent, txtEL; wxCoord txtPad = 0; @@ -538,7 +600,6 @@ void MyCanvas::OnDraw(wxDC& dc) s = wxT("Text position test page"); #endif // wxUSE_STATUSBAR break; - } #if wxUSE_STATUSBAR m_child->SetStatusText(s); diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index dcba364827..85105f91f2 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -130,6 +130,9 @@ void wxSVGFileDCImpl::Init (const wxString &filename, int Width, int Height, dou m_OK = true; + m_clipUniqueId = 0; + m_clipNestingLevel = 0; + m_mm_to_pix_x = dpi/25.4; m_mm_to_pix_y = dpi/25.4; @@ -470,6 +473,59 @@ void wxSVGFileDCImpl::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h, } } +void wxSVGFileDCImpl::DoSetClippingRegion( int x, int y, int width, int height ) +{ + wxString svg; + + // End current graphics group to ensure proper xml nesting (e.g. so that + // graphics can be subsequently changed inside the clipping region) + svg << "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n"; + + write(svg); + + // Re-apply current graphics to ensure proper xml nesting + DoStartNewGraphics(); + + m_clipUniqueId++; + m_clipNestingLevel++; +} + +void wxSVGFileDCImpl::DestroyClippingRegion() +{ + wxString svg; + + // End current graphics element to ensure proper xml nesting (e.g. graphics + // might have been changed inside the clipping region) + svg << "\n"; + + // Close clipping group elements + for ( size_t i = 0; i < m_clipUniqueId; i++ ) + { + svg << ""; + } + svg << "\n"; + + write(svg); + + // Re-apply current graphics (e.g. brush may have been changed inside one + // of the clipped regions - that change will have been lost after xml + // elements for the clipped region have been closed). + DoStartNewGraphics(); + + m_clipUniqueId = 0; +} + void wxSVGFileDCImpl::DoGetTextExtent(const wxString& string, wxCoord *w, wxCoord *h, wxCoord *descent , wxCoord *externalLeading , const wxFont *font) const { @@ -538,9 +594,16 @@ void wxSVGFileDCImpl::NewGraphicsIfNeeded() m_graphics_changed = false; + write(wxS("\n")); + + DoStartNewGraphics(); +} + +void wxSVGFileDCImpl::DoStartNewGraphics() +{ wxString s, sBrush, sPenCap, sPenJoin, sPenStyle, sLast; - sBrush = wxT("\n