Coverage Report

Created: 2026-01-25 07:18

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
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 &currentStyle)
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 &currentStyle)
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