Fixed layer management in Direct2D renderer.
ID2D1RenderTarget::PushAxisAlignedClip/PopAxisAlignedClip used to clip the region (with wxGraphicsRenderer::Clip) and ID2D1RenderTarget::PushLayer/PopLayer used to rendering to the transparent layer (with wxGraphicsRenderer::BeginLayer) are non independent but have to be used in the controlled sequences: "A PushAxisAlignedClip and PopAxisAlignedClip pair can occur around or within a PushLayer and PopLayer, but cannot overlap" (and of course finally each Push* call must have a matching Pop* call). To control the sequence of access to the AxisAlignedClips and Layers there is implemented a wxStack data member holding Clips/Layers parameters which reflects a physical stack of respective Clips/Layers in ID2D1RenderTarget. This way we know in which order to pop and what to pop from ID2D1RenderTarget stack if there is a need to do so. Closes #17590
This commit is contained in:
@@ -110,9 +110,9 @@ struct wxFontMetrics
|
|||||||
abstract API for drawing on any of them.
|
abstract API for drawing on any of them.
|
||||||
|
|
||||||
wxWidgets offers an alternative drawing API based on the modern drawing
|
wxWidgets offers an alternative drawing API based on the modern drawing
|
||||||
backends GDI+, CoreGraphics and Cairo. See wxGraphicsContext, wxGraphicsRenderer
|
backends GDI+, CoreGraphics, Cairo and Direct2D. See wxGraphicsContext,
|
||||||
and related classes. There is also a wxGCDC linking the APIs by offering
|
wxGraphicsRenderer and related classes. There is also a wxGCDC linking
|
||||||
the wxDC API on top of a wxGraphicsContext.
|
the APIs by offering the wxDC API on top of a wxGraphicsContext.
|
||||||
|
|
||||||
wxDC is an abstract base class and cannot be created directly.
|
wxDC is an abstract base class and cannot be created directly.
|
||||||
Use wxPaintDC, wxClientDC, wxWindowDC, wxScreenDC, wxMemoryDC or
|
Use wxPaintDC, wxClientDC, wxWindowDC, wxScreenDC, wxMemoryDC or
|
||||||
@@ -770,10 +770,10 @@ public:
|
|||||||
window redraws when only a known area of the screen is damaged.
|
window redraws when only a known area of the screen is damaged.
|
||||||
|
|
||||||
@remarks
|
@remarks
|
||||||
- Calling GetClippingBox() can only make the clipping region smaller,
|
- Calling this function can only make the clipping region smaller,
|
||||||
never larger.
|
never larger.
|
||||||
|
|
||||||
- You need to call DestroyClippingRegion() if you want to set
|
- You need to call DestroyClippingRegion() first if you want to set
|
||||||
the clipping region exactly to the region specified.
|
the clipping region exactly to the region specified.
|
||||||
|
|
||||||
- If resulting clipping region is empty, then all drawing on the DC is
|
- If resulting clipping region is empty, then all drawing on the DC is
|
||||||
|
@@ -451,12 +451,24 @@ public:
|
|||||||
static wxGraphicsContext* Create();
|
static wxGraphicsContext* Create();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Clips drawings to the specified region.
|
Sets the clipping region to the intersection of the given region
|
||||||
|
and the previously set clipping region.
|
||||||
|
The clipping region is an area to which drawing is restricted.
|
||||||
|
|
||||||
|
@remarks
|
||||||
|
- Calling this function can only make the clipping region smaller,
|
||||||
|
never larger.
|
||||||
|
|
||||||
|
- You need to call ResetClip() first if you want to set the clipping
|
||||||
|
region exactly to the region specified.
|
||||||
|
|
||||||
|
- If resulting clipping region is empty, then all drawing upon the context
|
||||||
|
is clipped out (all changes made by drawing operations are masked out).
|
||||||
*/
|
*/
|
||||||
virtual void Clip(const wxRegion& region) = 0;
|
virtual void Clip(const wxRegion& region) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Clips drawings to the specified rectangle.
|
@overload
|
||||||
*/
|
*/
|
||||||
virtual void Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h) = 0;
|
virtual void Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h) = 0;
|
||||||
|
|
||||||
|
@@ -3388,11 +3388,18 @@ private:
|
|||||||
ID2D1RenderTarget* GetRenderTarget() const;
|
ID2D1RenderTarget* GetRenderTarget() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum ClipMode
|
enum LayerType
|
||||||
{
|
{
|
||||||
CLIP_MODE_NONE,
|
CLIP_AXIS_ALIGNED_RECT,
|
||||||
CLIP_MODE_AXIS_ALIGNED_RECTANGLE,
|
CLIP_LAYER,
|
||||||
CLIP_MODE_GEOMETRY
|
OTHER_LAYER
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LayerData
|
||||||
|
{
|
||||||
|
LayerType type;
|
||||||
|
D2D1_LAYER_PARAMETERS params;
|
||||||
|
wxCOMPtr<ID2D1Layer> layer;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -3405,14 +3412,7 @@ private:
|
|||||||
// The context owns these pointers and is responsible for releasing them.
|
// The context owns these pointers and is responsible for releasing them.
|
||||||
wxStack<wxCOMPtr<ID2D1DrawingStateBlock> > m_stateStack;
|
wxStack<wxCOMPtr<ID2D1DrawingStateBlock> > m_stateStack;
|
||||||
|
|
||||||
ClipMode m_clipMode;
|
wxStack<LayerData> m_layers;
|
||||||
|
|
||||||
bool m_clipLayerAcquired;
|
|
||||||
|
|
||||||
// A direct2d layer is a device-dependent resource.
|
|
||||||
wxCOMPtr<ID2D1Layer> m_clipLayer;
|
|
||||||
|
|
||||||
wxStack<wxCOMPtr<ID2D1Layer> > m_layers;
|
|
||||||
|
|
||||||
ID2D1RenderTarget* m_cachedRenderTarget;
|
ID2D1RenderTarget* m_cachedRenderTarget;
|
||||||
|
|
||||||
@@ -3472,9 +3472,7 @@ wxD2DContext::wxD2DContext(wxGraphicsRenderer* renderer, ID2D1Factory* direct2dF
|
|||||||
void wxD2DContext::Init()
|
void wxD2DContext::Init()
|
||||||
{
|
{
|
||||||
m_cachedRenderTarget = NULL;
|
m_cachedRenderTarget = NULL;
|
||||||
m_clipMode = CLIP_MODE_NONE;
|
|
||||||
m_composition = wxCOMPOSITION_OVER;
|
m_composition = wxCOMPOSITION_OVER;
|
||||||
m_clipLayerAcquired = false;
|
|
||||||
m_renderTargetHolder->Bind(this);
|
m_renderTargetHolder->Bind(this);
|
||||||
m_enableOffset = true;
|
m_enableOffset = true;
|
||||||
EnsureInitialized();
|
EnsureInitialized();
|
||||||
@@ -3482,11 +3480,20 @@ void wxD2DContext::Init()
|
|||||||
|
|
||||||
wxD2DContext::~wxD2DContext()
|
wxD2DContext::~wxD2DContext()
|
||||||
{
|
{
|
||||||
ResetClip();
|
// Remove all layers from the stack of layers.
|
||||||
|
|
||||||
while ( !m_layers.empty() )
|
while ( !m_layers.empty() )
|
||||||
{
|
{
|
||||||
EndLayer();
|
LayerData ld = m_layers.top();
|
||||||
|
m_layers.pop();
|
||||||
|
|
||||||
|
if ( ld.type == CLIP_AXIS_ALIGNED_RECT )
|
||||||
|
{
|
||||||
|
GetRenderTarget()->PopAxisAlignedClip();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GetRenderTarget()->PopLayer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT result = GetRenderTarget()->EndDraw();
|
HRESULT result = GetRenderTarget()->EndDraw();
|
||||||
@@ -3502,47 +3509,74 @@ ID2D1RenderTarget* wxD2DContext::GetRenderTarget() const
|
|||||||
|
|
||||||
void wxD2DContext::Clip(const wxRegion& region)
|
void wxD2DContext::Clip(const wxRegion& region)
|
||||||
{
|
{
|
||||||
GetRenderTarget()->Flush();
|
|
||||||
ResetClip();
|
|
||||||
|
|
||||||
wxCOMPtr<ID2D1Geometry> clipGeometry = wxD2DConvertRegionToGeometry(m_direct2dFactory, region);
|
wxCOMPtr<ID2D1Geometry> clipGeometry = wxD2DConvertRegionToGeometry(m_direct2dFactory, region);
|
||||||
|
|
||||||
if (!m_clipLayerAcquired)
|
wxCOMPtr<ID2D1Layer> clipLayer;
|
||||||
{
|
GetRenderTarget()->CreateLayer(&clipLayer);
|
||||||
GetRenderTarget()->CreateLayer(&m_clipLayer);
|
|
||||||
m_clipLayerAcquired = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetRenderTarget()->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), clipGeometry), m_clipLayer);
|
LayerData ld;
|
||||||
|
ld.type = CLIP_LAYER;
|
||||||
|
ld.params = D2D1::LayerParameters(D2D1::InfiniteRect(), clipGeometry);
|
||||||
|
ld.layer = clipLayer;
|
||||||
|
|
||||||
m_clipMode = CLIP_MODE_GEOMETRY;
|
GetRenderTarget()->PushLayer(ld.params, clipLayer);
|
||||||
|
// Store layer parameters.
|
||||||
|
m_layers.push(ld);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxD2DContext::Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
|
void wxD2DContext::Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
|
||||||
{
|
{
|
||||||
GetRenderTarget()->Flush();
|
LayerData ld;
|
||||||
ResetClip();
|
ld.type = CLIP_AXIS_ALIGNED_RECT;
|
||||||
|
ld.params = D2D1::LayerParameters(D2D1::RectF(x, y, x + w, y + h),
|
||||||
|
NULL, D2D1_ANTIALIAS_MODE_ALIASED);
|
||||||
|
|
||||||
GetRenderTarget()->PushAxisAlignedClip(
|
GetRenderTarget()->PushAxisAlignedClip(ld.params.contentBounds,
|
||||||
D2D1::RectF(x, y, x + w, y + h),
|
|
||||||
D2D1_ANTIALIAS_MODE_ALIASED);
|
D2D1_ANTIALIAS_MODE_ALIASED);
|
||||||
|
// Store layer parameters.
|
||||||
m_clipMode = CLIP_MODE_AXIS_ALIGNED_RECTANGLE;
|
m_layers.push(ld);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxD2DContext::ResetClip()
|
void wxD2DContext::ResetClip()
|
||||||
{
|
{
|
||||||
if (m_clipMode == CLIP_MODE_AXIS_ALIGNED_RECTANGLE)
|
wxStack<LayerData> layersToRestore;
|
||||||
|
// Remove all clipping layers from the stack of layers.
|
||||||
|
while ( !m_layers.empty() )
|
||||||
|
{
|
||||||
|
LayerData ld = m_layers.top();
|
||||||
|
m_layers.pop();
|
||||||
|
|
||||||
|
if ( ld.type == CLIP_AXIS_ALIGNED_RECT )
|
||||||
{
|
{
|
||||||
GetRenderTarget()->PopAxisAlignedClip();
|
GetRenderTarget()->PopAxisAlignedClip();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_clipMode == CLIP_MODE_GEOMETRY)
|
if ( ld.type == CLIP_LAYER )
|
||||||
{
|
{
|
||||||
GetRenderTarget()->PopLayer();
|
GetRenderTarget()->PopLayer();
|
||||||
|
ld.layer.reset();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_clipMode = CLIP_MODE_NONE;
|
GetRenderTarget()->PopLayer();
|
||||||
|
// Save non-clipping layer
|
||||||
|
layersToRestore.push(ld);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT hr = GetRenderTarget()->Flush();
|
||||||
|
wxCHECK_HRESULT_RET(hr);
|
||||||
|
|
||||||
|
// Re-apply all remaining non-clipping layers.
|
||||||
|
while ( !layersToRestore.empty() )
|
||||||
|
{
|
||||||
|
LayerData ld = layersToRestore.top();
|
||||||
|
layersToRestore.pop();
|
||||||
|
|
||||||
|
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
||||||
|
// Store layer parameters.
|
||||||
|
m_layers.push(ld);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* wxD2DContext::GetNativeContext()
|
void* wxD2DContext::GetNativeContext()
|
||||||
@@ -3633,28 +3667,79 @@ void wxD2DContext::BeginLayer(wxDouble opacity)
|
|||||||
{
|
{
|
||||||
wxCOMPtr<ID2D1Layer> layer;
|
wxCOMPtr<ID2D1Layer> layer;
|
||||||
GetRenderTarget()->CreateLayer(&layer);
|
GetRenderTarget()->CreateLayer(&layer);
|
||||||
m_layers.push(layer);
|
|
||||||
|
|
||||||
GetRenderTarget()->PushLayer(
|
LayerData ld;
|
||||||
D2D1::LayerParameters(D2D1::InfiniteRect(),
|
ld.type = OTHER_LAYER;
|
||||||
|
ld.params = D2D1::LayerParameters(D2D1::InfiniteRect(),
|
||||||
NULL,
|
NULL,
|
||||||
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
|
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
|
||||||
D2D1::IdentityMatrix(), opacity),
|
D2D1::IdentityMatrix(), opacity);
|
||||||
layer);
|
ld.layer = layer;
|
||||||
|
|
||||||
|
GetRenderTarget()->PushLayer(ld.params, layer);
|
||||||
|
|
||||||
|
// Store layer parameters.
|
||||||
|
m_layers.push(ld);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxD2DContext::EndLayer()
|
void wxD2DContext::EndLayer()
|
||||||
{
|
{
|
||||||
if (!m_layers.empty())
|
wxStack<LayerData> layersToRestore;
|
||||||
|
// Temporarily remove all clipping layers
|
||||||
|
// above the first standard layer
|
||||||
|
// and next permanently remove this layer.
|
||||||
|
while ( !m_layers.empty() )
|
||||||
{
|
{
|
||||||
wxCOMPtr<ID2D1Layer> topLayer = m_layers.top();
|
LayerData ld = m_layers.top();
|
||||||
|
m_layers.pop();
|
||||||
|
|
||||||
|
if ( ld.type == CLIP_AXIS_ALIGNED_RECT )
|
||||||
|
{
|
||||||
|
GetRenderTarget()->PopAxisAlignedClip();
|
||||||
|
layersToRestore.push(ld);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ld.type == CLIP_LAYER )
|
||||||
|
{
|
||||||
GetRenderTarget()->PopLayer();
|
GetRenderTarget()->PopLayer();
|
||||||
|
layersToRestore.push(ld);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We found a non-clipping layer to remove.
|
||||||
|
GetRenderTarget()->PopLayer();
|
||||||
|
ld.layer.reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( m_layers.empty() )
|
||||||
|
{
|
||||||
HRESULT hr = GetRenderTarget()->Flush();
|
HRESULT hr = GetRenderTarget()->Flush();
|
||||||
wxCHECK_HRESULT_RET(hr);
|
wxCHECK_HRESULT_RET(hr);
|
||||||
|
}
|
||||||
|
|
||||||
m_layers.pop();
|
// Re-apply all stored clipping layers.
|
||||||
|
while ( !layersToRestore.empty() )
|
||||||
|
{
|
||||||
|
LayerData ld = layersToRestore.top();
|
||||||
|
layersToRestore.pop();
|
||||||
|
|
||||||
|
if ( ld.type == CLIP_AXIS_ALIGNED_RECT )
|
||||||
|
{
|
||||||
|
GetRenderTarget()->PushAxisAlignedClip(ld.params.contentBounds,
|
||||||
|
ld.params.maskAntialiasMode);
|
||||||
|
}
|
||||||
|
else if ( ld.type == CLIP_LAYER )
|
||||||
|
{
|
||||||
|
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxFAIL_MSG( wxS("Invalid layer type") );
|
||||||
|
}
|
||||||
|
// Store layer parameters.
|
||||||
|
m_layers.push(ld);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3880,9 +3965,6 @@ void wxD2DContext::AdjustRenderTargetSize()
|
|||||||
void wxD2DContext::ReleaseDeviceDependentResources()
|
void wxD2DContext::ReleaseDeviceDependentResources()
|
||||||
{
|
{
|
||||||
ReleaseResources();
|
ReleaseResources();
|
||||||
|
|
||||||
m_clipLayer.reset();
|
|
||||||
m_clipLayerAcquired = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxD2DContext::DrawRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
|
void wxD2DContext::DrawRectangle(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
|
||||||
@@ -3975,12 +4057,55 @@ void wxD2DContext::DrawEllipse(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
|
|||||||
|
|
||||||
void wxD2DContext::Flush()
|
void wxD2DContext::Flush()
|
||||||
{
|
{
|
||||||
HRESULT result = m_renderTargetHolder->Flush();
|
wxStack<LayerData> layersToRestore;
|
||||||
|
// Temporarily remove all layers from the stack of layers.
|
||||||
|
while ( !m_layers.empty() )
|
||||||
|
{
|
||||||
|
LayerData ld = m_layers.top();
|
||||||
|
m_layers.pop();
|
||||||
|
|
||||||
if (result == (HRESULT)D2DERR_RECREATE_TARGET)
|
if ( ld.type == CLIP_AXIS_ALIGNED_RECT )
|
||||||
|
{
|
||||||
|
GetRenderTarget()->PopAxisAlignedClip();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GetRenderTarget()->PopLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save layer data.
|
||||||
|
layersToRestore.push(ld);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT hr = m_renderTargetHolder->Flush();
|
||||||
|
|
||||||
|
if ( hr == (HRESULT)D2DERR_RECREATE_TARGET )
|
||||||
{
|
{
|
||||||
ReleaseDeviceDependentResources();
|
ReleaseDeviceDependentResources();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxCHECK_HRESULT_RET(hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-apply all layers.
|
||||||
|
while ( !layersToRestore.empty() )
|
||||||
|
{
|
||||||
|
LayerData ld = layersToRestore.top();
|
||||||
|
layersToRestore.pop();
|
||||||
|
|
||||||
|
if ( ld.type == CLIP_AXIS_ALIGNED_RECT )
|
||||||
|
{
|
||||||
|
GetRenderTarget()->PushAxisAlignedClip(ld.params.contentBounds,
|
||||||
|
ld.params.maskAntialiasMode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
||||||
|
}
|
||||||
|
// Store layer parameters.
|
||||||
|
m_layers.push(ld);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxD2DContext::GetDPI(wxDouble* dpiX, wxDouble* dpiY)
|
void wxD2DContext::GetDPI(wxDouble* dpiX, wxDouble* dpiY)
|
||||||
|
Reference in New Issue
Block a user