Modified wxGraphicsPath concatenation (with AddPath) for Direct2D renderer.

Because wxGraphicsPath comprises current ID2D1PathGeometry object and the collection of transformed (sub-)geometries (ID2D1TransformedGeometry objects) so to concatenate two paths we need to concatenate their current geometries as well as to combine the collections of transformed geometries.
This commit is contained in:
Artur Wieczorek
2016-05-26 17:54:45 +02:00
parent fa01392788
commit 2b13dc7473
2 changed files with 77 additions and 42 deletions

View File

@@ -98,6 +98,8 @@ public:
/** /**
Adds another path onto the current path. After this call the current Adds another path onto the current path. After this call the current
point will be at the added path's current point. point will be at the added path's current point.
For Direct2D the path being appended shouldn't contain
a started non-empty subpath when this function is called.
*/ */
virtual void AddPath(const wxGraphicsPath& path); virtual void AddPath(const wxGraphicsPath& path);

View File

@@ -1059,6 +1059,7 @@ private:
ID2D1Geometry* GetFullGeometry() const; ID2D1Geometry* GetFullGeometry() const;
bool IsEmpty() const;
bool IsStateSafeForFlush() const; bool IsStateSafeForFlush() const;
struct GeometryStateData struct GeometryStateData
@@ -1284,6 +1285,12 @@ ID2D1Geometry* wxD2DPathData::GetFullGeometry() const
return m_combinedGeometry; return m_combinedGeometry;
} }
bool wxD2DPathData::IsEmpty() const
{
return !m_currentPointSet && !m_figureOpened &&
m_pTransformedGeometries.size() == 0;
}
bool wxD2DPathData::IsStateSafeForFlush() const bool wxD2DPathData::IsStateSafeForFlush() const
{ {
// Only geometry with not yet started figure // Only geometry with not yet started figure
@@ -1493,61 +1500,87 @@ void wxD2DPathData::GetCurrentPoint(wxDouble* x, wxDouble* y) const
// adds another path // adds another path
void wxD2DPathData::AddPath(const wxGraphicsPathData* path) void wxD2DPathData::AddPath(const wxGraphicsPathData* path)
{ {
wxD2DPathData* d2dPath = wxD2DPathData* pathSrc =
const_cast<wxD2DPathData*>(static_cast<const wxD2DPathData*>(path)); const_cast<wxD2DPathData*>(static_cast<const wxD2DPathData*>(path));
// Nothing to do if geometry of appended path is not initialized. // Nothing to do if geometry of appended path is not initialized.
if ( d2dPath->m_pathGeometry == NULL || d2dPath->m_geometrySink == NULL ) if ( pathSrc->m_pathGeometry == NULL || pathSrc->m_geometrySink == NULL )
return; return;
// Close current sub-path (leaving the figure as is). // Because only closed geometry (with closed sink)
EndFigure(D2D1_FIGURE_END_OPEN);
// Because only geometry with closed sink (immutable)
// can be transferred to another geometry object with // can be transferred to another geometry object with
// ID2D1PathGeometry::Stream() so we have to make // ID2D1PathGeometry::Stream() so we have to close it
// a writable copy of the appended geometry // before any operation and to re-open afterwards.
// and re-assign it to the source path after actual appending. // Unfortunately, to close the sink it is also necessary
// to end the figure it contains, if it was open.
// After re-opening the geometry we should also re-start
// the figure (if it was open) and restore its state but
// it seems there is no straightforward way to do so
// if the figure is not empty.
//
// So, only if appended path has a sub-path closed or
// has an empty sub-path open there is possible to restore
// its state after appending it to the current path and only
// in this case the operation doesn't introduce side effects.
// Nothing to do if appended path is empty.
if ( pathSrc->IsEmpty() )
return;
// Save positional and auxiliary data
// of the appended path and its geometry.
GeometryStateData curStateSrc;
pathSrc->SaveGeometryState(curStateSrc);
// Raise warning if appended path has an open non-empty sub-path.
wxASSERT_MSG( pathSrc->IsStateSafeForFlush(),
wxS("Sub-path in appended path should be closed prior to this operation") );
// Close appended geometry.
pathSrc->Flush();
// Close current geometry (leaving the figure as is).
Flush();
// Close appended geometry sink.
d2dPath->EndFigure(D2D1_FIGURE_END_OPEN);
HRESULT hr; HRESULT hr;
hr = d2dPath->m_geometrySink->Close(); ID2D1TransformedGeometry* pTransformedGeometry = NULL;
wxFAILED_HRESULT_MSG(hr); // Add current geometry to the collection transformed geometries.
hr = m_direct2dfactory->CreateTransformedGeometry(m_pathGeometry,
D2D1::Matrix3x2F::Identity(), &pTransformedGeometry);
wxCHECK_HRESULT_RET(hr);
m_pTransformedGeometries.push_back(pTransformedGeometry);
// Transfer appended geometry to the current geometry sink. // Add to the collection transformed geometries from the appended path.
hr = d2dPath->m_pathGeometry->Stream(m_geometrySink); for ( size_t i = 0; i < pathSrc->m_pTransformedGeometries.size(); i++ )
wxFAILED_HRESULT_MSG(hr);
// Copy auxiliary data if appended path is non-empty.
UINT32 segCount = 0;
UINT32 figCount = 0;
hr = d2dPath->m_pathGeometry->GetSegmentCount(&segCount);
wxFAILED_HRESULT_MSG(hr);
hr = d2dPath->m_pathGeometry->GetFigureCount(&figCount);
wxFAILED_HRESULT_MSG(hr);
if( segCount > 0 || figCount > 0 || d2dPath->m_currentPointSet || d2dPath->m_figureOpened )
{ {
m_currentPointSet = d2dPath->m_currentPointSet; pTransformedGeometry = NULL;
m_currentPoint = d2dPath->m_currentPoint; hr = m_direct2dfactory->CreateTransformedGeometry(
m_figureOpened = d2dPath->m_figureOpened; pathSrc->m_pTransformedGeometries[i],
m_figureStart = d2dPath->m_figureStart; D2D1::Matrix3x2F::Identity(), &pTransformedGeometry);
wxCHECK_HRESULT_RET(hr);
m_pTransformedGeometries.push_back(pTransformedGeometry);
} }
// Make a writable copy of the appended geometry. // Clear and reopen current geometry.
wxCOMPtr<ID2D1PathGeometry> srcPathGeometry; m_pathGeometry.reset();
wxCOMPtr<ID2D1GeometrySink> srcGeometrySink; EnsureGeometryOpen();
hr = m_direct2dfactory->CreatePathGeometry(&srcPathGeometry);
wxFAILED_HRESULT_MSG(hr);
hr = srcPathGeometry->Open(&srcGeometrySink);
wxFAILED_HRESULT_MSG(hr);
// Transfer appended geometry.
hr = d2dPath->m_pathGeometry->Stream(srcGeometrySink);
wxFAILED_HRESULT_MSG(hr);
// Assign a writable copy of the appendeed // Transfer appended geometry to the current geometry sink.
// geometry back to the source path. hr = pathSrc->m_pathGeometry->Stream(m_geometrySink);
d2dPath->m_pathGeometry = srcPathGeometry; wxCHECK_HRESULT_RET(hr);
d2dPath->m_geometrySink = srcGeometrySink;
// Apply to the current path positional data from the appended path.
// This operation fully sets geometry to the required state
// only if it represents geometry without started figure
// or with started but empty figure.
RestoreGeometryState(curStateSrc);
// Reopen appended geometry.
pathSrc->EnsureGeometryOpen();
// Restore its positional data.
// This operation fully restores geometry to the required state
// only if it represents geometry without started figure
// or with started but empty figure.
pathSrc->RestoreGeometryState(curStateSrc);
} }
// closes the current sub-path // closes the current sub-path