Coverage Report

Created: 2026-04-29 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 &currentStyle)
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 &currentStyle)
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