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