Fix saving/restoring drawing state for wxGraphicsContext with Direct2D
Native ID2D1RenderTarget::SaveDrawingState method stores in ID2D1DrawingStateBlock only transform, antialiasing mode, text-rendering options and tags but not currently set ID2D1Layers (which are used in wxD2DContext for clipping purposes). Therefore current stack of layers has to be stored "manually" alongside ID2D1DrawingStateBlock in a dedicated data structure (LayerData) in PushState() and restored also "manually" in PopState(). Closes #17626.
This commit is contained in:
@@ -954,8 +954,8 @@ public:
|
|||||||
virtual void EndLayer() = 0;
|
virtual void EndLayer() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Push the current state of the context (eg. transformation matrix) on a
|
Push the current state (like transformations, clipping region and quality
|
||||||
stack.
|
settings) of the context on a stack.
|
||||||
Multiple balanced calls to PushState() and PopState() can be nested.
|
Multiple balanced calls to PushState() and PopState() can be nested.
|
||||||
|
|
||||||
@see PopState()
|
@see PopState()
|
||||||
|
@@ -3424,6 +3424,17 @@ private:
|
|||||||
D2D1_LAYER_PARAMETERS params;
|
D2D1_LAYER_PARAMETERS params;
|
||||||
wxCOMPtr<ID2D1Layer> layer;
|
wxCOMPtr<ID2D1Layer> layer;
|
||||||
wxCOMPtr<ID2D1Geometry> geometry;
|
wxCOMPtr<ID2D1Geometry> geometry;
|
||||||
|
D2D1_MATRIX_3X2_F transformMatrix;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StateData
|
||||||
|
{
|
||||||
|
// A ID2D1DrawingStateBlock represents the drawing state of a render target:
|
||||||
|
// the anti aliasing mode, transform, tags, and text-rendering options.
|
||||||
|
// The context owns these pointers and is responsible for releasing them.
|
||||||
|
wxCOMPtr<ID2D1DrawingStateBlock> drawingState;
|
||||||
|
// We need to store also current layers.
|
||||||
|
wxStack<LayerData> layers;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -3431,10 +3442,7 @@ private:
|
|||||||
|
|
||||||
wxSharedPtr<wxD2DRenderTargetResourceHolder> m_renderTargetHolder;
|
wxSharedPtr<wxD2DRenderTargetResourceHolder> m_renderTargetHolder;
|
||||||
|
|
||||||
// A ID2D1DrawingStateBlock represents the drawing state of a render target:
|
wxStack<StateData> m_stateStack;
|
||||||
// the anti aliasing mode, transform, tags, and text-rendering options.
|
|
||||||
// The context owns these pointers and is responsible for releasing them.
|
|
||||||
wxStack<wxCOMPtr<ID2D1DrawingStateBlock> > m_stateStack;
|
|
||||||
|
|
||||||
wxStack<LayerData> m_layers;
|
wxStack<LayerData> m_layers;
|
||||||
|
|
||||||
@@ -3545,6 +3553,8 @@ void wxD2DContext::Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
|
|||||||
|
|
||||||
void wxD2DContext::SetClipLayer(ID2D1Geometry* clipGeometry)
|
void wxD2DContext::SetClipLayer(ID2D1Geometry* clipGeometry)
|
||||||
{
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
wxCOMPtr<ID2D1Layer> clipLayer;
|
wxCOMPtr<ID2D1Layer> clipLayer;
|
||||||
HRESULT hr = GetRenderTarget()->CreateLayer(&clipLayer);
|
HRESULT hr = GetRenderTarget()->CreateLayer(&clipLayer);
|
||||||
wxCHECK_HRESULT_RET(hr);
|
wxCHECK_HRESULT_RET(hr);
|
||||||
@@ -3555,6 +3565,9 @@ void wxD2DContext::SetClipLayer(ID2D1Geometry* clipGeometry)
|
|||||||
wxD2DConvertAntialiasMode(m_antialias));
|
wxD2DConvertAntialiasMode(m_antialias));
|
||||||
ld.layer = clipLayer;
|
ld.layer = clipLayer;
|
||||||
ld.geometry = clipGeometry;
|
ld.geometry = clipGeometry;
|
||||||
|
// We need to store CTM to be able to re-apply
|
||||||
|
// the layer at the original position later on.
|
||||||
|
GetRenderTarget()->GetTransform(&ld.transformMatrix);
|
||||||
|
|
||||||
GetRenderTarget()->PushLayer(ld.params, clipLayer);
|
GetRenderTarget()->PushLayer(ld.params, clipLayer);
|
||||||
// Store layer parameters.
|
// Store layer parameters.
|
||||||
@@ -3587,15 +3600,22 @@ void wxD2DContext::ResetClip()
|
|||||||
wxCHECK_HRESULT_RET(hr);
|
wxCHECK_HRESULT_RET(hr);
|
||||||
|
|
||||||
// Re-apply all remaining non-clipping layers.
|
// Re-apply all remaining non-clipping layers.
|
||||||
|
// First, save current transformation matrix.
|
||||||
|
D2D1_MATRIX_3X2_F currTransform;
|
||||||
|
GetRenderTarget()->GetTransform(&currTransform);
|
||||||
while ( !layersToRestore.empty() )
|
while ( !layersToRestore.empty() )
|
||||||
{
|
{
|
||||||
LayerData ld = layersToRestore.top();
|
LayerData ld = layersToRestore.top();
|
||||||
layersToRestore.pop();
|
layersToRestore.pop();
|
||||||
|
|
||||||
|
// Restore layer at original position.
|
||||||
|
GetRenderTarget()->SetTransform(&ld.transformMatrix);
|
||||||
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
||||||
// Store layer parameters.
|
// Store layer parameters.
|
||||||
m_layers.push(ld);
|
m_layers.push(ld);
|
||||||
}
|
}
|
||||||
|
// Restore current transformation matrix.
|
||||||
|
GetRenderTarget()->SetTransform(&currTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* wxD2DContext::GetNativeContext()
|
void* wxD2DContext::GetNativeContext()
|
||||||
@@ -3684,6 +3704,8 @@ bool wxD2DContext::SetCompositionMode(wxCompositionMode compositionMode)
|
|||||||
|
|
||||||
void wxD2DContext::BeginLayer(wxDouble opacity)
|
void wxD2DContext::BeginLayer(wxDouble opacity)
|
||||||
{
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
|
||||||
wxCOMPtr<ID2D1Layer> layer;
|
wxCOMPtr<ID2D1Layer> layer;
|
||||||
HRESULT hr = GetRenderTarget()->CreateLayer(&layer);
|
HRESULT hr = GetRenderTarget()->CreateLayer(&layer);
|
||||||
wxCHECK_HRESULT_RET(hr);
|
wxCHECK_HRESULT_RET(hr);
|
||||||
@@ -3695,6 +3717,9 @@ void wxD2DContext::BeginLayer(wxDouble opacity)
|
|||||||
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
|
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
|
||||||
D2D1::IdentityMatrix(), opacity);
|
D2D1::IdentityMatrix(), opacity);
|
||||||
ld.layer = layer;
|
ld.layer = layer;
|
||||||
|
// We need to store CTM to be able to re-apply
|
||||||
|
// the layer at the original position later on.
|
||||||
|
GetRenderTarget()->GetTransform(&ld.transformMatrix);
|
||||||
|
|
||||||
GetRenderTarget()->PushLayer(ld.params, layer);
|
GetRenderTarget()->PushLayer(ld.params, layer);
|
||||||
|
|
||||||
@@ -3733,6 +3758,9 @@ void wxD2DContext::EndLayer()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Re-apply all stored clipping layers.
|
// Re-apply all stored clipping layers.
|
||||||
|
// First, save current transformation matrix.
|
||||||
|
D2D1_MATRIX_3X2_F currTransform;
|
||||||
|
GetRenderTarget()->GetTransform(&currTransform);
|
||||||
while ( !layersToRestore.empty() )
|
while ( !layersToRestore.empty() )
|
||||||
{
|
{
|
||||||
LayerData ld = layersToRestore.top();
|
LayerData ld = layersToRestore.top();
|
||||||
@@ -3740,6 +3768,8 @@ void wxD2DContext::EndLayer()
|
|||||||
|
|
||||||
if ( ld.type == CLIP_LAYER )
|
if ( ld.type == CLIP_LAYER )
|
||||||
{
|
{
|
||||||
|
// Restore layer at original position.
|
||||||
|
GetRenderTarget()->SetTransform(&ld.transformMatrix);
|
||||||
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -3749,6 +3779,8 @@ void wxD2DContext::EndLayer()
|
|||||||
// Store layer parameters.
|
// Store layer parameters.
|
||||||
m_layers.push(ld);
|
m_layers.push(ld);
|
||||||
}
|
}
|
||||||
|
// Restore current transformation matrix.
|
||||||
|
GetRenderTarget()->SetTransform(&currTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxD2DContext::Translate(wxDouble dx, wxDouble dy)
|
void wxD2DContext::Translate(wxDouble dx, wxDouble dy)
|
||||||
@@ -3855,23 +3887,63 @@ void wxD2DContext::DrawIcon(const wxIcon& icon, wxDouble x, wxDouble y, wxDouble
|
|||||||
|
|
||||||
void wxD2DContext::PushState()
|
void wxD2DContext::PushState()
|
||||||
{
|
{
|
||||||
ID2D1Factory* wxGetD2DFactory(wxGraphicsRenderer* renderer);
|
EnsureInitialized();
|
||||||
|
|
||||||
wxCOMPtr<ID2D1DrawingStateBlock> drawStateBlock;
|
StateData state;
|
||||||
wxGetD2DFactory(GetRenderer())->CreateDrawingStateBlock(&drawStateBlock);
|
m_direct2dFactory->CreateDrawingStateBlock(&state.drawingState);
|
||||||
GetRenderTarget()->SaveDrawingState(drawStateBlock);
|
GetRenderTarget()->SaveDrawingState(state.drawingState);
|
||||||
|
state.layers = m_layers;
|
||||||
|
|
||||||
m_stateStack.push(drawStateBlock);
|
m_stateStack.push(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxD2DContext::PopState()
|
void wxD2DContext::PopState()
|
||||||
{
|
{
|
||||||
wxCHECK_RET(!m_stateStack.empty(), wxT("No state to pop"));
|
wxCHECK_RET(!m_stateStack.empty(), wxS("No state to pop"));
|
||||||
|
|
||||||
wxCOMPtr<ID2D1DrawingStateBlock> drawStateBlock = m_stateStack.top();
|
// Remove all layers from the stack of layers.
|
||||||
|
while ( !m_layers.empty() )
|
||||||
|
{
|
||||||
|
LayerData ld = m_layers.top();
|
||||||
|
m_layers.pop();
|
||||||
|
|
||||||
|
GetRenderTarget()->PopLayer();
|
||||||
|
ld.layer.reset();
|
||||||
|
ld.geometry.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve state data.
|
||||||
|
StateData state;
|
||||||
|
state = m_stateStack.top();
|
||||||
m_stateStack.pop();
|
m_stateStack.pop();
|
||||||
|
|
||||||
GetRenderTarget()->RestoreDrawingState(drawStateBlock);
|
// Restore all saved layers.
|
||||||
|
wxStack<LayerData> layersToRestore;
|
||||||
|
// We have to restore layers on the stack from "bottom" to "top",
|
||||||
|
// so we have to create a "reverted" stack.
|
||||||
|
while ( !state.layers.empty() )
|
||||||
|
{
|
||||||
|
LayerData ld = state.layers.top();
|
||||||
|
state.layers.pop();
|
||||||
|
|
||||||
|
layersToRestore.push(ld);
|
||||||
|
}
|
||||||
|
// And next set layers from the top of "reverted" stack.
|
||||||
|
while ( !layersToRestore.empty() )
|
||||||
|
{
|
||||||
|
LayerData ld = layersToRestore.top();
|
||||||
|
layersToRestore.pop();
|
||||||
|
|
||||||
|
// Restore layer at original position.
|
||||||
|
GetRenderTarget()->SetTransform(&ld.transformMatrix);
|
||||||
|
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
||||||
|
|
||||||
|
// Store layer parameters.
|
||||||
|
m_layers.push(ld);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore drawing state.
|
||||||
|
GetRenderTarget()->RestoreDrawingState(state.drawingState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxD2DContext::GetTextExtent(
|
void wxD2DContext::GetTextExtent(
|
||||||
@@ -4090,16 +4162,23 @@ void wxD2DContext::Flush()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Re-apply all layers.
|
// Re-apply all layers.
|
||||||
|
// First, save current transformation matrix.
|
||||||
|
D2D1_MATRIX_3X2_F currTransform;
|
||||||
|
GetRenderTarget()->GetTransform(&currTransform);
|
||||||
while ( !layersToRestore.empty() )
|
while ( !layersToRestore.empty() )
|
||||||
{
|
{
|
||||||
LayerData ld = layersToRestore.top();
|
LayerData ld = layersToRestore.top();
|
||||||
layersToRestore.pop();
|
layersToRestore.pop();
|
||||||
|
|
||||||
|
// Restore layer at original position.
|
||||||
|
GetRenderTarget()->SetTransform(&ld.transformMatrix);
|
||||||
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
GetRenderTarget()->PushLayer(ld.params, ld.layer);
|
||||||
|
|
||||||
// Store layer parameters.
|
// Store layer parameters.
|
||||||
m_layers.push(ld);
|
m_layers.push(ld);
|
||||||
}
|
}
|
||||||
|
// Restore current transformation matrix.
|
||||||
|
GetRenderTarget()->SetTransform(&currTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxD2DContext::GetDPI(wxDouble* dpiX, wxDouble* dpiY)
|
void wxD2DContext::GetDPI(wxDouble* dpiX, wxDouble* dpiY)
|
||||||
|
Reference in New Issue
Block a user