Revision control

++ // Copyright (C) 2018 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qlottiebase_p.h"
++
++ #include <QLoggingCategory>
++ #include <QRegularExpression>
++ #include <QRegularExpressionMatch>
++ #include <QString>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace Qt::Literals::StringLiterals;
++
++ Q_LOGGING_CATEGORY(lcLottieQtLottieParser, "qt.lottieqt.lottie.parser", QtWarningMsg);
++ Q_LOGGING_CATEGORY(lcLottieQtLottieUpdate, "qt.lottieqt.lottie.update");
++ Q_LOGGING_CATEGORY(lcLottieQtLottieRender, "qt.lottieqt.lottie.render");
++
++ QLottieBase::QLottieBase(const QLottieBase &other)
++ {
++ m_definition = other.m_definition;
++ m_type = other.m_type;
++ m_hidden = other.m_hidden;
++ m_name = other.m_name;
++ for (QLottieBase *child : other.m_children) {
++ QLottieBase *clone = child->clone();
++ clone->setParent(this);
++ appendChild(clone);
++ }
++ }
++
++ QLottieBase::~QLottieBase()
++ {
++ qDeleteAll(m_children);
++ }
++
++ QLottieBase *QLottieBase::clone() const
++ {
++ return new QLottieBase(*this);
++ }
++
++ QString QLottieBase::name() const
++ {
++ return m_name;
++ }
++
++ void QLottieBase::setName(const QString &name)
++ {
++ m_name = name;
++ }
++
++ bool QLottieBase::setProperty(QLottieLiteral::PropertyType propertyName, QVariant value)
++ {
++ for (QLottieBase *child : std::as_const(m_children)) {
++ bool changed = child->setProperty(propertyName, value);
++ if (changed)
++ return true;
++ }
++ return false;
++ }
++
++ int QLottieBase::type() const
++ {
++ return m_type;
++ }
++
++ void QLottieBase::setType(int type)
++ {
++ m_type = type;
++ }
++
++ bool QLottieBase::isShapeElement() const
++ {
++ return (m_type >= LOTTIE_SHAPE_ELLIPSE_IX && m_type <= LOTTIE_SHAPE_REPEATER_IX);
++ }
++
++ bool QLottieBase::isPathElement() const
++ {
++ return (m_type == LOTTIE_SHAPE_ELLIPSE_IX || m_type == LOTTIE_SHAPE_RECT_IX
++ || m_type == LOTTIE_SHAPE_SHAPE_IX || m_type == LOTTIE_SHAPE_STAR_IX);
++ }
++
++ void QLottieBase::prependChild(QLottieBase *child)
++ {
++ m_children.push_front(child);
++ }
++
++ void QLottieBase::insertChildBeforeLast(QLottieBase *child)
++ {
++ m_children.insert(qMax(m_children.size() - 1, 0), child);
++ }
++
++ void QLottieBase::appendChild(QLottieBase *child)
++ {
++ m_children.push_back(child);
++ }
++
++ QLottieBase *QLottieBase::findChild(const QString &childName)
++ {
++ if (name() == childName)
++ return this;
++
++ QLottieBase *found = nullptr;
++ for (QLottieBase *child : std::as_const(m_children)) {
++ found = child->findChild(childName);
++ if (found)
++ break;
++ }
++ return found;
++ }
++
++ void QLottieBase::updateProperties(int frame)
++ {
++ if (m_hidden)
++ return;
++
++ for (QLottieBase *child : std::as_const(m_children))
++ child->updateProperties(frame);
++ }
++
++ void QLottieBase::render(QLottieRenderer &renderer) const
++ {
++ if (m_hidden)
++ return;
++
++ renderer.saveState();
++ renderChildren(renderer);
++ renderer.restoreState();
++ }
++
++ void QLottieBase::renderChildren(QLottieRenderer &renderer) const
++ {
++ QList<QLottieBase *> pathElements;
++ for (QLottieBase *child : std::as_const(m_children)) {
++ if (child->isPathElement()) {
++ if (!child->m_hidden)
++ pathElements.prepend(child);
++ } else {
++ if (!pathElements.isEmpty()) {
++ renderer.renderPathElements(pathElements);
++ pathElements.clear();
++ }
++ if (!child->m_hidden)
++ child->render(renderer);
++ }
++ }
++ if (!pathElements.isEmpty())
++ renderer.renderPathElements(pathElements);
++ }
++
++ bool QLottieBase::isStructureDumping() const
++ {
++ if (m_structureDumping < 0) {
++ QLottieBase *p = parent();
++ m_structureDumping = p ? p->isStructureDumping() : 0;
++ }
++ return bool(m_structureDumping);
++ }
++
++ void QLottieBase::resolveTopRoot()
++ {
++ if (!m_topRoot) {
++ QLottieBase *p = this;
++ while (p) {
++ m_topRoot = p;
++ p = p->parent();
++ }
++ }
++ Q_ASSERT(m_topRoot);
++ }
++
++ QLottieBase *QLottieBase::topRoot() const
++ {
++ if (!m_topRoot)
++ const_cast<QLottieBase *>(this)->resolveTopRoot();
++ return m_topRoot;
++ }
++
++ int QLottieBase::parse(const QJsonObject &definition)
++ {
++ qCDebug(lcLottieQtLottieParser) << "QLottieBase::parse()";
++
++ m_definition = definition;
++
++ m_hidden = definition.value("hd"_L1).toBool(false);
++ m_name = definition.value("nm"_L1).toString();
++ m_matchName = definition.value("mn"_L1).toString();
++
++ return 0;
++ }
++
++ const QJsonObject &QLottieBase::definition() const
++ {
++ return m_definition;
++ }
++
++ bool QLottieBase::active(int frame) const
++ {
++ Q_UNUSED(frame);
++ return !m_hidden;
++ }
++
++ bool QLottieBase::hidden() const
++ {
++ return m_hidden;
++ }
++
++ QSize QLottieBase::layerSize() const
++ {
++ return m_parent ? m_parent->layerSize() : QSize();
++ }
++
++ void QLottieBase::setParent(QLottieBase *parent)
++ {
++ m_parent = parent;
++ }
++
++ const QJsonObject QLottieBase::resolveExpression(const QJsonObject &definition)
++ {
++ QString expr = definition.value("x"_L1).toString();
++
++ // If there is no expression, return the original object definition
++ if (expr.isEmpty())
++ return definition;
++
++ // Find out layer handle
++ resolveTopRoot();
++
++ QRegularExpression re(QStringLiteral("effect\\(\\'(.*?)\\'\\)\\(\\'(.*?)\\'\\)"));
++ QRegularExpressionMatch match = re.match(expr);
++ if (!match.hasMatch())
++ return definition;
++
++ QString effect = match.captured(1);
++ QString elementName = match.captured(2);
++
++ QJsonObject retVal = definition;
++
++ if (QLottieBase *source = m_topRoot->findChild(effect)) {
++ if (source->children().size())
++ retVal = source->children().at(0)->definition().value("v"_L1).toObject();
++ else
++ retVal = source->definition().value("v"_L1).toObject();
++ if (source->children().size() > 1)
++ qCInfo(lcLottieQtLottieParser) << "Effect source points"
++ "to a group that has"
++ "many children. The"
++ "first is be picked";
++ } else {
++ qCInfo(lcLottieQtLottieParser) << "Failed to find specified effect" << effect;
++ }
++
++ // Let users of the json know that it is originated from expression,
++ // so they can adjust their behavior accordingly
++ retVal.insert("fromExpression"_L1, true);
++
++ return retVal;
++ }
++
++ bool QLottieBase::checkRequiredKeys(const QJsonObject &definition, const QLatin1StringView type,
++ const QList<QLatin1StringView> &keys, const QString &name) const
++ {
++ for (auto key : keys) {
++ if (!definition.contains(key)) {
++ qCWarning(lcLottieQtLottieParser) << type << name << "is missing required key \"" << key << "\"";
++ return false;
++ }
++ }
++ return true;
++ }
++
++ QT_END_NAMESPACE