diff options
author | Matthias Rauter <matthias.rauter@qt.io> | 2023-12-27 14:03:00 +0100 |
---|---|---|
committer | Matthias Rauter <matthias.rauter@qt.io> | 2024-01-06 11:17:10 +0100 |
commit | 604fe97dd110dbac52d9fafc81549788df33b482 (patch) | |
tree | 90db863ac31c1e4f3d8215c265f2ecb7ab30ad9d /src | |
parent | 1aaaf8b40f870c9113d5b77ceacaea521d2fdef0 (diff) |
Fix a crash when SVG filters fail
This bug was triggered by a test in the resvg test suit:
filters/filters/in=BackgroundAlpha.svg
The patch further includes a small optimization: The sourceAlpha
filter buffer is only created on request.
Pick-to: 6.7
Change-Id: I208d43c54f07d530f476b459f30c1b8aa62c2018
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/svg/qsvgfilter.cpp | 40 | ||||
-rw-r--r-- | src/svg/qsvgfilter_p.h | 6 | ||||
-rw-r--r-- | src/svg/qsvgstructure.cpp | 40 | ||||
-rw-r--r-- | src/svg/qsvgtinydocument_p.h | 1 |
4 files changed, 72 insertions, 15 deletions
diff --git a/src/svg/qsvgfilter.cpp b/src/svg/qsvgfilter.cpp index 2c86f7d..59d928d 100644 --- a/src/svg/qsvgfilter.cpp +++ b/src/svg/qsvgfilter.cpp @@ -61,6 +61,25 @@ void QSvgFeFilterPrimitive::clipToTransformedBounds(QImage *buffer, QPainter *p, painter.fillPath(clipPath, Qt::transparent); } +bool QSvgFeFilterPrimitive::requiresSourceAlpha() const +{ + return m_input == QLatin1StringView("SourceAlpha"); +} + +const QSvgFeFilterPrimitive *QSvgFeFilterPrimitive::castToFilterPrimitive(const QSvgNode *node) +{ + if (node->type() == QSvgNode::FeMerge || + node->type() == QSvgNode::FeColormatrix || + node->type() == QSvgNode::FeGaussianblur || + node->type() == QSvgNode::FeOffset || + node->type() == QSvgNode::FeComposite || + node->type() == QSvgNode::FeFlood ) { + return reinterpret_cast<const QSvgFeFilterPrimitive*>(node); + } else { + return nullptr; + } +} + QSvgFeColorMatrix::QSvgFeColorMatrix(QSvgNode *parent, QString input, QString result, const QSvgRectF &rect, ColorShiftType type, Matrix matrix) : QSvgFeFilterPrimitive(parent, input, result, rect) @@ -449,6 +468,19 @@ QImage QSvgFeMerge::apply(QSvgNode *item, const QMap<QString, QImage> &sources, return result; } +bool QSvgFeMerge::requiresSourceAlpha() const +{ + for (int i = 0; i < renderers().size(); i++) { + QSvgNode *child = renderers().at(i); + if (child->type() == QSvgNode::FeMergenode) { + QSvgFeMergeNode *filter = static_cast<QSvgFeMergeNode *>(child); + if (filter->requiresSourceAlpha()) + return true; + } + } + return false; +} + QSvgFeMergeNode::QSvgFeMergeNode(QSvgNode *parent, QString input, QString result, const QSvgRectF &rect) : QSvgFeFilterPrimitive(parent, input, result, rect) { @@ -601,6 +633,14 @@ QImage QSvgFeComposite::apply(QSvgNode *item, const QMap<QString, QImage> &sourc return result; } +bool QSvgFeComposite::requiresSourceAlpha() const +{ + if (QSvgFeFilterPrimitive::requiresSourceAlpha()) + return true; + return m_input2 == QLatin1StringView("SourceAlpha"); +} + + QSvgFeFlood::QSvgFeFlood(QSvgNode *parent, QString input, QString result, const QSvgRectF &rect, const QColor &color) : QSvgFeFilterPrimitive(parent, input, result, rect) diff --git a/src/svg/qsvgfilter_p.h b/src/svg/qsvgfilter_p.h index baeb0dc..22ecc59 100644 --- a/src/svg/qsvgfilter_p.h +++ b/src/svg/qsvgfilter_p.h @@ -43,12 +43,16 @@ public: virtual QImage apply(QSvgNode *item, const QMap<QString, QImage> &sources, QPainter *p, const QRectF &itemBounds, const QRectF &filterBounds, QSvg::UnitTypes primitiveUnits, QSvg::UnitTypes filterUnits) const = 0; + virtual bool requiresSourceAlpha() const; QString input() const { return m_input; } QString result() const { return m_result; } + + static const QSvgFeFilterPrimitive *castToFilterPrimitive(const QSvgNode *node); + protected: QString m_input; QString m_result; @@ -124,6 +128,7 @@ public: QImage apply(QSvgNode *item, const QMap<QString, QImage> &sources, QPainter *p, const QRectF &itemBounds, const QRectF &filterBounds, QSvg::UnitTypes primitiveUnits, QSvg::UnitTypes filterUnits) const override; + bool requiresSourceAlpha() const override; }; class Q_SVG_EXPORT QSvgFeMergeNode : public QSvgFeFilterPrimitive @@ -154,6 +159,7 @@ public: QImage apply(QSvgNode *item, const QMap<QString, QImage> &sources, QPainter *p, const QRectF &itemBounds, const QRectF &filterBounds, QSvg::UnitTypes primitiveUnits, QSvg::UnitTypes filterUnits) const override; + bool requiresSourceAlpha() const override; private: QString m_input2; Operator m_operator; diff --git a/src/svg/qsvgstructure.cpp b/src/svg/qsvgstructure.cpp index 9234fe5..1cfd441 100644 --- a/src/svg/qsvgstructure.cpp +++ b/src/svg/qsvgstructure.cpp @@ -387,28 +387,38 @@ QImage QSvgFilterContainer::applyFilter(QSvgNode *item, const QImage &buffer, QP QImage proxy = buffer.copy(filterBoundsGlobRel); proxy.setOffset(filterBoundsGlob.topLeft()); - - QImage proxyAlpha = proxy.convertedTo(QImage::Format_Alpha8).convertedTo(proxy.format()); - // ### TODO: allocation check - proxyAlpha.setOffset(proxy.offset()); + if (proxy.isNull()) + return buffer; QMap<QString, QImage> buffers; buffers[QStringLiteral("")] = proxy; buffers[QStringLiteral("SourceGraphic")] = proxy; - buffers[QStringLiteral("SourceAlpha")] = proxyAlpha; + + bool requiresSourceAlpha = false; + + const QList<QSvgNode *> children = renderers(); + for (const QSvgNode *renderer : children) { + const QSvgFeFilterPrimitive *filter = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer); + if (filter && filter->requiresSourceAlpha()) { + requiresSourceAlpha = true; + break; + } + } + + if (requiresSourceAlpha) { + QImage proxyAlpha = proxy.convertedTo(QImage::Format_Alpha8).convertedTo(proxy.format()); + proxyAlpha.setOffset(proxy.offset()); + if (proxyAlpha.isNull()) + return buffer; + buffers[QStringLiteral("SourceAlpha")] = proxyAlpha; + } QImage result; - for (int i = 0; i < renderers().size(); i++) { - QSvgNode *child = renderers().at(i); - if (child->type() == QSvgNode::FeMerge || - child->type() == QSvgNode::FeColormatrix || - child->type() == QSvgNode::FeGaussianblur || - child->type() == QSvgNode::FeOffset || - child->type() == QSvgNode::FeComposite || - child->type() == QSvgNode::FeFlood ) { - QSvgFeFilterPrimitive *filter = reinterpret_cast<QSvgFeFilterPrimitive*>(child); + for (const QSvgNode *renderer : children) { + const QSvgFeFilterPrimitive *filter = QSvgFeFilterPrimitive::castToFilterPrimitive(renderer); + if (filter) { result = filter->apply(item, buffers, p, bounds, filterBounds, m_primitiveUnits, m_filterUnits); - if (result.size().isValid()) { + if (!result.isNull()) { buffers[QStringLiteral("")] = result; buffers[filter->result()] = result; } diff --git a/src/svg/qsvgtinydocument_p.h b/src/svg/qsvgtinydocument_p.h index d0b1dbb..b6780ae 100644 --- a/src/svg/qsvgtinydocument_p.h +++ b/src/svg/qsvgtinydocument_p.h @@ -86,6 +86,7 @@ public: int currentFrame() const; void setCurrentFrame(int); void setFramesPerSecond(int num); + private: void mapSourceToTarget(QPainter *p, const QRectF &targetRect, const QRectF &sourceRect = QRectF()); private: |