summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim Motoyoshi Kalland <kim.kalland@nokia.com>2009-08-21 15:44:57 (GMT)
committerKim Motoyoshi Kalland <kim.kalland@nokia.com>2009-08-24 12:05:13 (GMT)
commit5c9206d8290232e8adc1a6cdb992a7c19d67269a (patch)
tree2f93f9581d5a41e37b37d54a675c07088d237bb1
parentfdf2a161b59de9e0a6fd059b1d6ba6407f768a25 (diff)
downloadQt-5c9206d8290232e8adc1a6cdb992a7c19d67269a.zip
Qt-5c9206d8290232e8adc1a6cdb992a7c19d67269a.tar.gz
Qt-5c9206d8290232e8adc1a6cdb992a7c19d67269a.tar.bz2
Fixed SVG stroke attributes to work with 'use' tags.
In the process of rewriting the stroke handling code, I also fixed gradients on strokes. Task-number: 202426, 250618 Reviewed-by: Trond
-rw-r--r--src/svg/qsvggraphics.cpp71
-rw-r--r--src/svg/qsvggraphics_p.h5
-rw-r--r--src/svg/qsvghandler.cpp188
-rw-r--r--src/svg/qsvgnode.cpp4
-rw-r--r--src/svg/qsvgstyle.cpp284
-rw-r--r--src/svg/qsvgstyle_p.h186
-rw-r--r--src/svg/qsvgtinydocument.cpp4
-rw-r--r--tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp5
8 files changed, 434 insertions, 313 deletions
diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp
index 40cf06b..6f30b90 100644
--- a/src/svg/qsvggraphics.cpp
+++ b/src/svg/qsvggraphics.cpp
@@ -56,20 +56,21 @@
QT_BEGIN_NAMESPACE
-#define QT_SVG_DRAW_SHAPE(command) \
- applyStyle(p, states); \
- qreal oldOpacity = p->opacity(); \
- QBrush oldBrush = p->brush(); \
- QPen oldPen = p->pen(); \
- p->setPen(Qt::NoPen); \
- p->setOpacity(oldOpacity * states.fillOpacity); \
- command; \
- p->setOpacity(oldOpacity); \
- p->setPen(oldPen); \
- p->setBrush(Qt::NoBrush); \
- command; \
- p->setBrush(oldBrush); \
- revertStyle(p, states);
+#define QT_SVG_DRAW_SHAPE(command) \
+ qreal oldOpacity = p->opacity(); \
+ QBrush oldBrush = p->brush(); \
+ QPen oldPen = p->pen(); \
+ p->setPen(Qt::NoPen); \
+ p->setOpacity(oldOpacity * states.fillOpacity); \
+ command; \
+ p->setPen(oldPen); \
+ if (oldPen.widthF() != 0) { \
+ p->setOpacity(oldOpacity * states.strokeOpacity); \
+ p->setBrush(Qt::NoBrush); \
+ command; \
+ p->setBrush(oldBrush); \
+ } \
+ p->setOpacity(oldOpacity);
void QSvgAnimation::draw(QPainter *, QSvgExtraStates &)
@@ -105,7 +106,9 @@ QRectF QSvgCircle::bounds() const
void QSvgCircle::draw(QPainter *p, QSvgExtraStates &states)
{
+ applyStyle(p, states);
QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
+ revertStyle(p, states);
}
QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path)
@@ -117,7 +120,12 @@ QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path)
void QSvgArc::draw(QPainter *p, QSvgExtraStates &states)
{
applyStyle(p, states);
- p->drawPath(cubic);
+ if (p->pen().widthF() != 0) {
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawPath(cubic);
+ p->setOpacity(oldOpacity);
+ }
revertStyle(p, states);
}
@@ -140,7 +148,9 @@ QRectF QSvgEllipse::bounds() const
void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states)
{
+ applyStyle(p, states);
QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds));
+ revertStyle(p, states);
}
QSvgImage::QSvgImage(QSvgNode *parent, const QImage &image,
@@ -171,7 +181,12 @@ QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line)
void QSvgLine::draw(QPainter *p, QSvgExtraStates &states)
{
applyStyle(p, states);
- p->drawLine(m_bounds);
+ if (p->pen().widthF() != 0) {
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawLine(m_bounds);
+ p->setOpacity(oldOpacity);
+ }
revertStyle(p, states);
}
@@ -184,7 +199,10 @@ QSvgPath::QSvgPath(QSvgNode *parent, const QPainterPath &qpath)
void QSvgPath::draw(QPainter *p, QSvgExtraStates &states)
{
+ applyStyle(p, states);
+ m_path.setFillRule(states.fillRule);
QT_SVG_DRAW_SHAPE(p->drawPath(m_path));
+ revertStyle(p, states);
}
QRectF QSvgPath::bounds() const
@@ -198,7 +216,7 @@ QRectF QSvgPath::bounds() const
}
QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly)
- : QSvgNode(parent), m_poly(poly), m_fillRule(Qt::WindingFill)
+ : QSvgNode(parent), m_poly(poly)
{
}
@@ -216,7 +234,9 @@ QRectF QSvgPolygon::bounds() const
void QSvgPolygon::draw(QPainter *p, QSvgExtraStates &states)
{
- QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly, m_fillRule));
+ applyStyle(p, states);
+ QT_SVG_DRAW_SHAPE(p->drawPolygon(m_poly, states.fillRule));
+ revertStyle(p, states);
}
@@ -229,13 +249,19 @@ QSvgPolyline::QSvgPolyline(QSvgNode *parent, const QPolygonF &poly)
void QSvgPolyline::draw(QPainter *p, QSvgExtraStates &states)
{
applyStyle(p, states);
+ qreal oldOpacity = p->opacity();
if (p->brush().style() != Qt::NoBrush) {
QPen save = p->pen();
p->setPen(QPen(Qt::NoPen));
- p->drawPolygon(m_poly);
+ p->setOpacity(oldOpacity * states.fillOpacity);
+ p->drawPolygon(m_poly, states.fillRule);
p->setPen(save);
}
- p->drawPolyline(m_poly);
+ if (p->pen().widthF() != 0) {
+ p->setOpacity(oldOpacity * states.strokeOpacity);
+ p->drawPolyline(m_poly);
+ }
+ p->setOpacity(oldOpacity);
revertStyle(p, states);
}
@@ -259,11 +285,13 @@ QRectF QSvgRect::bounds() const
void QSvgRect::draw(QPainter *p, QSvgExtraStates &states)
{
+ applyStyle(p, states);
if (m_rx || m_ry) {
QT_SVG_DRAW_SHAPE(p->drawRoundedRect(m_rect, m_rx, m_ry, Qt::RelativeSize));
} else {
QT_SVG_DRAW_SHAPE(p->drawRect(m_rect));
}
+ revertStyle(p, states);
}
QSvgTspan * const QSvgText::LINEBREAK = 0;
@@ -296,6 +324,8 @@ void QSvgText::setTextArea(const QSizeF &size)
void QSvgText::draw(QPainter *p, QSvgExtraStates &states)
{
applyStyle(p, states);
+ qreal oldOpacity = p->opacity();
+ p->setOpacity(oldOpacity * states.fillOpacity);
// Force the font to have a size of 100 pixels to avoid truncation problems
// when the font is very small.
@@ -456,6 +486,7 @@ void QSvgText::draw(QPainter *p, QSvgExtraStates &states)
}
p->setWorldTransform(oldTransform, false);
+ p->setOpacity(oldOpacity);
revertStyle(p, states);
}
diff --git a/src/svg/qsvggraphics_p.h b/src/svg/qsvggraphics_p.h
index 20310e3..02c312b 100644
--- a/src/svg/qsvggraphics_p.h
+++ b/src/svg/qsvggraphics_p.h
@@ -155,13 +155,8 @@ public:
virtual void draw(QPainter *p, QSvgExtraStates &states);
virtual Type type() const;
virtual QRectF bounds() const;
- void setFillRule(Qt::FillRule f)
- {
- m_fillRule = f;
- }
private:
QPolygonF m_poly;
- Qt::FillRule m_fillRule;
};
class QSvgPolyline : public QSvgNode
diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp
index adfe468..5c0eda9 100644
--- a/src/svg/qsvghandler.cpp
+++ b/src/svg/qsvghandler.cpp
@@ -493,8 +493,7 @@ static bool resolveColor(const QString &colorStr, QColor &color, QSvgHandler *ha
int(compo[1]),
int(compo[2]));
return true;
- } else if (colorStr == QLatin1String("inherited") ||
- colorStr == QT_INHERIT) {
+ } else if (colorStr == QT_INHERIT) {
return false;
} else if (colorStr == QLatin1String("currentColor")) {
color = handler->currentColor();
@@ -649,7 +648,7 @@ static void parseBrush(QSvgNode *node,
QString myId = someId(attributes);
if (!value.isEmpty() || !fillRule.isEmpty() || !opacity.isEmpty()) {
- QSvgFillStyle *prop = new QSvgFillStyle(0);
+ QSvgFillStyle *prop = new QSvgFillStyle;
//fill-rule attribute handling
if (!fillRule.isEmpty() && fillRule != QT_INHERIT) {
@@ -670,7 +669,8 @@ static void parseBrush(QSvgNode *node,
value = value.remove(0, 3);
QSvgStyleProperty *style = styleFromUrl(node, value);
if (style) {
- prop->setFillStyle(style);
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ prop->setFillStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
} else {
QString id = idFromUrl(value);
prop->setGradientId(id);
@@ -834,154 +834,95 @@ static void parsePen(QSvgNode *node,
//qDebug()<<"Node "<<node->type()<<", attrs are "<<value<<width;
- if (!value.isEmpty() || !width.isEmpty() || !dashArray.isEmpty() || !linecap.isEmpty() ||
- !linejoin.isEmpty() || !miterlimit.isEmpty() || !opacity.isEmpty() ||
- !dashOffset.isEmpty() || !vectorEffect.isEmpty()) {
+ if (!value.isEmpty() || !dashArray.isEmpty() || !dashOffset.isEmpty() || !linecap.isEmpty()
+ || !linejoin.isEmpty() || !miterlimit.isEmpty() || !opacity.isEmpty() || !width.isEmpty()
+ || !vectorEffect.isEmpty()) {
- QSvgStrokeStyle *inherited =
- static_cast<QSvgStrokeStyle*>(node->parent()->styleProperty(
- QSvgStyleProperty::STROKE));
-
- QPen pen(handler->defaultPen());
- bool stroke = false;
- if (inherited) {
- pen = inherited->qpen();
- stroke = inherited->strokePresent();
- }
-
- // stroke-opacity attribute handling
- qreal strokeAlpha;
- if (!opacity.isEmpty() && opacity != QT_INHERIT) {
- strokeAlpha = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity)));
- } else {
- strokeAlpha = pen.color().alphaF();
- }
+ QSvgStrokeStyle *prop = new QSvgStrokeStyle;
//stroke attribute handling
- if (!value.isEmpty() && value != QT_INHERIT) {
+ if ((!value.isEmpty()) && (value != QT_INHERIT) ) {
if (value.startsWith(QLatin1String("url"))) {
value = value.remove(0, 3);
QSvgStyleProperty *style = styleFromUrl(node, value);
if (style) {
- if (style->type() == QSvgStyleProperty::GRADIENT) {
- QBrush b(*((QSvgGradientStyle*)style)->qgradient());
- pen.setBrush(b);
- } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) {
- pen.setColor(
- ((QSvgSolidColorStyle*)style)->qcolor());
- }
- stroke = true;
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ prop->setStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
} else {
- qWarning() << "QSvgHandler::parsePen could not resolve property" << idFromUrl(value);
+ QString id = idFromUrl(value);
+ prop->setGradientId(id);
+ prop->setGradientResolved(false);
}
- } else if (value == QLatin1String("none")) {
- QColor color; //### fixme: dafalut value for color.
- color.setAlphaF(strokeAlpha);
- pen.setColor(color);
- stroke = false; // This is required because, parent may have a valid stroke but the child may have stroke = "none"
- } else {
+ } else if (value != QLatin1String("none")) {
QColor color;
- if (resolveColor(value, color, handler)) {
- color.setAlphaF(strokeAlpha);
- pen.setColor(color);
- }
- stroke = true;
+ if (resolveColor(value, color, handler))
+ prop->setStroke(QBrush(color));
+ } else {
+ prop->setStroke(QBrush(Qt::NoBrush));
}
- } else {
- QColor color = pen.color();
- color.setAlphaF(strokeAlpha);
- pen.setColor(color);
}
//stroke-width handling
+ qreal w = 0;
if (!width.isEmpty() && width != QT_INHERIT) {
- qreal widthF;
QSvgHandler::LengthType lt;
- widthF = parseLength(width, lt, handler);
- pen.setWidthF(widthF);
+ prop->setWidth(w = parseLength(width, lt, handler));
+ }
+
+ //stroke-dasharray
+ if (!dashArray.isEmpty() && dashArray != QT_INHERIT) {
+ if (dashArray == QLatin1String("none")) {
+ prop->setDashArrayNone();
+ } else {
+ const QChar *s = dashArray.constData();
+ QVector<qreal> dashes = parseNumbersList(s);
+ // if the dash count is odd the dashes should be duplicated
+ if ((dashes.size() & 1) != 0)
+ dashes << QVector<qreal>(dashes);
+ prop->setDashArray(dashes);
+ }
}
//stroke-linejoin attribute handling
if (!linejoin.isEmpty()) {
if (linejoin == QLatin1String("miter"))
- pen.setJoinStyle(Qt::SvgMiterJoin);
+ prop->setLineJoin(Qt::SvgMiterJoin);
else if (linejoin == QLatin1String("round"))
- pen.setJoinStyle(Qt::RoundJoin);
+ prop->setLineJoin(Qt::RoundJoin);
else if (linejoin == QLatin1String("bevel"))
- pen.setJoinStyle(Qt::BevelJoin);
+ prop->setLineJoin(Qt::BevelJoin);
}
//stroke-linecap attribute handling
if (!linecap.isEmpty()) {
if (linecap == QLatin1String("butt"))
- pen.setCapStyle(Qt::FlatCap);
+ prop->setLineCap(Qt::FlatCap);
else if (linecap == QLatin1String("round"))
- pen.setCapStyle(Qt::RoundCap);
+ prop->setLineCap(Qt::RoundCap);
else if (linecap == QLatin1String("square"))
- pen.setCapStyle(Qt::SquareCap);
- }
-
- //strok-dasharray attribute handling
- qreal penw = pen.widthF();
- if (!dashArray.isEmpty() && dashArray != QT_INHERIT) {
- const QChar *s = dashArray.constData();
- QVector<qreal> dashes = parseNumbersList(s);
- qreal *d = dashes.data();
- if (penw != 0)
- for (int i = 0; i < dashes.size(); ++i) {
- *d /= penw;
- ++d;
- }
- // if the dash count is odd the dashes should be duplicated
- if (dashes.size() % 2 != 0)
- dashes << QVector<qreal>(dashes);
- pen.setDashPattern(dashes);
- } else if (inherited) {
- QVector<qreal> dashes(inherited->qpen().dashPattern());
- qreal *d = dashes.data();
- if (!penw)
- penw = 1.0;
- qreal inheritpenw = inherited->qpen().widthF();
- if (!inheritpenw)
- inheritpenw = 1.0;
- for ( int i = 0; i < dashes.size(); ++i) {
- *d *= (inheritpenw/ penw);
- ++d;
- }
- pen.setDashPattern(dashes);
+ prop->setLineCap(Qt::SquareCap);
}
-
//stroke-dashoffset attribute handling
- if (!dashOffset.isEmpty() && dashOffset != QT_INHERIT) {
- qreal doffset = toDouble(dashOffset);
- if (penw != 0)
- doffset /= penw;
- pen.setDashOffset(doffset);
- } else if (inherited) {
- qreal doffset = pen.dashOffset();
- if (!penw)
- penw = 1.0;
- qreal inheritpenw = inherited->qpen().widthF();
- if (!inheritpenw)
- inheritpenw = 1.0;
- doffset *= (inheritpenw/ penw);
- pen.setDashOffset(doffset);
- }
+ if (!dashOffset.isEmpty() && dashOffset != QT_INHERIT)
+ prop->setDashOffset(toDouble(dashOffset));
//vector-effect attribute handling
if (!vectorEffect.isEmpty()) {
if (vectorEffect == QLatin1String("non-scaling-stroke"))
- pen.setCosmetic(true);
+ prop->setVectorEffect(true);
else if (vectorEffect == QLatin1String("none"))
- pen.setCosmetic(false);
+ prop->setVectorEffect(false);
}
+ //stroke-miterlimit
if (!miterlimit.isEmpty() && miterlimit != QT_INHERIT)
- pen.setMiterLimit(toDouble(miterlimit));
+ prop->setMiterLimit(toDouble(miterlimit));
+
+ //stroke-opacity atttribute handling
+ if (!opacity.isEmpty() && opacity != QT_INHERIT)
+ prop->setOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity))));
- QSvgStrokeStyle *prop = new QSvgStrokeStyle(pen);
- prop->setStroke(stroke);
node->appendStyleProperty(prop, myId);
}
}
@@ -2770,7 +2711,7 @@ static bool parseStopNode(QSvgStyleProperty *parent,
colorStr = QLatin1String("#000000");
}
- bool colorOK = constructColor(colorStr, opacityStr, color, handler);
+ constructColor(colorStr, opacityStr, color, handler);
QGradient *grad = style->qgradient();
@@ -2794,8 +2735,6 @@ static bool parseStopNode(QSvgStyleProperty *parent,
grad->setColorAt(offset, color);
style->setGradientStopsSet(true);
- if (!colorOK)
- style->addResolve(offset);
return true;
}
@@ -3422,15 +3361,28 @@ bool QSvgHandler::endElement(const QStringRef &localName)
QList<QSvgNode*>::iterator itr = ren.begin();
while (itr != ren.end()) {
QSvgNode *eleNode = *itr++;
- QSvgFillStyle *prop = static_cast<QSvgFillStyle*>(eleNode->styleProperty(QSvgStyleProperty::FILL));
- if (prop && !(prop->isGradientResolved())) {
- QString id = prop->getGradientId();
+ QSvgFillStyle *fill = static_cast<QSvgFillStyle*>(eleNode->styleProperty(QSvgStyleProperty::FILL));
+ if (fill && !(fill->isGradientResolved())) {
+ QString id = fill->gradientId();
+ QSvgStyleProperty *style = structureNode->scopeStyle(id);
+ if (style) {
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ fill->setFillStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
+ } else {
+ qWarning("Couldn't resolve property : %s",qPrintable(id));
+ fill->setBrush(QBrush(Qt::NoBrush));
+ }
+ }
+ QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle*>(eleNode->styleProperty(QSvgStyleProperty::STROKE));
+ if (stroke && !(stroke->isGradientResolved())) {
+ QString id = stroke->gradientId();
QSvgStyleProperty *style = structureNode->scopeStyle(id);
if (style) {
- prop->setFillStyle(style);
+ if (style->type() == QSvgStyleProperty::SOLID_COLOR || style->type() == QSvgStyleProperty::GRADIENT)
+ stroke->setStyle(reinterpret_cast<QSvgFillStyleProperty *>(style));
} else {
qWarning("Couldn't resolve property : %s",qPrintable(id));
- prop->setBrush(QBrush(Qt::NoBrush));
+ stroke->setStroke(QBrush(Qt::NoBrush));
}
}
}
diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp
index 33907fd..5f323b1 100644
--- a/src/svg/qsvgnode.cpp
+++ b/src/svg/qsvgnode.cpp
@@ -320,9 +320,9 @@ qreal QSvgNode::strokeWidth() const
{
QSvgStrokeStyle *stroke = static_cast<QSvgStrokeStyle*>(
styleProperty(QSvgStyleProperty::STROKE));
- if (!stroke || !stroke->strokePresent())
+ if (!stroke)
return 0;
- return stroke->qpen().widthF();
+ return stroke->width();
}
QT_END_NAMESPACE
diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp
index ef63443..6922ad9 100644
--- a/src/svg/qsvgstyle.cpp
+++ b/src/svg/qsvgstyle.cpp
@@ -59,9 +59,13 @@ QT_BEGIN_NAMESPACE
QSvgExtraStates::QSvgExtraStates()
: fillOpacity(1.0)
+ , strokeOpacity(1.0)
, svgFont(0)
, textAnchor(Qt::AlignLeft)
, fontWeight(400)
+ , fillRule(Qt::WindingFill)
+ , strokeDashOffset(0)
+ , vectorEffect(false)
{
}
@@ -69,6 +73,17 @@ QSvgStyleProperty::~QSvgStyleProperty()
{
}
+void QSvgFillStyleProperty::apply(QPainter *, const QRectF &, QSvgNode *, QSvgExtraStates &)
+{
+ Q_ASSERT(!"This should not be called!");
+}
+
+void QSvgFillStyleProperty::revert(QPainter *, QSvgExtraStates &)
+{
+ Q_ASSERT(!"This should not be called!");
+}
+
+
QSvgQualityStyle::QSvgQualityStyle(int color)
: m_colorRendering(color)
{
@@ -83,83 +98,55 @@ void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &)
}
-QSvgFillStyle::QSvgFillStyle(const QBrush &brush)
- : m_fill(brush)
- , m_style(0)
- , m_fillRuleSet(false)
- , m_fillRule(Qt::WindingFill)
- , m_fillOpacitySet(false)
- , m_fillOpacity(1.0)
- , m_oldOpacity(0)
- , m_gradientResolved(true)
- , m_fillSet(true)
-{
-}
-
-QSvgFillStyle::QSvgFillStyle(QSvgStyleProperty *style)
- : m_style(style)
- , m_fillRuleSet(false)
+QSvgFillStyle::QSvgFillStyle()
+ : m_style(0)
, m_fillRule(Qt::WindingFill)
- , m_fillOpacitySet(false)
+ , m_oldFillRule(Qt::WindingFill)
, m_fillOpacity(1.0)
- , m_oldOpacity(0)
- , m_gradientResolved(true)
- , m_fillSet(style != 0)
+ , m_oldFillOpacity(0)
+ , m_gradientResolved(1)
+ , m_fillRuleSet(0)
+ , m_fillOpacitySet(0)
+ , m_fillSet(0)
{
}
void QSvgFillStyle::setFillRule(Qt::FillRule f)
{
- m_fillRuleSet = true;
+ m_fillRuleSet = 1;
m_fillRule = f;
}
void QSvgFillStyle::setFillOpacity(qreal opacity)
{
- m_fillOpacitySet = true;
+ m_fillOpacitySet = 1;
m_fillOpacity = opacity;
}
-void QSvgFillStyle::setFillStyle(QSvgStyleProperty* style)
+void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style)
{
m_style = style;
- m_fillSet = true;
+ m_fillSet = 1;
}
void QSvgFillStyle::setBrush(QBrush brush)
{
m_fill = brush;
m_style = 0;
- m_fillSet = true;
+ m_fillSet = 1;
}
-static void recursivelySetFill(QSvgNode *node, Qt::FillRule f)
-{
- if (node->type() == QSvgNode::PATH) {
- QSvgPath *path = static_cast<QSvgPath*>(node);
- path->qpath()->setFillRule(f);
- } else if (node->type() == QSvgNode::POLYGON) {
- QSvgPolygon *polygon = static_cast<QSvgPolygon*>(node);
- polygon->setFillRule(f);
- } else if (node->type() == QSvgNode::G) {
- QList<QSvgNode*> renderers = static_cast<QSvgG*>(node)->renderers();
- foreach(QSvgNode *n, renderers) {
- recursivelySetFill(n, f);
- }
- }
-}
-void QSvgFillStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtraStates &states)
+void QSvgFillStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states)
{
m_oldFill = p->brush();
- m_oldOpacity = states.fillOpacity;
+ m_oldFillRule = states.fillRule;
+ m_oldFillOpacity = states.fillOpacity;
- if (m_fillRuleSet) {
- recursivelySetFill(node, m_fillRule);
- m_fillRuleSet = false;//set it only on the first run
- }
+ if (m_fillRuleSet)
+ states.fillRule = m_fillRule;
if (m_fillSet) {
if (m_style)
- m_style->apply(p, rect, node, states);
+ p->setBrush(m_style->brush(p, states));
else
p->setBrush(m_fill);
}
@@ -170,13 +157,11 @@ void QSvgFillStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgE
void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states)
{
if (m_fillOpacitySet)
- states.fillOpacity = m_oldOpacity;
- if (m_fillSet) {
- if (m_style)
- m_style->revert(p, states);
- else
- p->setBrush(m_oldFill);
- }
+ states.fillOpacity = m_oldFillOpacity;
+ if (m_fillSet)
+ p->setBrush(m_oldFill);
+ if (m_fillRuleSet)
+ states.fillRule = m_oldFillRule;
}
QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
@@ -286,47 +271,135 @@ void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states)
states.fontWeight = m_oldWeight;
}
-QSvgStrokeStyle::QSvgStrokeStyle(const QPen &pen)
- : m_stroke(pen), m_strokePresent(true)
+QSvgStrokeStyle::QSvgStrokeStyle()
+ : m_strokeOpacity(1.0)
+ , m_oldStrokeOpacity(0.0)
+ , m_vectorEffect(0)
+ , m_oldVectorEffect(0)
+ , m_strokeDashOffset(0)
+ , m_oldStrokeDashOffset(0)
+ , m_strokeSet(0)
+ , m_strokeDashArraySet(0)
+ , m_strokeDashOffsetSet(0)
+ , m_strokeLineCapSet(0)
+ , m_strokeLineJoinSet(0)
+ , m_strokeMiterLimitSet(0)
+ , m_strokeOpacitySet(0)
+ , m_strokeWidthSet(0)
+ , m_vectorEffectSet(0)
{
}
-void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
+void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states)
{
m_oldStroke = p->pen();
- if (!m_strokePresent || !m_stroke.widthF() || !m_stroke.color().alphaF()) {
- p->setPen(Qt::NoPen);
- } else {
- p->setPen(m_stroke);
+ m_oldStrokeOpacity = states.strokeOpacity;
+ m_oldStrokeDashOffset = states.strokeDashOffset;
+ m_oldVectorEffect = states.vectorEffect;
+
+ QPen pen = p->pen();
+
+ qreal oldWidth = pen.widthF();
+ qreal width = m_stroke.widthF();
+ if (oldWidth == 0)
+ oldWidth = 1;
+ if (width == 0)
+ width = 1;
+ qreal scale = oldWidth / width;
+
+ if (m_strokeOpacitySet)
+ states.strokeOpacity = m_strokeOpacity;
+
+ if (m_vectorEffectSet)
+ states.vectorEffect = m_vectorEffect;
+
+ if (m_strokeSet) {
+ if (m_style)
+ pen.setBrush(m_style->brush(p, states));
+ else
+ pen.setBrush(m_stroke.brush());
}
-}
-void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &)
-{
- p->setPen(m_oldStroke);
+ if (m_strokeWidthSet)
+ pen.setWidthF(m_stroke.widthF());
+
+ bool setDashOffsetNeeded = false;
+
+ if (m_strokeDashOffsetSet) {
+ states.strokeDashOffset = m_strokeDashOffset;
+ setDashOffsetNeeded = true;
+ }
+
+ if (m_strokeDashArraySet) {
+ if (m_stroke.style() == Qt::SolidLine) {
+ pen.setStyle(Qt::SolidLine);
+ } else if (m_strokeWidthSet || oldWidth == 1) {
+ // If both width and dash array was set, the dash array is already scaled correctly.
+ pen.setDashPattern(m_stroke.dashPattern());
+ setDashOffsetNeeded = true;
+ } else {
+ // If dash array was set, but not the width, the dash array has to be scaled with respect to the old width.
+ QVector<qreal> dashes = m_stroke.dashPattern();
+ for (int i = 0; i < dashes.size(); ++i)
+ dashes[i] /= oldWidth;
+ pen.setDashPattern(dashes);
+ setDashOffsetNeeded = true;
+ }
+ } else if (m_strokeWidthSet && pen.style() != Qt::SolidLine && scale != 1) {
+ // If the width was set, but not the dash array, the old dash array must be scaled with respect to the new width.
+ QVector<qreal> dashes = pen.dashPattern();
+ for (int i = 0; i < dashes.size(); ++i)
+ dashes[i] *= scale;
+ pen.setDashPattern(dashes);
+ setDashOffsetNeeded = true;
+ }
+
+ if (m_strokeLineCapSet)
+ pen.setCapStyle(m_stroke.capStyle());
+ if (m_strokeLineJoinSet)
+ pen.setJoinStyle(m_stroke.joinStyle());
+ if (m_strokeMiterLimitSet)
+ pen.setMiterLimit(m_stroke.miterLimit());
+
+ if (setDashOffsetNeeded) {
+ qreal currentWidth = pen.widthF();
+ if (currentWidth == 0)
+ currentWidth = 1;
+ pen.setDashOffset(states.strokeDashOffset / currentWidth);
+ }
+
+ pen.setCosmetic(states.vectorEffect);
+
+ p->setPen(pen);
}
-QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
- : m_solidColor(color)
+void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states)
{
+ p->setPen(m_oldStroke);
+ states.strokeOpacity = m_oldStrokeOpacity;
+ states.strokeDashOffset = m_oldStrokeDashOffset;
+ states.vectorEffect = m_oldVectorEffect;
}
-void QSvgSolidColorStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
+void QSvgStrokeStyle::setDashArray(const QVector<qreal> &dashes)
{
- m_oldFill = p->brush();
- m_oldStroke = p->pen();
- QBrush b = m_oldFill;
- b.setColor(m_solidColor);
- p->setBrush(b);
- QPen pen = m_oldStroke;
- pen.setColor(m_solidColor);
- p->setPen(pen);
+ if (m_strokeWidthSet) {
+ QVector<qreal> d = dashes;
+ qreal w = m_stroke.widthF();
+ if (w != 0 && w != 1) {
+ for (int i = 0; i < d.size(); ++i)
+ d[i] /= w;
+ }
+ m_stroke.setDashPattern(d);
+ } else {
+ m_stroke.setDashPattern(dashes);
+ }
+ m_strokeDashArraySet = 1;
}
-void QSvgSolidColorStyle::revert(QPainter *p, QSvgExtraStates &)
+QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
+ : m_solidColor(color)
{
- p->setBrush(m_oldFill);
- p->setPen(m_oldStroke);
}
QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
@@ -334,44 +407,24 @@ QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
{
}
-void QSvgGradientStyle::apply(QPainter *p, const QRectF &/*rect*/, QSvgNode *, QSvgExtraStates &)
+QBrush QSvgGradientStyle::brush(QPainter *, QSvgExtraStates &)
{
if (!m_link.isEmpty()) {
resolveStops();
}
- m_oldFill = p->brush();
-
- //resolving stop colors
- if (!m_resolvePoints.isEmpty()) {
- QColor color = p->brush().color();
- if (!color.isValid())
- color = p->pen().color();
- QList<qreal>::const_iterator itr = m_resolvePoints.constBegin();
- for (; itr != m_resolvePoints.constEnd(); ++itr) {
- //qDebug()<<"resolving "<<(*itr)<<" to "<<color;
- m_gradient->setColorAt(*itr, color);
- }
- }
-
// If the gradient is marked as empty, insert transparent black
if (!m_gradientStopsSet) {
m_gradient->setStops(QGradientStops() << QGradientStop(0.0, QColor(0, 0, 0, 0)));
m_gradientStopsSet = true;
}
- QBrush brush;
- brush = QBrush(*m_gradient);
+ QBrush b(*m_gradient);
if (!m_matrix.isIdentity())
- brush.setMatrix(m_matrix);
-
- p->setBrush(brush);
-}
+ b.setMatrix(m_matrix);
-void QSvgGradientStyle::revert(QPainter *p, QSvgExtraStates &)
-{
- p->setBrush(m_oldFill);
+ return b;
}
@@ -380,11 +433,6 @@ void QSvgGradientStyle::setMatrix(const QMatrix &mat)
m_matrix = mat;
}
-void QSvgGradientStyle::addResolve(qreal offset)
-{
- m_resolvePoints.append(offset);
-}
-
QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans)
: m_transform(trans)
{
@@ -490,14 +538,6 @@ void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtra
stroke->apply(p, rect, node, states);
}
- if (solidColor) {
- solidColor->apply(p, rect, node, states);
- }
-
- if (gradient) {
- gradient->apply(p, rect, node, states);
- }
-
if (transform) {
transform->apply(p, rect, node, states);
}
@@ -562,14 +602,6 @@ void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states)
stroke->revert(p, states);
}
- if (solidColor) {
- solidColor->revert(p, states);
- }
-
- if (gradient) {
- gradient->revert(p, states);
- }
-
//animated transforms need to be reverted _before_
//the native transforms
if (!animateTransforms.isEmpty()) {
@@ -815,7 +847,9 @@ void QSvgAnimateColor::apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgEx
if (totalTimeElapsed < m_from || m_finished)
return;
- qreal animationFrame = (totalTimeElapsed - m_from) / m_to;
+ qreal animationFrame = 0;
+ if (m_totalRunningTime != 0)
+ animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
m_finished = true;
diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h
index 056b73b..1cff561 100644
--- a/src/svg/qsvgstyle_p.h
+++ b/src/svg/qsvgstyle_p.h
@@ -143,9 +143,13 @@ struct QSvgExtraStates
QSvgExtraStates();
qreal fillOpacity;
+ qreal strokeOpacity;
QSvgFont *svgFont;
Qt::Alignment textAnchor;
int fontWeight;
+ Qt::FillRule fillRule;
+ qreal strokeDashOffset;
+ bool vectorEffect; // true if pen is cosmetic
};
class QSvgStyleProperty : public QSvgRefCounted
@@ -173,6 +177,14 @@ public:
virtual Type type() const=0;
};
+class QSvgFillStyleProperty : public QSvgStyleProperty
+{
+public:
+ virtual QBrush brush(QPainter *p, QSvgExtraStates &states) = 0;
+ virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
+ virtual void revert(QPainter *p, QSvgExtraStates &states);
+};
+
class QSvgQualityStyle : public QSvgStyleProperty
{
public:
@@ -220,15 +232,14 @@ private:
class QSvgFillStyle : public QSvgStyleProperty
{
public:
- QSvgFillStyle(const QBrush &brush);
- QSvgFillStyle(QSvgStyleProperty *style);
+ QSvgFillStyle();
virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
virtual void revert(QPainter *p, QSvgExtraStates &states);
virtual Type type() const;
void setFillRule(Qt::FillRule f);
void setFillOpacity(qreal opacity);
- void setFillStyle(QSvgStyleProperty* style);
+ void setFillStyle(QSvgFillStyleProperty* style);
void setBrush(QBrush brush);
const QBrush & qbrush() const
@@ -246,7 +257,7 @@ public:
return m_fillRule;
}
- QSvgStyleProperty* style() const
+ QSvgFillStyleProperty* style() const
{
return m_style;
}
@@ -256,7 +267,7 @@ public:
m_gradientId = Id;
}
- QString getGradientId() const
+ QString gradientId() const
{
return m_gradientId;
}
@@ -276,16 +287,19 @@ private:
// fill-opacity v v 'inherit' | <OpacityValue.datatype>
QBrush m_fill;
QBrush m_oldFill;
- QSvgStyleProperty *m_style;
+ QSvgFillStyleProperty *m_style;
- bool m_fillRuleSet;
Qt::FillRule m_fillRule;
- bool m_fillOpacitySet;
+ Qt::FillRule m_oldFillRule;
qreal m_fillOpacity;
- qreal m_oldOpacity;
+ qreal m_oldFillOpacity;
+
QString m_gradientId;
- bool m_gradientResolved;
- bool m_fillSet;
+ uint m_gradientResolved : 1;
+
+ uint m_fillRuleSet : 1;
+ uint m_fillOpacitySet : 1;
+ uint m_fillSet : 1;
};
class QSvgViewportFillStyle : public QSvgStyleProperty
@@ -384,36 +398,116 @@ private:
Qt::Alignment m_oldTextAnchor;
int m_oldWeight;
- unsigned m_familySet : 1;
- unsigned m_sizeSet : 1;
- unsigned m_styleSet : 1;
- unsigned m_variantSet : 1;
- unsigned m_weightSet : 1;
- unsigned m_textAnchorSet : 1;
+ uint m_familySet : 1;
+ uint m_sizeSet : 1;
+ uint m_styleSet : 1;
+ uint m_variantSet : 1;
+ uint m_weightSet : 1;
+ uint m_textAnchorSet : 1;
};
class QSvgStrokeStyle : public QSvgStyleProperty
{
public:
- QSvgStrokeStyle(const QPen &pen);
+ QSvgStrokeStyle();
virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
virtual void revert(QPainter *p, QSvgExtraStates &states);
virtual Type type() const;
- void setStroke(bool stroke)
+ void setStroke(QBrush brush)
+ {
+ m_stroke.setBrush(brush);
+ m_style = 0;
+ m_strokeSet = 1;
+ }
+
+ void setStyle(QSvgFillStyleProperty *style)
+ {
+ m_style = style;
+ m_strokeSet = 1;
+ }
+
+ void setDashArray(const QVector<qreal> &dashes);
+
+ void setDashArrayNone()
+ {
+ m_stroke.setStyle(Qt::SolidLine);
+ m_strokeDashArraySet = 1;
+ }
+
+ void setDashOffset(qreal offset)
{
- m_strokePresent = stroke;
+ m_strokeDashOffset = offset;
+ m_strokeDashOffsetSet = 1;
}
- bool strokePresent() const
+ void setLineCap(Qt::PenCapStyle cap)
{
- return m_strokePresent;
+ m_stroke.setCapStyle(cap);
+ m_strokeLineCapSet = 1;
}
- const QPen & qpen() const
+ void setLineJoin(Qt::PenJoinStyle join)
{
- return m_stroke;
+ m_stroke.setJoinStyle(join);
+ m_strokeLineJoinSet = 1;
+ }
+
+ void setMiterLimit(qreal limit)
+ {
+ m_stroke.setMiterLimit(limit);
+ m_strokeMiterLimitSet = 1;
+ }
+
+ void setOpacity(qreal opacity)
+ {
+ m_strokeOpacity = opacity;
+ m_strokeOpacitySet = 1;
+ }
+
+ void setWidth(qreal width)
+ {
+ m_stroke.setWidthF(width);
+ m_strokeWidthSet = 1;
+ Q_ASSERT(!m_strokeDashArraySet); // set width before dash array.
+ }
+
+ qreal width()
+ {
+ return m_stroke.widthF();
+ }
+
+ void setVectorEffect(bool nonScalingStroke)
+ {
+ m_vectorEffect = nonScalingStroke;
+ m_vectorEffectSet = 1;
+ }
+
+ QSvgFillStyleProperty* style() const
+ {
+ return m_style;
}
+
+ void setGradientId(const QString &Id)
+ {
+ m_gradientId = Id;
+ }
+
+ QString gradientId() const
+ {
+ return m_gradientId;
+ }
+
+ void setGradientResolved(bool resolved)
+ {
+ m_gradientResolved = resolved;
+ }
+
+ bool isGradientResolved() const
+ {
+ return m_gradientResolved;
+ }
+
private:
// stroke v v 'inherit' | <Paint.datatype>
// stroke-dasharray v v 'inherit' | <StrokeDashArrayValue.datatype>
@@ -425,22 +519,44 @@ private:
// stroke-width v v 'inherit' | <StrokeWidthValue.datatype>
QPen m_stroke;
QPen m_oldStroke;
- bool m_strokePresent;
-};
+ qreal m_strokeOpacity;
+ qreal m_oldStrokeOpacity;
+ qreal m_strokeDashOffset;
+ qreal m_oldStrokeDashOffset;
+ QSvgFillStyleProperty *m_style;
+ QString m_gradientId;
+ uint m_gradientResolved : 1;
+ uint m_vectorEffect : 1;
+ uint m_oldVectorEffect : 1;
+
+ uint m_strokeSet : 1;
+ uint m_strokeDashArraySet : 1;
+ uint m_strokeDashOffsetSet : 1;
+ uint m_strokeLineCapSet : 1;
+ uint m_strokeLineJoinSet : 1;
+ uint m_strokeMiterLimitSet : 1;
+ uint m_strokeOpacitySet : 1;
+ uint m_strokeWidthSet : 1;
+ uint m_vectorEffectSet : 1;
+};
-class QSvgSolidColorStyle : public QSvgStyleProperty
+class QSvgSolidColorStyle : public QSvgFillStyleProperty
{
public:
QSvgSolidColorStyle(const QColor &color);
- virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
- virtual void revert(QPainter *p, QSvgExtraStates &states);
virtual Type type() const;
const QColor & qcolor() const
{
return m_solidColor;
}
+
+ QBrush brush(QPainter *, QSvgExtraStates &)
+ {
+ return m_solidColor;
+ }
+
private:
// solid-color v x 'inherit' | <SVGColor.datatype>
// solid-opacity v x 'inherit' | <OpacityValue.datatype>
@@ -450,13 +566,11 @@ private:
QPen m_oldStroke;
};
-class QSvgGradientStyle : public QSvgStyleProperty
+class QSvgGradientStyle : public QSvgFillStyleProperty
{
public:
QSvgGradientStyle(QGradient *grad);
~QSvgGradientStyle() { delete m_gradient; }
- virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states);
- virtual void revert(QPainter *p, QSvgExtraStates &states);
virtual Type type() const;
void setStopLink(const QString &link, QSvgTinyDocument *doc);
@@ -474,8 +588,6 @@ public:
return m_gradient;
}
- void addResolve(qreal offset);
-
bool gradientStopsSet() const
{
return m_gradientStopsSet;
@@ -485,12 +597,10 @@ public:
{
m_gradientStopsSet = set;
}
+
+ QBrush brush(QPainter *, QSvgExtraStates &);
private:
QGradient *m_gradient;
- QList<qreal> m_resolvePoints;
-
- QBrush m_oldFill;
-
QMatrix m_matrix;
QSvgTinyDocument *m_doc;
diff --git a/src/svg/qsvgtinydocument.cpp b/src/svg/qsvgtinydocument.cpp
index 614494f..ba610e7 100644
--- a/src/svg/qsvgtinydocument.cpp
+++ b/src/svg/qsvgtinydocument.cpp
@@ -240,7 +240,9 @@ void QSvgTinyDocument::draw(QPainter *p, const QRectF &bounds)
//sets default style on the painter
//### not the most optimal way
mapSourceToTarget(p, bounds);
- p->setPen(Qt::NoPen);
+ QPen pen(QBrush(Qt::NoBrush), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+ pen.setMiterLimit(4);
+ p->setPen(pen);
p->setBrush(Qt::black);
p->setRenderHint(QPainter::Antialiasing);
p->setRenderHint(QPainter::SmoothPixmapTransform);
diff --git a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp
index 3d0d0c3..a57e793 100644
--- a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp
+++ b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp
@@ -756,7 +756,7 @@ static void opacity_drawSvgAndVerify(const QByteArray &data)
void tst_QSvgRenderer::opacity()
{
- static const char *opacities[] = {"-1,4641", "0", "0.5", "1", "1.337"};
+ static const char *opacities[] = {"-1.4641", "0", "0.5", "1", "1.337"};
static const char *firstColors[] = {"#7f7f7f", "#7f7f7f", "#402051", "blue", "#123456"};
static const char *secondColors[] = {"red", "#bad", "#bedead", "#7f7f7f", "#7f7f7f"};
@@ -796,8 +796,6 @@ void tst_QSvgRenderer::opacity()
data.append("\"/></svg>");
opacity_drawSvgAndVerify(data);
}
- // When support for gradients on strokes has been implemented, add the code below.
- /*
// Stroke-opacity
for (int i = 0; i < 5; ++i) {
QByteArray data("<svg viewBox=\"0 0 10 10\"><defs><linearGradient id=\"grad\"><stop offset=\"0\" stop-color=\"");
@@ -811,7 +809,6 @@ void tst_QSvgRenderer::opacity()
data.append("\" /></svg>");
opacity_drawSvgAndVerify(data);
}
- */
}
void tst_QSvgRenderer::paths()