Add wxGraphicsContext::GetClipBox() function

This method returns bounding box of the current clipping region.
Added declaration, documentation and implemented for GDI+, Direct2D, Cairo renderers.
This commit is contained in:
Artur Wieczorek
2016-08-21 20:57:39 +02:00
parent 1927a2f8a0
commit 480a003c00
7 changed files with 159 additions and 0 deletions

View File

@@ -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:

View File

@@ -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;

View File

@@ -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;
/** @}
*/

View File

@@ -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) \

View File

@@ -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 )
{

View File

@@ -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)

View File

@@ -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<ID2D1RectangleGeometry> rectGeometry;
hr = m_direct2dFactory->CreateRectangleGeometry(
D2D1::RectF(0.0F, 0.0F, (FLOAT)m_width, (FLOAT)m_height),
&rectGeometry);
wxCHECK_HRESULT_RET(hr);
wxCOMPtr<ID2D1Geometry> clipGeometry(rectGeometry);
wxStack<LayerData> 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<ID2D1RectangleGeometry> 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<ID2D1PathGeometry> pathGeometryClip;
hr = m_direct2dFactory->CreatePathGeometry(&pathGeometryClip);
wxCHECK_HRESULT_RET(hr);
wxCOMPtr<ID2D1GeometrySink> 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;