/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 | | QSvgExtraStates::QSvgExtraStates() |
20 | 38.9k | : fillOpacity(1.0), |
21 | 38.9k | strokeOpacity(1.0), |
22 | 38.9k | svgFont(0), |
23 | 38.9k | textAnchor(Qt::AlignLeft), |
24 | 38.9k | fontWeight(QFont::Normal), |
25 | 38.9k | fillRule(Qt::WindingFill), |
26 | 38.9k | strokeDashOffset(0), |
27 | 38.9k | vectorEffect(false), |
28 | 38.9k | imageRendering(QSvgQualityStyle::ImageRenderingAuto) |
29 | 38.9k | { |
30 | 38.9k | } |
31 | | |
32 | | QSvgStyleProperty::~QSvgStyleProperty() |
33 | 175k | { |
34 | 175k | } |
35 | | |
36 | | void QSvgPaintStyleProperty::apply(QPainter *, const QSvgNode *, QSvgExtraStates &) |
37 | 0 | { |
38 | 0 | Q_ASSERT(!"This should not be called!"); |
39 | 0 | } |
40 | | |
41 | | void QSvgPaintStyleProperty::revert(QPainter *, QSvgExtraStates &) |
42 | 0 | { |
43 | 0 | Q_ASSERT(!"This should not be called!"); |
44 | 0 | } |
45 | | |
46 | | |
47 | | QSvgQualityStyle::QSvgQualityStyle(int color) |
48 | 0 | : m_imageRendering(QSvgQualityStyle::ImageRenderingAuto) |
49 | 0 | , m_oldImageRendering(QSvgQualityStyle::ImageRenderingAuto) |
50 | 0 | , m_imageRenderingSet(0) |
51 | 0 | { |
52 | 0 | Q_UNUSED(color); |
53 | 0 | } |
54 | | |
55 | 0 | void QSvgQualityStyle::setImageRendering(ImageRendering hint) { |
56 | 0 | m_imageRendering = hint; |
57 | 0 | m_imageRenderingSet = 1; |
58 | 0 | } |
59 | | |
60 | | void QSvgQualityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) |
61 | 0 | { |
62 | 0 | m_oldImageRendering = states.imageRendering; |
63 | 0 | if (m_imageRenderingSet) { |
64 | 0 | states.imageRendering = m_imageRendering; |
65 | 0 | } |
66 | 0 | if (m_imageRenderingSet) { |
67 | 0 | bool smooth = false; |
68 | 0 | if (m_imageRendering == ImageRenderingAuto) |
69 | | // auto (the spec says to prefer quality) |
70 | 0 | smooth = true; |
71 | 0 | else |
72 | 0 | smooth = (m_imageRendering == ImageRenderingOptimizeQuality); |
73 | 0 | p->setRenderHint(QPainter::SmoothPixmapTransform, smooth); |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | | void QSvgQualityStyle::revert(QPainter *p, QSvgExtraStates &states) |
78 | 0 | { |
79 | 0 | if (m_imageRenderingSet) { |
80 | 0 | states.imageRendering = m_oldImageRendering; |
81 | 0 | bool smooth = false; |
82 | 0 | if (m_oldImageRendering == ImageRenderingAuto) |
83 | 0 | smooth = true; |
84 | 0 | else |
85 | 0 | smooth = (m_oldImageRendering == ImageRenderingOptimizeQuality); |
86 | 0 | p->setRenderHint(QPainter::SmoothPixmapTransform, smooth); |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | QSvgFillStyle::QSvgFillStyle() |
91 | 104k | : m_style(0) |
92 | 104k | , m_fillRule(Qt::WindingFill) |
93 | 104k | , m_oldFillRule(Qt::WindingFill) |
94 | 104k | , m_fillOpacity(1.0) |
95 | 104k | , m_oldFillOpacity(0) |
96 | 104k | , m_fillRuleSet(0) |
97 | 104k | , m_fillOpacitySet(0) |
98 | 104k | , m_fillSet(0) |
99 | 104k | { |
100 | 104k | } |
101 | | |
102 | | void QSvgFillStyle::setFillRule(Qt::FillRule f) |
103 | 47.4k | { |
104 | 47.4k | m_fillRuleSet = 1; |
105 | 47.4k | m_fillRule = f; |
106 | 47.4k | } |
107 | | |
108 | | void QSvgFillStyle::setFillOpacity(qreal opacity) |
109 | 89.6k | { |
110 | 89.6k | m_fillOpacitySet = 1; |
111 | 89.6k | m_fillOpacity = opacity; |
112 | 89.6k | } |
113 | | |
114 | | void QSvgFillStyle::setFillStyle(QSvgPaintStyleProperty* style) |
115 | 0 | { |
116 | 0 | m_style = style; |
117 | 0 | m_fillSet = 1; |
118 | 0 | } |
119 | | |
120 | | void QSvgFillStyle::setBrush(QBrush brush) |
121 | 21.2k | { |
122 | 21.2k | m_fill = std::move(brush); |
123 | 21.2k | m_style = nullptr; |
124 | 21.2k | m_fillSet = 1; |
125 | 21.2k | } |
126 | | |
127 | | void QSvgFillStyle::apply(QPainter *p, const QSvgNode *n, QSvgExtraStates &states) |
128 | 92.6k | { |
129 | 92.6k | m_oldFill = p->brush(); |
130 | 92.6k | m_oldFillRule = states.fillRule; |
131 | 92.6k | m_oldFillOpacity = states.fillOpacity; |
132 | | |
133 | 92.6k | if (m_fillRuleSet) |
134 | 44.3k | states.fillRule = m_fillRule; |
135 | 92.6k | if (m_fillSet) { |
136 | 13.2k | if (m_style) |
137 | 0 | p->setBrush(m_style->brush(p, n, states)); |
138 | 13.2k | else |
139 | 13.2k | p->setBrush(m_fill); |
140 | 13.2k | } |
141 | 92.6k | if (m_fillOpacitySet) |
142 | 83.7k | states.fillOpacity = m_fillOpacity; |
143 | 92.6k | } |
144 | | |
145 | | void QSvgFillStyle::revert(QPainter *p, QSvgExtraStates &states) |
146 | 92.6k | { |
147 | 92.6k | if (m_fillOpacitySet) |
148 | 83.7k | states.fillOpacity = m_oldFillOpacity; |
149 | 92.6k | if (m_fillSet) |
150 | 13.2k | p->setBrush(m_oldFill); |
151 | 92.6k | if (m_fillRuleSet) |
152 | 44.3k | states.fillRule = m_oldFillRule; |
153 | 92.6k | } |
154 | | |
155 | | QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush) |
156 | 0 | : m_viewportFill(brush) |
157 | 0 | { |
158 | 0 | } |
159 | | |
160 | | void QSvgViewportFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
161 | 0 | { |
162 | 0 | m_oldFill = p->brush(); |
163 | 0 | p->setBrush(m_viewportFill); |
164 | 0 | } |
165 | | |
166 | | void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &) |
167 | 0 | { |
168 | 0 | p->setBrush(m_oldFill); |
169 | 0 | } |
170 | | |
171 | | QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgDocument *doc) |
172 | 0 | : m_svgFont(font) |
173 | 0 | , m_doc(doc) |
174 | 0 | , m_familySet(0) |
175 | 0 | , m_sizeSet(0) |
176 | 0 | , m_styleSet(0) |
177 | 0 | , m_variantSet(0) |
178 | 0 | , m_weightSet(0) |
179 | 0 | , m_textAnchorSet(0) |
180 | 0 | { |
181 | 0 | } |
182 | | |
183 | | QSvgFontStyle::QSvgFontStyle() |
184 | 12 | : m_svgFont(0) |
185 | 12 | , m_doc(0) |
186 | 12 | , m_familySet(0) |
187 | 12 | , m_sizeSet(0) |
188 | 12 | , m_styleSet(0) |
189 | 12 | , m_variantSet(0) |
190 | 12 | , m_weightSet(0) |
191 | 12 | , m_textAnchorSet(0) |
192 | 12 | { |
193 | 12 | } |
194 | | |
195 | | void QSvgFontStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) |
196 | 6 | { |
197 | 6 | m_oldQFont = p->font(); |
198 | 6 | m_oldSvgFont = states.svgFont; |
199 | 6 | m_oldTextAnchor = states.textAnchor; |
200 | 6 | m_oldWeight = states.fontWeight; |
201 | | |
202 | 6 | if (m_textAnchorSet) |
203 | 0 | states.textAnchor = m_textAnchor; |
204 | | |
205 | 6 | QFont font = m_oldQFont; |
206 | 6 | if (m_familySet) { |
207 | 0 | states.svgFont = m_svgFont; |
208 | 0 | font.setFamilies(m_qfont.families()); |
209 | 0 | } |
210 | | |
211 | 6 | if (m_sizeSet) |
212 | 6 | font.setPointSizeF(m_qfont.pointSizeF()); |
213 | | |
214 | 6 | if (m_styleSet) |
215 | 0 | font.setStyle(m_qfont.style()); |
216 | | |
217 | 6 | if (m_variantSet) |
218 | 0 | font.setCapitalization(m_qfont.capitalization()); |
219 | | |
220 | 6 | if (m_weightSet) { |
221 | 0 | if (m_weight == BOLDER) { |
222 | 0 | states.fontWeight = qMin(states.fontWeight + 100, static_cast<int>(QFont::Black)); |
223 | 0 | } else if (m_weight == LIGHTER) { |
224 | 0 | states.fontWeight = qMax(states.fontWeight - 100, static_cast<int>(QFont::Thin)); |
225 | 0 | } else { |
226 | 0 | states.fontWeight = m_weight; |
227 | 0 | } |
228 | 0 | font.setWeight(QFont::Weight(qBound(static_cast<int>(QFont::Weight::Thin), |
229 | 0 | states.fontWeight, |
230 | 0 | static_cast<int>(QFont::Weight::Black)))); |
231 | 0 | } |
232 | | |
233 | 6 | p->setFont(font); |
234 | 6 | } |
235 | | |
236 | | void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states) |
237 | 6 | { |
238 | 6 | p->setFont(m_oldQFont); |
239 | 6 | states.svgFont = m_oldSvgFont; |
240 | 6 | states.textAnchor = m_oldTextAnchor; |
241 | 6 | states.fontWeight = m_oldWeight; |
242 | 6 | } |
243 | | |
244 | | QSvgStrokeStyle::QSvgStrokeStyle() |
245 | 48.6k | : m_strokeOpacity(1.0) |
246 | 48.6k | , m_oldStrokeOpacity(0.0) |
247 | 48.6k | , m_strokeDashOffset(0) |
248 | 48.6k | , m_oldStrokeDashOffset(0) |
249 | 48.6k | , m_style(0) |
250 | 48.6k | , m_vectorEffect(0) |
251 | 48.6k | , m_oldVectorEffect(0) |
252 | 48.6k | , m_strokeSet(0) |
253 | 48.6k | , m_strokeDashArraySet(0) |
254 | 48.6k | , m_strokeDashOffsetSet(0) |
255 | 48.6k | , m_strokeLineCapSet(0) |
256 | 48.6k | , m_strokeLineJoinSet(0) |
257 | 48.6k | , m_strokeMiterLimitSet(0) |
258 | 48.6k | , m_strokeOpacitySet(0) |
259 | 48.6k | , m_strokeWidthSet(0) |
260 | 48.6k | , m_vectorEffectSet(0) |
261 | 48.6k | { |
262 | 48.6k | } |
263 | | |
264 | | void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *n, QSvgExtraStates &states) |
265 | 54.6k | { |
266 | 54.6k | m_oldStroke = p->pen(); |
267 | 54.6k | m_oldStrokeOpacity = states.strokeOpacity; |
268 | 54.6k | m_oldStrokeDashOffset = states.strokeDashOffset; |
269 | 54.6k | m_oldVectorEffect = states.vectorEffect; |
270 | | |
271 | 54.6k | QPen pen = p->pen(); |
272 | | |
273 | 54.6k | qreal oldWidth = pen.widthF(); |
274 | 54.6k | qreal width = m_stroke.widthF(); |
275 | 54.6k | if (oldWidth == 0) |
276 | 0 | oldWidth = 1; |
277 | 54.6k | if (width == 0) |
278 | 0 | width = 1; |
279 | 54.6k | qreal scale = oldWidth / width; |
280 | | |
281 | 54.6k | if (m_strokeOpacitySet) |
282 | 294 | states.strokeOpacity = m_strokeOpacity; |
283 | | |
284 | 54.6k | if (m_vectorEffectSet) |
285 | 0 | states.vectorEffect = m_vectorEffect; |
286 | | |
287 | 54.6k | if (m_strokeSet) { |
288 | 52.3k | if (m_style) |
289 | 0 | pen.setBrush(m_style->brush(p, n, states)); |
290 | 52.3k | else |
291 | 52.3k | pen.setBrush(m_stroke.brush()); |
292 | 52.3k | } |
293 | | |
294 | 54.6k | if (m_strokeWidthSet) |
295 | 0 | pen.setWidthF(m_stroke.widthF()); |
296 | | |
297 | 54.6k | bool setDashOffsetNeeded = false; |
298 | | |
299 | 54.6k | if (m_strokeDashOffsetSet) { |
300 | 0 | states.strokeDashOffset = m_strokeDashOffset; |
301 | 0 | setDashOffsetNeeded = true; |
302 | 0 | } |
303 | | |
304 | 54.6k | if (m_strokeDashArraySet) { |
305 | 0 | if (m_stroke.style() == Qt::SolidLine) { |
306 | 0 | pen.setStyle(Qt::SolidLine); |
307 | 0 | } else if (m_strokeWidthSet || oldWidth == 1) { |
308 | | // If both width and dash array was set, the dash array is already scaled correctly. |
309 | 0 | pen.setDashPattern(m_stroke.dashPattern()); |
310 | 0 | setDashOffsetNeeded = true; |
311 | 0 | } else { |
312 | | // If dash array was set, but not the width, the dash array has to be scaled with respect to the old width. |
313 | 0 | QList<qreal> dashes = m_stroke.dashPattern(); |
314 | 0 | for (int i = 0; i < dashes.size(); ++i) |
315 | 0 | dashes[i] /= oldWidth; |
316 | 0 | pen.setDashPattern(dashes); |
317 | 0 | setDashOffsetNeeded = true; |
318 | 0 | } |
319 | 54.6k | } else if (m_strokeWidthSet && pen.style() != Qt::SolidLine && scale != 1) { |
320 | | // If the width was set, but not the dash array, the old dash array must be scaled with respect to the new width. |
321 | 0 | QList<qreal> dashes = pen.dashPattern(); |
322 | 0 | for (int i = 0; i < dashes.size(); ++i) |
323 | 0 | dashes[i] *= scale; |
324 | 0 | pen.setDashPattern(dashes); |
325 | 0 | setDashOffsetNeeded = true; |
326 | 0 | } |
327 | | |
328 | 54.6k | if (m_strokeLineCapSet) |
329 | 0 | pen.setCapStyle(m_stroke.capStyle()); |
330 | 54.6k | if (m_strokeLineJoinSet) |
331 | 0 | pen.setJoinStyle(m_stroke.joinStyle()); |
332 | 54.6k | if (m_strokeMiterLimitSet) |
333 | 0 | pen.setMiterLimit(m_stroke.miterLimit()); |
334 | | |
335 | | // You can have dash offset on solid strokes in SVG files, but not in Qt. |
336 | | // QPen::setDashOffset() will set the pen style to Qt::CustomDashLine, |
337 | | // so don't call the method if the pen is solid. |
338 | 54.6k | if (setDashOffsetNeeded && pen.style() != Qt::SolidLine) { |
339 | 0 | qreal currentWidth = pen.widthF(); |
340 | 0 | if (currentWidth == 0) |
341 | 0 | currentWidth = 1; |
342 | 0 | pen.setDashOffset(states.strokeDashOffset / currentWidth); |
343 | 0 | } |
344 | | |
345 | 54.6k | pen.setCosmetic(states.vectorEffect); |
346 | | |
347 | 54.6k | p->setPen(pen); |
348 | 54.6k | } |
349 | | |
350 | | void QSvgStrokeStyle::revert(QPainter *p, QSvgExtraStates &states) |
351 | 54.6k | { |
352 | 54.6k | p->setPen(m_oldStroke); |
353 | 54.6k | states.strokeOpacity = m_oldStrokeOpacity; |
354 | 54.6k | states.strokeDashOffset = m_oldStrokeDashOffset; |
355 | 54.6k | states.vectorEffect = m_oldVectorEffect; |
356 | 54.6k | } |
357 | | |
358 | | void QSvgStrokeStyle::setDashArray(const QList<qreal> &dashes) |
359 | 0 | { |
360 | 0 | if (m_strokeWidthSet) { |
361 | 0 | QList<qreal> d = dashes; |
362 | 0 | qreal w = m_stroke.widthF(); |
363 | 0 | if (w != 0 && w != 1) { |
364 | 0 | for (int i = 0; i < d.size(); ++i) |
365 | 0 | d[i] /= w; |
366 | 0 | } |
367 | 0 | m_stroke.setDashPattern(d); |
368 | 0 | } else { |
369 | 0 | m_stroke.setDashPattern(dashes); |
370 | 0 | } |
371 | 0 | m_strokeDashArraySet = 1; |
372 | 0 | } |
373 | | |
374 | | QSvgSolidColorStyle::QSvgSolidColorStyle(const QColor &color) |
375 | 0 | : m_solidColor(color) |
376 | 0 | { |
377 | 0 | } |
378 | | |
379 | | QSvgGradientStyle::QSvgGradientStyle(QGradient *grad) |
380 | 0 | : m_gradient(grad) |
381 | 0 | , m_gradientStopsSet(false) |
382 | 0 | { |
383 | 0 | } |
384 | | |
385 | | QBrush QSvgGradientStyle::brush(QPainter *, const QSvgNode *, QSvgExtraStates &) |
386 | 0 | { |
387 | 0 | if (!m_link.isEmpty()) { |
388 | 0 | resolveStops(); |
389 | 0 | } |
390 | | |
391 | | // If the gradient is marked as empty, insert transparent black |
392 | 0 | if (!m_gradientStopsSet) { |
393 | 0 | m_gradient->setStops(QGradientStops() << QGradientStop(0.0, QColor(0, 0, 0, 0))); |
394 | 0 | m_gradientStopsSet = true; |
395 | 0 | } |
396 | |
|
397 | 0 | QBrush b(*m_gradient); |
398 | |
|
399 | 0 | if (!m_transform.isIdentity()) |
400 | 0 | b.setTransform(m_transform); |
401 | |
|
402 | 0 | return b; |
403 | 0 | } |
404 | | |
405 | | |
406 | | void QSvgGradientStyle::setTransform(const QTransform &transform) |
407 | 0 | { |
408 | 0 | m_transform = transform; |
409 | 0 | } |
410 | | |
411 | | QSvgPatternStyle::QSvgPatternStyle(QSvgPattern *pattern) |
412 | 0 | : m_pattern(pattern) |
413 | 0 | { |
414 | |
|
415 | 0 | } |
416 | | |
417 | | QBrush QSvgPatternStyle::brush(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) |
418 | 0 | { |
419 | 0 | QBrush b(m_pattern->patternImage(p, states, node)); |
420 | 0 | b.setTransform(m_pattern->appliedTransform()); |
421 | 0 | return b; |
422 | 0 | } |
423 | | |
424 | | QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans) |
425 | 0 | : m_transform(trans) |
426 | 0 | { |
427 | 0 | } |
428 | | |
429 | | void QSvgTransformStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
430 | 0 | { |
431 | 0 | m_oldWorldTransform.push(p->worldTransform()); |
432 | 0 | p->setWorldTransform(m_transform, true); |
433 | 0 | } |
434 | | |
435 | | void QSvgTransformStyle::revert(QPainter *p, QSvgExtraStates &) |
436 | 0 | { |
437 | 0 | p->setWorldTransform(m_oldWorldTransform.pop(), false /* don't combine */); |
438 | 0 | } |
439 | | |
440 | | QSvgStyleProperty::Type QSvgQualityStyle::type() const |
441 | 0 | { |
442 | 0 | return QUALITY; |
443 | 0 | } |
444 | | |
445 | | QSvgStyleProperty::Type QSvgFillStyle::type() const |
446 | 104k | { |
447 | 104k | return FILL; |
448 | 104k | } |
449 | | |
450 | | QSvgStyleProperty::Type QSvgViewportFillStyle::type() const |
451 | 0 | { |
452 | 0 | return VIEWPORT_FILL; |
453 | 0 | } |
454 | | |
455 | | QSvgStyleProperty::Type QSvgFontStyle::type() const |
456 | 12 | { |
457 | 12 | return FONT; |
458 | 12 | } |
459 | | |
460 | | QSvgStyleProperty::Type QSvgStrokeStyle::type() const |
461 | 50.5k | { |
462 | 50.5k | return STROKE; |
463 | 50.5k | } |
464 | | |
465 | | QSvgStyleProperty::Type QSvgSolidColorStyle::type() const |
466 | 0 | { |
467 | 0 | return SOLID_COLOR; |
468 | 0 | } |
469 | | |
470 | | QSvgStyleProperty::Type QSvgGradientStyle::type() const |
471 | 0 | { |
472 | 0 | return GRADIENT; |
473 | 0 | } |
474 | | |
475 | | QSvgStyleProperty::Type QSvgPatternStyle::type() const |
476 | 0 | { |
477 | 0 | return PATTERN; |
478 | 0 | } |
479 | | |
480 | | QSvgStyleProperty::Type QSvgTransformStyle::type() const |
481 | 0 | { |
482 | 0 | return TRANSFORM; |
483 | 0 | } |
484 | | |
485 | | |
486 | | QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode) |
487 | 0 | : m_mode(mode) |
488 | 0 | { |
489 | |
|
490 | 0 | } |
491 | | |
492 | | void QSvgCompOpStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
493 | 0 | { |
494 | 0 | m_oldMode = p->compositionMode(); |
495 | 0 | p->setCompositionMode(m_mode); |
496 | 0 | } |
497 | | |
498 | | void QSvgCompOpStyle::revert(QPainter *p, QSvgExtraStates &) |
499 | 0 | { |
500 | 0 | p->setCompositionMode(m_oldMode); |
501 | 0 | } |
502 | | |
503 | | QSvgStyleProperty::Type QSvgCompOpStyle::type() const |
504 | 0 | { |
505 | 0 | return COMP_OP; |
506 | 0 | } |
507 | | |
508 | | void QSvgOffsetStyle::apply(QPainter *, const QSvgNode *, QSvgExtraStates &) |
509 | 0 | { |
510 | 0 | } |
511 | | |
512 | | void QSvgOffsetStyle::revert(QPainter *, QSvgExtraStates &) |
513 | 0 | { |
514 | 0 | } |
515 | | |
516 | | QSvgStyleProperty::Type QSvgOffsetStyle::type() const |
517 | 0 | { |
518 | 0 | return OFFSET; |
519 | 0 | } |
520 | | |
521 | | QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity) |
522 | 22.9k | : m_opacity(opacity), m_oldOpacity(0) |
523 | 22.9k | { |
524 | | |
525 | 22.9k | } |
526 | | |
527 | | void QSvgOpacityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) |
528 | 634 | { |
529 | 634 | m_oldOpacity = p->opacity(); |
530 | 634 | p->setOpacity(m_opacity * m_oldOpacity); |
531 | 634 | } |
532 | | |
533 | | void QSvgOpacityStyle::revert(QPainter *p, QSvgExtraStates &) |
534 | 634 | { |
535 | 634 | p->setOpacity(m_oldOpacity); |
536 | 634 | } |
537 | | |
538 | | QSvgStyleProperty::Type QSvgOpacityStyle::type() const |
539 | 22.9k | { |
540 | 22.9k | return OPACITY; |
541 | 22.9k | } |
542 | | |
543 | | void QSvgGradientStyle::setStopLink(const QString &link, QSvgDocument *doc) |
544 | 0 | { |
545 | 0 | m_link = link; |
546 | 0 | m_doc = doc; |
547 | 0 | } |
548 | | |
549 | | void QSvgGradientStyle::resolveStops() |
550 | 0 | { |
551 | 0 | QStringList visited; |
552 | 0 | resolveStops_helper(&visited); |
553 | 0 | } |
554 | | |
555 | | void QSvgGradientStyle::resolveStops_helper(QStringList *visited) |
556 | 0 | { |
557 | 0 | if (!m_link.isEmpty() && m_doc) { |
558 | 0 | QSvgStyleProperty *prop = m_doc->namedStyle(m_link); |
559 | 0 | if (prop && !visited->contains(m_link)) { |
560 | 0 | visited->append(m_link); |
561 | 0 | if (prop->type() == QSvgStyleProperty::GRADIENT) { |
562 | 0 | QSvgGradientStyle *st = |
563 | 0 | static_cast<QSvgGradientStyle*>(prop); |
564 | 0 | st->resolveStops_helper(visited); |
565 | 0 | m_gradient->setStops(st->qgradient()->stops()); |
566 | 0 | m_gradientStopsSet = st->gradientStopsSet(); |
567 | 0 | } |
568 | 0 | } else { |
569 | 0 | qWarning("Could not resolve property : %s", qPrintable(m_link)); |
570 | 0 | } |
571 | 0 | m_link = QString(); |
572 | 0 | } |
573 | 0 | } |
574 | | |
575 | | QSvgStaticStyle::QSvgStaticStyle() |
576 | 694k | : quality(0) |
577 | 694k | , fill(0) |
578 | 694k | , viewportFill(0) |
579 | 694k | , font(0) |
580 | 694k | , stroke(0) |
581 | 694k | , solidColor(0) |
582 | 694k | , gradient(0) |
583 | 694k | , pattern(0) |
584 | 694k | , transform(0) |
585 | 694k | , opacity(0) |
586 | 694k | , compop(0) |
587 | 694k | { |
588 | 694k | } |
589 | | |
590 | | QSvgStaticStyle::~QSvgStaticStyle() |
591 | 694k | { |
592 | 694k | } |
593 | | |
594 | | void QSvgStaticStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) |
595 | 107k | { |
596 | 107k | if (quality) { |
597 | 0 | quality->apply(p, node, states); |
598 | 0 | } |
599 | | |
600 | 107k | if (fill) { |
601 | 92.6k | fill->apply(p, node, states); |
602 | 92.6k | } |
603 | | |
604 | 107k | if (viewportFill) { |
605 | 0 | viewportFill->apply(p, node, states); |
606 | 0 | } |
607 | | |
608 | 107k | if (font) { |
609 | 6 | font->apply(p, node, states); |
610 | 6 | } |
611 | | |
612 | 107k | if (stroke) { |
613 | 54.6k | stroke->apply(p, node, states); |
614 | 54.6k | } |
615 | | |
616 | 107k | if (transform) { |
617 | 0 | transform->apply(p, node, states); |
618 | 0 | } |
619 | | |
620 | 107k | if (opacity) { |
621 | 634 | opacity->apply(p, node, states); |
622 | 634 | } |
623 | | |
624 | 107k | if (compop) { |
625 | 0 | compop->apply(p, node, states); |
626 | 0 | } |
627 | 107k | } |
628 | | |
629 | | void QSvgStaticStyle::revert(QPainter *p, QSvgExtraStates &states) |
630 | 107k | { |
631 | 107k | if (quality) { |
632 | 0 | quality->revert(p, states); |
633 | 0 | } |
634 | | |
635 | 107k | if (fill) { |
636 | 92.6k | fill->revert(p, states); |
637 | 92.6k | } |
638 | | |
639 | 107k | if (viewportFill) { |
640 | 0 | viewportFill->revert(p, states); |
641 | 0 | } |
642 | | |
643 | 107k | if (font) { |
644 | 6 | font->revert(p, states); |
645 | 6 | } |
646 | | |
647 | 107k | if (stroke) { |
648 | 54.6k | stroke->revert(p, states); |
649 | 54.6k | } |
650 | | |
651 | 107k | if (transform) { |
652 | 0 | transform->revert(p, states); |
653 | 0 | } |
654 | | |
655 | 107k | if (opacity) { |
656 | 634 | opacity->revert(p, states); |
657 | 634 | } |
658 | | |
659 | 107k | if (compop) { |
660 | 0 | compop->revert(p, states); |
661 | 0 | } |
662 | 107k | } |
663 | | |
664 | | namespace { |
665 | | |
666 | | QColor sumValue(const QColor &c1, const QColor &c2) |
667 | 0 | { |
668 | 0 | QRgb rgb1 = c1.rgba(); |
669 | 0 | QRgb rgb2 = c2.rgba(); |
670 | 0 | int sumRed = qRed(rgb1) + qRed(rgb2); |
671 | 0 | int sumGreen = qGreen(rgb1) + qGreen(rgb2); |
672 | 0 | int sumBlue = qBlue(rgb1) + qBlue(rgb2); |
673 | |
|
674 | 0 | QRgb sumRgb = qRgba(qBound(0, sumRed, 255), |
675 | 0 | qBound(0, sumGreen, 255), |
676 | 0 | qBound(0, sumBlue, 255), |
677 | 0 | 255); |
678 | |
|
679 | 0 | return QColor(sumRgb); |
680 | 0 | } |
681 | | |
682 | | qreal sumValue(qreal value1, qreal value2) |
683 | 0 | { |
684 | 0 | qreal sumValue = value1 + value2; |
685 | 0 | return qBound(0.0, sumValue, 1.0); |
686 | 0 | } |
687 | | |
688 | | } |
689 | | |
690 | | QSvgAnimatedStyle::QSvgAnimatedStyle() |
691 | 694k | { |
692 | 694k | } |
693 | | |
694 | | QSvgAnimatedStyle::~QSvgAnimatedStyle() |
695 | 694k | { |
696 | 694k | } |
697 | | |
698 | | void QSvgAnimatedStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) |
699 | 0 | { |
700 | 0 | QSharedPointer<QSvgAbstractAnimator> animator = node->document()->animator(); |
701 | 0 | QList<QSvgAbstractAnimation *> nodeAnims = animator->animationsForNode(node); |
702 | |
|
703 | 0 | savePaintingState(p, node, states); |
704 | 0 | if (nodeAnims.isEmpty()) |
705 | 0 | return; |
706 | | |
707 | 0 | QSvgStyleState currentStyle = m_static; |
708 | 0 | for (const QSvgAbstractAnimation *anim : std::as_const(nodeAnims)) { |
709 | 0 | if (!anim->isActive()) |
710 | 0 | continue; |
711 | | |
712 | 0 | fetchStyleState(anim, currentStyle); |
713 | 0 | } |
714 | |
|
715 | 0 | applyStyle(p, states, currentStyle); |
716 | 0 | } |
717 | | |
718 | | void QSvgAnimatedStyle::revert(QPainter *p, QSvgExtraStates &states) |
719 | 0 | { |
720 | 0 | p->setWorldTransform(m_worldTransform); |
721 | 0 | p->setBrush(m_static.fill); |
722 | 0 | p->setPen(m_static.stroke); |
723 | 0 | p->setOpacity(m_static.opacity); |
724 | 0 | states.fillOpacity = m_static.fillOpacity; |
725 | 0 | states.strokeOpacity = m_static.strokeOpacity; |
726 | 0 | } |
727 | | |
728 | | void QSvgAnimatedStyle::savePaintingState(const QPainter *p, const QSvgNode *node, QSvgExtraStates &states) |
729 | 0 | { |
730 | 0 | QSvgStaticStyle style = node->style(); |
731 | 0 | m_worldTransform = p->worldTransform(); |
732 | 0 | if (style.transform) |
733 | 0 | m_static.transform = style.transform->qtransform(); |
734 | |
|
735 | 0 | if (style.offset) { |
736 | 0 | m_static.offsetPath = style.offset->path(); |
737 | 0 | m_static.offsetRotateType = style.offset->rotateType(); |
738 | 0 | m_static.offsetRotate = style.offset->rotateAngle(); |
739 | 0 | } |
740 | |
|
741 | 0 | m_static.fill = p->brush(); |
742 | 0 | m_static.stroke = p->pen(); |
743 | 0 | m_static.fillOpacity = states.fillOpacity; |
744 | 0 | m_static.strokeOpacity = states.strokeOpacity; |
745 | 0 | m_static.opacity = p->opacity(); |
746 | 0 | m_transformToNode = m_static.transform.inverted() * m_worldTransform; |
747 | 0 | } |
748 | | |
749 | | void QSvgAnimatedStyle::fetchStyleState(const QSvgAbstractAnimation *animation, QSvgStyleState ¤tStyle) |
750 | 0 | { |
751 | 0 | bool replace = animation->animationType() == QSvgAbstractAnimation::CSS ? true : |
752 | 0 | (static_cast<const QSvgAnimateNode *>(animation))->additiveType() == QSvgAnimateNode::Replace; |
753 | |
|
754 | 0 | QList<QSvgAbstractAnimatedProperty *> properties = animation->properties(); |
755 | 0 | for (const QSvgAbstractAnimatedProperty *property : std::as_const(properties)) { |
756 | 0 | if (property->propertyName() == QStringLiteral("fill")) { |
757 | 0 | QBrush brush = currentStyle.fill; |
758 | 0 | QColor brushColor = brush.color(); |
759 | 0 | QColor animatedColor = property->interpolatedValue().value<QColor>(); |
760 | 0 | QColor sumOrReplaceColor = replace ? animatedColor : sumValue(brushColor, animatedColor); |
761 | 0 | brush.setColor(sumOrReplaceColor); |
762 | 0 | currentStyle.fill = brush; |
763 | 0 | } else if (property->propertyName() == QStringLiteral("stroke")) { |
764 | 0 | QPen &pen = currentStyle.stroke; |
765 | 0 | QBrush penBrush = pen.brush(); |
766 | 0 | QColor penColor = penBrush.color(); |
767 | 0 | QColor animatedColor = property->interpolatedValue().value<QColor>(); |
768 | 0 | QColor sumOrReplaceColor = replace ? animatedColor : sumValue(penColor, animatedColor); |
769 | 0 | penBrush.setColor(sumOrReplaceColor); |
770 | 0 | penBrush.setStyle(Qt::SolidPattern); |
771 | 0 | pen.setBrush(penBrush); |
772 | 0 | } else if (property->propertyName() == QStringLiteral("transform")) { |
773 | 0 | QTransform animatedTransform = property->interpolatedValue().value<QTransform>(); |
774 | 0 | QTransform sumOrReplaceTransform = replace ? animatedTransform : animatedTransform * currentStyle.transform; |
775 | 0 | currentStyle.transform = sumOrReplaceTransform; |
776 | 0 | } else if (property->propertyName() == QStringLiteral("fill-opacity")) { |
777 | 0 | qreal animatedFillOpacity = property->interpolatedValue().value<qreal>(); |
778 | 0 | qreal sumOrReplaceOpacity = replace ? animatedFillOpacity : sumValue(currentStyle.fillOpacity, animatedFillOpacity); |
779 | 0 | currentStyle.fillOpacity = sumOrReplaceOpacity; |
780 | 0 | } else if (property->propertyName() == QStringLiteral("stroke-opacity")) { |
781 | 0 | qreal animatedStrokeOpacity = property->interpolatedValue().value<qreal>(); |
782 | 0 | qreal sumOrReplaceOpacity = replace ? animatedStrokeOpacity : sumValue(currentStyle.strokeOpacity, animatedStrokeOpacity); |
783 | 0 | currentStyle.strokeOpacity = sumOrReplaceOpacity; |
784 | 0 | } else if (property->propertyName() == QStringLiteral("opacity")) { |
785 | 0 | qreal animatedOpacity = property->interpolatedValue().value<qreal>(); |
786 | 0 | qreal sumOrReplaceOpacity = replace ? animatedOpacity : sumValue(currentStyle.opacity, animatedOpacity); |
787 | 0 | currentStyle.opacity = sumOrReplaceOpacity; |
788 | 0 | } else if (property->propertyName() == QStringLiteral("offset-distance")) { |
789 | 0 | qreal offsetDistance = property->interpolatedValue().value<qreal>(); |
790 | 0 | currentStyle.offsetDistance = offsetDistance; |
791 | 0 | } |
792 | 0 | } |
793 | 0 | } |
794 | | |
795 | | void QSvgAnimatedStyle::applyStyle(QPainter *p, QSvgExtraStates &states, const QSvgStyleState ¤tStyle) |
796 | 0 | { |
797 | 0 | p->setBrush(currentStyle.fill); |
798 | 0 | states.fillOpacity = currentStyle.fillOpacity; |
799 | 0 | p->setPen(currentStyle.stroke); |
800 | 0 | states.strokeOpacity = currentStyle.strokeOpacity; |
801 | 0 | p->setOpacity(currentStyle.opacity); |
802 | |
|
803 | 0 | QTransform transform = currentStyle.transform; |
804 | 0 | QTransform offset; |
805 | 0 | if (m_static.offsetPath) { |
806 | 0 | qreal offsetDistance = currentStyle.offsetDistance; |
807 | 0 | qreal angle = m_static.offsetPath.value().angleAtPercent(offsetDistance); |
808 | 0 | QPointF position = m_static.offsetPath.value().pointAtPercent(offsetDistance); |
809 | 0 | offset.translate(position.x(), position.y()); |
810 | |
|
811 | 0 | switch (m_static.offsetRotateType) { |
812 | 0 | case QtSvg::OffsetRotateType::Auto: |
813 | 0 | offset.rotate(-angle); |
814 | 0 | break; |
815 | 0 | case QtSvg::OffsetRotateType::Angle: |
816 | 0 | offset.rotate(m_static.offsetRotate); |
817 | 0 | break; |
818 | 0 | case QtSvg::OffsetRotateType::AutoAngle: |
819 | 0 | offset.rotate(m_static.offsetRotate - angle); |
820 | 0 | break; |
821 | 0 | case QtSvg::OffsetRotateType::Reverse: |
822 | 0 | offset.rotate(180 - angle); |
823 | 0 | break; |
824 | 0 | case QtSvg::OffsetRotateType::ReverseAngle: |
825 | 0 | offset.rotate(180 + m_static.offsetRotate - angle); |
826 | 0 | break; |
827 | 0 | } |
828 | 0 | } |
829 | | |
830 | 0 | QTransform combinedTransform = transform * offset * m_transformToNode; |
831 | 0 | p->setWorldTransform(combinedTransform); |
832 | 0 | } |
833 | | |
834 | | QT_END_NAMESPACE |