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 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | 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 | |
33 | QSvgStyleProperty::~QSvgStyleProperty() |
34 | { |
35 | } |
36 | |
37 | void QSvgFillStyleProperty::(QPainter *, const QSvgNode *, QSvgExtraStates &) |
38 | { |
39 | Q_ASSERT(!"This should not be called!" ); |
40 | } |
41 | |
42 | void QSvgFillStyleProperty::(QPainter *, QSvgExtraStates &) |
43 | { |
44 | Q_ASSERT(!"This should not be called!" ); |
45 | } |
46 | |
47 | |
48 | QSvgQualityStyle::QSvgQualityStyle(int color) |
49 | : m_imageRendering(QSvgQualityStyle::ImageRenderingAuto) |
50 | , m_oldImageRendering(QSvgQualityStyle::ImageRenderingAuto) |
51 | , m_imageRenderingSet(0) |
52 | { |
53 | Q_UNUSED(color); |
54 | } |
55 | |
56 | void QSvgQualityStyle::setImageRendering(ImageRendering hint) { |
57 | m_imageRendering = hint; |
58 | m_imageRenderingSet = 1; |
59 | } |
60 | |
61 | void QSvgQualityStyle::(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 | |
78 | void QSvgQualityStyle::(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 | |
91 | QSvgFillStyle::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 | |
104 | void QSvgFillStyle::setFillRule(Qt::FillRule f) |
105 | { |
106 | m_fillRuleSet = 1; |
107 | m_fillRule = f; |
108 | } |
109 | |
110 | void QSvgFillStyle::setFillOpacity(qreal opacity) |
111 | { |
112 | m_fillOpacitySet = 1; |
113 | m_fillOpacity = opacity; |
114 | } |
115 | |
116 | void QSvgFillStyle::setFillStyle(QSvgFillStyleProperty* style) |
117 | { |
118 | m_style = style; |
119 | m_fillSet = 1; |
120 | } |
121 | |
122 | void QSvgFillStyle::setBrush(QBrush brush) |
123 | { |
124 | m_fill = std::move(brush); |
125 | m_style = nullptr; |
126 | m_fillSet = 1; |
127 | } |
128 | |
129 | void QSvgFillStyle::(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 | |
147 | void QSvgFillStyle::(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 | |
157 | QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush) |
158 | : m_viewportFill(brush) |
159 | { |
160 | } |
161 | |
162 | void QSvgViewportFillStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
163 | { |
164 | m_oldFill = p->brush(); |
165 | p->setBrush(m_viewportFill); |
166 | } |
167 | |
168 | void QSvgViewportFillStyle::(QPainter *p, QSvgExtraStates &) |
169 | { |
170 | p->setBrush(m_oldFill); |
171 | } |
172 | |
173 | QSvgFontStyle::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 | |
185 | QSvgFontStyle::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 | |
197 | void QSvgFontStyle::(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 | |
238 | void QSvgFontStyle::(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 | |
246 | QSvgStrokeStyle::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 | |
267 | void QSvgStrokeStyle::(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 | |
353 | void QSvgStrokeStyle::(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 | |
361 | void 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 | |
377 | QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color) |
378 | : m_solidColor(color) |
379 | { |
380 | } |
381 | |
382 | QSvgGradientStyle::QSvgGradientStyle(QGradient *grad) |
383 | : m_gradient(grad), m_gradientStopsSet(false) |
384 | { |
385 | } |
386 | |
387 | QBrush QSvgGradientStyle::(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 | |
408 | void QSvgGradientStyle::setTransform(const QTransform &transform) |
409 | { |
410 | m_transform = transform; |
411 | } |
412 | |
413 | QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans) |
414 | : m_transform(trans) |
415 | { |
416 | } |
417 | |
418 | void QSvgTransformStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
419 | { |
420 | m_oldWorldTransform = p->worldTransform(); |
421 | p->setWorldTransform(matrix: m_transform, combine: true); |
422 | } |
423 | |
424 | void QSvgTransformStyle::(QPainter *p, QSvgExtraStates &) |
425 | { |
426 | p->setWorldTransform(matrix: m_oldWorldTransform, combine: false /* don't combine */); |
427 | } |
428 | |
429 | QSvgStyleProperty::Type QSvgQualityStyle::type() const |
430 | { |
431 | return QUALITY; |
432 | } |
433 | |
434 | QSvgStyleProperty::Type QSvgFillStyle::type() const |
435 | { |
436 | return FILL; |
437 | } |
438 | |
439 | QSvgStyleProperty::Type QSvgViewportFillStyle::type() const |
440 | { |
441 | return VIEWPORT_FILL; |
442 | } |
443 | |
444 | QSvgStyleProperty::Type QSvgFontStyle::type() const |
445 | { |
446 | return FONT; |
447 | } |
448 | |
449 | QSvgStyleProperty::Type QSvgStrokeStyle::type() const |
450 | { |
451 | return STROKE; |
452 | } |
453 | |
454 | QSvgStyleProperty::Type QSvgSolidColorStyle::type() const |
455 | { |
456 | return SOLID_COLOR; |
457 | } |
458 | |
459 | QSvgStyleProperty::Type QSvgGradientStyle::type() const |
460 | { |
461 | return GRADIENT; |
462 | } |
463 | |
464 | QSvgStyleProperty::Type QSvgTransformStyle::type() const |
465 | { |
466 | return TRANSFORM; |
467 | } |
468 | |
469 | |
470 | QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode) |
471 | : m_mode(mode) |
472 | { |
473 | |
474 | } |
475 | |
476 | void QSvgCompOpStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
477 | { |
478 | m_oldMode = p->compositionMode(); |
479 | p->setCompositionMode(m_mode); |
480 | } |
481 | |
482 | void QSvgCompOpStyle::(QPainter *p, QSvgExtraStates &) |
483 | { |
484 | p->setCompositionMode(m_oldMode); |
485 | } |
486 | |
487 | QSvgStyleProperty::Type QSvgCompOpStyle::type() const |
488 | { |
489 | return COMP_OP; |
490 | } |
491 | |
492 | QSvgStyle::~QSvgStyle() |
493 | { |
494 | } |
495 | |
496 | void QSvgStyle::(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 | |
560 | void QSvgStyle::(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 | |
613 | QSvgAnimateTransform::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 | |
628 | void 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 | |
637 | void QSvgAnimateTransform::(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 | |
645 | void QSvgAnimateTransform::(QPainter *p, QSvgExtraStates &) |
646 | { |
647 | p->setWorldTransform(matrix: m_oldWorldTransform, combine: false /* don't combine */); |
648 | m_transformApplied = false; |
649 | } |
650 | |
651 | void 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 | |
775 | QSvgStyleProperty::Type QSvgAnimateTransform::type() const |
776 | { |
777 | return ANIMATE_TRANSFORM; |
778 | } |
779 | |
780 | void QSvgAnimateTransform::setFreeze(bool freeze) |
781 | { |
782 | m_freeze = freeze; |
783 | } |
784 | |
785 | void QSvgAnimateTransform::setRepeatCount(qreal repeatCount) |
786 | { |
787 | m_repeatCount = repeatCount; |
788 | } |
789 | |
790 | QSvgAnimateColor::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 | |
802 | void QSvgAnimateColor::setArgs(bool fill, |
803 | const QList<QColor> &colors) |
804 | { |
805 | m_fill = fill; |
806 | m_colors = colors; |
807 | } |
808 | |
809 | void QSvgAnimateColor::setFreeze(bool freeze) |
810 | { |
811 | m_freeze = freeze; |
812 | } |
813 | |
814 | void QSvgAnimateColor::setRepeatCount(qreal repeatCount) |
815 | { |
816 | m_repeatCount = repeatCount; |
817 | } |
818 | |
819 | void QSvgAnimateColor::(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 | |
877 | void QSvgAnimateColor::(QPainter *p, QSvgExtraStates &) |
878 | { |
879 | if (m_fill) { |
880 | p->setBrush(m_oldBrush); |
881 | } else { |
882 | p->setPen(m_oldPen); |
883 | } |
884 | } |
885 | |
886 | QSvgStyleProperty::Type QSvgAnimateColor::type() const |
887 | { |
888 | return ANIMATE_COLOR; |
889 | } |
890 | |
891 | QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity) |
892 | : m_opacity(opacity), m_oldOpacity(0) |
893 | { |
894 | |
895 | } |
896 | |
897 | void QSvgOpacityStyle::(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
898 | { |
899 | m_oldOpacity = p->opacity(); |
900 | p->setOpacity(m_opacity * m_oldOpacity); |
901 | } |
902 | |
903 | void QSvgOpacityStyle::(QPainter *p, QSvgExtraStates &) |
904 | { |
905 | p->setOpacity(m_oldOpacity); |
906 | } |
907 | |
908 | QSvgStyleProperty::Type QSvgOpacityStyle::type() const |
909 | { |
910 | return OPACITY; |
911 | } |
912 | |
913 | void QSvgGradientStyle::setStopLink(const QString &link, QSvgTinyDocument *doc) |
914 | { |
915 | m_link = link; |
916 | m_doc = doc; |
917 | } |
918 | |
919 | void QSvgGradientStyle::resolveStops() |
920 | { |
921 | QStringList visited; |
922 | resolveStops_helper(visited: &visited); |
923 | } |
924 | |
925 | void 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 | |
945 | QT_END_NAMESPACE |
946 | |