Coverage Report

Created: 2026-05-31 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtsvg/src/svg/css/qsvgcsshandler.cpp
Line
Count
Source
1
// Copyright (C) 2025 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:critical reason:data-parser
4
5
#include "qsvgcsshandler_p.h"
6
#include <QtSvg/private/qsvgstyleselector_p.h>
7
#include <QtSvg/private/qsvganimatedproperty_p.h>
8
#include <QtSvg/private/qsvgutils_p.h>
9
#include <QtGui/private/qmath_p.h>
10
#include <QtCore/qlist.h>
11
12
QT_BEGIN_NAMESPACE
13
14
namespace {
15
16
struct CssKeyFrameValue{
17
    qreal keyFrame;
18
    QList<QCss::Value> values;
19
    QCss::Declaration timingFunction;
20
};
21
22
bool fillPropertyEasing(const QList<CssKeyFrameValue> &keyFrames, QSvgAbstractAnimatedProperty *prop)
23
0
{
24
0
    for (const CssKeyFrameValue &keyFrameValue : keyFrames) {
25
0
        QStringView timingFunctionStr = QSvgCssHandler::parseDecltoString(keyFrameValue.timingFunction);
26
0
        QSvgEasingInterfacePtr easing;
27
0
        if (timingFunctionStr == QStringLiteral("linear")) {
28
0
            easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::Linear);
29
0
        } else if (timingFunctionStr == QStringLiteral("ease-in")) {
30
0
            easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::EaseIn);
31
0
        } else if (timingFunctionStr == QStringLiteral("ease-out")) {
32
0
            easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::EaseOut);
33
0
        } else if (timingFunctionStr == QStringLiteral("ease-in-out")) {
34
0
            easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::EaseInOut);
35
0
        } else if (timingFunctionStr == QStringLiteral("step-end")) {
36
0
            easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::Steps,
37
0
                                                  QSvgCssValues::StepValues{quint32(1),
38
0
                                                  QSvgCssValues::StepPosition::End});
39
0
        } else if (timingFunctionStr == QStringLiteral("step-start")) {
40
0
            easing = QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction::Steps,
41
0
                                                  QSvgCssValues::StepValues{quint32(1),
42
0
                                                  QSvgCssValues::StepPosition::Start});
43
0
        }
44
45
0
        prop->appendEasing(std::move(easing));
46
0
    }
47
48
49
50
0
    return true;
51
0
}
52
53
bool fillColorProperty(const QList<CssKeyFrameValue> &keyFrames, QSvgAnimatedPropertyColor *prop)
54
0
{
55
0
    for (const CssKeyFrameValue &keyFrame : keyFrames) {
56
0
        if (keyFrame.values.size() != 1)
57
0
            return false;
58
59
0
        QString colorStr = keyFrame.values.first().toString();
60
0
        QColor color = QColor::fromString(colorStr);
61
0
        prop->appendColor(color);
62
0
        prop->appendKeyFrame(keyFrame.keyFrame);
63
0
    }
64
65
0
    return true;
66
0
}
67
68
bool fillOpacityProperty(const QList<CssKeyFrameValue> &keyFrames, QSvgAnimatedPropertyFloat *prop)
69
0
{
70
0
    for (const CssKeyFrameValue &keyFrame : keyFrames) {
71
0
        if (keyFrame.values.size() != 1)
72
0
            return false;
73
74
0
        QString opacity = keyFrame.values.first().toString();
75
0
        prop->appendValue(opacity.toDouble());
76
0
        prop->appendKeyFrame(keyFrame.keyFrame);
77
0
    }
78
79
0
    return true;
80
0
}
81
82
0
bool validateTransform(QList<QList<QSvgAnimatedPropertyTransform::TransformComponent>> &keyFrameComponents) {
83
84
0
    if (keyFrameComponents.size() < 2)
85
0
        return false;
86
87
0
    qsizetype maxIndex = 0;
88
0
    qsizetype maxSize = 0;
89
0
    for (int i = 1; i < keyFrameComponents.size(); i++) {
90
0
        auto &listA = keyFrameComponents[i - 1];
91
0
        auto &listB = keyFrameComponents[i];
92
0
        for (int j = 0; j < qMin(listA.size(), listB.size()); j++) {
93
0
            auto typeA = listA.at(j).type;
94
0
            auto typeB = listB.at(j).type;
95
            // TODO: Handle type mismatch as mentioned in CSS Transform Module specs.
96
0
            if (typeA != typeB)
97
0
                return false;
98
0
        }
99
100
0
        if (listA.size() > maxSize) {
101
0
            maxIndex = i - 1;
102
0
            maxSize = listA.size();
103
0
        }
104
105
0
        if (listB.size() > maxSize) {
106
0
            maxIndex = i;
107
0
            maxSize = listB.size();
108
0
        }
109
0
    }
110
111
0
    const auto &longList = keyFrameComponents.at(maxIndex);
112
    // pad the shorter list with identical transforms set to default values.
113
0
    for (auto &list : keyFrameComponents) {
114
0
        qsizetype size = list.size();
115
116
0
        for (int j = size; j < maxSize; j++) {
117
0
            QSvgAnimatedPropertyTransform::TransformComponent comp = longList.value(j);
118
0
            switch (comp.type) {
119
0
            case QSvgAnimatedPropertyTransform::TransformComponent::Translate:
120
0
            case QSvgAnimatedPropertyTransform::TransformComponent::Skew:
121
0
                comp.values[0] = 0;
122
0
                comp.values[1] = 0;
123
0
                break;
124
0
            case QSvgAnimatedPropertyTransform::TransformComponent::Rotate:
125
0
                comp.values[0] = 0;
126
0
                comp.values[1] = 0;
127
0
                comp.values[2] = 0;
128
0
                break;
129
0
            case QSvgAnimatedPropertyTransform::TransformComponent::Scale:
130
0
                comp.values[0] = 1;
131
0
                comp.values[1] = 1;
132
0
                break;
133
0
            default:
134
0
                break;
135
0
            }
136
0
            list.append(comp);
137
0
        }
138
0
    }
139
140
0
    return true;
141
0
}
142
143
bool fillTransformProperty(const QList<CssKeyFrameValue> &keyFrames, QSvgAnimatedPropertyTransform *prop)
144
0
{
145
    // Stores each key frame's list of components
146
0
    QList<QList<QSvgAnimatedPropertyTransform::TransformComponent>> keyFramesComponents;
147
148
0
    for (const CssKeyFrameValue &keyFrame : keyFrames) {
149
0
        QList<QSvgAnimatedPropertyTransform::TransformComponent> components;
150
0
        for (const QCss::Value &val : keyFrame.values) {
151
0
            if (val.type == QCss::Value::Function) {
152
0
                QStringList lst = val.variant.toStringList();
153
0
                QStringView transformType = lst.value(0);
154
0
                QStringList args = lst.value(1).split(QStringLiteral(","), Qt::SkipEmptyParts);
155
0
                if (transformType == QStringLiteral("scale")) {
156
0
                    QSvgAnimatedPropertyTransform::TransformComponent component;
157
0
                    qreal scale0 = QSvgUtils::toDouble(args.value(0).trimmed());
158
0
                    qreal scale1 = QSvgUtils::toDouble(args.value(1).trimmed());
159
0
                    component.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
160
0
                    component.values.append(scale0);
161
0
                    component.values.append(scale1);
162
0
                    components.append(component);
163
0
                } else if (transformType == QStringLiteral("translate")) {
164
0
                    QSvgAnimatedPropertyTransform::TransformComponent component;
165
0
                    QSvgUtils::LengthType type;
166
0
                    qreal translate0 = QSvgUtils::parseLength(args.value(0), &type);
167
0
                    translate0 = QSvgUtils::convertToPixels(translate0, false, type);
168
0
                    qreal translate1 = QSvgUtils::parseLength(args.value(1), &type);
169
0
                    translate1 = QSvgUtils::convertToPixels(translate1, false, type);
170
0
                    component.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
171
0
                    component.values.append(translate0);
172
0
                    component.values.append(translate1);
173
0
                    components.append(component);
174
0
                } else if (transformType == QStringLiteral("rotate")) {
175
0
                    QSvgAnimatedPropertyTransform::TransformComponent component;
176
0
                    qreal rotationAngle = QSvgUtils::parseAngle(args.value(0)).value_or(0);
177
0
                    component.type = QSvgAnimatedPropertyTransform::TransformComponent::Rotate;
178
0
                    component.values.append(rotationAngle);
179
0
                    component.values.append(0);
180
0
                    component.values.append(0);
181
0
                    components.append(component);
182
0
                } else if (transformType == QStringLiteral("skew")) {
183
0
                    QSvgAnimatedPropertyTransform::TransformComponent component;
184
0
                    qreal skew0 = QSvgUtils::parseAngle(args.value(0)).value_or(0);
185
0
                    qreal skew1 = QSvgUtils::parseAngle(args.value(1)).value_or(0);
186
0
                    component.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
187
0
                    component.values.append(skew0);
188
0
                    component.values.append(skew1);
189
0
                    components.append(component);
190
0
                } else if (transformType == QStringLiteral("matrix")) {
191
0
                    QSvgAnimatedPropertyTransform::TransformComponent component1, component2, component3;
192
0
                    QSvgUtils::LengthType type;
193
0
                    qreal translate0 = QSvgUtils::parseLength(args.value(4), &type);
194
0
                    translate0 = QSvgUtils::convertToPixels(translate0, false, type);
195
0
                    qreal translate1 = QSvgUtils::parseLength(args.value(5), &type);
196
0
                    translate1 = QSvgUtils::convertToPixels(translate1, false, type);
197
0
                    qreal scale0 = QSvgUtils::toDouble(args.value(0).trimmed());
198
0
                    qreal scale1 = QSvgUtils::toDouble(args.value(3).trimmed());
199
0
                    qreal skew0 = QSvgUtils::toDouble((args.value(1).trimmed()));
200
0
                    qreal skew1 = QSvgUtils::toDouble((args.value(2).trimmed()));
201
0
                    component1.type = QSvgAnimatedPropertyTransform::TransformComponent::Translate;
202
0
                    component1.values.append(translate0);
203
0
                    component1.values.append(translate1);
204
0
                    component2.type = QSvgAnimatedPropertyTransform::TransformComponent::Scale;
205
0
                    component2.values.append(scale0);
206
0
                    component2.values.append(scale1);
207
0
                    component3.type = QSvgAnimatedPropertyTransform::TransformComponent::Skew;
208
0
                    component3.values.append(skew0);
209
0
                    component3.values.append(skew1);
210
0
                    components.append(component1);
211
0
                    components.append(component2);
212
0
                    components.append(component3);
213
0
                }
214
0
            }
215
0
        }
216
0
        keyFramesComponents.append(components);
217
0
        prop->appendKeyFrame(keyFrame.keyFrame);
218
0
    }
219
220
0
    if (!validateTransform(keyFramesComponents))
221
0
        return false;
222
223
0
    for (const auto &comp : std::as_const(keyFramesComponents)) {
224
0
        prop->appendComponents(comp);
225
0
    }
226
0
    prop->setTransformCount(keyFramesComponents.first().size());
227
228
0
    return true;
229
0
}
230
231
bool fillOffsetDistanceProperty(const QList<CssKeyFrameValue> &keyFrames, QSvgAnimatedPropertyFloat *prop)
232
0
{
233
0
    for (const CssKeyFrameValue &keyFrame : keyFrames) {
234
0
        if (keyFrame.values.size() != 1)
235
0
            return false;
236
237
0
        QString offsetDistance = keyFrame.values.first().toString();
238
239
0
        bool ok = false;
240
0
        qreal distance = offsetDistance.toDouble(&ok);
241
0
        if (!ok)
242
0
            return false;
243
244
0
        QCss::Value::Type type = keyFrame.values.first().type;
245
0
        if (type != QCss::Value::Percentage && !qFuzzyCompare(distance, 0.))
246
0
            return false;
247
0
        distance /= 100;
248
0
        prop->appendValue(distance);
249
0
        prop->appendKeyFrame(keyFrame.keyFrame);
250
0
    }
251
252
0
    return true;
253
0
}
254
255
}
256
257
QSvgCssHandler::QSvgCssHandler()
258
41.7k
    : m_selector(new QSvgStyleSelector)
259
41.7k
{
260
261
41.7k
}
262
263
QSvgCssHandler::~QSvgCssHandler()
264
41.7k
{
265
41.7k
    delete m_selector;
266
41.7k
    m_selector = nullptr;
267
41.7k
}
268
269
QSvgCssAnimation *QSvgCssHandler::createAnimation(QStringView name)
270
0
{
271
0
    if (!m_animations.contains(name))
272
0
        return nullptr;
273
274
0
    QCss::AnimationRule animationRule = m_animations[name];
275
0
    QHash<QString, QSvgAbstractAnimatedProperty*> animatedProperies;
276
0
    QSvgCssAnimation *animation = new QSvgCssAnimation;
277
278
279
    // Css Parser returns a list of all properties for each key frame. Here,
280
    // we store the key frames and values for each property for easier parsing.
281
0
    QHash<QString, QList<CssKeyFrameValue>> keyFrameValues;
282
0
    for (const auto &ruleSet : std::as_const(animationRule.ruleSets)) {
283
0
        for (const QCss::Declaration &decl : ruleSet.declarations) {
284
0
            QCss::Value timingFunction;
285
0
            CssKeyFrameValue keyFrameValue = {ruleSet.keyFrame, decl.d->values, ruleSet.timingFunction};
286
0
            QList<CssKeyFrameValue> &value = keyFrameValues[decl.d->property];
287
0
            value.append(keyFrameValue);
288
0
        }
289
0
    }
290
291
0
    for (auto it = keyFrameValues.begin(); it != keyFrameValues.end(); it++) {
292
0
        QStringView property = it.key();
293
0
        const QList<CssKeyFrameValue> &keyFrames = it.value();
294
0
        auto *prop = QSvgAbstractAnimatedProperty::createAnimatedProperty(property.toString());
295
0
        if (!prop)
296
0
            continue;
297
298
0
        bool result = false;
299
0
        if (property == QLatin1StringView("fill") || property == QLatin1StringView("stroke"))
300
0
            result = fillColorProperty(keyFrames, static_cast<QSvgAnimatedPropertyColor*>(prop));
301
0
        else if (property == QLatin1StringView("transform"))
302
0
            result = fillTransformProperty(keyFrames, static_cast<QSvgAnimatedPropertyTransform*>(prop));
303
0
        else if (property == QLatin1StringView("fill-opacity") || property == QLatin1StringView("stroke-opacity")
304
0
                 || property == QLatin1StringView("opacity"))
305
0
            result = fillOpacityProperty(keyFrames, static_cast<QSvgAnimatedPropertyFloat*>(prop));
306
0
        else if (property == QLatin1StringView("offset-distance"))
307
0
            result = fillOffsetDistanceProperty(keyFrames, static_cast<QSvgAnimatedPropertyFloat*>(prop));
308
309
0
        result &= fillPropertyEasing(keyFrames, prop);
310
0
        if (!result) {
311
0
            delete prop;
312
0
            continue;
313
0
        }
314
315
0
        animatedProperies[property] = prop;
316
0
    }
317
318
0
    for (auto it = animatedProperies.begin(); it != animatedProperies.end(); it++)
319
0
        animation->appendProperty(it.value());
320
321
0
    return animation;
322
0
}
323
324
QSvgCssEasingPtr QSvgCssHandler::createEasing(QSvgCssValues::EasingFunction easingFunction,
325
                                              const QSvgCssValues::EasingValues &values)
326
0
{
327
0
    QSvgCssEasingPtr easing;
328
329
0
    switch (easingFunction) {
330
0
    case QSvgCssValues::EasingFunction::Ease:
331
0
    case QSvgCssValues::EasingFunction::EaseIn:
332
0
    case QSvgCssValues::EasingFunction::EaseOut:
333
0
    case QSvgCssValues::EasingFunction::EaseInOut:
334
0
    case QSvgCssValues::EasingFunction::Linear:
335
0
        easing = createEasingFromKeyword(easingFunction);
336
0
        break;
337
0
    case QSvgCssValues::EasingFunction::Steps:
338
0
        easing = createStepsEasing(std::get<QSvgCssValues::StepValues>(values));
339
0
        break;
340
0
    default:
341
0
        easing = createEasingFromKeyword(QSvgCssValues::EasingFunction::Ease);
342
0
        break;
343
0
    }
344
345
0
    return easing;
346
0
}
347
348
void QSvgCssHandler::collectAnimations(const QCss::StyleSheet &sheet)
349
1.08M
{
350
1.08M
    auto sortFunction = [](QCss::AnimationRule::AnimationRuleSet r1, QCss::AnimationRule::AnimationRuleSet r2) {
351
0
        return r1.keyFrame < r2.keyFrame;
352
0
    };
353
354
1.08M
    QList<QCss::AnimationRule> animationRules = sheet.animationRules;
355
1.08M
    for (QCss::AnimationRule &rule : animationRules) {
356
0
        std::sort(rule.ruleSets.begin(), rule.ruleSets.end(), sortFunction);
357
0
        m_animations[rule.animName] = rule;
358
0
    }
359
1.08M
}
360
361
void QSvgCssHandler::parseStyleSheet(const QStringView str)
362
1.08M
{
363
1.08M
    QString css = str.toString();
364
1.08M
    QCss::StyleSheet sheet;
365
1.08M
    QCss::Parser(css).parse(&sheet);
366
1.08M
    m_selector->styleSheets.append(sheet);
367
368
1.08M
    collectAnimations(sheet);
369
1.08M
}
370
371
QString QSvgCssHandler::parseDecltoString(const QCss::Declaration &decl)
372
3.81M
{
373
3.81M
    if (decl.d->property.isEmpty())
374
0
        return QString();
375
376
3.81M
    QString valueStr;
377
3.81M
    const int valCount = decl.d->values.size();
378
9.90M
    for (int i = 0; i < valCount; ++i) {
379
6.09M
        QCss::Value val = decl.d->values.at(i);
380
6.09M
        switch (val.type) {
381
245k
        case QCss::Value::TermOperatorComma:
382
245k
            valueStr += QLatin1Char(';');
383
245k
            break;
384
364
        case QCss::Value::Uri:
385
364
        {
386
364
            QString temp = val.toString();
387
364
            temp.prepend(QLatin1String("url("));
388
364
            temp.append(QLatin1Char(')'));
389
364
            valueStr += temp;
390
364
            break;
391
0
        }
392
285
        case QCss::Value::Function:
393
285
        {
394
285
            QStringList lst = val.variant.toStringList();
395
285
            valueStr.append(lst.at(0));
396
285
            valueStr.append(QLatin1Char('('));
397
570
            for (int i = 1; i < lst.size(); ++i) {
398
285
                valueStr.append(lst.at(i));
399
285
                if ((i +1) < lst.size())
400
0
                    valueStr.append(QLatin1Char(','));
401
285
            }
402
285
            valueStr.append(QLatin1Char(')'));
403
285
            break;
404
0
        }
405
9.74k
        case QCss::Value::KnownIdentifier:
406
9.74k
            switch (val.variant.toInt()) {
407
1.92k
            case QCss::Value_None:
408
1.92k
                valueStr += QLatin1String("none");
409
1.92k
                break;
410
0
            case QCss::Value_Auto:
411
0
                valueStr += QLatin1String("auto");
412
0
                break;
413
7.82k
            default:
414
7.82k
                valueStr += val.toString();
415
7.82k
                break;
416
9.74k
            }
417
9.74k
            break;
418
59.9k
        case QCss::Value::Percentage:
419
59.9k
            valueStr += val.toString() + QLatin1Char('%');
420
59.9k
            break;
421
5.77M
        default:
422
5.77M
            valueStr += val.toString();
423
5.77M
            break;
424
6.09M
        }
425
426
6.09M
        if (i + 1 < valCount)
427
2.28M
            valueStr += QLatin1Char(' ');
428
6.09M
    }
429
430
3.81M
    return valueStr;
431
3.81M
}
432
433
void QSvgCssHandler::parseCSStoXMLAttrs(const QList<QCss::Declaration> &declarations, QXmlStreamAttributes &attributes) const
434
804k
{
435
4.61M
    for (int i = 0; i < declarations.size(); ++i) {
436
3.81M
        const QCss::Declaration &decl = declarations.at(i);
437
3.81M
        QString valueStr = parseDecltoString(decl);
438
3.81M
        if (!valueStr.isEmpty())
439
3.81M
            attributes.append(QString(), decl.d->property, valueStr);
440
3.81M
    }
441
804k
}
442
443
void QSvgCssHandler::parseCSStoXMLAttrs(const QString &css, QXmlStreamAttributes &attributes) const
444
80.3k
{
445
    // preprocess (for unicode escapes), tokenize and remove comments
446
80.3k
    QCss::Parser parser(css);
447
80.3k
    QString key;
448
449
384k
    while (parser.hasNext()) {
450
320k
        parser.skipSpace();
451
452
320k
        if (!parser.hasNext())
453
0
            break;
454
320k
        parser.next();
455
456
320k
        QString name;
457
320k
        QString value;
458
459
320k
        if (parser.hasEscapeSequences) {
460
2.48k
            key = parser.lexem();
461
2.48k
            name = key;
462
317k
        } else {
463
317k
            const QCss::Symbol &sym = parser.symbol();
464
317k
            name = sym.text.mid(sym.start, sym.len);
465
317k
        }
466
467
320k
        parser.skipSpace();
468
320k
        if (!parser.test(QCss::COLON))
469
15.9k
            break;
470
471
304k
        parser.skipSpace();
472
304k
        if (!parser.hasNext())
473
293
            break;
474
475
303k
        const int firstSymbol = parser.index;
476
303k
        int symbolCount = 0;
477
6.31M
        do {
478
6.31M
            parser.next();
479
6.31M
            ++symbolCount;
480
6.31M
        } while (parser.hasNext() && !parser.test(QCss::SEMICOLON));
481
482
303k
        bool canExtractValueByRef = !parser.hasEscapeSequences;
483
303k
        if (canExtractValueByRef) {
484
301k
            int len = parser.symbols.at(firstSymbol).len;
485
4.36M
            for (int i = firstSymbol + 1; i < firstSymbol + symbolCount; ++i) {
486
4.06M
                len += parser.symbols.at(i).len;
487
488
4.06M
                if (parser.symbols.at(i - 1).start + parser.symbols.at(i - 1).len
489
4.06M
                    != parser.symbols.at(i).start) {
490
0
                    canExtractValueByRef = false;
491
0
                    break;
492
0
                }
493
4.06M
            }
494
301k
            if (canExtractValueByRef) {
495
301k
                const QCss::Symbol &sym = parser.symbols.at(firstSymbol);
496
301k
                value = sym.text.mid(sym.start, len);
497
301k
            }
498
301k
        }
499
303k
        if (!canExtractValueByRef) {
500
501
1.95M
            for (int i = firstSymbol; i < parser.index - 1; ++i)
502
1.95M
                value += parser.symbols.at(i).lexem();
503
2.09k
        }
504
505
303k
        attributes.append(QString(), name, value);
506
507
303k
        parser.skipSpace();
508
303k
    }
509
80.3k
}
510
511
void QSvgCssHandler::styleLookup(QSvgNode *node, QXmlStreamAttributes &attributes) const
512
804k
{
513
804k
    QCss::StyleSelector::NodePtr cssNode;
514
804k
    cssNode.ptr = node;
515
804k
    QList<QCss::Declaration> decls = m_selector->declarationsForNode(cssNode);
516
517
804k
    parseCSStoXMLAttrs(decls, attributes);
518
804k
}
519
520
QSvgCssEasingPtr QSvgCssHandler::createEasingFromKeyword(QSvgCssValues::EasingFunction easingFunction)
521
0
{
522
0
    constexpr QPointF easeC1(0.25, 0.1);
523
0
    constexpr QPointF easeC2(0.25, 1);
524
0
    constexpr QPointF easeInC1(0.42, 0);
525
0
    constexpr QPointF easeInC2(1, 1);
526
0
    constexpr QPointF easeOutC1(0, 0);
527
0
    constexpr QPointF easeOutC2(0.58, 1);
528
0
    constexpr QPointF linearC1(0, 0);
529
0
    constexpr QPointF linearC2(1, 1);
530
531
0
    QSvgCssEasingPtr easing;
532
533
0
    switch (easingFunction) {
534
0
    case QSvgCssValues::EasingFunction::Ease:
535
0
        easing = std::make_unique<QSvgCssCubicBezierEasing>(easingFunction, easeC1, easeC2);
536
0
        break;
537
0
    case QSvgCssValues::EasingFunction::EaseIn:
538
0
        easing = std::make_unique<QSvgCssCubicBezierEasing>(easingFunction, easeInC1, easeInC2);
539
0
        break;
540
0
    case QSvgCssValues::EasingFunction::EaseOut:
541
0
        easing = std::make_unique<QSvgCssCubicBezierEasing>(easingFunction, easeOutC1, easeOutC2);
542
0
        break;
543
0
    case QSvgCssValues::EasingFunction::EaseInOut:
544
0
        easing = std::make_unique<QSvgCssCubicBezierEasing>(easingFunction, easeInC1, easeOutC2);
545
0
        break;
546
0
    case QSvgCssValues::EasingFunction::Linear:
547
0
        easing = std::make_unique<QSvgCssCubicBezierEasing>(easingFunction, linearC1, linearC2);
548
0
        break;
549
0
    default:
550
0
        Q_UNREACHABLE();
551
0
        break;
552
0
    }
553
554
0
    return easing;
555
0
}
556
557
QSvgCssEasingPtr QSvgCssHandler::createStepsEasing(const QSvgCssValues::StepValues &values)
558
0
{
559
0
    quint32 steps = values.steps;
560
0
    QSvgCssValues::StepPosition position = values.stepPosition;
561
562
0
    return std::make_unique<QSvgCssStepsEasing>(steps, position);
563
0
}
564
565
QT_END_NAMESPACE