diff --git a/docs/changes.txt b/docs/changes.txt index a7d1fc62f7..851bf2b8a4 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -97,6 +97,7 @@ All (GUI): - Fix displaying edited value of wxUIntProperty (wxPropertyGrid). - Fix displaying validation errors for numeric wxPropertyGrid properties. - Add wxSYS_CARET_{ON,OFF,TIMEOUT}_MSEC system settings (brawer). +- Add wxGraphicsContext::GetClipBox(). wxGTK: diff --git a/include/wx/graphics.h b/include/wx/graphics.h index f8d016f216..66a45763d8 100644 --- a/include/wx/graphics.h +++ b/include/wx/graphics.h @@ -544,6 +544,9 @@ public: // resets the clipping to original extent virtual void ResetClip() = 0; + // returns bounding box of the clipping region + virtual void GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) = 0; + // returns the native context virtual void * GetNativeContext() = 0; diff --git a/interface/wx/graphics.h b/interface/wx/graphics.h index 4137f9f071..cc6feb5d90 100644 --- a/interface/wx/graphics.h +++ b/interface/wx/graphics.h @@ -510,6 +510,17 @@ public: */ virtual void Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h) = 0; + /** + Returns bounding box of the current clipping region. + + @remarks + - If clipping region is empty, then empty rectangle is returned + (@a x, @a y, @a w, @a h are set to zero). + + @since 3.1.1 + */ + virtual void GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) = 0; + /** @} */ diff --git a/src/common/cairo.cpp b/src/common/cairo.cpp index 3832448724..8baa0da911 100644 --- a/src/common/cairo.cpp +++ b/src/common/cairo.cpp @@ -172,6 +172,8 @@ (cairo_t *cr, cairo_surface_t *surface, double x, double y), (cr, surface, x, y) ) \ m( cairo_matrix_init_identity, \ (cairo_matrix_t *matrix), (matrix) ) \ + m( cairo_clip_extents, \ + (cairo_t *cr, double *x1, double *y1, double *x2, double *y2), (cr, x1, y1, x2, y2) ) \ #ifdef __WXMAC__ #define wxCAIRO_PLATFORM_METHODS(m) \ diff --git a/src/generic/graphicc.cpp b/src/generic/graphicc.cpp index 1850c8172e..ef7e830af5 100644 --- a/src/generic/graphicc.cpp +++ b/src/generic/graphicc.cpp @@ -458,6 +458,9 @@ public: // resets the clipping to original extent virtual void ResetClip() wxOVERRIDE; + // returns bounding box of the clipping region + virtual void GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) wxOVERRIDE; + virtual void * GetNativeContext() wxOVERRIDE; virtual bool SetAntialiasMode(wxAntialiasMode antialias) wxOVERRIDE; @@ -2371,6 +2374,23 @@ void wxCairoContext::ResetClip() cairo_reset_clip(m_context); } +void wxCairoContext::GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) +{ + double x1, y1, x2, y2; + cairo_clip_extents(m_context, &x1, &y1, &x2, &y2); + // Check if we have an empty clipping box. + if ( x2 - x1 <= DBL_MIN || y2 - y1 <= DBL_MIN ) + x1 = x2 = y1 = y2 = 0.0; + + if ( x ) + *x = x1; + if ( y ) + *y = y1; + if ( w ) + *w = x2 - x1; + if ( h ) + *h = y2 - y1; +} void wxCairoContext::StrokePath( const wxGraphicsPath& path ) { diff --git a/src/msw/graphics.cpp b/src/msw/graphics.cpp index 971c1e34ff..b102cb37e4 100644 --- a/src/msw/graphics.cpp +++ b/src/msw/graphics.cpp @@ -364,6 +364,9 @@ public: // resets the clipping to original extent virtual void ResetClip(); + // returns bounding box of the clipping region + virtual void GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) wxOVERRIDE; + virtual void * GetNativeContext(); virtual void StrokePath( const wxGraphicsPath& p ); @@ -1663,6 +1666,26 @@ void wxGDIPlusContext::ResetClip() m_context->ResetClip(); } +void wxGDIPlusContext::GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) +{ + RectF r; + m_context->SetPixelOffsetMode(PixelOffsetModeNone); + m_context->GetVisibleClipBounds(&r); + m_context->SetPixelOffsetMode(PixelOffsetModeHalf); + // Check if we have an empty clipping box. + if ( r.Width <= REAL_MIN || r.Height <= REAL_MIN ) + r.X = r.Y = r.Width = r.Height = 0.0F; + + if ( x ) + *x = r.X; + if ( y ) + *y = r.Y; + if ( w ) + *w = r.Width; + if ( h ) + *h = r.Height; +} + void wxGDIPlusContext::DrawRectangle( wxDouble x, wxDouble y, wxDouble w, wxDouble h ) { if (m_composition == wxCOMPOSITION_DEST) diff --git a/src/msw/graphicsd2d.cpp b/src/msw/graphicsd2d.cpp index f655640cbc..41745c2a2b 100644 --- a/src/msw/graphicsd2d.cpp +++ b/src/msw/graphicsd2d.cpp @@ -3230,6 +3230,7 @@ public: void Clip(const wxRegion&) wxOVERRIDE {} void Clip(wxDouble, wxDouble, wxDouble, wxDouble) wxOVERRIDE {} void ResetClip() wxOVERRIDE {} + void GetClipBox(wxDouble*, wxDouble*, wxDouble*, wxDouble*) wxOVERRIDE {} void* GetNativeContext() wxOVERRIDE { return NULL; } bool SetAntialiasMode(wxAntialiasMode) wxOVERRIDE { return false; } bool SetInterpolationQuality(wxInterpolationQuality) wxOVERRIDE { return false; } @@ -3327,6 +3328,8 @@ public: void ResetClip() wxOVERRIDE; + void GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) wxOVERRIDE; + // The native context used by wxD2DContext is a Direct2D render target. void* GetNativeContext() wxOVERRIDE; @@ -3618,6 +3621,102 @@ void wxD2DContext::ResetClip() GetRenderTarget()->SetTransform(&currTransform); } +void wxD2DContext::GetClipBox(wxDouble* x, wxDouble* y, wxDouble* w, wxDouble* h) +{ + // To obtain actual clipping box we have to start with rectangle + // covering the entire render target and interesect with this rectangle + // all clipping layers. Bounding box of the final geometry + // (being intersection of all clipping layers) is a clipping box. + + HRESULT hr; + wxCOMPtr rectGeometry; + hr = m_direct2dFactory->CreateRectangleGeometry( + D2D1::RectF(0.0F, 0.0F, (FLOAT)m_width, (FLOAT)m_height), + &rectGeometry); + wxCHECK_HRESULT_RET(hr); + + wxCOMPtr clipGeometry(rectGeometry); + + wxStack layers(m_layers); + while( !layers.empty() ) + { + LayerData ld = layers.top(); + layers.pop(); + + if ( ld.type == CLIP_LAYER ) + { + // If current geometry is empty (null region) + // or there is no intersection between geometries + // then final result is "null" rectangle geometry. + FLOAT area; + hr = ld.geometry->ComputeArea(ld.transformMatrix, &area); + wxCHECK_HRESULT_RET(hr); + D2D1_GEOMETRY_RELATION geomRel; + hr = clipGeometry->CompareWithGeometry(ld.geometry, ld.transformMatrix, &geomRel); + wxCHECK_HRESULT_RET(hr); + if ( area <= FLT_MIN || geomRel == D2D1_GEOMETRY_RELATION_DISJOINT ) + { + wxCOMPtr nullGeometry; + hr = m_direct2dFactory->CreateRectangleGeometry( + D2D1::RectF(0.0F, 0.0F, 0.0F, 0.0F), &nullGeometry); + wxCHECK_HRESULT_RET(hr); + + clipGeometry.reset(); + clipGeometry = nullGeometry; + break; + } + + wxCOMPtr pathGeometryClip; + hr = m_direct2dFactory->CreatePathGeometry(&pathGeometryClip); + wxCHECK_HRESULT_RET(hr); + wxCOMPtr pGeometrySink; + hr = pathGeometryClip->Open(&pGeometrySink); + wxCHECK_HRESULT_RET(hr); + + hr = clipGeometry->CombineWithGeometry(ld.geometry, D2D1_COMBINE_MODE_INTERSECT, + ld.transformMatrix, pGeometrySink); + wxCHECK_HRESULT_RET(hr); + hr = pGeometrySink->Close(); + wxCHECK_HRESULT_RET(hr); + pGeometrySink.reset(); + + clipGeometry = pathGeometryClip; + pathGeometryClip.reset(); + } + } + + // Final clipping geometry is given in device coordinates + // so we need to transform its bounds to logical coordinates. + D2D1::Matrix3x2F currTransform; + GetRenderTarget()->GetTransform(&currTransform); + currTransform.Invert(); + + D2D1_RECT_F bounds; + // First check if clip region is empty. + FLOAT clipArea; + hr = clipGeometry->ComputeArea(currTransform, &clipArea); + wxCHECK_HRESULT_RET(hr); + if ( clipArea <= FLT_MIN ) + { + bounds.left = bounds.top = bounds.right = bounds.bottom = 0.0F; + } + else + { + // If it is not empty then get it bounds. + hr = clipGeometry->GetBounds(currTransform, &bounds); + wxCHECK_HRESULT_RET(hr); + } + + if ( x ) + *x = bounds.left; + if ( y ) + *y = bounds.top; + if ( w ) + *w = (double)bounds.right - bounds.left; + if ( h ) + *h = (double)bounds.bottom - bounds.top; +} + void* wxD2DContext::GetNativeContext() { return &m_renderTargetHolder;