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:
Artur Wieczorek 2016-08-12 21:23:02 +02:00
parent 7d9d5934b5
commit d9193d5368
2 changed files with 93 additions and 14 deletions

View File

@ -954,8 +954,8 @@ public:
virtual void EndLayer() = 0;
/**
Push the current state of the context (eg. transformation matrix) on a
stack.
Push the current state (like transformations, clipping region and quality
settings) of the context on a stack.
Multiple balanced calls to PushState() and PopState() can be nested.
@see PopState()

View File

@ -3424,6 +3424,17 @@ private:
D2D1_LAYER_PARAMETERS params;
wxCOMPtr<ID2D1Layer> layer;
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:
@ -3431,10 +3442,7 @@ private:
wxSharedPtr<wxD2DRenderTargetResourceHolder> m_renderTargetHolder;
// 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.
wxStack<wxCOMPtr<ID2D1DrawingStateBlock> > m_stateStack;
wxStack<StateData> m_stateStack;
wxStack<LayerData> m_layers;
@ -3545,6 +3553,8 @@ void wxD2DContext::Clip(wxDouble x, wxDouble y, wxDouble w, wxDouble h)
void wxD2DContext::SetClipLayer(ID2D1Geometry* clipGeometry)
{
EnsureInitialized();
wxCOMPtr<ID2D1Layer> clipLayer;
HRESULT hr = GetRenderTarget()->CreateLayer(&clipLayer);
wxCHECK_HRESULT_RET(hr);
@ -3555,6 +3565,9 @@ void wxD2DContext::SetClipLayer(ID2D1Geometry* clipGeometry)
wxD2DConvertAntialiasMode(m_antialias));
ld.layer = clipLayer;
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);
// Store layer parameters.
@ -3587,15 +3600,22 @@ void wxD2DContext::ResetClip()
wxCHECK_HRESULT_RET(hr);
// Re-apply all remaining non-clipping layers.
// First, save current transformation matrix.
D2D1_MATRIX_3X2_F currTransform;
GetRenderTarget()->GetTransform(&currTransform);
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 current transformation matrix.
GetRenderTarget()->SetTransform(&currTransform);
}
void* wxD2DContext::GetNativeContext()
@ -3684,6 +3704,8 @@ bool wxD2DContext::SetCompositionMode(wxCompositionMode compositionMode)
void wxD2DContext::BeginLayer(wxDouble opacity)
{
EnsureInitialized();
wxCOMPtr<ID2D1Layer> layer;
HRESULT hr = GetRenderTarget()->CreateLayer(&layer);
wxCHECK_HRESULT_RET(hr);
@ -3695,6 +3717,9 @@ void wxD2DContext::BeginLayer(wxDouble opacity)
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
D2D1::IdentityMatrix(), opacity);
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);
@ -3733,6 +3758,9 @@ void wxD2DContext::EndLayer()
}
// Re-apply all stored clipping layers.
// First, save current transformation matrix.
D2D1_MATRIX_3X2_F currTransform;
GetRenderTarget()->GetTransform(&currTransform);
while ( !layersToRestore.empty() )
{
LayerData ld = layersToRestore.top();
@ -3740,6 +3768,8 @@ void wxD2DContext::EndLayer()
if ( ld.type == CLIP_LAYER )
{
// Restore layer at original position.
GetRenderTarget()->SetTransform(&ld.transformMatrix);
GetRenderTarget()->PushLayer(ld.params, ld.layer);
}
else
@ -3749,6 +3779,8 @@ void wxD2DContext::EndLayer()
// Store layer parameters.
m_layers.push(ld);
}
// Restore current transformation matrix.
GetRenderTarget()->SetTransform(&currTransform);
}
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()
{
ID2D1Factory* wxGetD2DFactory(wxGraphicsRenderer* renderer);
EnsureInitialized();
wxCOMPtr<ID2D1DrawingStateBlock> drawStateBlock;
wxGetD2DFactory(GetRenderer())->CreateDrawingStateBlock(&drawStateBlock);
GetRenderTarget()->SaveDrawingState(drawStateBlock);
StateData state;
m_direct2dFactory->CreateDrawingStateBlock(&state.drawingState);
GetRenderTarget()->SaveDrawingState(state.drawingState);
state.layers = m_layers;
m_stateStack.push(drawStateBlock);
m_stateStack.push(state);
}
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();
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(
@ -4090,16 +4162,23 @@ void wxD2DContext::Flush()
}
// Re-apply all layers.
// First, save current transformation matrix.
D2D1_MATRIX_3X2_F currTransform;
GetRenderTarget()->GetTransform(&currTransform);
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 current transformation matrix.
GetRenderTarget()->SetTransform(&currTransform);
}
void wxD2DContext::GetDPI(wxDouble* dpiX, wxDouble* dpiY)