/src/qtsvg/src/svg/qsvgstyle.cpp
Line | Count | Source |
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 | | // Qt-Security score:significant reason:default |
4 | | |
5 | | |
6 | | #include "qsvgstyle_p.h" |
7 | | |
8 | | #include "qsvgfont_p.h" |
9 | | #include "qsvgnode_p.h" |
10 | | #include "private/qsvganimate_p.h" |
11 | | #include "qsvgdocument_p.h" |
12 | | |
13 | | #include "qpainter.h" |
14 | | #include "qcolor.h" |
15 | | #include "qdebug.h" |
16 | | |
17 | | QT_BEGIN_NAMESPACE |
18 | | |
19 | 0 | QSvgRefCounted::~QSvgRefCounted() |
20 | | = default; |
21 | | |
22 | | QSvgExtraStates::QSvgExtraStates() |
23 | 92.3k | : fillOpacity(1.0), |
24 | 92.3k | strokeOpacity(1.0), |
25 | 92.3k | svgFont(0), |
26 | 92.3k | textAnchor(Qt::AlignLeft), |
27 | 92.3k | fontWeight(QFont::Normal), |
28 | 92.3k | fillRule(Qt::WindingFill), |
29 | 92.3k | strokeDashOffset(0), |
30 | 92.3k | vectorEffect(false), |
31 | 92.3k | imageRendering(QSvgQualityStyle::ImageRenderingAuto) |
32 | 92.3k | { |
33 | 92.3k | } |
34 | | |
35 | 332k | QSvgStyleProperty::~QSvgStyleProperty() |
36 | | = default; |
37 | | |
38 | | QSvgQualityStyle::QSvgQualityStyle(int color) |
39 | 0 | : m_imageRendering(QSvgQualityStyle::ImageRenderingAuto) |
40 | 0 | , m_oldImageRendering(QSvgQualityStyle::ImageRenderingAuto) |
41 | 0 | , m_imageRenderingSet(0) |
42 | 0 | { |
43 | 0 | Q_UNUSED(color); |
44 | 0 | } |
45 | | |
46 | | QSvgQualityStyle::~QSvgQualityStyle() |
47 | | = default; |
48 | | |
49 | 0 | void QSvgQualityStyle::setImageRendering(ImageRendering hint) { |
50 | 0 | m_imageRendering = hint; |
51 | 0 | m_imageRenderingSet = 1; |
52 | 0 | } |
53 | | |
54 | | void QSvgQualityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) |
55 | 0 | { |
56 | 0 | m_oldImageRendering = states.imageRendering; |
57 | 0 | if (m_imageRenderingSet) { |
58 | 0 | states.imageRendering = m_imageRendering; |
59 | 0 | } |
60 | 0 | if (m_imageRenderingSet) { |
61 | 0 | bool smooth = false; |
62 | 0 | if (m_imageRendering == ImageRenderingAuto) |
63 | | // auto (the spec says to prefer quality) |
64 | 0 | smooth = true; |
65 | 0 | else |
66 | 0 | smooth = (m_imageRendering == ImageRenderingOptimizeQuality); |
67 | 0 | p->setRenderHint(QPainter::SmoothPixmapTransform, smooth); |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | | void QSvgQualityStyle::revert(QPainter *p, QSvgExtraStates &states) |
72 | 0 | { |
73 | 0 | if (m_imageRenderingSet) { |
74 | 0 | states.imageRendering = m_oldImageRendering; |
75 | 0 | bool smooth = false; |
76 | 0 | if (m_oldImageRendering == ImageRenderingAuto) |
77 | 0 | smooth = true; |
78 | 0 | else |
79 | 0 | smooth = (m_oldImageRendering == ImageRenderingOptimizeQuality); |
80 | 0 | p->setRenderHint(QPainter::SmoothPixmapTransform, smooth); |
81 | 0 | } |
82 | 0 | } |
83 | | |
84 | | QSvgFillStyle::QSvgFillStyle() |
85 | 141k | : m_fillRuleSet(0) |
86 | 141k | , m_fillOpacitySet(0) |
87 | 141k | , m_fillSet(0) |
88 | 141k | { |
89 | 141k | } |
90 | | |
91 | 141k | QSvgFillStyle::~QSvgFillStyle() |
92 | | = default; |
93 | | |
94 | | void QSvgFillStyle::setFillRule(Qt::FillRule f) |
95 | 39.4k | { |
96 | 39.4k | m_fillRuleSet = 1; |
97 | 39.4k | m_fillRule = f; |
98 | 39.4k | } |
99 | | |
100 | | void QSvgFillStyle::setFillOpacity(qreal opacity) |
101 | 67.3k | { |
102 | 67.3k | m_fillOpacitySet = 1; |
103 | 67.3k | m_fillOpacity = opacity; |
104 | 67.3k | } |
105 | | |
106 | | void QSvgFillStyle::setPaintServer(QSvgPaintServerSharedPtr paintServer) |
107 | 0 | { |
108 | 0 | m_paintServer = std::move(paintServer); |
109 | 0 | m_fillSet = 1; |
110 | 0 | } |
111 | | |
112 | | void QSvgFillStyle::setBrush(QBrush brush) |
113 | 26.3k | { |
114 | 26.3k | m_fill = std::move(brush); |
115 | 26.3k | m_paintServer.reset(); |
116 | 26.3k | m_fillSet = 1; |
117 | 26.3k | } |
118 | | |
119 | | void QSvgFillStyle::apply(QPainter *p, const QSvgNode *n, QSvgExtraStates &states) |
120 | 115k | { |
121 | 115k | m_oldFill = p->brush(); |
122 | 115k | m_oldFillRule = states.fillRule; |
123 | 115k | m_oldFillOpacity = states.fillOpacity; |
124 | | |
125 | 115k | if (m_fillRuleSet) |
126 | 38.2k | states.fillRule = m_fillRule; |
127 | 115k | if (m_fillSet) { |
128 | 15.1k | if (m_paintServer) |
129 | 0 | p->setBrush(m_paintServer->brush(p, n, states)); |
130 | 15.1k | else |
131 | 15.1k | p->setBrush(m_fill); |
132 | 15.1k | } |
133 | 115k | if (m_fillOpacitySet) |
134 | 74.4k | states.fillOpacity = m_fillOpacity; |
135 | 115k | } |
136 | | |
137 | | void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states) |
138 | 115k | { |
139 | 115k | if (m_fillOpacitySet) |
140 | 74.4k | states.fillOpacity = m_oldFillOpacity; |
141 | 115k | if (m_fillSet) |
142 | 15.1k | p->setBrush(m_oldFill); |
143 | 115k | if (m_fillRuleSet) |
144 | 38.2k | states.fillRule = m_oldFillRule; |
145 | 115k | } |
146 | | |
147 | | QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush) |
148 | 0 | : m_viewportFill(brush) |
149 | 0 | { |
150 | 0 | } |
151 | | |
152 | 0 | QSvgViewportFillStyle::~QSvgViewportFillStyle() |
153 | | = default; |
154 | | |
155 | | void QSvgViewportFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
156 | 0 | { |
157 | 0 | m_oldFill = p->brush(); |
158 | 0 | p->setBrush(m_viewportFill); |
159 | 0 | } |
160 | | |
161 | | void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &) |
162 | 0 | { |
163 | 0 | p->setBrush(m_oldFill); |
164 | 0 | } |
165 | | |
166 | | const int QSvgFontStyle::LIGHTER; |
167 | | const int QSvgFontStyle::BOLDER; |
168 | | |
169 | | QSvgFontStyle::QSvgFontStyle(QSvgFont *font) |
170 | 0 | : m_svgFont(font) |
171 | 0 | , m_familySet(0) |
172 | 0 | , m_sizeSet(0) |
173 | 0 | , m_styleSet(0) |
174 | 0 | , m_variantSet(0) |
175 | 0 | , m_weightSet(0) |
176 | 0 | , m_textAnchorSet(0) |
177 | 0 | { |
178 | 0 | } |
179 | | |
180 | | QSvgFontStyle::QSvgFontStyle() |
181 | 19.8k | : m_svgFont(0) |
182 | 19.8k | , m_familySet(0) |
183 | 19.8k | , m_sizeSet(0) |
184 | 19.8k | , m_styleSet(0) |
185 | 19.8k | , m_variantSet(0) |
186 | 19.8k | , m_weightSet(0) |
187 | 19.8k | , m_textAnchorSet(0) |
188 | 19.8k | { |
189 | 19.8k | } |
190 | | |
191 | 19.8k | QSvgFontStyle::~QSvgFontStyle() |
192 | | = default; |
193 | | |
194 | | void QSvgFontStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) |
195 | 7.34k | { |
196 | 7.34k | m_oldQFont = p->font(); |
197 | 7.34k | m_oldSvgFont = states.svgFont; |
198 | 7.34k | m_oldTextAnchor = states.textAnchor; |
199 | 7.34k | m_oldWeight = states.fontWeight; |
200 | | |
201 | 7.34k | if (m_textAnchorSet) |
202 | 0 | states.textAnchor = m_textAnchor; |
203 | | |
204 | 7.34k | QFont font = m_oldQFont; |
205 | 7.34k | if (m_familySet) { |
206 | 0 | states.svgFont = m_svgFont; |
207 | 0 | font.setFamilies(m_qfont.families()); |
208 | 0 | } |
209 | | |
210 | 7.34k | if (m_sizeSet) |
211 | 1.36k | font.setPointSizeF(m_qfont.pointSizeF()); |
212 | | |
213 | 7.34k | if (m_styleSet) |
214 | 0 | font.setStyle(m_qfont.style()); |
215 | | |
216 | 7.34k | if (m_variantSet) |
217 | 0 | font.setCapitalization(m_qfont.capitalization()); |
218 | | |
219 | 7.34k | if (m_weightSet) { |
220 | 6.07k | if (m_weight == BOLDER) { |
221 | 744 | states.fontWeight = qMin(states.fontWeight, QFont::Weight::Black - 100) + 100; |
222 | 5.32k | } else if (m_weight == LIGHTER) { |
223 | 0 | states.fontWeight = qMax(states.fontWeight, QFont::Weight::Thin + 100) - 100; |
224 | 5.32k | } else { |
225 | 5.32k | states.fontWeight = m_weight; |
226 | 5.32k | } |
227 | 6.07k | font.setWeight(QFont::Weight(qBound(static_cast<int>(QFont::Weight::Thin), |
228 | 6.07k | states.fontWeight, |
229 | 6.07k | static_cast<int>(QFont::Weight::Black)))); |
230 | 6.07k | } |
231 | | |
232 | 7.34k | p->setFont(font); |
233 | 7.34k | } |
234 | | |
235 | | void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states) |
236 | 7.34k | { |
237 | 7.34k | p->setFont(m_oldQFont); |
238 | 7.34k | states.svgFont = m_oldSvgFont; |
239 | 7.34k | states.textAnchor = m_oldTextAnchor; |
240 | 7.34k | states.fontWeight = m_oldWeight; |
241 | 7.34k | } |
242 | | |
243 | | QSvgStrokeStyle::QSvgStrokeStyle() |
244 | 128k | : m_vectorEffect(0) |
245 | 128k | , m_oldVectorEffect(0) |
246 | 128k | , m_strokeSet(0) |
247 | 128k | , m_strokeDashArraySet(0) |
248 | 128k | , m_strokeDashOffsetSet(0) |
249 | 128k | , m_strokeLineCapSet(0) |
250 | 128k | , m_strokeLineJoinSet(0) |
251 | 128k | , m_strokeMiterLimitSet(0) |
252 | 128k | , m_strokeOpacitySet(0) |
253 | 128k | , m_strokeWidthSet(0) |
254 | 128k | , m_vectorEffectSet(0) |
255 | 128k | { |
256 | 128k | } |
257 | | |
258 | 128k | QSvgStrokeStyle::~QSvgStrokeStyle() |
259 | | = default; |
260 | | |
261 | | void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *n, QSvgExtraStates &states) |
262 | 414k | { |
263 | 414k | m_oldStroke = p->pen(); |
264 | 414k | m_oldStrokeOpacity = states.strokeOpacity; |
265 | 414k | m_oldStrokeDashOffset = states.strokeDashOffset; |
266 | 414k | m_oldVectorEffect = states.vectorEffect; |
267 | | |
268 | 414k | QPen pen = p->pen(); |
269 | | |
270 | 414k | qreal oldWidth = pen.widthF(); |
271 | 414k | qreal width = m_stroke.widthF(); |
272 | 414k | if (oldWidth == 0) |
273 | 0 | oldWidth = 1; |
274 | 414k | if (width == 0) |
275 | 0 | width = 1; |
276 | 414k | qreal scale = oldWidth / width; |
277 | | |
278 | 414k | if (m_strokeOpacitySet) |
279 | 636 | states.strokeOpacity = m_strokeOpacity; |
280 | | |
281 | 414k | if (m_vectorEffectSet) |
282 | 0 | states.vectorEffect = m_vectorEffect; |
283 | | |
284 | 414k | if (m_strokeSet) { |
285 | 91.0k | if (m_paintServer) |
286 | 0 | pen.setBrush(m_paintServer->brush(p, n, states)); |
287 | 91.0k | else |
288 | 91.0k | pen.setBrush(m_stroke.brush()); |
289 | 91.0k | } |
290 | | |
291 | 414k | if (m_strokeWidthSet) |
292 | 0 | pen.setWidthF(m_stroke.widthF()); |
293 | | |
294 | 414k | bool setDashOffsetNeeded = false; |
295 | | |
296 | 414k | if (m_strokeDashOffsetSet) { |
297 | 0 | states.strokeDashOffset = m_strokeDashOffset; |
298 | 0 | setDashOffsetNeeded = true; |
299 | 0 | } |
300 | | |
301 | 414k | if (m_strokeDashArraySet) { |
302 | 0 | if (m_stroke.style() == Qt::SolidLine) { |
303 | 0 | pen.setStyle(Qt::SolidLine); |
304 | 0 | } else if (m_strokeWidthSet || oldWidth == 1) { |
305 | | // If both width and dash array was set, the dash array is already scaled correctly. |
306 | 0 | pen.setDashPattern(m_stroke.dashPattern()); |
307 | 0 | setDashOffsetNeeded = true; |
308 | 0 | } else { |
309 | | // If dash array was set, but not the width, the dash array has to be scaled with respect to the old width. |
310 | 0 | QList<qreal> dashes = m_stroke.dashPattern(); |
311 | 0 | for (int i = 0; i < dashes.size(); ++i) |
312 | 0 | dashes[i] /= oldWidth; |
313 | 0 | pen.setDashPattern(dashes); |
314 | 0 | setDashOffsetNeeded = true; |
315 | 0 | } |
316 | 414k | } else if (m_strokeWidthSet && pen.style() != Qt::SolidLine && scale != 1) { |
317 | | // If the width was set, but not the dash array, the old dash array must be scaled with respect to the new width. |
318 | 0 | QList<qreal> dashes = pen.dashPattern(); |
319 | 0 | for (int i = 0; i < dashes.size(); ++i) |
320 | 0 | dashes[i] *= scale; |
321 | 0 | pen.setDashPattern(dashes); |
322 | 0 | setDashOffsetNeeded = true; |
323 | 0 | } |
324 | | |
325 | 414k | if (m_strokeLineCapSet) |
326 | 0 | pen.setCapStyle(m_stroke.capStyle()); |
327 | 414k | if (m_strokeLineJoinSet) |
328 | 0 | pen.setJoinStyle(m_stroke.joinStyle()); |
329 | 414k | if (m_strokeMiterLimitSet) |
330 | 0 | pen.setMiterLimit(m_stroke.miterLimit()); |
331 | | |
332 | | // You can have dash offset on solid strokes in SVG files, but not in Qt. |
333 | | // QPen::setDashOffset() will set the pen style to Qt::CustomDashLine, |
334 | | // so don't call the method if the pen is solid. |
335 | 414k | if (setDashOffsetNeeded && pen.style() != Qt::SolidLine) { |
336 | 0 | qreal currentWidth = pen.widthF(); |
337 | 0 | if (currentWidth == 0) |
338 | 0 | currentWidth = 1; |
339 | 0 | pen.setDashOffset(states.strokeDashOffset / currentWidth); |
340 | 0 | } |
341 | | |
342 | 414k | pen.setCosmetic(states.vectorEffect); |
343 | | |
344 | 414k | p->setPen(pen); |
345 | 414k | } |
346 | | |
347 | | void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states) |
348 | 414k | { |
349 | 414k | p->setPen(m_oldStroke); |
350 | 414k | states.strokeOpacity = m_oldStrokeOpacity; |
351 | 414k | states.strokeDashOffset = m_oldStrokeDashOffset; |
352 | 414k | states.vectorEffect = m_oldVectorEffect; |
353 | 414k | } |
354 | | |
355 | | void QSvgStrokeStyle::setDashArray(const QList<qreal> &dashes) |
356 | 0 | { |
357 | 0 | if (m_strokeWidthSet) { |
358 | 0 | QList<qreal> d = dashes; |
359 | 0 | qreal w = m_stroke.widthF(); |
360 | 0 | if (w != 0 && w != 1) { |
361 | 0 | for (int i = 0; i < d.size(); ++i) |
362 | 0 | d[i] /= w; |
363 | 0 | } |
364 | 0 | m_stroke.setDashPattern(d); |
365 | 0 | } else { |
366 | 0 | m_stroke.setDashPattern(dashes); |
367 | 0 | } |
368 | 0 | m_strokeDashArraySet = 1; |
369 | 0 | } |
370 | | |
371 | | QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans) |
372 | 0 | : m_transform(trans) |
373 | 0 | { |
374 | 0 | } |
375 | | |
376 | 0 | QSvgTransformStyle::~QSvgTransformStyle() |
377 | | = default; |
378 | | |
379 | | void QSvgTransformStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
380 | 0 | { |
381 | 0 | m_oldWorldTransform.push(p->worldTransform()); |
382 | 0 | p->setWorldTransform(m_transform, true); |
383 | 0 | } |
384 | | |
385 | | void QSvgTransformStyle::revert(QPainter *p, QSvgExtraStates &) |
386 | 0 | { |
387 | 0 | p->setWorldTransform(m_oldWorldTransform.pop(), false /* don't combine */); |
388 | 0 | } |
389 | | |
390 | | QSvgStyleProperty::Type QSvgQualityStyle::type() const |
391 | 0 | { |
392 | 0 | return Quality; |
393 | 0 | } |
394 | | |
395 | | QSvgStyleProperty::Type QSvgFillStyle::type() const |
396 | 293k | { |
397 | 293k | return Fill; |
398 | 293k | } |
399 | | |
400 | | QSvgStyleProperty::Type QSvgViewportFillStyle::type() const |
401 | 0 | { |
402 | 0 | return ViewportFill; |
403 | 0 | } |
404 | | |
405 | | QSvgStyleProperty::Type QSvgFontStyle::type() const |
406 | 39.6k | { |
407 | 39.6k | return Font; |
408 | 39.6k | } |
409 | | |
410 | | QSvgStyleProperty::Type QSvgStrokeStyle::type() const |
411 | 288k | { |
412 | 288k | return Stroke; |
413 | 288k | } |
414 | | |
415 | | QSvgStyleProperty::Type QSvgTransformStyle::type() const |
416 | 0 | { |
417 | 0 | return Transform; |
418 | 0 | } |
419 | | |
420 | | |
421 | | QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode) |
422 | 0 | : m_mode(mode) |
423 | 0 | { |
424 | |
|
425 | 0 | } |
426 | | |
427 | | QSvgCompOpStyle::~QSvgCompOpStyle() |
428 | | = default; |
429 | | |
430 | | void QSvgCompOpStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
431 | 0 | { |
432 | 0 | m_oldMode = p->compositionMode(); |
433 | 0 | p->setCompositionMode(m_mode); |
434 | 0 | } |
435 | | |
436 | | void QSvgCompOpStyle::revert(QPainter *p, QSvgExtraStates &) |
437 | 0 | { |
438 | 0 | p->setCompositionMode(m_oldMode); |
439 | 0 | } |
440 | | |
441 | | QSvgStyleProperty::Type QSvgCompOpStyle::type() const |
442 | 0 | { |
443 | 0 | return CompOp; |
444 | 0 | } |
445 | | |
446 | 0 | QSvgOffsetStyle::~QSvgOffsetStyle() |
447 | | = default; |
448 | | |
449 | | void QSvgOffsetStyle::apply(QPainter *, const QSvgNode *, QSvgExtraStates &) |
450 | 0 | { |
451 | 0 | } |
452 | | |
453 | | void QSvgOffsetStyle::revert(QPainter *, QSvgExtraStates &) |
454 | 0 | { |
455 | 0 | } |
456 | | |
457 | | QSvgStyleProperty::Type QSvgOffsetStyle::type() const |
458 | 0 | { |
459 | 0 | return Offset; |
460 | 0 | } |
461 | | |
462 | | QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity) |
463 | 41.9k | : m_opacity(opacity), m_oldOpacity(0) |
464 | 41.9k | { |
465 | | |
466 | 41.9k | } |
467 | | |
468 | | QSvgOpacityStyle::~QSvgOpacityStyle() |
469 | | = default; |
470 | | |
471 | | void QSvgOpacityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
472 | 24.6k | { |
473 | 24.6k | m_oldOpacity = p->opacity(); |
474 | 24.6k | p->setOpacity(m_opacity * m_oldOpacity); |
475 | 24.6k | } |
476 | | |
477 | | void QSvgOpacityStyle::revert(QPainter *p, QSvgExtraStates &) |
478 | 24.6k | { |
479 | 24.6k | p->setOpacity(m_oldOpacity); |
480 | 24.6k | } |
481 | | |
482 | | QSvgStyleProperty::Type QSvgOpacityStyle::type() const |
483 | 83.9k | { |
484 | 83.9k | return Opacity; |
485 | 83.9k | } |
486 | | |
487 | 1.08M | QSvgStaticStyle::QSvgStaticStyle() |
488 | | = default; |
489 | 1.08M | QSvgStaticStyle::~QSvgStaticStyle() |
490 | | = default; |
491 | | |
492 | | void QSvgStaticStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) |
493 | 1.74M | { |
494 | 15.6M | for (auto &prop : m_properties) { |
495 | 15.6M | if (prop) |
496 | 561k | prop->apply(p, node, states); |
497 | 15.6M | } |
498 | 1.74M | } |
499 | | |
500 | | void QSvgStaticStyle::revert(QPainter *p, QSvgExtraStates &states) |
501 | 1.74M | { |
502 | 15.6M | for (auto &prop : m_properties) { |
503 | 15.6M | if (prop) |
504 | 561k | prop->revert(p, states); |
505 | 15.6M | } |
506 | 1.74M | } |
507 | | |
508 | | namespace { |
509 | | |
510 | | QColor sumValue(const QColor &c1, const QColor &c2) |
511 | 0 | { |
512 | 0 | QRgb rgb1 = c1.rgba(); |
513 | 0 | QRgb rgb2 = c2.rgba(); |
514 | 0 | int sumRed = qRed(rgb1) + qRed(rgb2); |
515 | 0 | int sumGreen = qGreen(rgb1) + qGreen(rgb2); |
516 | 0 | int sumBlue = qBlue(rgb1) + qBlue(rgb2); |
517 | |
|
518 | 0 | QRgb sumRgb = qRgba(qBound(0, sumRed, 255), |
519 | 0 | qBound(0, sumGreen, 255), |
520 | 0 | qBound(0, sumBlue, 255), |
521 | 0 | 255); |
522 | |
|
523 | 0 | return QColor(sumRgb); |
524 | 0 | } |
525 | | |
526 | | qreal sumValue(qreal value1, qreal value2) |
527 | 0 | { |
528 | 0 | qreal sumValue = value1 + value2; |
529 | 0 | return qBound(0.0, sumValue, 1.0); |
530 | 0 | } |
531 | | |
532 | | } |
533 | | |
534 | | QSvgAnimatedStyle::QSvgAnimatedStyle() |
535 | 1.08M | { |
536 | 1.08M | } |
537 | | |
538 | | QSvgAnimatedStyle::~QSvgAnimatedStyle() |
539 | 1.08M | { |
540 | 1.08M | } |
541 | | |
542 | | void QSvgAnimatedStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) |
543 | 0 | { |
544 | 0 | QSharedPointer<QSvgAbstractAnimator> animator = node->document()->animator(); |
545 | 0 | QList<QSvgAbstractAnimation *> nodeAnims = animator->animationsForNode(node); |
546 | |
|
547 | 0 | savePaintingState(p, node, states); |
548 | 0 | if (nodeAnims.isEmpty()) |
549 | 0 | return; |
550 | | |
551 | 0 | QSvgStyleState currentStyle = m_static; |
552 | 0 | for (const QSvgAbstractAnimation *anim : std::as_const(nodeAnims)) { |
553 | 0 | if (!anim->isActive()) |
554 | 0 | continue; |
555 | | |
556 | 0 | fetchStyleState(anim, currentStyle); |
557 | 0 | } |
558 | |
|
559 | 0 | applyStyle(p, states, currentStyle); |
560 | 0 | } |
561 | | |
562 | | void QSvgAnimatedStyle::revert(QPainter *p, QSvgExtraStates &states) |
563 | 0 | { |
564 | 0 | p->setWorldTransform(m_worldTransform); |
565 | 0 | p->setBrush(m_static.fill); |
566 | 0 | p->setPen(m_static.stroke); |
567 | 0 | p->setOpacity(m_static.opacity); |
568 | 0 | states.fillOpacity = m_static.fillOpacity; |
569 | 0 | states.strokeOpacity = m_static.strokeOpacity; |
570 | 0 | } |
571 | | |
572 | | void QSvgAnimatedStyle::savePaintingState(const QPainter *p, const QSvgNode *node, QSvgExtraStates &states) |
573 | 0 | { |
574 | 0 | m_worldTransform = p->worldTransform(); |
575 | 0 | if (auto prop = node->style().property(QSvgStyleProperty::Transform)) |
576 | 0 | m_static.transform = (static_cast<QSvgTransformStyle*>(prop))->qtransform(); |
577 | |
|
578 | 0 | if (auto prop = node->style().property(QSvgStyleProperty::Offset)) { |
579 | 0 | QSvgOffsetStyle *offset = static_cast<QSvgOffsetStyle*>(prop); |
580 | 0 | m_static.offsetPath = offset->path(); |
581 | 0 | m_static.offsetRotateType = offset->rotateType(); |
582 | 0 | m_static.offsetRotate = offset->rotateAngle(); |
583 | 0 | } |
584 | |
|
585 | 0 | m_static.fill = p->brush(); |
586 | 0 | m_static.stroke = p->pen(); |
587 | 0 | m_static.fillOpacity = states.fillOpacity; |
588 | 0 | m_static.strokeOpacity = states.strokeOpacity; |
589 | 0 | m_static.opacity = p->opacity(); |
590 | 0 | m_transformToNode = m_static.transform.inverted() * m_worldTransform; |
591 | 0 | } |
592 | | |
593 | | void QSvgAnimatedStyle::fetchStyleState(const QSvgAbstractAnimation *animation, QSvgStyleState ¤tStyle) |
594 | 0 | { |
595 | 0 | bool replace = animation->animationType() == QSvgAbstractAnimation::CSS ? true : |
596 | 0 | (static_cast<const QSvgAnimateNode *>(animation))->additiveType() == QSvgAnimateNode::Replace; |
597 | |
|
598 | 0 | QList<QSvgAbstractAnimatedProperty *> properties = animation->properties(); |
599 | 0 | for (const QSvgAbstractAnimatedProperty *property : std::as_const(properties)) { |
600 | 0 | if (property->propertyName() == QStringLiteral("fill")) { |
601 | 0 | QBrush brush = currentStyle.fill; |
602 | 0 | QColor brushColor = brush.color(); |
603 | 0 | QColor animatedColor = property->interpolatedValue().value<QColor>(); |
604 | 0 | QColor sumOrReplaceColor = replace ? animatedColor : sumValue(brushColor, animatedColor); |
605 | 0 | brush.setColor(sumOrReplaceColor); |
606 | 0 | currentStyle.fill = brush; |
607 | 0 | } else if (property->propertyName() == QStringLiteral("stroke")) { |
608 | 0 | QPen &pen = currentStyle.stroke; |
609 | 0 | QBrush penBrush = pen.brush(); |
610 | 0 | QColor penColor = penBrush.color(); |
611 | 0 | QColor animatedColor = property->interpolatedValue().value<QColor>(); |
612 | 0 | QColor sumOrReplaceColor = replace ? animatedColor : sumValue(penColor, animatedColor); |
613 | 0 | penBrush.setColor(sumOrReplaceColor); |
614 | 0 | penBrush.setStyle(Qt::SolidPattern); |
615 | 0 | pen.setBrush(penBrush); |
616 | 0 | } else if (property->propertyName() == QStringLiteral("transform")) { |
617 | 0 | QTransform animatedTransform = property->interpolatedValue().value<QTransform>(); |
618 | 0 | QTransform sumOrReplaceTransform = replace ? animatedTransform : animatedTransform * currentStyle.transform; |
619 | 0 | currentStyle.transform = sumOrReplaceTransform; |
620 | 0 | } else if (property->propertyName() == QStringLiteral("fill-opacity")) { |
621 | 0 | qreal animatedFillOpacity = property->interpolatedValue().value<qreal>(); |
622 | 0 | qreal sumOrReplaceOpacity = replace ? animatedFillOpacity : sumValue(currentStyle.fillOpacity, animatedFillOpacity); |
623 | 0 | currentStyle.fillOpacity = sumOrReplaceOpacity; |
624 | 0 | } else if (property->propertyName() == QStringLiteral("stroke-opacity")) { |
625 | 0 | qreal animatedStrokeOpacity = property->interpolatedValue().value<qreal>(); |
626 | 0 | qreal sumOrReplaceOpacity = replace ? animatedStrokeOpacity : sumValue(currentStyle.strokeOpacity, animatedStrokeOpacity); |
627 | 0 | currentStyle.strokeOpacity = sumOrReplaceOpacity; |
628 | 0 | } else if (property->propertyName() == QStringLiteral("opacity")) { |
629 | 0 | qreal animatedOpacity = property->interpolatedValue().value<qreal>(); |
630 | 0 | qreal sumOrReplaceOpacity = replace ? animatedOpacity : sumValue(currentStyle.opacity, animatedOpacity); |
631 | 0 | currentStyle.opacity = sumOrReplaceOpacity; |
632 | 0 | } else if (property->propertyName() == QStringLiteral("offset-distance")) { |
633 | 0 | qreal offsetDistance = property->interpolatedValue().value<qreal>(); |
634 | 0 | currentStyle.offsetDistance = offsetDistance; |
635 | 0 | } |
636 | 0 | } |
637 | 0 | } |
638 | | |
639 | | void QSvgAnimatedStyle::applyStyle(QPainter *p, QSvgExtraStates &states, const QSvgStyleState ¤tStyle) |
640 | 0 | { |
641 | 0 | p->setBrush(currentStyle.fill); |
642 | 0 | states.fillOpacity = currentStyle.fillOpacity; |
643 | 0 | p->setPen(currentStyle.stroke); |
644 | 0 | states.strokeOpacity = currentStyle.strokeOpacity; |
645 | 0 | p->setOpacity(currentStyle.opacity); |
646 | |
|
647 | 0 | QTransform transform = currentStyle.transform; |
648 | 0 | QTransform offset; |
649 | 0 | if (m_static.offsetPath) { |
650 | 0 | qreal offsetDistance = currentStyle.offsetDistance; |
651 | 0 | qreal angle = m_static.offsetPath.value().angleAtPercent(offsetDistance); |
652 | 0 | QPointF position = m_static.offsetPath.value().pointAtPercent(offsetDistance); |
653 | 0 | offset.translate(position.x(), position.y()); |
654 | |
|
655 | 0 | switch (m_static.offsetRotateType) { |
656 | 0 | case QtSvg::OffsetRotateType::Auto: |
657 | 0 | offset.rotate(-angle); |
658 | 0 | break; |
659 | 0 | case QtSvg::OffsetRotateType::Angle: |
660 | 0 | offset.rotate(m_static.offsetRotate); |
661 | 0 | break; |
662 | 0 | case QtSvg::OffsetRotateType::AutoAngle: |
663 | 0 | offset.rotate(m_static.offsetRotate - angle); |
664 | 0 | break; |
665 | 0 | case QtSvg::OffsetRotateType::Reverse: |
666 | 0 | offset.rotate(180 - angle); |
667 | 0 | break; |
668 | 0 | case QtSvg::OffsetRotateType::ReverseAngle: |
669 | 0 | offset.rotate(180 + m_static.offsetRotate - angle); |
670 | 0 | break; |
671 | 0 | } |
672 | 0 | } |
673 | | |
674 | 0 | QTransform combinedTransform = transform * offset * m_transformToNode; |
675 | 0 | p->setWorldTransform(combinedTransform); |
676 | 0 | } |
677 | | |
678 | | QT_END_NAMESPACE |