diff options
-rw-r--r-- | src/svg/qsvggenerator.cpp | 9 | ||||
-rw-r--r-- | src/svg/qsvghandler.cpp | 28 | ||||
-rw-r--r-- | src/svg/qsvgstyle.cpp | 39 | ||||
-rw-r--r-- | src/svg/qsvgstyle_p.h | 13 | ||||
-rw-r--r-- | tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp | 48 |
5 files changed, 131 insertions, 6 deletions
diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp index f69e420..7306551 100644 --- a/src/svg/qsvggenerator.cpp +++ b/src/svg/qsvggenerator.cpp @@ -951,12 +951,19 @@ void QSvgPaintEngine::drawImage(const QRectF &r, const QImage &image, Q_UNUSED(sr); Q_UNUSED(flags); + QString quality; + if (state->renderHints() & QPainter::SmoothPixmapTransform) { + quality = QLatin1String("optimizeQuality"); + } else { + quality = QLatin1String("optimizeSpeed"); + } stream() << "<image "; stream() << "x=\""<<r.x()<<"\" " "y=\""<<r.y()<<"\" " "width=\""<<r.width()<<"\" " "height=\""<<r.height()<<"\" " - "preserveAspectRatio=\"none\" "; + "preserveAspectRatio=\"none\" " + "image-rendering=\""<<quality<<"\" "; QByteArray data; QBuffer buffer(&data); diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index 523efa6..9fe892d 100644 --- a/src/svg/qsvghandler.cpp +++ b/src/svg/qsvghandler.cpp @@ -221,6 +221,7 @@ struct QSvgAttributes QStringView offset; QStringView stopColor; QStringView stopOpacity; + QStringView imageRendering; #ifndef QT_NO_CSSPARSER QList<QSvgCssAttribute> m_cssAttributes; @@ -274,6 +275,10 @@ QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHa else if (name == QLatin1String("font-variant")) fontVariant = value; break; + case 'i': + if (name == QLatin1String("image-rendering")) + imageRendering = value; + break; case 'o': if (name == QLatin1String("opacity")) @@ -376,6 +381,8 @@ QSvgAttributes::QSvgAttributes(const QXmlStreamAttributes &xmlAttributes, QSvgHa case 'i': if (name == QLatin1String("id")) id = value.toString(); + else if (name == QLatin1String("image-rendering")) + imageRendering = value; break; case 'o': @@ -2251,6 +2258,25 @@ static void parseOthers(QSvgNode *node, } } +static void parseRenderingHints(QSvgNode *node, + const QSvgAttributes &attributes, + QSvgHandler *) +{ + if (attributes.imageRendering.isEmpty()) + return; + + QString ir = attributes.imageRendering.toString().trimmed(); + QSvgQualityStyle *p = new QSvgQualityStyle(0); + if (ir == QLatin1String("auto")) + p->setImageRendering(QSvgQualityStyle::ImageRenderingAuto); + else if (ir == QLatin1String("optimizeSpeed")) + p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeSpeed); + else if (ir == QLatin1String("optimizeQuality")) + p->setImageRendering(QSvgQualityStyle::ImageRenderingOptimizeQuality); + node->appendStyleProperty(p, attributes.id); +} + + static bool parseStyle(QSvgNode *node, const QSvgAttributes &attributes, QSvgHandler *handler) @@ -2263,7 +2289,9 @@ static bool parseStyle(QSvgNode *node, parseVisibility(node, attributes, handler); parseOpacity(node, attributes, handler); parseCompOp(node, attributes, handler); + parseRenderingHints(node, attributes, handler); parseOthers(node, attributes, handler); + #if 0 value = attributes.value("audio-level"); diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp index d425923..cf17944 100644 --- a/src/svg/qsvgstyle.cpp +++ b/src/svg/qsvgstyle.cpp @@ -61,7 +61,8 @@ QSvgExtraStates::QSvgExtraStates() fontWeight(QFont::Normal), fillRule(Qt::WindingFill), strokeDashOffset(0), - vectorEffect(false) + vectorEffect(false), + imageRendering(QSvgQualityStyle::ImageRenderingAuto) { } @@ -81,16 +82,46 @@ void QSvgFillStyleProperty::revert(QPainter *, QSvgExtraStates &) QSvgQualityStyle::QSvgQualityStyle(int color) + : m_imageRendering(QSvgQualityStyle::ImageRenderingAuto) + , m_oldImageRendering(QSvgQualityStyle::ImageRenderingAuto) + , m_imageRenderingSet(0) { Q_UNUSED(color); } -void QSvgQualityStyle::apply(QPainter *, const QSvgNode *, QSvgExtraStates &) -{ +void QSvgQualityStyle::setImageRendering(ImageRendering hint) { + m_imageRendering = hint; + m_imageRenderingSet = 1; } -void QSvgQualityStyle::revert(QPainter *, QSvgExtraStates &) + +void QSvgQualityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) { + m_oldImageRendering = states.imageRendering; + if (m_imageRenderingSet) { + states.imageRendering = m_imageRendering; + } + if (m_imageRenderingSet) { + bool smooth = false; + if (m_imageRendering == ImageRenderingAuto) + // auto (the spec says to prefer quality) + smooth = true; + else + smooth = (m_imageRendering == ImageRenderingOptimizeQuality); + p->setRenderHint(QPainter::SmoothPixmapTransform, smooth); + } +} +void QSvgQualityStyle::revert(QPainter *p, QSvgExtraStates &states) +{ + if (m_imageRenderingSet) { + states.imageRendering = m_oldImageRendering; + bool smooth = false; + if (m_oldImageRendering == ImageRenderingAuto) + smooth = true; + else + smooth = (m_oldImageRendering == ImageRenderingOptimizeQuality); + p->setRenderHint(QPainter::SmoothPixmapTransform, smooth); + } } QSvgFillStyle::QSvgFillStyle() diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h index 420dd67..8664f7a 100644 --- a/src/svg/qsvgstyle_p.h +++ b/src/svg/qsvgstyle_p.h @@ -148,6 +148,7 @@ struct Q_SVG_PRIVATE_EXPORT QSvgExtraStates int nestedUseLevel = 0; int nestedUseCount = 0; bool vectorEffect; // true if pen is cosmetic + qint8 imageRendering; // QSvgQualityStyle::ImageRendering }; class Q_SVG_PRIVATE_EXPORT QSvgStyleProperty : public QSvgRefCounted @@ -186,10 +187,18 @@ public: class Q_SVG_PRIVATE_EXPORT QSvgQualityStyle : public QSvgStyleProperty { public: + enum ImageRendering: qint8 { + ImageRenderingAuto = 0, + ImageRenderingOptimizeSpeed = 1, + ImageRenderingOptimizeQuality = 2, + }; + QSvgQualityStyle(int color); void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) override; void revert(QPainter *p, QSvgExtraStates &states) override; Type type() const override; + + void setImageRendering(ImageRendering); private: // color-render ing v v 'auto' | 'optimizeSpeed' | // 'optimizeQuality' | 'inherit' @@ -210,7 +219,9 @@ private: // image-rendering v v 'auto' | 'optimizeSpeed' | 'optimizeQuality' | // 'inherit' - //QSvgImageRendering m_imageRendering; + qint32 m_imageRendering: 4; + qint32 m_oldImageRendering: 4; + qint32 m_imageRenderingSet: 1; }; diff --git a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp index ea23d2d..2f83301 100644 --- a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +++ b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp @@ -85,6 +85,7 @@ private slots: void oss_fuzz_23731(); void oss_fuzz_24131(); void oss_fuzz_24738(); + void imageRendering(); #ifndef QT_NO_COMPRESS void testGzLoading(); @@ -1632,5 +1633,52 @@ void tst_QSvgRenderer::oss_fuzz_24738() QSvgRenderer().load(QByteArray("<svg><path d=\"a 2 1e-212.....\">")); } +QByteArray image_data_url(QImage &image) { + QByteArray data; + QBuffer buffer(&data); + buffer.open(QBuffer::ReadWrite); + image.save(&buffer, "PNG"); + buffer.close(); + QByteArray url("data:image/png;base64,"); + url.append(data.toBase64()); + return url; +} + +void tst_QSvgRenderer::imageRendering() { + QImage img(2, 2, QImage::Format_ARGB32_Premultiplied); + img.fill(Qt::green); + img.setPixel(0, 0, qRgb(255, 0, 0)); + img.setPixel(1, 1, qRgb(255, 0, 0)); + QByteArray imgurl(image_data_url(img)); + QString svgtemplate( + "<svg><g transform='scale(2, 2)'>" + "<image image-rendering='%1' xlink:href='%2' width='2' height='2' />" + "</g></svg>" + ); + const char *cases[] = {"optimizeQuality", "optimizeSpeed"}; + for (auto ir: cases) { + QString svg = svgtemplate.arg(QLatin1String(ir)).arg(QLatin1String(imgurl)); + QImage img1(4, 4, QImage::Format_ARGB32); + QPainter p1; + p1.begin(&img1); + QSvgRenderer renderer(svg.toLatin1()); + Q_ASSERT(renderer.isValid()); + renderer.render(&p1); + p1.end(); + + QImage img2(4, 4, QImage::Format_ARGB32); + QPainter p2(&img2); + p2.scale(2, 2); + if (QLatin1String(ir) == QLatin1String("optimizeSpeed")) + p2.setRenderHint(QPainter::SmoothPixmapTransform, false); + else if (QLatin1String(ir) == QLatin1String("optimizeQuality")) + p2.setRenderHint(QPainter::SmoothPixmapTransform, true); + p2.drawImage(0, 0, img); + p2.end(); + QCOMPARE(img1, img2); + } +} + + QTEST_MAIN(tst_QSvgRenderer) #include "tst_qsvgrenderer.moc" |