static ComPtr<ID2D1PathGeometry1> vectorPathToID2D1PathGeometry(const QVectorPath &path, bool alias) { ComPtr<ID2D1PathGeometry1> pathGeometry; HRESULT hr = factory()->CreatePathGeometry(pathGeometry.GetAddressOf()); if (FAILED(hr)) { qWarning("%s: Could not create path geometry: %#x", __FUNCTION__, hr); return NULL; } if (path.isEmpty()) return pathGeometry; ComPtr<ID2D1GeometrySink> sink; hr = pathGeometry->Open(sink.GetAddressOf()); if (FAILED(hr)) { qWarning("%s: Could not create geometry sink: %#x", __FUNCTION__, hr); return NULL; } sink->SetFillMode(path.hasWindingFill() ? D2D1_FILL_MODE_WINDING : D2D1_FILL_MODE_ALTERNATE); bool inFigure = false; const QPainterPath::ElementType *types = path.elements(); const int count = path.elementCount(); const qreal *points = 0; QScopedArrayPointer<qreal> rounded_points; if (alias) { // Aliased painting, round to whole numbers rounded_points.reset(new qreal[count * 2]); points = rounded_points.data(); for (int i = 0; i < (count * 2); i++) rounded_points[i] = qRound(path.points()[i]); } else { // Antialiased painting, keep original numbers points = path.points(); } Q_ASSERT(points); if (types) { qreal x, y; for (int i = 0; i < count; i++) { x = points[i * 2]; y = points[i * 2 + 1]; switch (types[i]) { case QPainterPath::MoveToElement: if (inFigure) sink->EndFigure(D2D1_FIGURE_END_OPEN); sink->BeginFigure(D2D1::Point2F(x, y), D2D1_FIGURE_BEGIN_FILLED); inFigure = true; break; case QPainterPath::LineToElement: sink->AddLine(D2D1::Point2F(x, y)); break; case QPainterPath::CurveToElement: { Q_ASSERT((i + 2) < count); Q_ASSERT(types[i+1] == QPainterPath::CurveToDataElement); Q_ASSERT(types[i+2] == QPainterPath::CurveToDataElement); i++; const qreal x2 = points[i * 2]; const qreal y2 = points[i * 2 + 1]; i++; const qreal x3 = points[i * 2]; const qreal y3 = points[i * 2 + 1]; D2D1_BEZIER_SEGMENT segment = { D2D1::Point2F(x, y), D2D1::Point2F(x2, y2), D2D1::Point2F(x3, y3) }; sink->AddBezier(segment); } break; case QPainterPath::CurveToDataElement: qWarning("%s: Unhandled Curve Data Element", __FUNCTION__); break; } } } else { sink->BeginFigure(D2D1::Point2F(points[0], points[1]), D2D1_FIGURE_BEGIN_FILLED); inFigure = true; for (int i = 1; i < count; i++) sink->AddLine(D2D1::Point2F(points[i * 2], points[i * 2 + 1])); } if (inFigure) { if (path.hasImplicitClose()) sink->AddLine(D2D1::Point2F(points[0], points[1])); sink->EndFigure(D2D1_FIGURE_END_OPEN); } sink->Close(); return pathGeometry; }
static ComPtr<ID2D1PathGeometry1> painterPathToPathGeometry(const QPainterPath &path) { ComPtr<ID2D1PathGeometry1> geometry; ComPtr<ID2D1GeometrySink> sink; HRESULT hr = factory()->CreatePathGeometry(&geometry); if (FAILED(hr)) { qWarning("%s: Could not create path geometry: %#x", __FUNCTION__, hr); return NULL; } hr = geometry->Open(&sink); if (FAILED(hr)) { qWarning("%s: Could not create geometry sink: %#x", __FUNCTION__, hr); return NULL; } switch (path.fillRule()) { case Qt::WindingFill: sink->SetFillMode(D2D1_FILL_MODE_WINDING); break; case Qt::OddEvenFill: sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE); break; } bool inFigure = false; for (int i = 0; i < path.elementCount(); i++) { const QPainterPath::Element element = path.elementAt(i); switch (element.type) { case QPainterPath::MoveToElement: if (inFigure) sink->EndFigure(D2D1_FIGURE_END_OPEN); sink->BeginFigure(to_d2d_point_2f(element), D2D1_FIGURE_BEGIN_FILLED); inFigure = true; break; case QPainterPath::LineToElement: sink->AddLine(to_d2d_point_2f(element)); break; case QPainterPath::CurveToElement: { const QPainterPath::Element data1 = path.elementAt(++i); const QPainterPath::Element data2 = path.elementAt(++i); Q_ASSERT(i < path.elementCount()); Q_ASSERT(data1.type == QPainterPath::CurveToDataElement); Q_ASSERT(data2.type == QPainterPath::CurveToDataElement); D2D1_BEZIER_SEGMENT segment; segment.point1 = to_d2d_point_2f(element); segment.point2 = to_d2d_point_2f(data1); segment.point3 = to_d2d_point_2f(data2); sink->AddBezier(segment); } break; case QPainterPath::CurveToDataElement: qWarning("%s: Unhandled Curve Data Element", __FUNCTION__); break; } } if (inFigure) sink->EndFigure(D2D1_FIGURE_END_OPEN); sink->Close(); return geometry; }
void JigsawPuzzleRenderer::CreatePuzzlePieceGeometry() { // Create the geometry outline for the jigsaw puzzle piece. ComPtr<ID2D1PathGeometry> path; ComPtr<ID2D1GeometrySink> sink; DX::ThrowIfFailed(m_deviceResources->GetD2DFactory()->CreatePathGeometry(&path)); DX::ThrowIfFailed(path->Open(&sink)); // The following math calculates the information needed to determine where the arcs for the puzzle piece connector begin and end. // The inner radius is the radius of the arc that defines the stem of the connector. The outer radius is the radius of the arc // that defines the connector. float edgeLength = Constants::PuzzlePieceSize; float innerRadius = Constants::PuzzlePieceConnectorInnerRadius; float outerRadius = Constants::PuzzlePieceConnectorOuterRadius; float stemPosition = (edgeLength / 2.0f) - (innerRadius * 2.0f); float connectorParallelOffset = (2.0f * pow(innerRadius, 2.0f)) / (innerRadius + outerRadius); float connectorPerpendicularOffset = sqrt(pow(innerRadius, 2.0f) - pow(connectorParallelOffset, 2.0f)) + innerRadius; sink->BeginFigure(D2D1::Point2F(0, 0), D2D1_FIGURE_BEGIN_FILLED); sink->AddLine(D2D1::Point2F(edgeLength, 0)); sink->AddLine(D2D1::Point2F(edgeLength, stemPosition)); sink->AddArc( D2D1::ArcSegment( D2D1::Point2F(edgeLength - connectorPerpendicularOffset, stemPosition + connectorParallelOffset), D2D1::SizeF(innerRadius, innerRadius), 0.0f, D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_ARC_SIZE_SMALL ) ); sink->AddArc( D2D1::ArcSegment( D2D1::Point2F(edgeLength - connectorPerpendicularOffset, edgeLength - stemPosition - connectorParallelOffset), D2D1::SizeF(outerRadius, outerRadius), 0.0f, D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, D2D1_ARC_SIZE_LARGE ) ); sink->AddArc( D2D1::ArcSegment( D2D1::Point2F(edgeLength, edgeLength - stemPosition), D2D1::SizeF(innerRadius, innerRadius), 0.0f, D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_ARC_SIZE_SMALL ) ); sink->AddLine(D2D1::Point2F(edgeLength, edgeLength)); sink->AddLine(D2D1::Point2F(edgeLength - stemPosition, edgeLength)); sink->AddArc( D2D1::ArcSegment( D2D1::Point2F(edgeLength - stemPosition - connectorParallelOffset, edgeLength + connectorPerpendicularOffset), D2D1::SizeF(innerRadius, innerRadius), 0.0f, D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, D2D1_ARC_SIZE_SMALL ) ); sink->AddArc( D2D1::ArcSegment( D2D1::Point2F(stemPosition + connectorParallelOffset, edgeLength + connectorPerpendicularOffset), D2D1::SizeF(outerRadius, outerRadius), 0.0f, D2D1_SWEEP_DIRECTION_CLOCKWISE, D2D1_ARC_SIZE_LARGE ) ); sink->AddArc( D2D1::ArcSegment( D2D1::Point2F(stemPosition, edgeLength), D2D1::SizeF(innerRadius, innerRadius), 0.0f, D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, D2D1_ARC_SIZE_SMALL ) ); sink->AddLine(D2D1::Point2F(0, edgeLength)); sink->EndFigure(D2D1_FIGURE_END_CLOSED); DX::ThrowIfFailed(sink->Close()); m_geometry = path; }