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
54QT_BEGIN_NAMESPACE
55
56QSvgExtraStates::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
68QSvgStyleProperty::~QSvgStyleProperty()
69{
70}
71
72void QSvgFillStyleProperty::apply(QPainter *, const QSvgNode *, QSvgExtraStates &)
73{
74 Q_ASSERT(!"This should not be called!");
75}
76
77void QSvgFillStyleProperty::revert(QPainter *, QSvgExtraStates &)
78{
79 Q_ASSERT(!"This should not be called!");
80}
81
82
83QSvgQualityStyle::QSvgQualityStyle(int color)
84{
85 Q_UNUSED(color);
86}
87void QSvgQualityStyle::apply(QPainter *, const QSvgNode *, QSvgExtraStates &)
88{
89
90}
91void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &)
92{
93
94}
95
96QSvgFillStyle::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
109void QSvgFillStyle::setFillRule(Qt::FillRule f)
110{
111 m_fillRuleSet = 1;
112 m_fillRule = f;
113}
114
115void QSvgFillStyle::setFillOpacity(qreal opacity)
116{
117 m_fillOpacitySet = 1;
118 m_fillOpacity = opacity;
119}
120
121void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style)
122{
123 m_style = style;
124 m_fillSet = 1;
125}
126
127void QSvgFillStyle::setBrush(QBrush brush)
128{
129 m_fill = std::move(brush);
130 m_style = 0;
131 m_fillSet = 1;
132}
133
134void 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
152void 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
162QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush)
163 : m_viewportFill(brush)
164{
165}
166
167void QSvgViewportFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
168{
169 m_oldFill = p->brush();
170 p->setBrush(m_viewportFill);
171}
172
173void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &)
174{
175 p->setBrush(m_oldFill);
176}
177
178QSvgFontStyle::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
190QSvgFontStyle::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
202int 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
222void 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(a: states.fontWeight + 100, b: 900);
250 } else if (m_weight == LIGHTER) {
251 states.fontWeight = qMax(a: states.fontWeight - 100, b: 100);
252 } else {
253 states.fontWeight = m_weight;
254 }
255 font.setWeight(SVGToQtWeight(weight: states.fontWeight));
256 }
257
258 p->setFont(font);
259}
260
261void 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
269QSvgStrokeStyle::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
290void 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
376void 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
384void 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
400QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color)
401 : m_solidColor(color)
402{
403}
404
405QSvgGradientStyle::QSvgGradientStyle(QGradient *grad)
406 : m_gradient(grad), m_gradientStopsSet(false)
407{
408}
409
410QBrush 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
431void QSvgGradientStyle::setTransform(const QTransform &transform)
432{
433 m_transform = transform;
434}
435
436QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans)
437 : m_transform(trans)
438{
439}
440
441void QSvgTransformStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
442{
443 m_oldWorldTransform = p->worldTransform();
444 p->setWorldTransform(matrix: m_transform, combine: true);
445}
446
447void QSvgTransformStyle::revert(QPainter *p, QSvgExtraStates &)
448{
449 p->setWorldTransform(matrix: m_oldWorldTransform, combine: false /* don't combine */);
450}
451
452QSvgStyleProperty::Type QSvgQualityStyle::type() const
453{
454 return QUALITY;
455}
456
457QSvgStyleProperty::Type QSvgFillStyle::type() const
458{
459 return FILL;
460}
461
462QSvgStyleProperty::Type QSvgViewportFillStyle::type() const
463{
464 return VIEWPORT_FILL;
465}
466
467QSvgStyleProperty::Type QSvgFontStyle::type() const
468{
469 return FONT;
470}
471
472QSvgStyleProperty::Type QSvgStrokeStyle::type() const
473{
474 return STROKE;
475}
476
477QSvgStyleProperty::Type QSvgSolidColorStyle::type() const
478{
479 return SOLID_COLOR;
480}
481
482QSvgStyleProperty::Type QSvgGradientStyle::type() const
483{
484 return GRADIENT;
485}
486
487QSvgStyleProperty::Type QSvgTransformStyle::type() const
488{
489 return TRANSFORM;
490}
491
492
493QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode)
494 : m_mode(mode)
495{
496
497}
498
499void QSvgCompOpStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
500{
501 m_oldMode = p->compositionMode();
502 p->setCompositionMode(m_mode);
503}
504
505void QSvgCompOpStyle::revert(QPainter *p, QSvgExtraStates &)
506{
507 p->setCompositionMode(m_oldMode);
508}
509
510QSvgStyleProperty::Type QSvgCompOpStyle::type() const
511{
512 return COMP_OP;
513}
514
515QSvgStyle::~QSvgStyle()
516{
517}
518
519void 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
583void 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
636QSvgAnimateTransform::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
651void 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
660void QSvgAnimateTransform::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &)
661{
662 m_oldWorldTransform = p->worldTransform();
663 resolveMatrix(node);
664 p->setWorldTransform(matrix: m_transform, combine: true);
665 m_transformApplied = true;
666}
667
668void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &)
669{
670 p->setWorldTransform(matrix: m_oldWorldTransform, combine: false /* don't combine */);
671 m_transformApplied = false;
672}
673
674void 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(v: currentPosition);
698 int startElem = qMax(a: endElem - 1, b: 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(dx: transX, dy: 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(sx: transX, sy: 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(dx: transX, dy: transY);
761 m_transform.rotate(a: rotationDiff);
762 m_transform.translate(dx: -transX, dy: -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(sh: qTan(v: transX * deg2rad), sv: 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(sh: 0, sv: qTan(v: transY * deg2rad));
792 break;
793 }
794 default:
795 break;
796 }
797}
798
799QSvgStyleProperty::Type QSvgAnimateTransform::type() const
800{
801 return ANIMATE_TRANSFORM;
802}
803
804void QSvgAnimateTransform::setFreeze(bool freeze)
805{
806 m_freeze = freeze;
807}
808
809void QSvgAnimateTransform::setRepeatCount(qreal repeatCount)
810{
811 m_repeatCount = repeatCount;
812}
813
814QSvgAnimateColor::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
826void QSvgAnimateColor::setArgs(bool fill,
827 const QList<QColor> &colors)
828{
829 m_fill = fill;
830 m_colors = colors;
831}
832
833void QSvgAnimateColor::setFreeze(bool freeze)
834{
835 m_freeze = freeze;
836}
837
838void QSvgAnimateColor::setRepeatCount(qreal repeatCount)
839{
840 m_repeatCount = repeatCount;
841}
842
843void 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(v: currentPosition);
866 int endElem = qCeil(v: 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
901void 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
910QSvgStyleProperty::Type QSvgAnimateColor::type() const
911{
912 return ANIMATE_COLOR;
913}
914
915QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity)
916 : m_opacity(opacity), m_oldOpacity(0)
917{
918
919}
920
921void QSvgOpacityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &)
922{
923 m_oldOpacity = p->opacity();
924 p->setOpacity(m_opacity * m_oldOpacity);
925}
926
927void QSvgOpacityStyle::revert(QPainter *p, QSvgExtraStates &)
928{
929 p->setOpacity(m_oldOpacity);
930}
931
932QSvgStyleProperty::Type QSvgOpacityStyle::type() const
933{
934 return OPACITY;
935}
936
937void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc)
938{
939 m_link = link;
940 m_doc = doc;
941}
942
943void QSvgGradientStyle::resolveStops()
944{
945 QStringList visited;
946 resolveStops_helper(visited: &visited);
947}
948
949void QSvgGradientStyle::resolveStops_helper(QStringList *visited)
950{
951 if (!m_link.isEmpty() && m_doc) {
952 QSvgStyleProperty *prop = m_doc->styleProperty(id: m_link);
953 if (prop && !visited->contains(str: m_link)) {
954 visited->append(t: 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(msg: "Could not resolve property : %s", qPrintable(m_link));
964 }
965 m_link = QString();
966 }
967}
968
969QT_END_NAMESPACE
970

source code of qtsvg/src/svg/qsvgstyle.cpp