1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt SVG module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qsvgstyle_p.h"
41 
42 #include "qsvgfont_p.h"
43 #include "qsvggraphics_p.h"
44 #include "qsvgnode_p.h"
45 #include "qsvgtinydocument_p.h"
46 
47 #include "qpainter.h"
48 #include "qpair.h"
49 #include "qcolor.h"
50 #include "qdebug.h"
51 #include "qmath.h"
52 #include "qnumeric.h"
53 
54 QT_BEGIN_NAMESPACE
55 
QSvgExtraStates()56 QSvgExtraStates::QSvgExtraStates()
57     : fillOpacity(1.0)
58     , strokeOpacity(1.0)
59     , svgFont(0)
60     , textAnchor(Qt::AlignLeft)
61     , fontWeight(400)
62     , fillRule(Qt::WindingFill)
63     , strokeDashOffset(0)
64     , vectorEffect(false)
65 {
66 }
67 
~QSvgStyleProperty()68 QSvgStyleProperty::~QSvgStyleProperty()
69 {
70 }
71 
apply(QPainter *,const QSvgNode *,QSvgExtraStates &)72 void QSvgFillStyleProperty::apply(QPainter *, const QSvgNode *, QSvgExtraStates &)
73 {
74     Q_ASSERT(!"This should not be called!");
75 }
76 
revert(QPainter *,QSvgExtraStates &)77 void QSvgFillStyleProperty::revert(QPainter *, QSvgExtraStates &)
78 {
79     Q_ASSERT(!"This should not be called!");
80 }
81 
82 
QSvgQualityStyle(int color)83 QSvgQualityStyle::QSvgQualityStyle(int color)
84 {
85     Q_UNUSED(color);
86 }
apply(QPainter *,const QSvgNode *,QSvgExtraStates &)87 void QSvgQualityStyle::apply(QPainter *, const QSvgNode *, QSvgExtraStates &)
88 {
89 
90 }
revert(QPainter *,QSvgExtraStates &)91 void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &)
92 {
93 
94 }
95 
QSvgFillStyle()96 QSvgFillStyle::QSvgFillStyle()
97     : m_style(0)
98     , m_fillRule(Qt::WindingFill)
99     , m_oldFillRule(Qt::WindingFill)
100     , m_fillOpacity(1.0)
101     , m_oldFillOpacity(0)
102     , m_gradientResolved(1)
103     , m_fillRuleSet(0)
104     , m_fillOpacitySet(0)
105     , m_fillSet(0)
106 {
107 }
108 
setFillRule(Qt::FillRule f)109 void QSvgFillStyle::setFillRule(Qt::FillRule f)
110 {
111     m_fillRuleSet = 1;
112     m_fillRule = f;
113 }
114 
setFillOpacity(qreal opacity)115 void QSvgFillStyle::setFillOpacity(qreal opacity)
116 {
117     m_fillOpacitySet = 1;
118     m_fillOpacity = opacity;
119 }
120 
setFillStyle(QSvgFillStyleProperty * style)121 void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style)
122 {
123     m_style = style;
124     m_fillSet = 1;
125 }
126 
setBrush(QBrush brush)127 void QSvgFillStyle::setBrush(QBrush brush)
128 {
129     m_fill = std::move(brush);
130     m_style = 0;
131     m_fillSet = 1;
132 }
133 
apply(QPainter * p,const QSvgNode *,QSvgExtraStates & states)134 void QSvgFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
135 {
136     m_oldFill = p->brush();
137     m_oldFillRule = states.fillRule;
138     m_oldFillOpacity = states.fillOpacity;
139 
140     if (m_fillRuleSet)
141         states.fillRule = m_fillRule;
142     if (m_fillSet) {
143         if (m_style)
144             p->setBrush(m_style->brush(p, states));
145         else
146             p->setBrush(m_fill);
147     }
148     if (m_fillOpacitySet)
149         states.fillOpacity = m_fillOpacity;
150 }
151 
revert(QPainter * p,QSvgExtraStates & states)152 void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states)
153 {
154     if (m_fillOpacitySet)
155         states.fillOpacity = m_oldFillOpacity;
156     if (m_fillSet)
157         p->setBrush(m_oldFill);
158     if (m_fillRuleSet)
159         states.fillRule = m_oldFillRule;
160 }
161 
QSvgViewportFillStyle(const QBrush & brush)162 QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
163     : m_viewportFill(brush)
164 {
165 }
166 
apply(QPainter * p,const QSvgNode *,QSvgExtraStates &)167 void QSvgViewportFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
168 {
169     m_oldFill = p->brush();
170     p->setBrush(m_viewportFill);
171 }
172 
revert(QPainter * p,QSvgExtraStates &)173 void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &)
174 {
175     p->setBrush(m_oldFill);
176 }
177 
QSvgFontStyle(QSvgFont * font,QSvgTinyDocument * doc)178 QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc)
179     : m_svgFont(font)
180     , m_doc(doc)
181     , m_familySet(0)
182     , m_sizeSet(0)
183     , m_styleSet(0)
184     , m_variantSet(0)
185     , m_weightSet(0)
186     , m_textAnchorSet(0)
187 {
188 }
189 
QSvgFontStyle()190 QSvgFontStyle::QSvgFontStyle()
191     : m_svgFont(0)
192     , m_doc(0)
193     , m_familySet(0)
194     , m_sizeSet(0)
195     , m_styleSet(0)
196     , m_variantSet(0)
197     , m_weightSet(0)
198     , m_textAnchorSet(0)
199 {
200 }
201 
SVGToQtWeight(int weight)202 int QSvgFontStyle::SVGToQtWeight(int weight) {
203     switch (weight) {
204     case 100:
205     case 200:
206         return QFont::Light;
207     case 300:
208     case 400:
209         return QFont::Normal;
210     case 500:
211     case 600:
212         return QFont::DemiBold;
213     case 700:
214     case 800:
215         return QFont::Bold;
216     case 900:
217         return QFont::Black;
218     }
219     return QFont::Normal;
220 }
221 
apply(QPainter * p,const QSvgNode *,QSvgExtraStates & states)222 void QSvgFontStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
223 {
224     m_oldQFont = p->font();
225     m_oldSvgFont = states.svgFont;
226     m_oldTextAnchor = states.textAnchor;
227     m_oldWeight = states.fontWeight;
228 
229     if (m_textAnchorSet)
230         states.textAnchor = m_textAnchor;
231 
232     QFont font = m_oldQFont;
233     if (m_familySet) {
234         states.svgFont = m_svgFont;
235         font.setFamilies(m_qfont.families());
236     }
237 
238     if (m_sizeSet)
239         font.setPointSizeF(m_qfont.pointSizeF());
240 
241     if (m_styleSet)
242         font.setStyle(m_qfont.style());
243 
244     if (m_variantSet)
245         font.setCapitalization(m_qfont.capitalization());
246 
247     if (m_weightSet) {
248         if (m_weight == BOLDER) {
249             states.fontWeight = qMin(states.fontWeight + 100, 900);
250         } else if (m_weight == LIGHTER) {
251             states.fontWeight = qMax(states.fontWeight - 100, 100);
252         } else {
253             states.fontWeight = m_weight;
254         }
255         font.setWeight(SVGToQtWeight(states.fontWeight));
256     }
257 
258     p->setFont(font);
259 }
260 
revert(QPainter * p,QSvgExtraStates & states)261 void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states)
262 {
263     p->setFont(m_oldQFont);
264     states.svgFont = m_oldSvgFont;
265     states.textAnchor = m_oldTextAnchor;
266     states.fontWeight = m_oldWeight;
267 }
268 
QSvgStrokeStyle()269 QSvgStrokeStyle::QSvgStrokeStyle()
270     : m_strokeOpacity(1.0)
271     , m_oldStrokeOpacity(0.0)
272     , m_strokeDashOffset(0)
273     , m_oldStrokeDashOffset(0)
274     , m_style(0)
275     , m_gradientResolved(1)
276     , m_vectorEffect(0)
277     , m_oldVectorEffect(0)
278     , m_strokeSet(0)
279     , m_strokeDashArraySet(0)
280     , m_strokeDashOffsetSet(0)
281     , m_strokeLineCapSet(0)
282     , m_strokeLineJoinSet(0)
283     , m_strokeMiterLimitSet(0)
284     , m_strokeOpacitySet(0)
285     , m_strokeWidthSet(0)
286     , m_vectorEffectSet(0)
287 {
288 }
289 
apply(QPainter * p,const QSvgNode *,QSvgExtraStates & states)290 void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states)
291 {
292     m_oldStroke = p->pen();
293     m_oldStrokeOpacity = states.strokeOpacity;
294     m_oldStrokeDashOffset = states.strokeDashOffset;
295     m_oldVectorEffect = states.vectorEffect;
296 
297     QPen pen = p->pen();
298 
299     qreal oldWidth = pen.widthF();
300     qreal width = m_stroke.widthF();
301     if (oldWidth == 0)
302         oldWidth = 1;
303     if (width == 0)
304         width = 1;
305     qreal scale = oldWidth / width;
306 
307     if (m_strokeOpacitySet)
308         states.strokeOpacity = m_strokeOpacity;
309 
310     if (m_vectorEffectSet)
311         states.vectorEffect = m_vectorEffect;
312 
313     if (m_strokeSet) {
314         if (m_style)
315             pen.setBrush(m_style->brush(p, states));
316         else
317             pen.setBrush(m_stroke.brush());
318     }
319 
320     if (m_strokeWidthSet)
321         pen.setWidthF(m_stroke.widthF());
322 
323     bool setDashOffsetNeeded = false;
324 
325     if (m_strokeDashOffsetSet) {
326         states.strokeDashOffset = m_strokeDashOffset;
327         setDashOffsetNeeded = true;
328     }
329 
330     if (m_strokeDashArraySet) {
331         if (m_stroke.style() == Qt::SolidLine) {
332             pen.setStyle(Qt::SolidLine);
333         } else if (m_strokeWidthSet || oldWidth == 1) {
334             // If both width and dash array was set, the dash array is already scaled correctly.
335             pen.setDashPattern(m_stroke.dashPattern());
336             setDashOffsetNeeded = true;
337         } else {
338             // If dash array was set, but not the width, the dash array has to be scaled with respect to the old width.
339             QVector<qreal> dashes = m_stroke.dashPattern();
340             for (int i = 0; i < dashes.size(); ++i)
341                 dashes[i] /= oldWidth;
342             pen.setDashPattern(dashes);
343             setDashOffsetNeeded = true;
344         }
345     } else if (m_strokeWidthSet && pen.style() != Qt::SolidLine && scale != 1) {
346         // If the width was set, but not the dash array, the old dash array must be scaled with respect to the new width.
347         QVector<qreal> dashes = pen.dashPattern();
348         for (int i = 0; i < dashes.size(); ++i)
349             dashes[i] *= scale;
350         pen.setDashPattern(dashes);
351         setDashOffsetNeeded = true;
352     }
353 
354     if (m_strokeLineCapSet)
355         pen.setCapStyle(m_stroke.capStyle());
356     if (m_strokeLineJoinSet)
357         pen.setJoinStyle(m_stroke.joinStyle());
358     if (m_strokeMiterLimitSet)
359         pen.setMiterLimit(m_stroke.miterLimit());
360 
361     // You can have dash offset on solid strokes in SVG files, but not in Qt.
362     // QPen::setDashOffset() will set the pen style to Qt::CustomDashLine,
363     // so don't call the method if the pen is solid.
364     if (setDashOffsetNeeded && pen.style() != Qt::SolidLine) {
365         qreal currentWidth = pen.widthF();
366         if (currentWidth == 0)
367             currentWidth = 1;
368         pen.setDashOffset(states.strokeDashOffset / currentWidth);
369     }
370 
371     pen.setCosmetic(states.vectorEffect);
372 
373     p->setPen(pen);
374 }
375 
revert(QPainter * p,QSvgExtraStates & states)376 void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states)
377 {
378     p->setPen(m_oldStroke);
379     states.strokeOpacity = m_oldStrokeOpacity;
380     states.strokeDashOffset = m_oldStrokeDashOffset;
381     states.vectorEffect = m_oldVectorEffect;
382 }
383 
setDashArray(const QVector<qreal> & dashes)384 void QSvgStrokeStyle::setDashArray(const QVector<qreal> &dashes)
385 {
386     if (m_strokeWidthSet) {
387         QVector<qreal> d = dashes;
388         qreal w = m_stroke.widthF();
389         if (w != 0 && w != 1) {
390             for (int i = 0; i < d.size(); ++i)
391                 d[i] /= w;
392         }
393         m_stroke.setDashPattern(d);
394     } else {
395         m_stroke.setDashPattern(dashes);
396     }
397     m_strokeDashArraySet = 1;
398 }
399 
QSvgSolidColorStyle(const QColor & color)400 QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
401     : m_solidColor(color)
402 {
403 }
404 
QSvgGradientStyle(QGradient * grad)405 QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
406     : m_gradient(grad), m_gradientStopsSet(false)
407 {
408 }
409 
brush(QPainter *,QSvgExtraStates &)410 QBrush QSvgGradientStyle::brush(QPainter *, QSvgExtraStates &)
411 {
412     if (!m_link.isEmpty()) {
413         resolveStops();
414     }
415 
416     // If the gradient is marked as empty, insert transparent black
417     if (!m_gradientStopsSet) {
418         m_gradient->setStops(QGradientStops() << QGradientStop(0.0, QColor(0, 0, 0, 0)));
419         m_gradientStopsSet = true;
420     }
421 
422     QBrush b(*m_gradient);
423 
424     if (!m_transform.isIdentity())
425         b.setTransform(m_transform);
426 
427     return b;
428 }
429 
430 
setTransform(const QTransform & transform)431 void QSvgGradientStyle::setTransform(const QTransform &transform)
432 {
433     m_transform = transform;
434 }
435 
QSvgTransformStyle(const QTransform & trans)436 QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans)
437     : m_transform(trans)
438 {
439 }
440 
apply(QPainter * p,const QSvgNode *,QSvgExtraStates &)441 void QSvgTransformStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
442 {
443     m_oldWorldTransform = p->worldTransform();
444     p->setWorldTransform(m_transform, true);
445 }
446 
revert(QPainter * p,QSvgExtraStates &)447 void QSvgTransformStyle::revert(QPainter *p, QSvgExtraStates &)
448 {
449     p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
450 }
451 
type() const452 QSvgStyleProperty::Type QSvgQualityStyle::type() const
453 {
454     return QUALITY;
455 }
456 
type() const457 QSvgStyleProperty::Type QSvgFillStyle::type() const
458 {
459     return FILL;
460 }
461 
type() const462 QSvgStyleProperty::Type QSvgViewportFillStyle::type() const
463 {
464     return VIEWPORT_FILL;
465 }
466 
type() const467 QSvgStyleProperty::Type QSvgFontStyle::type() const
468 {
469     return FONT;
470 }
471 
type() const472 QSvgStyleProperty::Type QSvgStrokeStyle::type() const
473 {
474     return STROKE;
475 }
476 
type() const477 QSvgStyleProperty::Type QSvgSolidColorStyle::type() const
478 {
479     return SOLID_COLOR;
480 }
481 
type() const482 QSvgStyleProperty::Type QSvgGradientStyle::type() const
483 {
484     return GRADIENT;
485 }
486 
type() const487 QSvgStyleProperty::Type QSvgTransformStyle::type() const
488 {
489     return TRANSFORM;
490 }
491 
492 
QSvgCompOpStyle(QPainter::CompositionMode mode)493 QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode)
494     : m_mode(mode)
495 {
496 
497 }
498 
apply(QPainter * p,const QSvgNode *,QSvgExtraStates &)499 void QSvgCompOpStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
500 {
501     m_oldMode = p->compositionMode();
502     p->setCompositionMode(m_mode);
503 }
504 
revert(QPainter * p,QSvgExtraStates &)505 void QSvgCompOpStyle::revert(QPainter *p, QSvgExtraStates &)
506 {
507     p->setCompositionMode(m_oldMode);
508 }
509 
type() const510 QSvgStyleProperty::Type QSvgCompOpStyle::type() const
511 {
512     return COMP_OP;
513 }
514 
~QSvgStyle()515 QSvgStyle::~QSvgStyle()
516 {
517 }
518 
apply(QPainter * p,const QSvgNode * node,QSvgExtraStates & states)519 void QSvgStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states)
520 {
521     if (quality) {
522         quality->apply(p, node, states);
523     }
524 
525     if (fill) {
526         fill->apply(p, node, states);
527     }
528 
529     if (viewportFill) {
530         viewportFill->apply(p, node, states);
531     }
532 
533     if (font) {
534         font->apply(p, node, states);
535     }
536 
537     if (stroke) {
538         stroke->apply(p, node, states);
539     }
540 
541     if (transform) {
542         transform->apply(p, node, states);
543     }
544 
545     if (animateColor) {
546         animateColor->apply(p, node, states);
547     }
548 
549     //animated transforms have to be applied
550     //_after_ the original object transformations
551     if (!animateTransforms.isEmpty()) {
552         qreal totalTimeElapsed = node->document()->currentElapsed();
553         // Find the last animateTransform with additive="replace", since this will override all
554         // previous animateTransforms.
555         QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constEnd();
556         do {
557             --itr;
558             if ((*itr)->animActive(totalTimeElapsed)
559                 && (*itr)->additiveType() == QSvgAnimateTransform::Replace) {
560                 // An animateTransform with additive="replace" will replace the transform attribute.
561                 if (transform)
562                     transform->revert(p, states);
563                 break;
564             }
565         } while (itr != animateTransforms.constBegin());
566 
567         // Apply the animateTransforms after and including the last one with additive="replace".
568         for (; itr != animateTransforms.constEnd(); ++itr) {
569             if ((*itr)->animActive(totalTimeElapsed))
570                 (*itr)->apply(p, node, states);
571         }
572     }
573 
574     if (opacity) {
575         opacity->apply(p, node, states);
576     }
577 
578     if (compop) {
579         compop->apply(p, node, states);
580     }
581 }
582 
revert(QPainter * p,QSvgExtraStates & states)583 void QSvgStyle::revert(QPainter *p, QSvgExtraStates &states)
584 {
585     if (quality) {
586         quality->revert(p, states);
587     }
588 
589     if (fill) {
590         fill->revert(p, states);
591     }
592 
593     if (viewportFill) {
594         viewportFill->revert(p, states);
595     }
596 
597     if (font) {
598         font->revert(p, states);
599     }
600 
601     if (stroke) {
602         stroke->revert(p, states);
603     }
604 
605     //animated transforms need to be reverted _before_
606     //the native transforms
607     if (!animateTransforms.isEmpty()) {
608         QList<QSvgRefCounter<QSvgAnimateTransform> >::const_iterator itr = animateTransforms.constBegin();
609         for (; itr != animateTransforms.constEnd(); ++itr) {
610             if ((*itr)->transformApplied()) {
611                 (*itr)->revert(p, states);
612                 break;
613             }
614         }
615         for (; itr != animateTransforms.constEnd(); ++itr)
616             (*itr)->clearTransformApplied();
617     }
618 
619     if (transform) {
620         transform->revert(p, states);
621     }
622 
623     if (animateColor) {
624         animateColor->revert(p, states);
625     }
626 
627     if (opacity) {
628         opacity->revert(p, states);
629     }
630 
631     if (compop) {
632         compop->revert(p, states);
633     }
634 }
635 
QSvgAnimateTransform(int startMs,int endMs,int byMs)636 QSvgAnimateTransform::QSvgAnimateTransform(int startMs, int endMs, int byMs )
637     : QSvgStyleProperty(),
638       m_from(startMs),
639       m_totalRunningTime(endMs - startMs),
640       m_type(Empty),
641       m_additive(Replace),
642       m_count(0),
643       m_finished(false),
644       m_freeze(false),
645       m_repeatCount(-1.),
646       m_transformApplied(false)
647 {
648     Q_UNUSED(byMs);
649 }
650 
setArgs(TransformType type,Additive additive,const QVector<qreal> & args)651 void QSvgAnimateTransform::setArgs(TransformType type, Additive additive, const QVector<qreal> &args)
652 {
653     m_type = type;
654     m_args = args;
655     m_additive = additive;
656     Q_ASSERT(!(args.count()%3));
657     m_count = args.count() / 3;
658 }
659 
apply(QPainter * p,const QSvgNode * node,QSvgExtraStates &)660 void QSvgAnimateTransform::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &)
661 {
662     m_oldWorldTransform = p->worldTransform();
663     resolveMatrix(node);
664     p->setWorldTransform(m_transform, true);
665     m_transformApplied = true;
666 }
667 
revert(QPainter * p,QSvgExtraStates &)668 void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &)
669 {
670     p->setWorldTransform(m_oldWorldTransform, false /* don't combine */);
671     m_transformApplied = false;
672 }
673 
resolveMatrix(const QSvgNode * node)674 void QSvgAnimateTransform::resolveMatrix(const QSvgNode *node)
675 {
676     static const qreal deg2rad = qreal(0.017453292519943295769);
677     qreal totalTimeElapsed = node->document()->currentElapsed();
678     if (totalTimeElapsed < m_from || m_finished)
679         return;
680 
681     qreal animationFrame = 0;
682     if (m_totalRunningTime != 0) {
683         animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
684 
685         if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
686             m_finished = true;
687             animationFrame = m_repeatCount;
688         }
689     }
690 
691     qreal percentOfAnimation = animationFrame;
692     if (percentOfAnimation > 1) {
693         percentOfAnimation -= ((int)percentOfAnimation);
694     }
695 
696     qreal currentPosition = percentOfAnimation * (m_count - 1);
697     int endElem   = qCeil(currentPosition);
698     int startElem = qMax(endElem - 1, 0);
699 
700     switch(m_type)
701     {
702     case Translate: {
703         startElem *= 3;
704         endElem   *= 3;
705         qreal from1, from2;
706         qreal to1, to2;
707         from1 = m_args[startElem++];
708         from2 = m_args[startElem++];
709         to1   = m_args[endElem++];
710         to2   = m_args[endElem++];
711 
712         qreal transXDiff = (to1-from1) * percentOfAnimation;
713         qreal transX = from1 + transXDiff;
714         qreal transYDiff = (to2-from2) * percentOfAnimation;
715         qreal transY = from2 + transYDiff;
716         m_transform = QTransform();
717         m_transform.translate(transX, transY);
718         break;
719     }
720     case Scale: {
721         startElem *= 3;
722         endElem   *= 3;
723         qreal from1, from2;
724         qreal to1, to2;
725         from1 = m_args[startElem++];
726         from2 = m_args[startElem++];
727         to1   = m_args[endElem++];
728         to2   = m_args[endElem++];
729 
730         qreal transXDiff = (to1-from1) * percentOfAnimation;
731         qreal transX = from1 + transXDiff;
732         qreal transYDiff = (to2-from2) * percentOfAnimation;
733         qreal transY = from2 + transYDiff;
734         if (transY == 0)
735             transY = transX;
736         m_transform = QTransform();
737         m_transform.scale(transX, transY);
738         break;
739     }
740     case Rotate: {
741         startElem *= 3;
742         endElem   *= 3;
743         qreal from1, from2, from3;
744         qreal to1, to2, to3;
745         from1 = m_args[startElem++];
746         from2 = m_args[startElem++];
747         from3 = m_args[startElem++];
748         to1   = m_args[endElem++];
749         to2   = m_args[endElem++];
750         to3   = m_args[endElem++];
751 
752         qreal rotationDiff = (to1 - from1) * percentOfAnimation;
753         //qreal rotation = from1 + rotationDiff;
754 
755         qreal transXDiff = (to2-from2) * percentOfAnimation;
756         qreal transX = from2 + transXDiff;
757         qreal transYDiff = (to3-from3) * percentOfAnimation;
758         qreal transY = from3 + transYDiff;
759         m_transform = QTransform();
760         m_transform.translate(transX, transY);
761         m_transform.rotate(rotationDiff);
762         m_transform.translate(-transX, -transY);
763         break;
764     }
765     case SkewX: {
766         startElem *= 3;
767         endElem   *= 3;
768         qreal from1;
769         qreal to1;
770         from1 = m_args[startElem++];
771         to1   = m_args[endElem++];
772 
773         qreal transXDiff = (to1-from1) * percentOfAnimation;
774         qreal transX = from1 + transXDiff;
775         m_transform = QTransform();
776         m_transform.shear(qTan(transX * deg2rad), 0);
777         break;
778     }
779     case SkewY: {
780         startElem *= 3;
781         endElem   *= 3;
782         qreal from1;
783         qreal to1;
784         from1 = m_args[startElem++];
785         to1   = m_args[endElem++];
786 
787 
788         qreal transYDiff = (to1 - from1) * percentOfAnimation;
789         qreal transY = from1 + transYDiff;
790         m_transform = QTransform();
791         m_transform.shear(0, qTan(transY * deg2rad));
792         break;
793     }
794     default:
795         break;
796     }
797 }
798 
type() const799 QSvgStyleProperty::Type QSvgAnimateTransform::type() const
800 {
801     return ANIMATE_TRANSFORM;
802 }
803 
setFreeze(bool freeze)804 void QSvgAnimateTransform::setFreeze(bool freeze)
805 {
806     m_freeze = freeze;
807 }
808 
setRepeatCount(qreal repeatCount)809 void QSvgAnimateTransform::setRepeatCount(qreal repeatCount)
810 {
811     m_repeatCount = repeatCount;
812 }
813 
QSvgAnimateColor(int startMs,int endMs,int byMs)814 QSvgAnimateColor::QSvgAnimateColor(int startMs, int endMs, int byMs)
815     : QSvgStyleProperty(),
816       m_from(startMs),
817       m_totalRunningTime(endMs - startMs),
818       m_fill(false),
819       m_finished(false),
820       m_freeze(false),
821       m_repeatCount(-1.)
822 {
823     Q_UNUSED(byMs);
824 }
825 
setArgs(bool fill,const QList<QColor> & colors)826 void QSvgAnimateColor::setArgs(bool fill,
827                                const QList<QColor> &colors)
828 {
829     m_fill = fill;
830     m_colors = colors;
831 }
832 
setFreeze(bool freeze)833 void QSvgAnimateColor::setFreeze(bool freeze)
834 {
835     m_freeze = freeze;
836 }
837 
setRepeatCount(qreal repeatCount)838 void QSvgAnimateColor::setRepeatCount(qreal repeatCount)
839 {
840     m_repeatCount = repeatCount;
841 }
842 
apply(QPainter * p,const QSvgNode * node,QSvgExtraStates &)843 void QSvgAnimateColor::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &)
844 {
845     qreal totalTimeElapsed = node->document()->currentElapsed();
846     if (totalTimeElapsed < m_from || m_finished)
847         return;
848 
849     qreal animationFrame = 0;
850     if (m_totalRunningTime != 0)
851         animationFrame = (totalTimeElapsed - m_from) / m_totalRunningTime;
852 
853     if (m_repeatCount >= 0 && m_repeatCount < animationFrame) {
854         m_finished = true;
855         animationFrame = m_repeatCount;
856     }
857 
858     qreal percentOfAnimation = animationFrame;
859     if (percentOfAnimation > 1) {
860         percentOfAnimation -= ((int)percentOfAnimation);
861     }
862 
863     qreal currentPosition = percentOfAnimation * (m_colors.count() - 1);
864 
865     int startElem = qFloor(currentPosition);
866     int endElem   = qCeil(currentPosition);
867     QColor start = m_colors[startElem];
868     QColor end = m_colors[endElem];
869 
870     qreal percentOfColorMorph = currentPosition;
871     if (percentOfColorMorph > 1) {
872         percentOfColorMorph -= ((int)percentOfColorMorph);
873     }
874 
875     // Interpolate between the two fixed colors start and end
876     qreal aDiff = (end.alpha() - start.alpha()) * percentOfColorMorph;
877     qreal rDiff = (end.red()   - start.red()) * percentOfColorMorph;
878     qreal gDiff = (end.green() - start.green()) * percentOfColorMorph;
879     qreal bDiff = (end.blue()  - start.blue()) * percentOfColorMorph;
880 
881     int alpha  = int(start.alpha() + aDiff);
882     int red    = int(start.red() + rDiff);
883     int green  = int(start.green() + gDiff);
884     int blue   = int(start.blue() + bDiff);
885 
886     QColor color(red, green, blue, alpha);
887 
888     if (m_fill) {
889         QBrush b = p->brush();
890         m_oldBrush = b;
891         b.setColor(color);
892         p->setBrush(b);
893     } else {
894         QPen pen = p->pen();
895         m_oldPen = pen;
896         pen.setColor(color);
897         p->setPen(pen);
898     }
899 }
900 
revert(QPainter * p,QSvgExtraStates &)901 void QSvgAnimateColor::revert(QPainter *p, QSvgExtraStates &)
902 {
903     if (m_fill) {
904         p->setBrush(m_oldBrush);
905     } else {
906         p->setPen(m_oldPen);
907     }
908 }
909 
type() const910 QSvgStyleProperty::Type QSvgAnimateColor::type() const
911 {
912     return ANIMATE_COLOR;
913 }
914 
QSvgOpacityStyle(qreal opacity)915 QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity)
916     : m_opacity(opacity), m_oldOpacity(0)
917 {
918 
919 }
920 
apply(QPainter * p,const QSvgNode *,QSvgExtraStates &)921 void QSvgOpacityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
922 {
923     m_oldOpacity = p->opacity();
924     p->setOpacity(m_opacity * m_oldOpacity);
925 }
926 
revert(QPainter * p,QSvgExtraStates &)927 void QSvgOpacityStyle::revert(QPainter *p, QSvgExtraStates &)
928 {
929     p->setOpacity(m_oldOpacity);
930 }
931 
type() const932 QSvgStyleProperty::Type QSvgOpacityStyle::type() const
933 {
934     return OPACITY;
935 }
936 
setStopLink(const QString & link,QSvgTinyDocument * doc)937 void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc)
938 {
939     m_link = link;
940     m_doc  = doc;
941 }
942 
resolveStops()943 void QSvgGradientStyle::resolveStops()
944 {
945     QStringList visited;
946     resolveStops_helper(&visited);
947 }
948 
resolveStops_helper(QStringList * visited)949 void QSvgGradientStyle::resolveStops_helper(QStringList *visited)
950 {
951     if (!m_link.isEmpty() && m_doc) {
952         QSvgStyleProperty *prop = m_doc->styleProperty(m_link);
953         if (prop && !visited->contains(m_link)) {
954             visited->append(m_link);
955             if (prop->type() == QSvgStyleProperty::GRADIENT) {
956                 QSvgGradientStyle *st =
957                     static_cast<QSvgGradientStyle*>(prop);
958                 st->resolveStops_helper(visited);
959                 m_gradient->setStops(st->qgradient()->stops());
960                 m_gradientStopsSet = st->gradientStopsSet();
961             }
962         } else {
963             qWarning("Could not resolve property : %s", qPrintable(m_link));
964         }
965         m_link = QString();
966     }
967 }
968 
969 QT_END_NAMESPACE
970