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