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 | |
56 | 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 | |
68 | QSvgStyleProperty::~QSvgStyleProperty() |
69 | { |
70 | } |
71 | |
72 | void QSvgFillStyleProperty::(QPainter *, const QSvgNode *, QSvgExtraStates &) |
73 | { |
74 | Q_ASSERT(!"This should not be called!" ); |
75 | } |
76 | |
77 | void QSvgFillStyleProperty::(QPainter *, QSvgExtraStates &) |
78 | { |
79 | Q_ASSERT(!"This should not be called!" ); |
80 | } |
81 | |
82 | |
83 | QSvgQualityStyle::QSvgQualityStyle(int color) |
84 | { |
85 | Q_UNUSED(color); |
86 | } |
87 | void QSvgQualityStyle::(QPainter *, const QSvgNode *, QSvgExtraStates &) |
88 | { |
89 | |
90 | } |
91 | void QSvgQualityStyle::(QPainter *, QSvgExtraStates &) |
92 | { |
93 | |
94 | } |
95 | |
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 | |
109 | void QSvgFillStyle::setFillRule(Qt::FillRule f) |
110 | { |
111 | m_fillRuleSet = 1; |
112 | m_fillRule = f; |
113 | } |
114 | |
115 | void QSvgFillStyle::setFillOpacity(qreal opacity) |
116 | { |
117 | m_fillOpacitySet = 1; |
118 | m_fillOpacity = opacity; |
119 | } |
120 | |
121 | void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style) |
122 | { |
123 | m_style = style; |
124 | m_fillSet = 1; |
125 | } |
126 | |
127 | void QSvgFillStyle::setBrush(QBrush brush) |
128 | { |
129 | m_fill = std::move(brush); |
130 | m_style = 0; |
131 | m_fillSet = 1; |
132 | } |
133 | |
134 | void QSvgFillStyle::(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 | |
152 | void QSvgFillStyle::(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 | |
162 | QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush) |
163 | : m_viewportFill(brush) |
164 | { |
165 | } |
166 | |
167 | void QSvgViewportFillStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
168 | { |
169 | m_oldFill = p->brush(); |
170 | p->setBrush(m_viewportFill); |
171 | } |
172 | |
173 | void QSvgViewportFillStyle::(QPainter *p, QSvgExtraStates &) |
174 | { |
175 | p->setBrush(m_oldFill); |
176 | } |
177 | |
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 | |
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 | |
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 | |
222 | void QSvgFontStyle::(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 | |
261 | void QSvgFontStyle::(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 | |
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 | |
290 | void QSvgStrokeStyle::(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 | |
376 | void QSvgStrokeStyle::(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 | |
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 | |
400 | QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color) |
401 | : m_solidColor(color) |
402 | { |
403 | } |
404 | |
405 | QSvgGradientStyle::QSvgGradientStyle(QGradient *grad) |
406 | : m_gradient(grad), m_gradientStopsSet(false) |
407 | { |
408 | } |
409 | |
410 | QBrush QSvgGradientStyle::(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 | |
431 | void QSvgGradientStyle::setTransform(const QTransform &transform) |
432 | { |
433 | m_transform = transform; |
434 | } |
435 | |
436 | QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans) |
437 | : m_transform(trans) |
438 | { |
439 | } |
440 | |
441 | void QSvgTransformStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
442 | { |
443 | m_oldWorldTransform = p->worldTransform(); |
444 | p->setWorldTransform(matrix: m_transform, combine: true); |
445 | } |
446 | |
447 | void QSvgTransformStyle::(QPainter *p, QSvgExtraStates &) |
448 | { |
449 | p->setWorldTransform(matrix: m_oldWorldTransform, combine: false /* don't combine */); |
450 | } |
451 | |
452 | QSvgStyleProperty::Type QSvgQualityStyle::type() const |
453 | { |
454 | return QUALITY; |
455 | } |
456 | |
457 | QSvgStyleProperty::Type QSvgFillStyle::type() const |
458 | { |
459 | return FILL; |
460 | } |
461 | |
462 | QSvgStyleProperty::Type QSvgViewportFillStyle::type() const |
463 | { |
464 | return VIEWPORT_FILL; |
465 | } |
466 | |
467 | QSvgStyleProperty::Type QSvgFontStyle::type() const |
468 | { |
469 | return FONT; |
470 | } |
471 | |
472 | QSvgStyleProperty::Type QSvgStrokeStyle::type() const |
473 | { |
474 | return STROKE; |
475 | } |
476 | |
477 | QSvgStyleProperty::Type QSvgSolidColorStyle::type() const |
478 | { |
479 | return SOLID_COLOR; |
480 | } |
481 | |
482 | QSvgStyleProperty::Type QSvgGradientStyle::type() const |
483 | { |
484 | return GRADIENT; |
485 | } |
486 | |
487 | QSvgStyleProperty::Type QSvgTransformStyle::type() const |
488 | { |
489 | return TRANSFORM; |
490 | } |
491 | |
492 | |
493 | QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode) |
494 | : m_mode(mode) |
495 | { |
496 | |
497 | } |
498 | |
499 | void QSvgCompOpStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
500 | { |
501 | m_oldMode = p->compositionMode(); |
502 | p->setCompositionMode(m_mode); |
503 | } |
504 | |
505 | void QSvgCompOpStyle::(QPainter *p, QSvgExtraStates &) |
506 | { |
507 | p->setCompositionMode(m_oldMode); |
508 | } |
509 | |
510 | QSvgStyleProperty::Type QSvgCompOpStyle::type() const |
511 | { |
512 | return COMP_OP; |
513 | } |
514 | |
515 | QSvgStyle::~QSvgStyle() |
516 | { |
517 | } |
518 | |
519 | void QSvgStyle::(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 | |
583 | void QSvgStyle::(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 | |
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 | |
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 | |
660 | void QSvgAnimateTransform::(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 | |
668 | void QSvgAnimateTransform::(QPainter *p, QSvgExtraStates &) |
669 | { |
670 | p->setWorldTransform(matrix: m_oldWorldTransform, combine: false /* don't combine */); |
671 | m_transformApplied = false; |
672 | } |
673 | |
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(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 | |
799 | QSvgStyleProperty::Type QSvgAnimateTransform::type() const |
800 | { |
801 | return ANIMATE_TRANSFORM; |
802 | } |
803 | |
804 | void QSvgAnimateTransform::setFreeze(bool freeze) |
805 | { |
806 | m_freeze = freeze; |
807 | } |
808 | |
809 | void QSvgAnimateTransform::setRepeatCount(qreal repeatCount) |
810 | { |
811 | m_repeatCount = repeatCount; |
812 | } |
813 | |
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 | |
826 | void QSvgAnimateColor::setArgs(bool fill, |
827 | const QList<QColor> &colors) |
828 | { |
829 | m_fill = fill; |
830 | m_colors = colors; |
831 | } |
832 | |
833 | void QSvgAnimateColor::setFreeze(bool freeze) |
834 | { |
835 | m_freeze = freeze; |
836 | } |
837 | |
838 | void QSvgAnimateColor::setRepeatCount(qreal repeatCount) |
839 | { |
840 | m_repeatCount = repeatCount; |
841 | } |
842 | |
843 | void QSvgAnimateColor::(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 | |
901 | void QSvgAnimateColor::(QPainter *p, QSvgExtraStates &) |
902 | { |
903 | if (m_fill) { |
904 | p->setBrush(m_oldBrush); |
905 | } else { |
906 | p->setPen(m_oldPen); |
907 | } |
908 | } |
909 | |
910 | QSvgStyleProperty::Type QSvgAnimateColor::type() const |
911 | { |
912 | return ANIMATE_COLOR; |
913 | } |
914 | |
915 | QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity) |
916 | : m_opacity(opacity), m_oldOpacity(0) |
917 | { |
918 | |
919 | } |
920 | |
921 | void QSvgOpacityStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
922 | { |
923 | m_oldOpacity = p->opacity(); |
924 | p->setOpacity(m_opacity * m_oldOpacity); |
925 | } |
926 | |
927 | void QSvgOpacityStyle::(QPainter *p, QSvgExtraStates &) |
928 | { |
929 | p->setOpacity(m_oldOpacity); |
930 | } |
931 | |
932 | QSvgStyleProperty::Type QSvgOpacityStyle::type() const |
933 | { |
934 | return OPACITY; |
935 | } |
936 | |
937 | void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc) |
938 | { |
939 | m_link = link; |
940 | m_doc = doc; |
941 | } |
942 | |
943 | void QSvgGradientStyle::resolveStops() |
944 | { |
945 | QStringList visited; |
946 | resolveStops_helper(visited: &visited); |
947 | } |
948 | |
949 | void 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 | |
969 | QT_END_NAMESPACE |
970 | |