summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHatem ElKharashy <hatem.elkharashy@qt.io>2024-04-30 10:58:57 +0300
committerHatem ElKharashy <hatem.elkharashy@qt.io>2024-05-06 09:44:05 +0000
commit86aee217c9ea05658a53ab46f9f84bb36233f6c1 (patch)
tree136526ac1c50425e76c8d818c8bcffef82a1f467
parent69d4dcf5d4bfab4c3a8a3ea7a90e6694b995f799 (diff)
Skip rendering filters with unsupported primitives
In Qt 6.7, <filter> and some of the filter primitives were supported. However, this caused a regression in behavior because Qt Svg used to skip filters altogether, but starting from Qt 6.7, the filter and the filter primitives are parsed and the unsupported filter primitives are skipped which leads to unexpected output. It is a good idea to start supporting the rest of the filter primitives in the future, but for now the renderer will skip applying the filter if it has an unsupported filter primitive. Fixes: QTBUG-123994 Pick-to: 6.7 Change-Id: I14b91c919ace8a886ee285ff1ed466110d13322a Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
-rw-r--r--src/svg/qsvgfilter.cpp18
-rw-r--r--src/svg/qsvgfilter_p.h10
-rw-r--r--src/svg/qsvghandler.cpp86
-rw-r--r--src/svg/qsvghandler_p.h6
-rw-r--r--src/svg/qsvgnode.cpp8
-rw-r--r--src/svg/qsvgnode_p.h3
-rw-r--r--src/svg/qsvgstructure.cpp11
-rw-r--r--src/svg/qsvgstructure_p.h3
-rw-r--r--src/svg/qsvgvisitor.cpp1
9 files changed, 121 insertions, 25 deletions
diff --git a/src/svg/qsvgfilter.cpp b/src/svg/qsvgfilter.cpp
index f773ec6..b38a0fe 100644
--- a/src/svg/qsvgfilter.cpp
+++ b/src/svg/qsvgfilter.cpp
@@ -674,5 +674,23 @@ QImage QSvgFeFlood::apply(QSvgNode *item, const QMap<QString, QImage> &,
return result;
}
+QSvgFeUnsupported::QSvgFeUnsupported(QSvgNode *parent, QString input, QString result,
+ const QSvgRectF &rect)
+ : QSvgFeFilterPrimitive(parent, input, result, rect)
+{
+}
+
+QSvgNode::Type QSvgFeUnsupported::type() const
+{
+ return QSvgNode::FeUnsupported;
+}
+
+QImage QSvgFeUnsupported::apply(QSvgNode *, const QMap<QString, QImage> &,
+ QPainter *, const QRectF &, const QRectF &,
+ QtSvg::UnitTypes, QtSvg::UnitTypes) const
+{
+ qCDebug(lcSvgDraw) <<"Unsupported filter primitive should not be applied.";
+ return QImage();
+}
QT_END_NAMESPACE
diff --git a/src/svg/qsvgfilter_p.h b/src/svg/qsvgfilter_p.h
index 70c50de..071ee40 100644
--- a/src/svg/qsvgfilter_p.h
+++ b/src/svg/qsvgfilter_p.h
@@ -178,7 +178,15 @@ private:
QColor m_color;
};
-
+class Q_SVG_EXPORT QSvgFeUnsupported : public QSvgFeFilterPrimitive
+{
+public:
+ QSvgFeUnsupported(QSvgNode *parent, QString input, QString result, const QSvgRectF &rect);
+ Type type() const override;
+ QImage apply(QSvgNode *item, const QMap<QString, QImage> &sources,
+ QPainter *p, const QRectF &itemBounds, const QRectF &filterBounds,
+ QtSvg::UnitTypes primitiveUnits, QtSvg::UnitTypes filterUnits) const override;
+};
QT_END_NAMESPACE
diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp
index b6b94d3..c49d3a7 100644
--- a/src/svg/qsvghandler.cpp
+++ b/src/svg/qsvghandler.cpp
@@ -3483,6 +3483,21 @@ static QSvgNode *createFeMergeNodeNode(QSvgNode *parent,
return filter;
}
+static QSvgNode *createFeUnsupportedNode(QSvgNode *parent,
+ const QXmlStreamAttributes &attributes,
+ QSvgHandler *handler)
+{
+ QString inputString;
+ QString outputString;
+ QSvgRectF rect;
+
+ parseFilterAttributes(parent, attributes, handler,
+ &inputString, &outputString, &rect);
+
+ QSvgNode *filter = new QSvgFeUnsupported(parent, inputString, outputString, rect);
+ return filter;
+}
+
static bool parseSymbolLikeAttributes(const QXmlStreamAttributes &attributes, QSvgHandler *handler,
QRectF *rect, QRectF *viewBox, QPointF *refPoint,
QSvgSymbolLike::PreserveAspectRatios *aspect,
@@ -4313,7 +4328,7 @@ static FactoryMethod findGraphicsFactory(const QString &name, QtSvg::Options opt
return 0;
}
-static FactoryMethod findFilterFtory(const QString &name, QtSvg::Options options)
+static FactoryMethod findFilterFactory(const QString &name, QtSvg::Options options)
{
if (options.testFlag(QtSvg::Tiny12FeaturesOnly))
return 0;
@@ -4332,6 +4347,27 @@ static FactoryMethod findFilterFtory(const QString &name, QtSvg::Options options
if (name == QLatin1String("feComposite")) return createFeCompositeNode;
if (name == QLatin1String("feFlood")) return createFeFloodNode;
+ static const QStringList unsupportedFilters = {
+ QStringLiteral("feBlend"),
+ QStringLiteral("feComponentTransfer"),
+ QStringLiteral("feConvolveMatrix"),
+ QStringLiteral("feDiffuseLighting"),
+ QStringLiteral("feDisplacementMap"),
+ QStringLiteral("feDropShadow"),
+ QStringLiteral("feFuncA"),
+ QStringLiteral("feFuncB"),
+ QStringLiteral("feFuncG"),
+ QStringLiteral("feFuncR"),
+ QStringLiteral("feImage"),
+ QStringLiteral("feMorphology"),
+ QStringLiteral("feSpecularLighting"),
+ QStringLiteral("feTile"),
+ QStringLiteral("feTurbulence")
+ };
+
+ if (unsupportedFilters.contains(name))
+ return createFeUnsupportedNode;
+
return 0;
}
@@ -4686,6 +4722,8 @@ bool QSvgHandler::startElement(const QString &localName,
cssStyleLookup(node, this, m_selector);
#endif
parseStyle(node, attributes, this);
+ if (node->type() == QSvgNode::Filter)
+ m_toBeResolved.append(node);
}
}
} else if (FactoryMethod method = findGraphicsFactory(localName, options())) {
@@ -4751,7 +4789,7 @@ bool QSvgHandler::startElement(const QString &localName,
}
}
}
- } else if (FactoryMethod method = findFilterFtory(localName, options())) {
+ } else if (FactoryMethod method = findFilterFactory(localName, options())) {
//filter nodes to be aded to be filtercontainer
Q_ASSERT(!m_nodes.isEmpty());
node = method(m_nodes.top(), attributes, this);
@@ -4874,26 +4912,38 @@ void QSvgHandler::resolvePaintServers(QSvgNode *node, int nestedDepth)
void QSvgHandler::resolveNodes()
{
- for (QSvgUse *useNode : std::as_const(m_toBeResolved)) {
- const auto parent = useNode->parent();
- if (!parent)
- continue;
+ for (QSvgNode *node : std::as_const(m_toBeResolved)) {
+ if (node->type() == QSvgNode::Use) {
+ QSvgUse *useNode = static_cast<QSvgUse *>(node);
+ const auto parent = useNode->parent();
+ if (!parent)
+ continue;
- QSvgNode::Type t = parent->type();
- if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
- continue;
+ QSvgNode::Type t = parent->type();
+ if (t != QSvgNode::Doc && t != QSvgNode::Defs && t != QSvgNode::Group && t != QSvgNode::Switch)
+ continue;
- QSvgStructureNode *group = static_cast<QSvgStructureNode *>(parent);
- QSvgNode *link = group->scopeNode(useNode->linkId());
- if (!link) {
- qCWarning(lcSvgHandler, "link #%s is undefined!", qPrintable(useNode->linkId()));
- continue;
- }
+ QSvgStructureNode *group = static_cast<QSvgStructureNode *>(parent);
+ QSvgNode *link = group->scopeNode(useNode->linkId());
+ if (!link) {
+ qCWarning(lcSvgHandler, "link #%s is undefined!", qPrintable(useNode->linkId()));
+ continue;
+ }
- if (useNode->parent()->isDescendantOf(link))
- qCWarning(lcSvgHandler, "link #%s is recursive!", qPrintable(useNode->linkId()));
+ if (useNode->parent()->isDescendantOf(link))
+ qCWarning(lcSvgHandler, "link #%s is recursive!", qPrintable(useNode->linkId()));
- useNode->setLink(link);
+ useNode->setLink(link);
+ } else if (node->type() == QSvgNode::Filter) {
+ QSvgFilterContainer *filter = static_cast<QSvgFilterContainer *>(node);
+ for (const QSvgNode *renderer : filter->renderers()) {
+ const QSvgFeFilterPrimitive *primitive = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer);
+ if (!primitive || primitive->type() == QSvgNode::FeUnsupported) {
+ filter->setSupported(false);
+ break;
+ }
+ }
+ }
}
m_toBeResolved.clear();
}
diff --git a/src/svg/qsvghandler_p.h b/src/svg/qsvghandler_p.h
index 2857ac0..1627223 100644
--- a/src/svg/qsvghandler_p.h
+++ b/src/svg/qsvghandler_p.h
@@ -114,8 +114,10 @@ private:
QSvgTinyDocument *m_doc;
QStack<QSvgNode *> m_nodes;
// TODO: This is only needed during parsing, so it unnecessarily takes up space after that.
- // Temporary container for <use> nodes which haven't been resolved yet.
- QList<QSvgUse *> m_toBeResolved;
+ // Temporary container for :
+ // - <use> nodes which haven't been resolved yet.
+ // - <filter> nodes to be checked for unsupported filter primitives.
+ QList<QSvgNode *> m_toBeResolved;
enum CurrentNode
{
diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp
index 8ad2240..dbf71fd 100644
--- a/src/svg/qsvgnode.cpp
+++ b/src/svg/qsvgnode.cpp
@@ -44,15 +44,16 @@ void QSvgNode::draw(QPainter *p, QSvgExtraStates &states)
if (shouldDrawNode(p, states)) {
applyStyle(p, states);
QSvgNode *maskNode = this->hasMask() ? document()->namedNode(this->maskId()) : nullptr;
- QSvgNode *filterNode = this->hasFilter() ? document()->namedNode(this->filterId()) : nullptr;
- if (filterNode && filterNode->type() == QSvgNode::Filter) {
+ QSvgFilterContainer *filterNode = this->hasFilter() ? static_cast<QSvgFilterContainer*>(document()->namedNode(this->filterId()))
+ : nullptr;
+ if (filterNode && filterNode->supported()) {
QTransform xf = p->transform();
p->resetTransform();
QRectF localRect = bounds(p, states);
QRectF boundsRect = xf.mapRect(localRect);
p->setTransform(xf);
QImage proxy = drawIntoBuffer(p, states, boundsRect.toRect());
- proxy = static_cast<QSvgFilterContainer*>(filterNode)->applyFilter(this, proxy, p, localRect);
+ proxy = filterNode->applyFilter(this, proxy, p, localRect);
boundsRect = QRectF(proxy.offset(), proxy.size());
localRect = p->transform().inverted().mapRect(boundsRect);
@@ -413,6 +414,7 @@ QString QSvgNode::typeName() const
case FeOffset: return QStringLiteral("feOffset");
case FeComposite: return QStringLiteral("feComposite");
case FeFlood: return QStringLiteral("feFlood");
+ case FeUnsupported: return QStringLiteral("feUnsupported");
}
return QStringLiteral("unknown");
}
diff --git a/src/svg/qsvgnode_p.h b/src/svg/qsvgnode_p.h
index f616a45..2fa837b 100644
--- a/src/svg/qsvgnode_p.h
+++ b/src/svg/qsvgnode_p.h
@@ -61,7 +61,8 @@ public:
FeGaussianblur,
FeOffset,
FeComposite,
- FeFlood
+ FeFlood,
+ FeUnsupported
};
enum DisplayMode {
InlineMode,
diff --git a/src/svg/qsvgstructure.cpp b/src/svg/qsvgstructure.cpp
index 22f1b0d..b729fa8 100644
--- a/src/svg/qsvgstructure.cpp
+++ b/src/svg/qsvgstructure.cpp
@@ -211,6 +211,7 @@ QSvgFilterContainer::QSvgFilterContainer(QSvgNode *parent, const QSvgRectF &boun
, m_rect(bounds)
, m_filterUnits(filterUnits)
, m_primitiveUnits(primitiveUnits)
+ , m_supported(true)
{
}
@@ -432,6 +433,16 @@ QImage QSvgFilterContainer::applyFilter(QSvgNode *item, const QImage &buffer, QP
return result;
}
+void QSvgFilterContainer::setSupported(bool supported)
+{
+ m_supported = supported;
+}
+
+bool QSvgFilterContainer::supported() const
+{
+ return m_supported;
+}
+
QSvgNode::Type QSvgFilterContainer::type() const
{
return Filter;
diff --git a/src/svg/qsvgstructure_p.h b/src/svg/qsvgstructure_p.h
index 9a3c2a0..d2cef9d 100644
--- a/src/svg/qsvgstructure_p.h
+++ b/src/svg/qsvgstructure_p.h
@@ -165,10 +165,13 @@ public:
void drawCommand(QPainter *, QSvgExtraStates &) override {};
Type type() const override;
QImage applyFilter(QSvgNode *referenceNode, const QImage &buffer, QPainter *p, QRectF bounds) const;
+ void setSupported(bool supported);
+ bool supported() const;
private:
QSvgRectF m_rect;
QtSvg::UnitTypes m_filterUnits;
QtSvg::UnitTypes m_primitiveUnits;
+ bool m_supported;
};
diff --git a/src/svg/qsvgvisitor.cpp b/src/svg/qsvgvisitor.cpp
index 018ace0..8c1d3f5 100644
--- a/src/svg/qsvgvisitor.cpp
+++ b/src/svg/qsvgvisitor.cpp
@@ -119,6 +119,7 @@ void QSvgVisitor::traverse(const QSvgNode *node)
case QSvgNode::FeOffset:
case QSvgNode::FeComposite:
case QSvgNode::FeFlood:
+ case QSvgNode::FeUnsupported:
qDebug() << "Unhandled type in switch" << node->type();
break;
}