summaryrefslogtreecommitdiff
path: root/src/svg/qsvgstyle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/svg/qsvgstyle.cpp')
-rw-r--r--src/svg/qsvgstyle.cpp958
1 files changed, 958 insertions, 0 deletions
diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp
new file mode 100644
index 0000000..06adec5
--- /dev/null
+++ b/src/svg/qsvgstyle.cpp
@@ -0,0 +1,958 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSvg module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsvgstyle_p.h"
+
+#ifndef QT_NO_SVG
+
+#include "qsvgfont_p.h"
+#include "qsvggraphics_p.h"
+#include "qsvgnode_p.h"
+#include "qsvgtinydocument_p.h"
+
+#include "qpainter.h"
+#include "qpair.h"
+#include "qcolor.h"
+#include "qdebug.h"
+#include "qmath.h"
+#include "qnumeric.h"
+
+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)
+{
+}
+
+QSvgStyleProperty::~QSvgStyleProperty()
+{
+}
+
+void QSvgFillStyleProperty::apply(QPainter *, const 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)
+{
+
+}
+void QSvgQualityStyle::apply(QPainter *, const QSvgNode *, QSvgExtraStates &)
+{
+
+}
+void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &)
+{
+
+}
+
+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 = 1;
+ m_fillRule = f;
+}
+
+void QSvgFillStyle::setFillOpacity(qreal opacity)
+{
+ m_fillOpacitySet = 1;
+ m_fillOpacity = opacity;
+}
+
+void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style)
+{
+ m_style = style;
+ m_fillSet = 1;
+}
+
+void QSvgFillStyle::setBrush(QBrush brush)
+{
+ m_fill = brush;
+ m_style = 0;
+ m_fillSet = 1;
+}
+
+void QSvgFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
+{
+ m_oldFill = p->brush();
+ m_oldFillRule = states.fillRule;
+ m_oldFillOpacity = states.fillOpacity;
+
+ 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);
+ }
+ if (m_fillOpacitySet)
+ states.fillOpacity = m_fillOpacity;
+}
+
+void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states)
+{
+ if (m_fillOpacitySet)
+ states.fillOpacity = m_oldFillOpacity;
+ if (m_fillSet)
+ p->setBrush(m_oldFill);
+ if (m_fillRuleSet)
+ states.fillRule = m_oldFillRule;
+}
+
+QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
+ : m_viewportFill(brush)
+{
+}
+
+void QSvgViewportFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
+{
+ m_oldFill = p->brush();
+ p->setBrush(m_viewportFill);
+}
+
+void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setBrush(m_oldFill);
+}
+
+QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *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 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.setPointSizeF(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));
+ }
+
+ p->setFont(font);
+}
+
+void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states)
+{
+ p->setFont(m_oldQFont);
+ states.svgFont = m_oldSvgFont;
+ states.textAnchor = m_oldTextAnchor;
+ states.fontWeight = m_oldWeight;
+}
+
+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)
+{
+}
+
+void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
+{
+ 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;
+ }
+
+ if (m_strokeLineCapSet)
+ pen.setCapStyle(m_stroke.capStyle());
+ if (m_strokeLineJoinSet)
+ pen.setJoinStyle(m_stroke.joinStyle());
+ if (m_strokeMiterLimitSet)
+ pen.setMiterLimit(m_stroke.miterLimit());
+
+ // You can have dash offset on solid strokes in SVG files, but not in Qt.
+ // QPen::setDashOffset() will set the pen style to Qt::CustomDashLine,
+ // so don't call the method if the pen is solid.
+ if (setDashOffsetNeeded && pen.style() != Qt::SolidLine) {
+ qreal currentWidth = pen.widthF();
+ if (currentWidth == 0)
+ currentWidth = 1;
+ pen.setDashOffset(states.strokeDashOffset / currentWidth);
+ }
+
+ pen.setCosmetic(states.vectorEffect);
+
+ p->setPen(pen);
+}
+
+void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states)
+{
+ p->setPen(m_oldStroke);
+ states.strokeOpacity = m_oldStrokeOpacity;
+ states.strokeDashOffset = m_oldStrokeDashOffset;
+ states.vectorEffect = m_oldVectorEffect;
+}
+
+void QSvgStrokeStyle::setDashArray(const QVector<qreal> &dashes)
+{
+ 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;
+}
+
+QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
+ : m_solidColor(color)
+{
+}
+
+QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
+ : m_gradient(grad), m_gradientStopsSet(false)
+{
+}
+
+QBrush QSvgGradientStyle::brush(QPainter *, QSvgExtraStates &)
+{
+ if (!m_link.isEmpty()) {
+ resolveStops();
+ }
+
+ // 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 b(*m_gradient);
+
+ if (!m_matrix.isIdentity())
+ b.setMatrix(m_matrix);
+
+ return b;
+}
+
+
+void QSvgGradientStyle::setMatrix(const QMatrix &mat)
+{
+ m_matrix = mat;
+}
+
+QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans)
+ : m_transform(trans)
+{
+}
+
+void QSvgTransformStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
+{
+ m_oldWorldTransform = p->worldTransform();
+ p->setWorldTransform(m_transform, true);
+}
+
+void QSvgTransformStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
+}
+
+QSvgStyleProperty::Type QSvgQualityStyle::type() const
+{
+ return QUALITY;
+}
+
+QSvgStyleProperty::Type QSvgFillStyle::type() const
+{
+ return FILL;
+}
+
+QSvgStyleProperty::Type QSvgViewportFillStyle::type() const
+{
+ return VIEWPORT_FILL;
+}
+
+QSvgStyleProperty::Type QSvgFontStyle::type() const
+{
+ return FONT;
+}
+
+QSvgStyleProperty::Type QSvgStrokeStyle::type() const
+{
+ return STROKE;
+}
+
+QSvgStyleProperty::Type QSvgSolidColorStyle::type() const
+{
+ return SOLID_COLOR;
+}
+
+QSvgStyleProperty::Type QSvgGradientStyle::type() const
+{
+ return GRADIENT;
+}
+
+QSvgStyleProperty::Type QSvgTransformStyle::type() const
+{
+ return TRANSFORM;
+}
+
+
+QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode)
+ : m_mode(mode)
+{
+
+}
+
+void QSvgCompOpStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
+{
+ m_oldMode = p->compositionMode();
+ p->setCompositionMode(m_mode);
+}
+
+void QSvgCompOpStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setCompositionMode(m_oldMode);
+}
+
+QSvgStyleProperty::Type QSvgCompOpStyle::type() const
+{
+ return COMP_OP;
+}
+
+QSvgStyle::~QSvgStyle()
+{
+}
+
+void QSvgStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states)
+{
+ if (quality) {
+ quality->apply(p, node, states);
+ }
+
+ if (fill) {
+ fill->apply(p, node, states);
+ }
+
+ if (viewportFill) {
+ viewportFill->apply(p, node, states);
+ }
+
+ if (font) {
+ font->apply(p, node, states);
+ }
+
+ if (stroke) {
+ stroke->apply(p, node, states);
+ }
+
+ if (transform) {
+ transform->apply(p, node, states);
+ }
+
+ if (animateColor) {
+ animateColor->apply(p, node, states);
+ }
+
+ //animated transforms have to be applied
+ //_after_ the original object transformations
+ if (!animateTransforms.isEmpty()) {
+ 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, node, states);
+ }
+ }
+
+ if (opacity) {
+ opacity->apply(p, node, states);
+ }
+
+ if (compop) {
+ compop->apply(p, node, states);
+ }
+}
+
+void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states)
+{
+ if (quality) {
+ quality->revert(p, states);
+ }
+
+ if (fill) {
+ fill->revert(p, states);
+ }
+
+ if (viewportFill) {
+ viewportFill->revert(p, states);
+ }
+
+ if (font) {
+ font->revert(p, states);
+ }
+
+ if (stroke) {
+ stroke->revert(p, states);
+ }
+
+ //animated transforms need to be reverted _before_
+ //the native transforms
+ if (!animateTransforms.isEmpty()) {
+ 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) {
+ transform->revert(p, states);
+ }
+
+ if (animateColor) {
+ animateColor->revert(p, states);
+ }
+
+ if (opacity) {
+ opacity->revert(p, states);
+ }
+
+ if (compop) {
+ compop->revert(p, states);
+ }
+}
+
+QSvgAnimateTransform::QSvgAnimateTransform(int startMs, int endMs, int byMs )
+ : QSvgStyleProperty(),
+ m_from(startMs), m_to(endMs), m_by(byMs),
+ 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, 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;
+}
+
+void QSvgAnimateTransform::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &)
+{
+ m_oldWorldTransform = p->worldTransform();
+ resolveMatrix(node);
+ p->setWorldTransform(m_transform, true);
+ m_transformApplied = true;
+}
+
+void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
+ m_transformApplied = false;
+}
+
+void QSvgAnimateTransform::resolveMatrix(const QSvgNode *node)
+{
+ static const qreal deg2rad = qreal(0.017453292519943295769);
+ qreal totalTimeElapsed = node->document()->currentElapsed();
+ if (totalTimeElapsed < m_from || m_finished)
+ return;
+
+ qreal animationFrame = 0;
+ if (m_totalRunningTime != 0) {
+ animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
+
+ if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
+ m_finished = true;
+ animationFrame = m_repeatCount;
+ }
+ }
+
+ qreal percentOfAnimation = animationFrame;
+ if (percentOfAnimation > 1) {
+ percentOfAnimation -= ((int)percentOfAnimation);
+ }
+
+ qreal currentPosition = percentOfAnimation * (m_count - 1);
+ int startElem = qFloor(currentPosition);
+ int endElem = qCeil(currentPosition);
+
+ switch(m_type)
+ {
+ case Translate: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1, from2;
+ qreal to1, to2;
+ from1 = m_args[startElem++];
+ from2 = m_args[startElem++];
+ to1 = m_args[endElem++];
+ to2 = m_args[endElem++];
+
+ qreal transXDiff = (to1-from1) * percentOfAnimation;
+ qreal transX = from1 + transXDiff;
+ qreal transYDiff = (to2-from2) * percentOfAnimation;
+ qreal transY = from2 + transYDiff;
+ m_transform = QTransform();
+ m_transform.translate(transX, transY);
+ break;
+ }
+ case Scale: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1, from2;
+ qreal to1, to2;
+ from1 = m_args[startElem++];
+ from2 = m_args[startElem++];
+ to1 = m_args[endElem++];
+ to2 = m_args[endElem++];
+
+ qreal transXDiff = (to1-from1) * percentOfAnimation;
+ qreal transX = from1 + transXDiff;
+ qreal transYDiff = (to2-from2) * percentOfAnimation;
+ qreal transY = from2 + transYDiff;
+ if (transY == 0)
+ transY = transX;
+ m_transform = QTransform();
+ m_transform.scale(transX, transY);
+ break;
+ }
+ case Rotate: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1, from2, from3;
+ qreal to1, to2, to3;
+ from1 = m_args[startElem++];
+ from2 = m_args[startElem++];
+ from3 = m_args[startElem++];
+ to1 = m_args[endElem++];
+ to2 = m_args[endElem++];
+ to3 = m_args[endElem++];
+
+ qreal rotationDiff = (to1 - from1) * percentOfAnimation;
+ //qreal rotation = from1 + rotationDiff;
+
+ qreal transXDiff = (to2-from2) * percentOfAnimation;
+ qreal transX = from2 + transXDiff;
+ qreal transYDiff = (to3-from3) * percentOfAnimation;
+ qreal transY = from3 + transYDiff;
+ m_transform = QTransform();
+ m_transform.translate(transX, transY);
+ m_transform.rotate(rotationDiff);
+ m_transform.translate(-transX, -transY);
+ break;
+ }
+ case SkewX: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1;
+ qreal to1;
+ from1 = m_args[startElem++];
+ to1 = m_args[endElem++];
+
+ qreal transXDiff = (to1-from1) * percentOfAnimation;
+ qreal transX = from1 + transXDiff;
+ m_transform = QTransform();
+ m_transform.shear(qTan(transX * deg2rad), 0);
+ break;
+ }
+ case SkewY: {
+ startElem *= 3;
+ endElem *= 3;
+ qreal from1;
+ qreal to1;
+ from1 = m_args[startElem++];
+ to1 = m_args[endElem++];
+
+
+ qreal transYDiff = (to1 - from1) * percentOfAnimation;
+ qreal transY = from1 + transYDiff;
+ m_transform = QTransform();
+ m_transform.shear(0, qTan(transY * deg2rad));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+QSvgStyleProperty::Type QSvgAnimateTransform::type() const
+{
+ return ANIMATE_TRANSFORM;
+}
+
+void QSvgAnimateTransform::setFreeze(bool freeze)
+{
+ m_freeze = freeze;
+}
+
+void QSvgAnimateTransform::setRepeatCount(qreal repeatCount)
+{
+ m_repeatCount = repeatCount;
+}
+
+QSvgAnimateColor::QSvgAnimateColor(int startMs, int endMs, int byMs)
+ : QSvgStyleProperty(),
+ m_from(startMs), m_to(endMs), m_by(byMs),
+ m_finished(false)
+{
+ m_totalRunningTime = m_to - m_from;
+}
+
+void QSvgAnimateColor::setArgs(bool fill,
+ const QList<QColor> &colors)
+{
+ m_fill = fill;
+ m_colors = colors;
+}
+
+void QSvgAnimateColor::setFreeze(bool freeze)
+{
+ m_freeze = freeze;
+}
+
+void QSvgAnimateColor::setRepeatCount(qreal repeatCount)
+{
+ m_repeatCount = repeatCount;
+}
+
+void QSvgAnimateColor::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &)
+{
+ qreal totalTimeElapsed = node->document()->currentElapsed();
+ if (totalTimeElapsed < m_from || m_finished)
+ return;
+
+ qreal animationFrame = 0;
+ if (m_totalRunningTime != 0)
+ animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
+
+ if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
+ m_finished = true;
+ animationFrame = m_repeatCount;
+ }
+
+ qreal percentOfAnimation = animationFrame;
+ if (percentOfAnimation > 1) {
+ percentOfAnimation -= ((int)percentOfAnimation);
+ }
+
+ qreal currentPosition = percentOfAnimation * (m_colors.count() - 1);
+
+ int startElem = qFloor(currentPosition);
+ int endElem = qCeil(currentPosition);
+ QColor start = m_colors[startElem];
+ QColor end = m_colors[endElem];
+
+ qreal percentOfColorMorph = currentPosition;
+ if (percentOfColorMorph > 1) {
+ percentOfColorMorph -= ((int)percentOfColorMorph);
+ }
+
+ // Interpolate between the two fixed colors start and end
+ qreal aDiff = (end.alpha() - start.alpha()) * percentOfColorMorph;
+ qreal rDiff = (end.red() - start.red()) * percentOfColorMorph;
+ qreal gDiff = (end.green() - start.green()) * percentOfColorMorph;
+ qreal bDiff = (end.blue() - start.blue()) * percentOfColorMorph;
+
+ int alpha = int(start.alpha() + aDiff);
+ int red = int(start.red() + rDiff);
+ int green = int(start.green() + gDiff);
+ int blue = int(start.blue() + bDiff);
+
+ QColor color(red, green, blue, alpha);
+
+ if (m_fill) {
+ QBrush b = p->brush();
+ m_oldBrush = b;
+ b.setColor(color);
+ p->setBrush(b);
+ } else {
+ QPen pen = p->pen();
+ m_oldPen = pen;
+ pen.setColor(color);
+ p->setPen(pen);
+ }
+}
+
+void QSvgAnimateColor::revert(QPainter *p, QSvgExtraStates &)
+{
+ if (m_fill) {
+ p->setBrush(m_oldBrush);
+ } else {
+ p->setPen(m_oldPen);
+ }
+}
+
+QSvgStyleProperty::Type QSvgAnimateColor::type() const
+{
+ return ANIMATE_COLOR;
+}
+
+QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity)
+ : m_opacity(opacity), m_oldOpacity(0)
+{
+
+}
+
+void QSvgOpacityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
+{
+ m_oldOpacity = p->opacity();
+ p->setOpacity(m_opacity * m_oldOpacity);
+}
+
+void QSvgOpacityStyle::revert(QPainter *p, QSvgExtraStates &)
+{
+ p->setOpacity(m_oldOpacity);
+}
+
+QSvgStyleProperty::Type QSvgOpacityStyle::type() const
+{
+ return OPACITY;
+}
+
+void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc)
+{
+ m_link = link;
+ m_doc = doc;
+}
+
+void QSvgGradientStyle::resolveStops()
+{
+ if (!m_link.isEmpty() && m_doc) {
+ QSvgStyleProperty *prop = m_doc->styleProperty(m_link);
+ if (prop) {
+ if (prop->type() == QSvgStyleProperty::GRADIENT) {
+ QSvgGradientStyle *st =
+ static_cast<QSvgGradientStyle*>(prop);
+ st->resolveStops();
+ m_gradient->setStops(st->qgradient()->stops());
+ m_gradientStopsSet = st->gradientStopsSet();
+ }
+ } else {
+ qWarning("Could not resolve property : %s", qPrintable(m_link));
+ }
+ m_link = QString();
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_SVG