From acb66cad2280bda20d26d08d606a66b9660b17aa Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Fri, 20 Dec 2019 16:43:48 +0100 Subject: Fix yet another viewbox scaling issue, for render to bounds The recent introduction of keepAspectRation scaling led to wrong output (outside bounds) in the case of an explicitly specified target bounds rect, i.e. QSVGRenderer::render(QPainter *p, QRectF bounds). Fix by reverting to old code path in this case, i.e. allow the user to override the keepAspectRatio behavior by explicitly specifying target bounds. As a driveby, also fix the keepAspectRatio code path in case of a target rect having non-zero x/y coordinates. Now the fix above means that this will never happen in the code as it stands, but it may come in handy later. [ChangeLog][QSVGRenderer] From Qt 5.14.0, normal rendering will keep aspect ratio implied by the viewbox. The render() methods taking an explicit target bounds QRectF parameter can now be used to override that behavior. They will scale the output to the bounds while ignoring aspect ratio, as was the default rendering prior to 5.14.0. Fixes: QTBUG-80888 Change-Id: I399b05ca50d290b8e4b01bdc47b5b6f74c890c9a Reviewed-by: Alessandro Portale --- src/svg/qsvgrenderer.cpp | 8 +++---- src/svg/qsvgtinydocument.cpp | 12 +++++------ tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp | 31 ++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/svg/qsvgrenderer.cpp b/src/svg/qsvgrenderer.cpp index d4ad373..da31a31 100644 --- a/src/svg/qsvgrenderer.cpp +++ b/src/svg/qsvgrenderer.cpp @@ -401,10 +401,10 @@ void QSvgRenderer::render(QPainter *painter, const QString &elementId, } /*! - Renders the current document, or the current frame of an animated - document, using the given \a painter on the specified \a bounds within - the painter. If the bounding rectangle is not specified - the SVG file is mapped to the whole paint device. + Renders the current document, or the current frame of an animated document, + using the given \a painter on the specified \a bounds within the painter. + If \a bounds is not empty, the output will be scaled to fill it, ignoring + any aspect ratio implied by the SVG. */ void QSvgRenderer::render(QPainter *painter, const QRectF &bounds) { diff --git a/src/svg/qsvgtinydocument.cpp b/src/svg/qsvgtinydocument.cpp index 56960bf..173baaa 100644 --- a/src/svg/qsvgtinydocument.cpp +++ b/src/svg/qsvgtinydocument.cpp @@ -420,9 +420,10 @@ void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, source = viewBox(); if (source != target && !source.isNull()) { - if (m_implicitViewBox || !sourceRect.isNull()) { + if (m_implicitViewBox || !sourceRect.isNull() || !targetRect.isNull()) { // Code path used when no view box is set, or when an explicit source size is given which - // overrides it (which is the case when we're rendering only a specific element by id). + // overrides it (which is the case when we're rendering only a specific element by id), + // or when user has given explicit target bounds that overrides viebox aspect ratio QTransform transform; transform.scale(target.width() / source.width(), target.height() / source.height()); @@ -441,15 +442,14 @@ void QSvgTinyDocument::mapSourceToTarget(QPainter *p, const QRectF &targetRect, viewBoxSize.scale(target.width(), target.height(), Qt::KeepAspectRatio); // Center the view box in the view port - p->translate((target.width() - viewBoxSize.width()) / 2, - (target.height() - viewBoxSize.height()) / 2); + p->translate(target.x() + (target.width() - viewBoxSize.width()) / 2, + target.y() + (target.height() - viewBoxSize.height()) / 2); p->scale(viewBoxSize.width() / source.width(), viewBoxSize.height() / source.height()); // Apply the view box translation if specified. - p->translate(target.x() - source.x(), - target.y() - source.y()); + p->translate(-source.x(), -source.y()); } } } diff --git a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp index 309c646..8ad74f2 100644 --- a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +++ b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp @@ -60,6 +60,7 @@ private slots: void testMapViewBoxToTarget(); void testRenderElement(); void testRenderElementToBounds(); + void testRenderDocumentWithSizeToBounds(); void constructorQXmlStreamReader() const; void loadQXmlStreamReader() const; void nestedQXmlStreamReader() const; @@ -372,6 +373,36 @@ void tst_QSvgRenderer::testRenderElementToBounds() QCOMPARE(reference, rendering); } +void tst_QSvgRenderer::testRenderDocumentWithSizeToBounds() +{ + // QTBUG-80888 + QImage reference(400, 200, QImage::Format_ARGB32); + { + reference.fill(Qt::transparent); + QPainter p(&reference); + p.fillRect(100, 100, 100, 50, Qt::blue); + p.fillRect(200, 50, 100, 50, Qt::blue); + } + + QImage rendering(400, 200, QImage::Format_ARGB32); + { + const char *const src = R"src( + + + + + + )src"; + const QByteArray data(src); + QSvgRenderer rend(data); + rendering.fill(Qt::transparent); + QPainter p(&rendering); + rend.render(&p, QRectF(100, 50, 200, 100)); + } + + QCOMPARE(reference, rendering); +} + void tst_QSvgRenderer::constructorQXmlStreamReader() const { const QByteArray data(src); -- cgit v1.2.1