VectorImage: support stroke styling for paths
SVG different stroke attibutes can be easily mapped to QQuickShapePath properties. Task-number: QTBUG-121650 Change-Id: Id52f3e7d99a81c84851b7a7645f75fdee1efbaeb Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
parent
de436b8525
commit
645e1ee76b
|
@ -123,7 +123,7 @@ void QQuickItemGenerator::outputShapePath(const PathNodeInfo &info, const QPaint
|
||||||
Q_UNUSED(pathSelector)
|
Q_UNUSED(pathSelector)
|
||||||
Q_ASSERT(painterPath || quadPath);
|
Q_ASSERT(painterPath || quadPath);
|
||||||
|
|
||||||
const bool noPen = info.strokeColor == QColorConstants::Transparent;
|
const bool noPen = info.strokeStyle.color == QColorConstants::Transparent;
|
||||||
if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
|
if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -145,12 +145,18 @@ void QQuickItemGenerator::outputShapePath(const PathNodeInfo &info, const QPaint
|
||||||
if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
|
if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
|
||||||
shapePath->setStrokeColor(Qt::transparent);
|
shapePath->setStrokeColor(Qt::transparent);
|
||||||
} else {
|
} else {
|
||||||
shapePath->setStrokeColor(info.strokeColor);
|
shapePath->setStrokeColor(info.strokeStyle.color);
|
||||||
shapePath->setStrokeWidth(info.strokeWidth);
|
shapePath->setStrokeWidth(info.strokeStyle.width);
|
||||||
|
shapePath->setCapStyle(QQuickShapePath::CapStyle(info.strokeStyle.lineCapStyle));
|
||||||
|
shapePath->setJoinStyle(QQuickShapePath::JoinStyle(info.strokeStyle.lineJoinStyle));
|
||||||
|
shapePath->setMiterLimit(info.strokeStyle.miterLimit);
|
||||||
|
if (info.strokeStyle.dashArray.length() != 0) {
|
||||||
|
shapePath->setStrokeStyle(QQuickShapePath::DashLine);
|
||||||
|
shapePath->setDashPattern(info.strokeStyle.dashArray.toVector());
|
||||||
|
shapePath->setDashOffset(info.strokeStyle.dashOffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shapePath->setCapStyle(QQuickShapePath::CapStyle(info.capStyle));
|
|
||||||
|
|
||||||
if (!(pathSelector & QQuickVectorImageGenerator::FillPath))
|
if (!(pathSelector & QQuickVectorImageGenerator::FillPath))
|
||||||
shapePath->setFillColor(Qt::transparent);
|
shapePath->setFillColor(Qt::transparent);
|
||||||
else if (info.grad.type() != QGradient::NoGradient)
|
else if (info.grad.type() != QGradient::NoGradient)
|
||||||
|
|
|
@ -44,14 +44,23 @@ struct ImageNodeInfo : NodeInfo
|
||||||
QRectF rect;
|
QRectF rect;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StrokeStyle
|
||||||
|
{
|
||||||
|
Qt::PenCapStyle lineCapStyle = Qt::SquareCap;
|
||||||
|
Qt::PenJoinStyle lineJoinStyle = Qt::MiterJoin;
|
||||||
|
qreal miterLimit = 4;
|
||||||
|
qreal dashOffset = 0;
|
||||||
|
QList<qreal> dashArray;
|
||||||
|
QColor color = QColorConstants::Transparent;
|
||||||
|
qreal width = 1.0;
|
||||||
|
};
|
||||||
|
|
||||||
struct PathNodeInfo : NodeInfo
|
struct PathNodeInfo : NodeInfo
|
||||||
{
|
{
|
||||||
QPainterPath painterPath;
|
QPainterPath painterPath;
|
||||||
Qt::FillRule fillRule = Qt::FillRule::WindingFill;
|
Qt::FillRule fillRule = Qt::FillRule::WindingFill;
|
||||||
Qt::PenCapStyle capStyle = Qt::SquareCap;
|
|
||||||
QColor strokeColor;
|
|
||||||
qreal strokeWidth;
|
|
||||||
QColor fillColor;
|
QColor fillColor;
|
||||||
|
StrokeStyle strokeStyle;
|
||||||
QGradient grad;
|
QGradient grad;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainte
|
||||||
Q_UNUSED(pathSelector)
|
Q_UNUSED(pathSelector)
|
||||||
Q_ASSERT(painterPath || quadPath);
|
Q_ASSERT(painterPath || quadPath);
|
||||||
|
|
||||||
const bool noPen = info.strokeColor == QColorConstants::Transparent;
|
const bool noPen = info.strokeStyle.color == QColorConstants::Transparent;
|
||||||
if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
|
if (pathSelector == QQuickVectorImageGenerator::StrokePath && noPen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -275,11 +275,17 @@ void QQuickQmlGenerator::outputShapePath(const PathNodeInfo &info, const QPainte
|
||||||
if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
|
if (noPen || !(pathSelector & QQuickVectorImageGenerator::StrokePath)) {
|
||||||
stream() << "strokeColor: \"transparent\"";
|
stream() << "strokeColor: \"transparent\"";
|
||||||
} else {
|
} else {
|
||||||
stream() << "strokeColor: \"" << info.strokeColor.name(QColor::HexArgb) << "\"";
|
stream() << "strokeColor: \"" << info.strokeStyle.color.name(QColor::HexArgb) << "\"";
|
||||||
stream() << "strokeWidth: " << info.strokeWidth;
|
stream() << "strokeWidth: " << info.strokeStyle.width;
|
||||||
|
stream() << "capStyle: " << QQuickVectorImageGenerator::Utils::strokeCapStyleString(info.strokeStyle.lineCapStyle);
|
||||||
|
stream() << "joinStyle: " << QQuickVectorImageGenerator::Utils::strokeJoinStyleString(info.strokeStyle.lineJoinStyle);
|
||||||
|
stream() << "miterLimit: " << info.strokeStyle.miterLimit;
|
||||||
|
if (info.strokeStyle.dashArray.length() != 0) {
|
||||||
|
stream() << "strokeStyle: " << "ShapePath.DashLine";
|
||||||
|
stream() << "dashPattern: " << QQuickVectorImageGenerator::Utils::listString(info.strokeStyle.dashArray);
|
||||||
|
stream() << "dashOffset: " << info.strokeStyle.dashOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (info.capStyle == Qt::FlatCap)
|
|
||||||
stream() << "capStyle: ShapePath.FlatCap"; //### TODO Add the rest of the styles, as well as join styles etc.
|
|
||||||
|
|
||||||
if (!(pathSelector & QQuickVectorImageGenerator::FillPath)) {
|
if (!(pathSelector & QQuickVectorImageGenerator::FillPath)) {
|
||||||
stream() << "fillColor: \"transparent\"";
|
stream() << "fillColor: \"transparent\"";
|
||||||
|
|
|
@ -34,52 +34,6 @@ using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(lcQuickVectorImage)
|
Q_DECLARE_LOGGING_CATEGORY(lcQuickVectorImage)
|
||||||
|
|
||||||
static inline bool isPathContainer(const QSvgStructureNode *node)
|
|
||||||
{
|
|
||||||
bool foundPath = false;
|
|
||||||
for (const auto *child : node->renderers()) {
|
|
||||||
switch (child->type()) {
|
|
||||||
// nodes that shouldn't go inside Shape{}
|
|
||||||
case QSvgNode::Switch:
|
|
||||||
case QSvgNode::Doc:
|
|
||||||
case QSvgNode::Group:
|
|
||||||
case QSvgNode::Animation:
|
|
||||||
case QSvgNode::Use:
|
|
||||||
case QSvgNode::Video:
|
|
||||||
//qCDebug(lcQuickVectorGraphics) << "NOT path container because" << node->typeName() ;
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// nodes that could go inside Shape{}
|
|
||||||
case QSvgNode::Defs:
|
|
||||||
case QSvgNode::Image:
|
|
||||||
case QSvgNode::Textarea:
|
|
||||||
case QSvgNode::Text:
|
|
||||||
case QSvgNode::Tspan:
|
|
||||||
break;
|
|
||||||
|
|
||||||
// nodes that are done as pure ShapePath{}
|
|
||||||
case QSvgNode::Rect:
|
|
||||||
case QSvgNode::Circle:
|
|
||||||
case QSvgNode::Ellipse:
|
|
||||||
case QSvgNode::Line:
|
|
||||||
case QSvgNode::Path:
|
|
||||||
case QSvgNode::Polygon:
|
|
||||||
case QSvgNode::Polyline:
|
|
||||||
if (!child->style().transform.isDefault()) {
|
|
||||||
//qCDebug(lcQuickVectorGraphics) << "NOT path container because local transform";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
foundPath = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
qCDebug(lcQuickVectorImage) << "Unhandled type in switch" << child->type();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//qCDebug(lcQuickVectorGraphics) << "Container" << node->nodeId() << node->typeName() << "is" << foundPath;
|
|
||||||
return foundPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
class QSvgStyleResolver
|
class QSvgStyleResolver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -141,17 +95,6 @@ public:
|
||||||
return strokeColor;
|
return strokeColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal currentStrokeOpacity() const
|
|
||||||
{
|
|
||||||
return m_svgState.strokeOpacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
float currentStrokeWidth() const
|
|
||||||
{
|
|
||||||
float penWidth = m_dummyPainter.pen().widthF();
|
|
||||||
return penWidth ? penWidth : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QGradient applyOpacityToGradient(const QGradient &gradient, float opacity)
|
static QGradient applyOpacityToGradient(const QGradient &gradient, float opacity)
|
||||||
{
|
{
|
||||||
QGradient grad = gradient;
|
QGradient grad = gradient;
|
||||||
|
@ -166,6 +109,17 @@ public:
|
||||||
return grad;
|
return grad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float currentStrokeWidth() const
|
||||||
|
{
|
||||||
|
float penWidth = m_dummyPainter.pen().widthF();
|
||||||
|
return penWidth ? penWidth : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPen currentStroke() const
|
||||||
|
{
|
||||||
|
return m_dummyPainter.pen();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QPainter m_dummyPainter;
|
QPainter m_dummyPainter;
|
||||||
QImage m_dummyImage;
|
QImage m_dummyImage;
|
||||||
|
@ -175,6 +129,67 @@ protected:
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(QSvgStyleResolver, styleResolver)
|
Q_GLOBAL_STATIC(QSvgStyleResolver, styleResolver)
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
inline bool isPathContainer(const QSvgStructureNode *node)
|
||||||
|
{
|
||||||
|
bool foundPath = false;
|
||||||
|
for (const auto *child : node->renderers()) {
|
||||||
|
switch (child->type()) {
|
||||||
|
// nodes that shouldn't go inside Shape{}
|
||||||
|
case QSvgNode::Switch:
|
||||||
|
case QSvgNode::Doc:
|
||||||
|
case QSvgNode::Group:
|
||||||
|
case QSvgNode::Animation:
|
||||||
|
case QSvgNode::Use:
|
||||||
|
case QSvgNode::Video:
|
||||||
|
//qCDebug(lcQuickVectorGraphics) << "NOT path container because" << node->typeName() ;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// nodes that could go inside Shape{}
|
||||||
|
case QSvgNode::Defs:
|
||||||
|
case QSvgNode::Image:
|
||||||
|
case QSvgNode::Textarea:
|
||||||
|
case QSvgNode::Text:
|
||||||
|
case QSvgNode::Tspan:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// nodes that are done as pure ShapePath{}
|
||||||
|
case QSvgNode::Rect:
|
||||||
|
case QSvgNode::Circle:
|
||||||
|
case QSvgNode::Ellipse:
|
||||||
|
case QSvgNode::Line:
|
||||||
|
case QSvgNode::Path:
|
||||||
|
case QSvgNode::Polygon:
|
||||||
|
case QSvgNode::Polyline:
|
||||||
|
if (!child->style().transform.isDefault()) {
|
||||||
|
//qCDebug(lcQuickVectorGraphics) << "NOT path container because local transform";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
foundPath = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCDebug(lcQuickVectorImage) << "Unhandled type in switch" << child->type();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//qCDebug(lcQuickVectorGraphics) << "Container" << node->nodeId() << node->typeName() << "is" << foundPath;
|
||||||
|
return foundPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void populateStrokeStyle(StrokeStyle &srokeStyle)
|
||||||
|
{
|
||||||
|
QPen p = styleResolver->currentStroke();
|
||||||
|
srokeStyle.lineCapStyle = p.capStyle();
|
||||||
|
srokeStyle.lineJoinStyle = p.joinStyle() == Qt::SvgMiterJoin ? Qt::MiterJoin : p.joinStyle(); //TODO support SvgMiterJoin
|
||||||
|
srokeStyle.miterLimit = p.miterLimit();
|
||||||
|
srokeStyle.dashOffset = p.dashOffset();
|
||||||
|
srokeStyle.dashArray = p.dashPattern();
|
||||||
|
srokeStyle.color = styleResolver->currentStrokeColor();
|
||||||
|
srokeStyle.width = p.widthF();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
QSvgVisitorImpl::QSvgVisitorImpl(const QString svgFileName, QQuickGenerator *generator)
|
QSvgVisitorImpl::QSvgVisitorImpl(const QString svgFileName, QQuickGenerator *generator)
|
||||||
: m_svgFileName(svgFileName)
|
: m_svgFileName(svgFileName)
|
||||||
, m_generator(generator)
|
, m_generator(generator)
|
||||||
|
@ -277,11 +292,10 @@ void QSvgVisitorImpl::visitPathNode(const QSvgPath *node)
|
||||||
|
|
||||||
void QSvgVisitorImpl::visitLineNode(const QSvgLine *node)
|
void QSvgVisitorImpl::visitLineNode(const QSvgLine *node)
|
||||||
{
|
{
|
||||||
// TODO: proper end caps (should be flat by default?)
|
|
||||||
QPainterPath p;
|
QPainterPath p;
|
||||||
p.moveTo(node->line().p1());
|
p.moveTo(node->line().p1());
|
||||||
p.lineTo(node->line().p2());
|
p.lineTo(node->line().p2());
|
||||||
handlePathNode(node, p, Qt::FlatCap);
|
handlePathNode(node, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QSvgVisitorImpl::visitPolygonNode(const QSvgPolygon *node)
|
void QSvgVisitorImpl::visitPolygonNode(const QSvgPolygon *node)
|
||||||
|
@ -293,7 +307,7 @@ void QSvgVisitorImpl::visitPolygonNode(const QSvgPolygon *node)
|
||||||
void QSvgVisitorImpl::visitPolylineNode(const QSvgPolyline *node)
|
void QSvgVisitorImpl::visitPolylineNode(const QSvgPolyline *node)
|
||||||
{
|
{
|
||||||
QPainterPath p = QQuickVectorImageGenerator::Utils::polygonToPath(node->polygon(), false);
|
QPainterPath p = QQuickVectorImageGenerator::Utils::polygonToPath(node->polygon(), false);
|
||||||
handlePathNode(node, p, Qt::FlatCap);
|
handlePathNode(node, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QSvgVisitorImpl::gradientCssDescription(const QGradient *gradient)
|
QString QSvgVisitorImpl::gradientCssDescription(const QGradient *gradient)
|
||||||
|
@ -526,11 +540,11 @@ void QSvgVisitorImpl::visitTextNode(const QSvgText *node)
|
||||||
info.painterPath = p;
|
info.painterPath = p;
|
||||||
|
|
||||||
if (fmt.hasProperty(QTextCharFormat::TextOutline)) {
|
if (fmt.hasProperty(QTextCharFormat::TextOutline)) {
|
||||||
info.strokeWidth = fmt.textOutline().widthF();
|
info.strokeStyle.width = fmt.textOutline().widthF();
|
||||||
info.strokeColor = fmt.textOutline().color();
|
info.strokeStyle.color = fmt.textOutline().color();
|
||||||
} else {
|
} else {
|
||||||
info.strokeColor = styleResolver->currentStrokeColor();
|
info.strokeStyle.color = styleResolver->currentStrokeColor();
|
||||||
info.strokeWidth = styleResolver->currentStrokeWidth();
|
info.strokeStyle.width = styleResolver->currentStrokeWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.grad.type() == QGradient::NoGradient && styleResolver->currentFillGradient() != nullptr)
|
if (info.grad.type() == QGradient::NoGradient && styleResolver->currentFillGradient() != nullptr)
|
||||||
|
@ -738,7 +752,7 @@ void QSvgVisitorImpl::handleBaseNodeEnd(const QSvgNode *node)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QSvgVisitorImpl::handlePathNode(const QSvgNode *node, const QPainterPath &path, Qt::PenCapStyle capStyle)
|
void QSvgVisitorImpl::handlePathNode(const QSvgNode *node, const QPainterPath &path)
|
||||||
{
|
{
|
||||||
handleBaseNodeSetup(node);
|
handleBaseNodeSetup(node);
|
||||||
|
|
||||||
|
@ -749,10 +763,8 @@ void QSvgVisitorImpl::handlePathNode(const QSvgNode *node, const QPainterPath &p
|
||||||
info.fillRule = fillStyle->fillRule();
|
info.fillRule = fillStyle->fillRule();
|
||||||
|
|
||||||
info.painterPath = path;
|
info.painterPath = path;
|
||||||
info.capStyle = capStyle;
|
|
||||||
info.fillColor = styleResolver->currentFillColor();
|
info.fillColor = styleResolver->currentFillColor();
|
||||||
info.strokeColor = styleResolver->currentStrokeColor();
|
populateStrokeStyle(info.strokeStyle);
|
||||||
info.strokeWidth = styleResolver->currentStrokeWidth();
|
|
||||||
if (styleResolver->currentFillGradient() != nullptr)
|
if (styleResolver->currentFillGradient() != nullptr)
|
||||||
info.grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
|
info.grad = styleResolver->applyOpacityToGradient(*styleResolver->currentFillGradient(), styleResolver->currentFillOpacity());
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ private:
|
||||||
void handleBaseNodeSetup(const QSvgNode *node);
|
void handleBaseNodeSetup(const QSvgNode *node);
|
||||||
void handleBaseNode(const QSvgNode *node);
|
void handleBaseNode(const QSvgNode *node);
|
||||||
void handleBaseNodeEnd(const QSvgNode *node);
|
void handleBaseNodeEnd(const QSvgNode *node);
|
||||||
void handlePathNode(const QSvgNode *node, const QPainterPath &path, Qt::PenCapStyle capStyle = Qt::SquareCap);
|
void handlePathNode(const QSvgNode *node, const QPainterPath &path);
|
||||||
void outputShapePath(QPainterPath pathCopy, const PathNodeInfo &info);
|
void outputShapePath(QPainterPath pathCopy, const PathNodeInfo &info);
|
||||||
static QString gradientCssDescription(const QGradient *gradient);
|
static QString gradientCssDescription(const QGradient *gradient);
|
||||||
static QString colorCssDescription(QColor color);
|
static QString colorCssDescription(QColor color);
|
||||||
|
|
|
@ -193,6 +193,70 @@ inline QString toSvgString(const QQuadPath &path)
|
||||||
return svgPathString;
|
return svgPathString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline QString strokeCapStyleString(Qt::PenCapStyle strokeCapStyle)
|
||||||
|
{
|
||||||
|
QString capStyle;
|
||||||
|
switch (strokeCapStyle) {
|
||||||
|
case Qt::FlatCap:
|
||||||
|
capStyle = QStringLiteral("ShapePath.FlatCap");
|
||||||
|
break;
|
||||||
|
case Qt::SquareCap:
|
||||||
|
capStyle = QStringLiteral("ShapePath.SquareCap");
|
||||||
|
break;
|
||||||
|
case Qt::RoundCap:
|
||||||
|
capStyle = QStringLiteral("ShapePath.RoundCap");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return capStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString strokeJoinStyleString(Qt::PenJoinStyle strokeJoinStyle)
|
||||||
|
{
|
||||||
|
QString joinStyle;
|
||||||
|
switch (strokeJoinStyle) {
|
||||||
|
case Qt::MiterJoin:
|
||||||
|
joinStyle = QStringLiteral("ShapePath.MiterJoin");
|
||||||
|
break;
|
||||||
|
case Qt::BevelJoin:
|
||||||
|
joinStyle = QStringLiteral("ShapePath.BevelJoin");
|
||||||
|
break;
|
||||||
|
case Qt::RoundJoin:
|
||||||
|
joinStyle = QStringLiteral("ShapePath.RoundJoin");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//TODO: Add support for SvgMiter case
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return joinStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline QString listString(QList<T> list)
|
||||||
|
{
|
||||||
|
if (list.isEmpty())
|
||||||
|
return QStringLiteral("[]");
|
||||||
|
|
||||||
|
QString l;
|
||||||
|
QTextStream stream(&l);
|
||||||
|
stream << "[";
|
||||||
|
|
||||||
|
if (list.length() > 1) {
|
||||||
|
for (int i = 0; i < list.length() - 1; i++) {
|
||||||
|
T v = list[i];
|
||||||
|
stream << v << ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << list.last() << "]";
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<svg width="900" height="600" viewBox="0 0 90 60" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<g id="capTest">
|
||||||
|
<polyline points="10,10 80,10" fill="none" stroke-width="8" stroke="black"/>
|
||||||
|
<polyline points="10,10 80,10" fill="none" stroke-width="0.5" stroke-linecap="butt" stroke="yellow"/>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
<use href="#capTest" stroke-linecap="butt"/>
|
||||||
|
<use href="#capTest" y="20" stroke-linecap="round"/>
|
||||||
|
<use href="#capTest" y=" 40" stroke-linecap="square"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 492 B |
|
@ -0,0 +1,12 @@
|
||||||
|
<svg width="900" height="300" viewBox="0 0 130 40" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<polyline id="letterz" points="10,10 40,10 10,30 40,30" fill="none" stroke-width="2.5" stroke="black"/>
|
||||||
|
<polyline id="highlight" points="10,10 40,10 10,30 40,30" fill="none" stroke-width="0.1" stroke="yellow"/>
|
||||||
|
</defs>
|
||||||
|
<use href="#letterz" stroke-linecap="butt"/>
|
||||||
|
<use href="#highlight"/>
|
||||||
|
<use href="#letterz" x="40" stroke-linecap="round"/>
|
||||||
|
<use href="#highlight" x="40"/>
|
||||||
|
<use href="#letterz" x=" 80" stroke-linecap="square"/>
|
||||||
|
<use href="#highlight" x="80"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 586 B |
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="460" height="200" viewBox="0 0 30 13" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<line x1="0" y1="1" x2="30" y2="1" stroke="black" stroke-dashoffset="2"/>
|
||||||
|
<line x1="0" y1="3" x2="30" y2="3" stroke="black" stroke-dasharray="4 2"/>
|
||||||
|
<!--positive dashoffset pulls the dashes-->
|
||||||
|
<line x1="0" y1="5" x2="30" y2="5" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="2"/>
|
||||||
|
<line x1="0" y1="7" x2="30" y2="7" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="4"/>
|
||||||
|
<!--negative dashoffset pushs the dashes-->
|
||||||
|
<line x1="0" y1="9" x2="30" y2="9" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="-2"/>
|
||||||
|
<line x1="0" y1="11" x2="30" y2="11" stroke="black" stroke-dasharray="4 2" stroke-dashoffset="-4"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 736 B |
|
@ -0,0 +1,12 @@
|
||||||
|
<svg width="600" height="200" viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<g id="capTest">
|
||||||
|
<polyline points="1,8 1,2 7,2" fill="none" stroke-width="1" stroke="black"/>
|
||||||
|
<polyline points="1,8 1,2 7,2" fill="none" stroke-width="0.03" stroke="yellow"/>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<use href="#capTest" x="1" stroke-linejoin="bevel"/>
|
||||||
|
<use href="#capTest" x="11" stroke-linejoin="round"/>
|
||||||
|
<use href="#capTest" x="21" stroke-linejoin="miter"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 482 B |
Loading…
Reference in New Issue