// Accelerated paths void QBlitterPaintEngine::fill(const QVectorPath &path, const QBrush &brush) { Q_D(QBlitterPaintEngine); if (path.shape() == QVectorPath::RectangleHint) { QRectF rect(((QPointF *) path.points())[0], ((QPointF *) path.points())[2]); fillRect(rect, brush); } else { d->lock(); d->pmData->markRasterOverlay(path); QRasterPaintEngine::fill(path, brush); } }
void QGL2PEXVertexArray::addCentroid(const QVectorPath &path, int subPathIndex) { const QPointF *const points = reinterpret_cast<const QPointF *>(path.points()); const QPainterPath::ElementType *const elements = path.elements(); QPointF sum = points[subPathIndex]; int count = 1; for (int i = subPathIndex + 1; i < path.elementCount() && (!elements || elements[i] != QPainterPath::MoveToElement); ++i) { sum += points[i]; ++count; } const QPointF centroid = sum / qreal(count); vertexArray.add(centroid); }
Q_GUI_EXPORT QPainterPath qt_painterPathFromVectorPath(const QVectorPath &path) { const qreal *points = path.points(); const QPainterPath::ElementType *types = path.elements(); QPainterPath p; if (types) { int id = 0; for (int i=0; i<path.elementCount(); ++i) { switch(types[i]) { case QPainterPath::MoveToElement: p.moveTo(QPointF(points[id], points[id+1])); id+=2; break; case QPainterPath::LineToElement: p.lineTo(QPointF(points[id], points[id+1])); id+=2; break; case QPainterPath::CurveToElement: { QPointF p1(points[id], points[id+1]); QPointF p2(points[id+2], points[id+3]); QPointF p3(points[id+4], points[id+5]); p.cubicTo(p1, p2, p3); id+=6; break; } case QPainterPath::CurveToDataElement: ; break; } } } else { p.moveTo(QPointF(points[0], points[1])); int id = 2; for (int i=1; i<path.elementCount(); ++i) { p.lineTo(QPointF(points[id], points[id+1])); id+=2; } } if (path.hints() & QVectorPath::WindingFill) p.setFillRule(Qt::WindingFill); return p; }
void QEmulationPaintEngine::stroke(const QVectorPath &path, const QPen &pen) { QPainterState *s = state(); if (s->bgMode == Qt::OpaqueMode && pen.style() > Qt::SolidLine) { QPen bgPen = pen; bgPen.setBrush(s->bgBrush); bgPen.setStyle(Qt::SolidLine); real_engine->stroke(path, bgPen); } QBrush brush = pen.brush(); QPen copy = pen; Qt::BrushStyle style = qbrush_style(brush); if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) { const QGradient *g = brush.gradient(); if (g->coordinateMode() > QGradient::LogicalMode) { if (g->coordinateMode() == QGradient::StretchToDeviceMode) { QTransform mat = brush.transform(); mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height()); brush.setTransform(mat); copy.setBrush(brush); real_engine->stroke(path, copy); return; } else if (g->coordinateMode() == QGradient::ObjectBoundingMode) { QTransform mat = brush.transform(); QRectF r = path.controlPointRect(); mat.translate(r.x(), r.y()); mat.scale(r.width(), r.height()); brush.setTransform(mat); copy.setBrush(brush); real_engine->stroke(path, copy); return; } } } real_engine->stroke(path, pen); }
void QEmulationPaintEngine::fill(const QVectorPath &path, const QBrush &brush) { QPainterState *s = state(); if (s->bgMode == Qt::OpaqueMode) { Qt::BrushStyle style = brush.style(); if (style >= Qt::Dense1Pattern && style <= Qt::DiagCrossPattern) real_engine->fill(path, s->bgBrush); } Qt::BrushStyle style = qbrush_style(brush); if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) { const QGradient *g = brush.gradient(); if (g->coordinateMode() > QGradient::LogicalMode) { if (g->coordinateMode() == QGradient::StretchToDeviceMode) { QBrush copy = brush; QTransform mat = copy.transform(); mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height()); copy.setTransform(mat); real_engine->fill(path, copy); return; } else if (g->coordinateMode() == QGradient::ObjectBoundingMode) { QBrush copy = brush; QTransform mat = copy.transform(); QRectF r = path.controlPointRect(); mat.translate(r.x(), r.y()); mat.scale(r.width(), r.height()); copy.setTransform(mat); real_engine->fill(path, copy); return; } } } real_engine->fill(path, brush); }
void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, const QRectF &) { const qreal *pts = path.points(); const QPainterPath::ElementType *types = path.elements(); int count = path.elementCount(); if (count < 2) return; float realWidth = qpen_widthf(pen); if (realWidth == 0) realWidth = 1; m_width = realWidth / 2; bool cosmetic = pen.isCosmetic(); if (cosmetic) { m_width = m_width * m_inv_scale; } m_join_style = qpen_joinStyle(pen); m_cap_style = qpen_capStyle(pen); m_vertices.reset(); m_miter_limit = pen.miterLimit() * qpen_widthf(pen); // The curvyness is based on the notion that I originally wanted // roughly one line segment pr 4 pixels. This may seem little, but // because we sample at constantly incrementing B(t) E [0<t<1], we // will get longer segments where the curvature is small and smaller // segments when the curvature is high. // // To get a rough idea of the length of each curve, I pretend that // the curve is a 90 degree arc, whose radius is // qMax(curveBounds.width, curveBounds.height). Based on this // logic we can estimate the length of the outline edges based on // the radius + a pen width and adjusting for scale factors // depending on if the pen is cosmetic or not. // // The curvyness value of PI/14 was based on, // arcLength = 2*PI*r/4 = PI*r/2 and splitting length into somewhere // between 3 and 8 where 5 seemed to be give pretty good results // hence: Q_PI/14. Lower divisors will give more detail at the // direct cost of performance. // simplfy pens that are thin in device size (2px wide or less) if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) { if (m_cap_style == Qt::RoundCap) m_cap_style = Qt::SquareCap; if (m_join_style == Qt::RoundJoin) m_join_style = Qt::MiterJoin; m_curvyness_add = 0.5; m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; m_roundness = 1; } else if (cosmetic) { m_curvyness_add = realWidth / 2; m_curvyness_mul = CURVE_FLATNESS; m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS); } else { m_curvyness_add = m_width; m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; m_roundness = qMax<int>(4, realWidth * m_curvyness_mul); } // Over this level of segmentation, there doesn't seem to be any // benefit, even for huge penWidth if (m_roundness > 24) m_roundness = 24; m_sin_theta = qFastSin(Q_PI / m_roundness); m_cos_theta = qFastCos(Q_PI / m_roundness); const qreal *endPts = pts + (count<<1); const qreal *startPts = 0; Qt::PenCapStyle cap = m_cap_style; if (!types) { // skip duplicate points while((pts + 2) < endPts && pts[0] == pts[2] && pts[1] == pts[3]) pts += 2; if ((pts + 2) == endPts) return; startPts = pts; bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1); if (endsAtStart || path.hasImplicitClose()) m_cap_style = Qt::FlatCap; moveTo(pts); m_cap_style = cap; pts += 2; lineTo(pts); pts += 2; while (pts < endPts) { if (m_cx != pts[0] || m_cy != pts[1]) { join(pts); lineTo(pts); } pts += 2; } endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); } else { bool endsAtStart = false; while (pts < endPts) { switch (*types) { case QPainterPath::MoveToElement: { if (pts != path.points()) endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); startPts = pts; int end = (endPts - pts) / 2; int i = 2; // Start looking to ahead since we never have two moveto's in a row while (i<end && types[i] != QPainterPath::MoveToElement) { ++i; } endsAtStart = startPts[0] == pts[i*2 - 2] && startPts[1] == pts[i*2 - 1]; if (endsAtStart || path.hasImplicitClose()) m_cap_style = Qt::FlatCap; moveTo(pts); m_cap_style = cap; pts+=2; ++types; break; } case QPainterPath::LineToElement: if (*(types - 1) != QPainterPath::MoveToElement) join(pts); lineTo(pts); pts+=2; ++types; break; case QPainterPath::CurveToElement: if (*(types - 1) != QPainterPath::MoveToElement) join(pts); cubicTo(pts); pts+=6; types+=3; break; default: Q_ASSERT(false); break; } } endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); } }
void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen, const QRectF &clip) { const qreal *pts = path.points(); const QPainterPath::ElementType *types = path.elements(); int count = path.elementCount(); bool cosmetic = pen.isCosmetic(); m_points.reset(); m_types.reset(); m_points.reserve(path.elementCount()); m_types.reserve(path.elementCount()); qreal width = qpen_widthf(pen); if (width == 0) width = 1; m_dash_stroker.setDashPattern(pen.dashPattern()); m_dash_stroker.setStrokeWidth(cosmetic ? width * m_inv_scale : width); m_dash_stroker.setDashOffset(pen.dashOffset()); m_dash_stroker.setMiterLimit(pen.miterLimit()); m_dash_stroker.setClipRect(clip); float curvynessAdd, curvynessMul; // simplify pens that are thin in device size (2px wide or less) if (width < 2.5 && (cosmetic || m_inv_scale == 1)) { curvynessAdd = 0.5; curvynessMul = CURVE_FLATNESS / m_inv_scale; } else if (cosmetic) { curvynessAdd= width / 2; curvynessMul= CURVE_FLATNESS; } else { curvynessAdd = width * m_inv_scale; curvynessMul = CURVE_FLATNESS / m_inv_scale; } if (count < 2) return; const qreal *endPts = pts + (count<<1); m_dash_stroker.begin(this); if (!types) { m_dash_stroker.moveTo(pts[0], pts[1]); pts += 2; while (pts < endPts) { m_dash_stroker.lineTo(pts[0], pts[1]); pts += 2; } } else { while (pts < endPts) { switch (*types) { case QPainterPath::MoveToElement: m_dash_stroker.moveTo(pts[0], pts[1]); pts += 2; ++types; break; case QPainterPath::LineToElement: m_dash_stroker.lineTo(pts[0], pts[1]); pts += 2; ++types; break; case QPainterPath::CurveToElement: { QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1), *(((const QPointF *) pts)), *(((const QPointF *) pts) + 1), *(((const QPointF *) pts) + 2)); QRectF bounds = b.bounds(); float rad = qMax(bounds.width(), bounds.height()); int threshold = qMin<float>(64, (rad + curvynessAdd) * curvynessMul); if (threshold < 4) threshold = 4; qreal threshold_minus_1 = threshold - 1; for (int i=0; i<threshold; ++i) { QPointF pt = b.pointAt(i / threshold_minus_1); m_dash_stroker.lineTo(pt.x(), pt.y()); } pts += 6; types += 3; break; } default: break; } } } m_dash_stroker.end(); }
void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline) { const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); const QPainterPath::ElementType* const elements = path.elements(); if (boundingRectDirty) { minX = maxX = points[0].x(); minY = maxY = points[0].y(); boundingRectDirty = false; } if (!outline && !path.isConvex()) addCentroid(path, 0); int lastMoveTo = vertexArray.size(); vertexArray.add(points[0]); // The first element is always a moveTo do { if (!elements) { // qDebug("QVectorPath has no elements"); // If the path has a null elements pointer, the elements implicitly // start with a moveTo (already added) and continue with lineTos: for (int i=1; i<path.elementCount(); ++i) lineToArray(points[i].x(), points[i].y()); break; } // qDebug("QVectorPath has element types"); for (int i=1; i<path.elementCount(); ++i) { switch (elements[i]) { case QPainterPath::MoveToElement: if (!outline) addClosingLine(lastMoveTo); // qDebug("element[%d] is a MoveToElement", i); vertexArrayStops.add(vertexArray.size()); if (!outline) { if (!path.isConvex()) addCentroid(path, i); lastMoveTo = vertexArray.size(); } lineToArray(points[i].x(), points[i].y()); // Add the moveTo as a new vertex break; case QPainterPath::LineToElement: // qDebug("element[%d] is a LineToElement", i); lineToArray(points[i].x(), points[i].y()); break; case QPainterPath::CurveToElement: { QBezier b = QBezier::fromPoints(*(((const QPointF *) points) + i - 1), points[i], points[i+1], points[i+2]); QRectF bounds = b.bounds(); // threshold based on same algorithm as in qtriangulatingstroker.cpp int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * 3.14f / (curveInverseScale * 6)); if (threshold < 3) threshold = 3; qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1); for (int t=0; t<threshold; ++t) { QPointF pt = b.pointAt(t * one_over_threshold_minus_1); lineToArray(pt.x(), pt.y()); } i += 2; break; } default: break; } } } while (0); if (!outline) addClosingLine(lastMoveTo); vertexArrayStops.add(vertexArray.size()); }
void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) { #ifdef QT_DEBUG_DRAW qDebug() << "QPaintEngineEx::stroke()" << pen; #endif Q_D(QPaintEngineEx); if (path.isEmpty()) return; if (!d->strokeHandler) { d->strokeHandler = new StrokeHandler(path.elementCount()+4); d->stroker.setMoveToHook(qpaintengineex_moveTo); d->stroker.setLineToHook(qpaintengineex_lineTo); d->stroker.setCubicToHook(qpaintengineex_cubicTo); } if (!qpen_fast_equals(pen, d->strokerPen)) { d->strokerPen = pen; d->stroker.setJoinStyle(pen.joinStyle()); d->stroker.setCapStyle(pen.capStyle()); d->stroker.setMiterLimit(pen.miterLimit()); qreal penWidth = pen.widthF(); if (penWidth == 0) d->stroker.setStrokeWidth(1); else d->stroker.setStrokeWidth(penWidth); Qt::PenStyle style = pen.style(); if (style == Qt::SolidLine) { d->activeStroker = &d->stroker; } else if (style == Qt::NoPen) { d->activeStroker = 0; } else { d->dasher.setDashPattern(pen.dashPattern()); d->dasher.setDashOffset(pen.dashOffset()); d->activeStroker = &d->dasher; } } if (!d->activeStroker) { return; } if (pen.style() > Qt::SolidLine) { if (pen.isCosmetic()) { d->activeStroker->setClipRect(d->exDeviceRect); } else { QRectF clipRect = state()->matrix.inverted().mapRect(QRectF(d->exDeviceRect)); d->activeStroker->setClipRect(clipRect); } } const QPainterPath::ElementType *types = path.elements(); const qreal *points = path.points(); int pointCount = path.elementCount(); const qreal *lastPoint = points + (pointCount<<1); d->strokeHandler->types.reset(); d->strokeHandler->pts.reset(); // Some engines might decide to optimize for the non-shape hint later on... uint flags = QVectorPath::WindingFill; if (path.elementCount() > 2) flags |= QVectorPath::NonConvexShapeMask; if (d->stroker.capStyle() == Qt::RoundCap || d->stroker.joinStyle() == Qt::RoundJoin) flags |= QVectorPath::CurvedShapeMask; // ### Perspective Xforms are currently not supported... if (!pen.isCosmetic()) { // We include cosmetic pens in this case to avoid having to // change the current transform. Normal transformed, // non-cosmetic pens will be transformed as part of fill // later, so they are also covered here.. d->activeStroker->setCurveThresholdFromTransform(state()->matrix); d->activeStroker->begin(d->strokeHandler); if (types) { while (points < lastPoint) { switch (*types) { case QPainterPath::MoveToElement: d->activeStroker->moveTo(points[0], points[1]); points += 2; ++types; break; case QPainterPath::LineToElement: d->activeStroker->lineTo(points[0], points[1]); points += 2; ++types; break; case QPainterPath::CurveToElement: d->activeStroker->cubicTo(points[0], points[1], points[2], points[3], points[4], points[5]); points += 6; types += 3; flags |= QVectorPath::CurvedShapeMask; break; default: break; } } if (path.hasImplicitClose()) d->activeStroker->lineTo(path.points()[0], path.points()[1]); } else { d->activeStroker->moveTo(points[0], points[1]); points += 2; while (points < lastPoint) { d->activeStroker->lineTo(points[0], points[1]); points += 2; } if (path.hasImplicitClose()) d->activeStroker->lineTo(path.points()[0], path.points()[1]); } d->activeStroker->end(); if (!d->strokeHandler->types.size()) // an empty path... return; QVectorPath strokePath(d->strokeHandler->pts.data(), d->strokeHandler->types.size(), d->strokeHandler->types.data(), flags); fill(strokePath, pen.brush()); } else { // For cosmetic pens we need a bit of trickery... We to process xform the input points if (state()->matrix.type() >= QTransform::TxProject) { QPainterPath painterPath = state()->matrix.map(path.convertToPainterPath()); d->activeStroker->strokePath(painterPath, d->strokeHandler, QTransform()); } else { d->activeStroker->setCurveThresholdFromTransform(QTransform()); d->activeStroker->begin(d->strokeHandler); if (types) { while (points < lastPoint) { switch (*types) { case QPainterPath::MoveToElement: { QPointF pt = (*(QPointF *) points) * state()->matrix; d->activeStroker->moveTo(pt.x(), pt.y()); points += 2; ++types; break; } case QPainterPath::LineToElement: { QPointF pt = (*(QPointF *) points) * state()->matrix; d->activeStroker->lineTo(pt.x(), pt.y()); points += 2; ++types; break; } case QPainterPath::CurveToElement: { QPointF c1 = ((QPointF *) points)[0] * state()->matrix; QPointF c2 = ((QPointF *) points)[1] * state()->matrix; QPointF e = ((QPointF *) points)[2] * state()->matrix; d->activeStroker->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y()); points += 6; types += 3; flags |= QVectorPath::CurvedShapeMask; break; } default: break; } } if (path.hasImplicitClose()) { QPointF pt = * ((QPointF *) path.points()) * state()->matrix; d->activeStroker->lineTo(pt.x(), pt.y()); } } else { QPointF p = ((QPointF *)points)[0] * state()->matrix; d->activeStroker->moveTo(p.x(), p.y()); points += 2; while (points < lastPoint) { QPointF p = ((QPointF *)points)[0] * state()->matrix; d->activeStroker->lineTo(p.x(), p.y()); points += 2; } if (path.hasImplicitClose()) d->activeStroker->lineTo(p.x(), p.y()); } d->activeStroker->end(); } QVectorPath strokePath(d->strokeHandler->pts.data(), d->strokeHandler->types.size(), d->strokeHandler->types.data(), flags); QTransform xform = state()->matrix; state()->matrix = QTransform(); transformChanged(); QBrush brush = pen.brush(); if (qbrush_style(brush) != Qt::SolidPattern) brush.setTransform(brush.transform() * xform); fill(strokePath, brush); state()->matrix = xform; transformChanged(); } }
void QOutlineMapper::endOutline() { closeSubpath(); if (m_elements.isEmpty()) { memset(&m_outline, 0, sizeof(m_outline)); return; } QPointF *elements = m_elements.data(); // Transform the outline if (m_txop == QTransform::TxNone) { // Nothing to do. } else if (m_txop == QTransform::TxTranslate) { for (int i = 0; i < m_elements.size(); ++i) { QPointF &e = elements[i]; e = QPointF(e.x() + m_dx, e.y() + m_dy); } } else if (m_txop == QTransform::TxScale) { for (int i = 0; i < m_elements.size(); ++i) { QPointF &e = elements[i]; e = QPointF(m_m11 * e.x() + m_dx, m_m22 * e.y() + m_dy); } } else if (m_txop < QTransform::TxProject) { for (int i = 0; i < m_elements.size(); ++i) { QPointF &e = elements[i]; e = QPointF(m_m11 * e.x() + m_m21 * e.y() + m_dx, m_m22 * e.y() + m_m12 * e.x() + m_dy); } } else { const QVectorPath vp((qreal *)elements, m_elements.size(), m_element_types.size() ? m_element_types.data() : 0); QPainterPath path = vp.convertToPainterPath(); path = QTransform(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33).map(path); if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL)) path.setFillRule(Qt::WindingFill); uint old_txop = m_txop; m_txop = QTransform::TxNone; if (path.isEmpty()) m_valid = false; else convertPath(path); m_txop = old_txop; return; } if (m_round_coords) { // round coordinates to match outlines drawn with drawLine_midpoint_i for (int i = 0; i < m_elements.size(); ++i) elements[i] = QPointF(qFloor(elements[i].x() + aliasedCoordinateDelta), qFloor(elements[i].y() + aliasedCoordinateDelta)); } controlPointRect = boundingRect(elements, m_elements.size()); #ifdef QT_DEBUG_CONVERT printf(" - control point rect (%.2f, %.2f) %.2f x %.2f, clip=(%d,%d, %dx%d)\n", controlPointRect.x(), controlPointRect.y(), controlPointRect.width(), controlPointRect.height(), m_clip_rect.x(), m_clip_rect.y(), m_clip_rect.width(), m_clip_rect.height()); #endif // Check for out of dev bounds... const bool do_clip = !m_in_clip_elements && ((controlPointRect.left() < -QT_RASTER_COORD_LIMIT || controlPointRect.right() > QT_RASTER_COORD_LIMIT || controlPointRect.top() < -QT_RASTER_COORD_LIMIT || controlPointRect.bottom() > QT_RASTER_COORD_LIMIT || controlPointRect.width() > QT_RASTER_COORD_LIMIT || controlPointRect.height() > QT_RASTER_COORD_LIMIT)); if (do_clip) { clipElements(elements, elementTypes(), m_elements.size()); } else { convertElements(elements, elementTypes(), m_elements.size()); } }
void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen) { const qreal *pts = path.points(); const QPainterPath::ElementType *types = path.elements(); int count = path.elementCount(); m_points.reset(); m_types.reset(); qreal width = qpen_widthf(pen); if (width == 0) width = 1; m_dash_stroker.setDashPattern(pen.dashPattern()); m_dash_stroker.setStrokeWidth(pen.isCosmetic() ? width * m_inv_scale : width); m_dash_stroker.setMiterLimit(pen.miterLimit()); qreal curvyness = sqrt(width) * m_inv_scale / 8; if (count < 2) return; const qreal *endPts = pts + (count<<1); m_dash_stroker.begin(this); if (!types) { m_dash_stroker.moveTo(pts[0], pts[1]); pts += 2; while (pts < endPts) { m_dash_stroker.lineTo(pts[0], pts[1]); pts += 2; } } else { while (pts < endPts) { switch (*types) { case QPainterPath::MoveToElement: m_dash_stroker.moveTo(pts[0], pts[1]); pts += 2; ++types; break; case QPainterPath::LineToElement: m_dash_stroker.lineTo(pts[0], pts[1]); pts += 2; ++types; break; case QPainterPath::CurveToElement: { QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1), *(((const QPointF *) pts)), *(((const QPointF *) pts) + 1), *(((const QPointF *) pts) + 2)); QRectF bounds = b.bounds(); int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * curvyness); if (threshold < 4) threshold = 4; qreal threshold_minus_1 = threshold - 1; for (int i=0; i<threshold; ++i) { QPointF pt = b.pointAt(i / threshold_minus_1); m_dash_stroker.lineTo(pt.x(), pt.y()); } pts += 6; types += 3; break; } default: break; } } } m_dash_stroker.end(); }
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; }