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

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