summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAles Erjavec <ales.erjavec@fri.uni-lj.si>2020-01-31 11:18:31 +0100
committerAles Erjavec <ales.erjavec@fri.uni-lj.si>2020-11-03 10:25:25 +0200
commit50457678278f81d6dc07b8396dfb235fd9acf557 (patch)
tree01d7a28d21e34726003dfb8ea68052c960aff02b
parent1fcc86f538200ceca7412ab292947cdb2991d12c (diff)
downloadqtsvg-50457678278f81d6dc07b8396dfb235fd9acf557.tar.gz
Add support for 'image-rendering' attribute
Write and parse the 'image-rendering' attibute. The value is mapped to QPainter::SmoothPixmapTransform render hint. [ChangeLog] Add support for 'image-rendering' attribute Task-number: QTBUG-4145 Change-Id: I5268eac73b234cd195adade502ab9945a89f3ff6 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
-rw-r--r--src/svg/qsvggenerator.cpp9
-rw-r--r--src/svg/qsvghandler.cpp28
-rw-r--r--src/svg/qsvgstyle.cpp39
-rw-r--r--src/svg/qsvgstyle_p.h13
-rw-r--r--tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp48
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"