summaryrefslogtreecommitdiffstats
path: root/src/svg/qsvgstyle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/svg/qsvgstyle.cpp')
-rw-r--r--src/svg/qsvgstyle.cpp448
1 files changed, 294 insertions, 154 deletions
diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp
index a19dab6..e3b75d0 100644
--- a/src/svg/qsvgstyle.cpp
+++ b/src/svg/qsvgstyle.cpp
@@ -59,6 +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)
{
}
@@ -66,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)
{
@@ -80,65 +98,70 @@ void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &)
}
-QSvgFillStyle::QSvgFillStyle(const QBrush &brush)
- : m_fill(brush), m_style(0), m_fillRuleSet(false), m_fillRule(Qt::OddEvenFill),
- m_fillOpacitySet(false), m_fillOpacity(0),m_oldOpacity(0)
-{
-}
-
-QSvgFillStyle::QSvgFillStyle(QSvgStyleProperty *style)
- : m_style(style), m_fillRuleSet(false), m_fillRule(Qt::OddEvenFill),
- m_fillOpacitySet(false), m_fillOpacity(0),m_oldOpacity(0)
+QSvgFillStyle::QSvgFillStyle()
+ : m_style(0)
+ , m_fillRule(Qt::WindingFill)
+ , m_oldFillRule(Qt::WindingFill)
+ , m_fillOpacity(1.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;
}
-static void recursivelySetFill(QSvgNode *node, Qt::FillRule f)
+void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style)
{
- if (node->type() == QSvgNode::PATH) {
- QSvgPath *path = static_cast<QSvgPath*>(node);
- path->qpath()->setFillRule(f);
- } else if (node->type() == QSvgNode::G) {
- QList<QSvgNode*> renderers = static_cast<QSvgG*>(node)->renderers();
- foreach(QSvgNode *n, renderers) {
- recursivelySetFill(n, f);
- }
- }
+ m_style = style;
+ m_fillSet = 1;
}
-void QSvgFillStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtraStates &states)
+
+void QSvgFillStyle::setBrush(QBrush brush)
+{
+ m_fill = brush;
+ m_style = 0;
+ m_fillSet = 1;
+}
+
+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)
+ p->setBrush(m_style->brush(p, states));
+ else
+ p->setBrush(m_fill);
}
- p->setBrush(m_fill);
if (m_fillOpacitySet)
states.fillOpacity = m_fillOpacity;
- if (m_style)
- m_style->apply(p, rect, node, states);
}
void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states)
{
- if (m_style)
- m_style->revert(p, states);
- p->setBrush(m_oldFill);
if (m_fillOpacitySet)
- states.fillOpacity = m_oldOpacity;
+ states.fillOpacity = m_oldFillOpacity;
+ if (m_fillSet)
+ p->setBrush(m_oldFill);
+ if (m_fillRuleSet)
+ states.fillRule = m_oldFillRule;
}
QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
@@ -158,78 +181,227 @@ void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &)
}
QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc)
- : m_font(font), m_pointSize(24), m_doc(doc)
-{
-}
+ : m_svgFont(font)
+ , m_doc(doc)
+ , m_familySet(0)
+ , m_sizeSet(0)
+ , m_styleSet(0)
+ , m_variantSet(0)
+ , m_weightSet(0)
+ , m_textAnchorSet(0)
+{
+}
+
+QSvgFontStyle::QSvgFontStyle()
+ : m_svgFont(0)
+ , m_doc(0)
+ , m_familySet(0)
+ , m_sizeSet(0)
+ , m_styleSet(0)
+ , m_variantSet(0)
+ , m_weightSet(0)
+ , m_textAnchorSet(0)
+{
+}
+
+int QSvgFontStyle::SVGToQtWeight(int weight) {
+ switch (weight) {
+ case 100:
+ case 200:
+ return QFont::Light;
+ case 300:
+ case 400:
+ return QFont::Normal;
+ case 500:
+ case 600:
+ return QFont::DemiBold;
+ case 700:
+ case 800:
+ return QFont::Bold;
+ case 900:
+ return QFont::Black;
+ }
+ return QFont::Normal;
+}
+
+void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states)
+{
+ m_oldQFont = p->font();
+ m_oldSvgFont = states.svgFont;
+ m_oldTextAnchor = states.textAnchor;
+ m_oldWeight = states.fontWeight;
+
+ if (m_textAnchorSet)
+ states.textAnchor = m_textAnchor;
+
+ QFont font = m_oldQFont;
+ if (m_familySet) {
+ states.svgFont = m_svgFont;
+ font.setFamily(m_qfont.family());
+ }
+
+ if (m_sizeSet)
+ font.setPointSize(m_qfont.pointSizeF());
+
+ if (m_styleSet)
+ font.setStyle(m_qfont.style());
+
+ if (m_variantSet)
+ font.setCapitalization(m_qfont.capitalization());
+
+ if (m_weightSet) {
+ if (m_weight == BOLDER) {
+ states.fontWeight = qMin(states.fontWeight + 100, 900);
+ } else if (m_weight == LIGHTER) {
+ states.fontWeight = qMax(states.fontWeight - 100, 100);
+ } else {
+ states.fontWeight = m_weight;
+ }
+ font.setWeight(SVGToQtWeight(states.fontWeight));
+ }
-QSvgFontStyle::QSvgFontStyle(const QFont &font, QSvgTinyDocument *doc)
- : m_font(0), m_pointSize(24), m_doc(doc), m_qfont(font)
-{
+ p->setFont(font);
}
-
-void QSvgFontStyle::setPointSize(qreal size)
+void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states)
{
- m_pointSize = size;
+ p->setFont(m_oldQFont);
+ states.svgFont = m_oldSvgFont;
+ states.textAnchor = m_oldTextAnchor;
+ states.fontWeight = m_oldWeight;
}
-qreal QSvgFontStyle::pointSize() const
+QSvgStrokeStyle::QSvgStrokeStyle()
+ : m_strokeOpacity(1.0)
+ , m_oldStrokeOpacity(0.0)
+ , m_strokeDashOffset(0)
+ , m_oldStrokeDashOffset(0)
+ , m_style(0)
+ , m_gradientResolved(1)
+ , m_vectorEffect(0)
+ , m_oldVectorEffect(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)
{
- return m_pointSize;
}
-void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
+void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states)
{
- if (!m_font) {
- m_oldFont = p->font();
- p->setFont(m_qfont);
+ m_oldStroke = p->pen();
+ 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());
+ }
+
+ 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;
}
-}
-void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &)
-{
- if (!m_font) {
- p->setFont(m_oldFont);
+ 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);
}
-}
-QSvgStrokeStyle::QSvgStrokeStyle(const QPen &pen)
- : m_stroke(pen)
-{
-}
+ pen.setCosmetic(states.vectorEffect);
-void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &)
-{
- m_oldStroke = p->pen();
- p->setPen(m_stroke);
+ p->setPen(pen);
}
-void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &)
+void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states)
{
p->setPen(m_oldStroke);
+ states.strokeOpacity = m_oldStrokeOpacity;
+ states.strokeDashOffset = m_oldStrokeDashOffset;
+ states.vectorEffect = m_oldVectorEffect;
}
-QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
- : m_solidColor(color)
-{
-}
-
-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)
@@ -237,44 +409,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;
}
@@ -283,11 +435,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)
{
@@ -393,14 +540,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);
}
@@ -412,10 +551,25 @@ void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtra
//animated transforms have to be applied
//_after_ the original object transformations
if (!animateTransforms.isEmpty()) {
- QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr;
- for (itr = animateTransforms.constBegin(); itr != animateTransforms.constEnd();
- ++itr) {
- (*itr)->apply(p, rect, node, states);
+ qreal totalTimeElapsed = node->document()->currentElapsed();
+ // Find the last animateTransform with additive="replace", since this will override all
+ // previous animateTransforms.
+ QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constEnd();
+ do {
+ --itr;
+ if ((*itr)->animActive(totalTimeElapsed)
+ && (*itr)->additiveType() == QSvgAnimateTransform::Replace) {
+ // An animateTransform with additive="replace" will replace the transform attribute.
+ if (transform)
+ transform->revert(p, states);
+ break;
+ }
+ } while (itr != animateTransforms.constBegin());
+
+ // Apply the animateTransforms after and including the last one with additive="replace".
+ for (; itr != animateTransforms.constEnd(); ++itr) {
+ if ((*itr)->animActive(totalTimeElapsed))
+ (*itr)->apply(p, rect, node, states);
}
}
@@ -450,24 +604,18 @@ 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()) {
- QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr;
- itr = animateTransforms.constBegin();
- //only need to rever the first one because that
- //one has the original world matrix for the primitve
- if (itr != animateTransforms.constEnd()) {
- (*itr)->revert(p, states);
+ QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constBegin();
+ for (; itr != animateTransforms.constEnd(); ++itr) {
+ if ((*itr)->transformApplied()) {
+ (*itr)->revert(p, states);
+ break;
+ }
}
+ for (; itr != animateTransforms.constEnd(); ++itr)
+ (*itr)->clearTransformApplied();
}
if (transform) {
@@ -490,15 +638,16 @@ void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states)
QSvgAnimateTransform::QSvgAnimateTransform(int startMs, int endMs, int byMs )
: QSvgStyleProperty(),
m_from(startMs), m_to(endMs), m_by(byMs),
- m_type(Empty), m_count(0), m_finished(false)
+ m_type(Empty), m_additive(Replace), m_count(0), m_finished(false), m_transformApplied(false)
{
m_totalRunningTime = m_to - m_from;
}
-void QSvgAnimateTransform::setArgs(TransformType type, const QVector<qreal> &args)
+void QSvgAnimateTransform::setArgs(TransformType type, Additive additive, const QVector<qreal> &args)
{
m_type = type;
m_args = args;
+ m_additive = additive;
Q_ASSERT(!(args.count()%3));
m_count = args.count() / 3;
}
@@ -507,15 +656,14 @@ void QSvgAnimateTransform::apply(QPainter *p, const QRectF &, QSvgNode *node, QS
{
m_oldWorldTransform = p->worldTransform();
resolveMatrix(node);
- if (!m_finished || m_freeze)
- p->setWorldTransform(m_transform, true);
+ p->setWorldTransform(m_transform, true);
+ m_transformApplied = true;
}
void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &)
{
- if (!m_finished || m_freeze) {
- p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
- }
+ p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
+ m_transformApplied = false;
}
void QSvgAnimateTransform::resolveMatrix(QSvgNode *node)
@@ -701,7 +849,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;
@@ -765,18 +915,8 @@ QSvgStyleProperty::Type QSvgAnimateColor::type() const
return ANIMATE_COLOR;
}
-QString QSvgFontStyle::textAnchor() const
-{
- return m_textAnchor;
-}
-
-void QSvgFontStyle::setTextAnchor(const QString &anchor)
-{
- m_textAnchor = anchor;
-}
-
QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity)
- : m_opacity(opacity)
+ : m_opacity(opacity), m_oldOpacity(0)
{
}