From d54484122b7653ad1a96e7ef361c6250bb666f3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Mon, 8 Mar 2010 12:20:53 +0100 Subject: Added text benchmarks from qtbench to tests/benchmarks Reviewed-by: Gunnar Sletta --- tests/benchmarks/gui/painting/painting.pro | 3 +- .../gui/painting/qtbench/benchmarktests.h | 800 +++++++++++++++++++++ tests/benchmarks/gui/painting/qtbench/qtbench.pro | 6 + .../gui/painting/qtbench/tst_qtbench.cpp | 254 +++++++ 4 files changed, 1062 insertions(+), 1 deletion(-) create mode 100644 tests/benchmarks/gui/painting/qtbench/benchmarktests.h create mode 100644 tests/benchmarks/gui/painting/qtbench/qtbench.pro create mode 100644 tests/benchmarks/gui/painting/qtbench/tst_qtbench.cpp diff --git a/tests/benchmarks/gui/painting/painting.pro b/tests/benchmarks/gui/painting/painting.pro index 2c042b5..76c26c1 100644 --- a/tests/benchmarks/gui/painting/painting.pro +++ b/tests/benchmarks/gui/painting/painting.pro @@ -3,4 +3,5 @@ SUBDIRS = \ qpainter \ qregion \ qtransform \ - qtracebench + qtracebench \ + qtbench diff --git a/tests/benchmarks/gui/painting/qtbench/benchmarktests.h b/tests/benchmarks/gui/painting/qtbench/benchmarktests.h new file mode 100644 index 0000000..8626ae7 --- /dev/null +++ b/tests/benchmarks/gui/painting/qtbench/benchmarktests.h @@ -0,0 +1,800 @@ +#ifndef BENCHMARKTESTS_H +#define BENCHMARKTESTS_H + +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) +# include +#endif + +class Benchmark +{ +public: + virtual ~Benchmark() {} + + Benchmark(const QSize &size) + : m_size(size) + { + for (int i=0; i<16; ++i) { + m_colors[i] = QColor::fromRgbF((rand() % 4) / 3.0, + (rand() % 4) / 3.0, + (rand() % 4) / 3.0, + 1); + } + } + + virtual void draw(QPainter *p, const QRect &rect, int iteration) = 0; + virtual QString name() const = 0; + + inline const QSize &size() const + { + return m_size; + } + virtual void begin(QPainter *, int iterations = 1) { Q_UNUSED(iterations); } + virtual void end(QPainter *) { } + + inline const QColor &randomColor(int i) { return m_colors[i % 16]; } + +protected: + QColor m_colors[16]; + QSize m_size; +}; + +class PaintingRectAdjuster +{ +public: + PaintingRectAdjuster() + : m_benchmark(0), + m_bounds(), + m_screen_filled(false) + { + } + + const QRect &newPaintingRect() { + m_rect.translate(m_rect.width(), 0); + + if (m_rect.right() > m_bounds.width()) { + m_rect.moveLeft(m_bounds.left()); + m_rect.translate(0,m_rect.height()); + if (m_rect.bottom() > m_bounds.height()) { + m_screen_filled = true; + m_rect.moveTo(m_bounds.topLeft()); + } + } + return m_rect; + } + + inline bool isScreenFilled() const + { return m_screen_filled; } + + void reset(const QRect &bounds) + { + m_bounds = bounds; + m_rect.moveTo(m_bounds.topLeft()); + m_rect = QRect(m_bounds.topLeft(),m_benchmark->size()); + m_rect.translate(-m_rect.width(),0); + m_screen_filled = false; + } + + inline void setNewBenchmark( Benchmark *benchmark ) + { + m_benchmark = benchmark; + } + +protected: + Benchmark *m_benchmark; + QRect m_rect; + QRect m_bounds; + bool m_screen_filled; +}; + +class FillRectBenchmark : public Benchmark +{ +public: + FillRectBenchmark(int size) + : Benchmark(QSize(size, size)) + { + } + + virtual void draw(QPainter *p, const QRect &rect, int iterationCount) { + p->fillRect(rect, randomColor(iterationCount)); + } + + virtual QString name() const { + return QString::fromLatin1("fillRect(%1)").arg(m_size.width()); + } +}; + +class ImageFillRectBenchmark : public Benchmark +{ +public: + ImageFillRectBenchmark(int size) + : Benchmark(QSize(size, size)) + { + int s = rand() % 24 + 8; + m_content = QImage(s, s, QImage::Format_ARGB32_Premultiplied); + QPainter p(&m_content); + p.fillRect(0, 0, s, s, Qt::white); + p.fillRect(s/2, 0, s/2, s/2, Qt::gray); + p.fillRect(0, s/2, s/2, s/2, Qt::gray); + p.end(); + + m_brush = QBrush(m_content); + } + + virtual void draw(QPainter *p, const QRect &rect, int) { + p->fillRect(rect, m_brush); + } + + virtual QString name() const { + return QString::fromLatin1("fillRect with image(%1)").arg(m_size.width()); + } + +private: + QImage m_content; + QBrush m_brush; +}; + + +class DrawRectBenchmark : public Benchmark +{ +public: + DrawRectBenchmark(int size) + : Benchmark(QSize(size, size)) + { + } + + virtual void begin(QPainter *p, int) { + p->setPen(Qt::NoPen); + p->setBrush(randomColor(m_size.width())); + } + + + virtual void draw(QPainter *p, const QRect &rect, int) { + p->drawRect(rect); + } + + virtual QString name() const { + return QString::fromLatin1("drawRect(%1)").arg(m_size.width()); + } +}; + + +class DrawRectWithBrushChangeBenchmark : public Benchmark +{ +public: + DrawRectWithBrushChangeBenchmark(int size) + : Benchmark(QSize(size, size)) + { + } + + virtual void begin(QPainter *p, int) { + p->setPen(Qt::NoPen); + } + + + virtual void draw(QPainter *p, const QRect &rect, int i) { + p->setBrush(randomColor(i)); + p->drawRect(rect); + } + + virtual QString name() const { + return QString::fromLatin1("drawRect with brushchange(%1)").arg(m_size.width()); + } +}; + +class RoundRectBenchmark : public Benchmark +{ +public: + RoundRectBenchmark(int size) + : Benchmark(QSize(size, size)) + { + m_roundness = size / 4.; + } + + virtual void begin(QPainter *p, int) { + p->setPen(Qt::NoPen); + p->setBrush(Qt::red); + } + + virtual void draw(QPainter *p, const QRect &rect, int) { + p->drawRoundedRect(rect, m_roundness, m_roundness); + } + + virtual QString name() const { + return QString::fromLatin1("drawRoundedRect(%1)").arg(m_size.width()); + } + + qreal m_roundness; +}; + + +class ArcsBenchmark : public Benchmark +{ +public: + enum Type { + Stroked = 0x0001, + Filled = 0x0002, + + ArcShape = 0x0010, + ChordShape = 0x0020, + PieShape = 0x0040, + CircleShape = 0x0080, + Shapes = 0x00f0 + + }; + + ArcsBenchmark(int size, uint type) + : Benchmark(QSize(size, size)), + m_type(type) + { + } + + virtual void begin(QPainter *p, int) { + if (m_type & Stroked) + p->setPen(Qt::black); + else + p->setPen(Qt::NoPen); + + if (m_type & Filled) + p->setBrush(Qt::red); + else + p->setBrush(Qt::NoBrush); + } + + virtual void draw(QPainter *p, const QRect &rect, int) { + switch (m_type & Shapes) { + case ArcShape: + p->drawArc(rect, 45*16, 120*16); + break; + case ChordShape: + p->drawChord(rect, 45*16, 120*16); + break; + case PieShape: + p->drawPie(rect, 45*16, 120*16); + break; + case CircleShape: + p->drawEllipse(rect); + break; + } + } + + virtual QString name() const { + QString fillStroke; + + if ((m_type & (Stroked|Filled)) == (Stroked|Filled)) { + fillStroke = QLatin1String("Fill & Outline"); + } else if (m_type & Stroked) { + fillStroke = QLatin1String("Outline"); + } else if (m_type & Filled) { + fillStroke = QLatin1String("Fill"); + } + + QString shape; + if (m_type & PieShape) shape = QLatin1String("drawPie"); + else if (m_type & ChordShape) shape = QLatin1String("drawChord"); + else if (m_type & ArcShape) shape = QLatin1String("drawArc"); + else if (m_type & CircleShape) shape = QLatin1String("drawEllipse"); + + return QString::fromLatin1("%1(%2) %3").arg(shape).arg(m_size.width()).arg(fillStroke); + } + + uint m_type; +}; + + +class DrawScaledImage : public Benchmark +{ +public: + DrawScaledImage(const QImage &image, qreal scale, bool asPixmap) + : Benchmark(QSize(image.width(), image.height())), + m_image(image), + m_type(m_as_pixmap ? "Pixmap" : "Image"), + m_scale(scale), + m_as_pixmap(asPixmap) + { + m_pixmap = QPixmap::fromImage(m_image); + } + DrawScaledImage(const QString& type, const QPixmap &pixmap, qreal scale) + : Benchmark(QSize(pixmap.width(), pixmap.height())), + m_type(type), + m_scale(scale), + m_as_pixmap(true), + m_pixmap(pixmap) + { + } + + virtual void begin(QPainter *p, int) { + p->scale(m_scale, m_scale); + } + + virtual void draw(QPainter *p, const QRect &rect, int) { + if (m_as_pixmap) + p->drawPixmap(rect.topLeft(), m_pixmap); + else + p->drawImage(rect.topLeft(), m_image); + } + + virtual QString name() const { + return QString::fromLatin1("draw%4(%1) at scale=%2, depth=%3") + .arg(m_size.width()) + .arg(m_scale) + .arg(m_as_pixmap ? m_pixmap.depth() : m_image.depth()) + .arg(m_type); + } + +private: + QImage m_image; + QString m_type; + qreal m_scale; + bool m_as_pixmap; + QPixmap m_pixmap; +}; + +class DrawTransformedImage : public Benchmark +{ +public: + DrawTransformedImage(const QImage &image, bool asPixmap) + : Benchmark(QSize(image.width(), image.height())), + m_image(image), + m_type(m_as_pixmap ? "Pixmap" : "Image"), + m_as_pixmap(asPixmap) + { + m_pixmap = QPixmap::fromImage(m_image); + } + DrawTransformedImage(const QString& type, const QPixmap &pixmap) + : Benchmark(QSize(pixmap.width(), pixmap.height())), + m_type(type), + m_as_pixmap(true), + m_pixmap(pixmap) + { + } + + virtual void draw(QPainter *p, const QRect &rect, int) { + QTransform oldTransform = p->transform(); + p->translate(0.5 * rect.width() + rect.left(), 0.5 * rect.height() + rect.top()); + p->shear(0.25, 0.0); + p->rotate(5.0); + if (m_as_pixmap) + p->drawPixmap(-0.5 * rect.width(), -0.5 * rect.height(), m_pixmap); + else + p->drawImage(-0.5 * rect.width(), -0.5 * rect.height(), m_image); + p->setTransform(oldTransform); + } + + virtual QString name() const { + return QString::fromLatin1("draw%3(%1) w/transform, depth=%2") + .arg(m_size.width()) + .arg(m_as_pixmap ? m_pixmap.depth() : m_image.depth()) + .arg(m_type); + } + +private: + QImage m_image; + QString m_type; + bool m_as_pixmap; + QPixmap m_pixmap; +}; + + +class DrawImage : public Benchmark +{ +public: + DrawImage(const QImage &image, bool asPixmap) + : Benchmark(QSize(image.width(), image.height())), + m_image(image), + m_type(m_as_pixmap ? "Pixmap" : "Image"), + m_as_pixmap(asPixmap) + { + m_pixmap = QPixmap::fromImage(image); + } + DrawImage(const QString& type, const QPixmap &pixmap) + : Benchmark(QSize(pixmap.width(), pixmap.height())), + m_type(type), + m_as_pixmap(true), + m_pixmap(pixmap) + { + } + + virtual void draw(QPainter *p, const QRect &rect, int) { + if (m_as_pixmap) + p->drawPixmap(rect.topLeft(), m_pixmap); + else + p->drawImage(rect.topLeft(), m_image); + } + + virtual QString name() const { + return QString::fromLatin1("draw%2(%1), depth=%3") + .arg(m_size.width()) + .arg(m_type) + .arg(m_as_pixmap ? m_pixmap.depth() : m_image.depth()); + } + +private: + QImage m_image; + QString m_type; + bool m_as_pixmap; + QPixmap m_pixmap; +}; + + +class DrawText : public Benchmark +{ +public: + enum Mode { + PainterMode, + PainterQPointMode, + LayoutMode, + DocumentMode, + PixmapMode + +#if QT_VERSION >= 0x040700 + , StaticTextMode, + StaticTextWithMaximumSizeMode, + StaticTextBackendOptimizations +#endif + }; + + DrawText(const QString &text, Mode mode) + : Benchmark(QSize()), m_mode(mode), m_text(text), m_document(text), m_layout(text) + { + } + + virtual void begin(QPainter *p, int iterations) { +#if QT_VERSION >= 0x040700 + m_staticTexts.clear(); + m_currentStaticText = 0; +#else + Q_UNUSED(iterations); +#endif + m_pixmaps.clear(); + m_currentPixmap = 0; + QRect m_bounds = QRect(0,0,p->device()->width(), p->device()->height()); + switch (m_mode) { + case PainterMode: + m_size = (p->boundingRect(m_bounds, 0, m_text)).size(); +// m_rect = m_rect.translated(-m_rect.topLeft()); + break; + case DocumentMode: + m_size = QSize(m_document.size().toSize()); + break; + case PixmapMode: + for (int i=0; i<4; ++i) { + m_size = (p->boundingRect(m_bounds, 0, m_text)).size(); + QPixmap pixmap = QPixmap(m_size); + pixmap.fill(Qt::transparent); + { + QPainter p(&pixmap); + p.drawText(pixmap.rect(), m_text); + } + m_pixmaps.append(pixmap); + } + break; + + case LayoutMode: { + QRect r = p->boundingRect(m_bounds, 0, m_text); + QStringList lines = m_text.split('\n'); + int height = 0; + int leading = p->fontMetrics().leading(); + m_layout.beginLayout(); + for (int i=0; i= 0x040700 + case StaticTextWithMaximumSizeMode: { + QStaticText staticText; + m_size = (p->boundingRect(m_bounds, 0, m_text)).size(); + staticText.setMaximumSize(m_size + QSize(10, 10)); + staticText.setText(m_text); + staticText.prepare(p->transform(), p->font()); + m_staticTexts.append(staticText); + break; + } + case StaticTextBackendOptimizations: { + m_size = (p->boundingRect(m_bounds, 0, m_text)).size(); + for (int i=0; itransform(), p->font()); + m_staticTexts.append(staticText); + } + + break; + } + case StaticTextMode: { + QStaticText staticText; + staticText.setText(m_text); + staticText.prepare(p->transform(), p->font()); + m_staticTexts.append(staticText); + + QFontMetrics fm(p->font()); + m_size = QSize(fm.width(m_text, m_text.length()), fm.height()); + + break; + } +#endif + + case PainterQPointMode: { + QFontMetrics fm(p->font()); + m_size = QSize(fm.width(m_text, m_text.length()), fm.height()); + break; + } + + } + } + + virtual void draw(QPainter *p, const QRect &rect, int) + { + switch (m_mode) { + case PainterMode: + p->drawText(rect, 0, m_text); + break; + case PainterQPointMode: + p->drawText(rect.topLeft(), m_text); + break; + case PixmapMode: + p->drawPixmap(rect.topLeft(), m_pixmaps.at(m_currentPixmap)); + m_currentPixmap = (m_currentPixmap + 1) % m_pixmaps.size(); + break; + case DocumentMode: + p->translate(rect.topLeft()); + m_document.drawContents(p); + p->translate(-rect.topLeft()); + break; + case LayoutMode: + m_layout.draw(p, rect.topLeft()); + break; + +#if QT_VERSION >= 0x040700 + case StaticTextWithMaximumSizeMode: + case StaticTextMode: + p->drawStaticText(rect.topLeft(), m_staticTexts.at(0)); + break; + case StaticTextBackendOptimizations: + p->drawStaticText(rect.topLeft(), m_staticTexts.at(m_currentStaticText)); + m_currentStaticText = (m_currentStaticText + 1) % m_staticTexts.size(); + break; +#endif + } + } + + virtual QString name() const { + int letters = m_text.length(); + int lines = m_text.count('\n'); + if (lines == 0) + lines = 1; + QString type; + switch (m_mode) { + case PainterMode: type = "drawText(rect)"; break; + case PainterQPointMode: type = "drawText(point)"; break; + case LayoutMode: type = "layout.draw()"; break; + case DocumentMode: type = "doc.drawContents()"; break; + case PixmapMode: type = "pixmap cached text"; break; + +#if QT_VERSION >= 0x040700 + case StaticTextMode: type = "drawStaticText()"; break; + case StaticTextWithMaximumSizeMode: type = "drawStaticText() w/ maxsize"; break; + case StaticTextBackendOptimizations: type = "drawStaticText() w/ backend optimizations"; break; +#endif + } + + return QString::fromLatin1("%3, len=%1, lines=%2") + .arg(letters) + .arg(lines) + .arg(type); + } + +private: + Mode m_mode; + QString m_text; + QTextDocument m_document; + QTextLayout m_layout; + + QList m_pixmaps; + int m_currentPixmap; + +#if QT_VERSION >= 0x040700 + int m_currentStaticText; + QList m_staticTexts; +#endif +}; + + + + +class ClippedDrawRectBenchmark : public Benchmark +{ +public: + enum ClipType { + RectClip, + TwoRectRegionClip, + EllipseRegionClip, + TwoRectPathClip, + EllipsePathClip, + AAEllipsePathClip, + EllipseRegionThenRectClip, + EllipsePathThenRectClip + }; + + ClippedDrawRectBenchmark(int size, ClipType type) + : Benchmark(QSize(size, size)), m_type(type) + { + } + + virtual void begin(QPainter *p, int) { + QRect m_bounds = QRect(0,0,p->device()->width(), p->device()->height()); + p->setPen(Qt::NoPen); + p->setBrush(Qt::red); + + switch (m_type) { + case RectClip: + p->setClipRect(m_bounds.adjusted(1, 1, -1, -1)); + break; + case TwoRectRegionClip: + p->setClipRegion(QRegion(m_bounds.adjusted(0, 0, -1, -1)) + | QRegion(m_bounds.adjusted(1, 1, 0, 0))); + break; + case EllipseRegionClip: + p->setClipRegion(QRegion(m_bounds, QRegion::Ellipse)); + break; + case TwoRectPathClip: + { + QPainterPath path; + path.addRect(m_bounds.adjusted(0, 0, -1, -1)); + path.addRect(m_bounds.adjusted(1, 1, 0, 0)); + path.setFillRule(Qt::WindingFill); + p->setClipPath(path); + } + break; + case EllipsePathClip: + { + QPainterPath path; + path.addEllipse(m_bounds); + p->setClipPath(path); + } + break; + case AAEllipsePathClip: + { + QPainterPath path; + path.addEllipse(m_bounds); + p->setRenderHint(QPainter::Antialiasing); + p->setClipPath(path); + p->setRenderHint(QPainter::Antialiasing, false); + } + break; + case EllipseRegionThenRectClip: + p->setClipRegion(QRegion(m_bounds, QRegion::Ellipse)); + p->setClipRegion(QRegion(m_bounds.width() / 4, + m_bounds.height() / 4, + m_bounds.width() / 2, + m_bounds.height() / 2), Qt::IntersectClip); + break; + case EllipsePathThenRectClip: + { + QPainterPath path; + path.addEllipse(m_bounds); + p->setClipPath(path); + p->setClipRegion(QRegion(m_bounds.width() / 4, + m_bounds.height() / 4, + m_bounds.width() / 2, + m_bounds.height() / 2), Qt::IntersectClip); + } + break; + } + } + + virtual void draw(QPainter *p, const QRect &rect, int) { + p->drawRect(rect); + } + + virtual QString name() const { + QString namedType; + switch (m_type) { + case RectClip: + namedType = "rect"; + break; + case TwoRectRegionClip: + namedType = "two-rect-region"; + break; + case EllipseRegionClip: + namedType = "ellipse-region"; + break; + case TwoRectPathClip: + namedType = "two-rect-path"; + break; + case EllipsePathClip: + namedType = "ellipse-path"; + break; + case AAEllipsePathClip: + namedType = "aa-ellipse-path"; + break; + case EllipseRegionThenRectClip: + namedType = "ellipseregion&rect"; + break; + case EllipsePathThenRectClip: + namedType = "ellipsepath&rect"; + break; + } + return QString::fromLatin1("%1-clipped-drawRect(%2)").arg(namedType).arg(m_size.width()); + } + + ClipType m_type; +}; + +class LinesBenchmark : public Benchmark +{ +public: + enum LineType { + Horizontal_Integer, + Diagonal_Integer, + Vertical_Integer, + Horizontal_Float, + Diagonal_Float, + Vertical_Float + }; + + LinesBenchmark(int length, LineType type) + : Benchmark(QSize(qAbs(length), qAbs(length))), + m_type(type), + m_length(length) + { + + } + + virtual void draw(QPainter *p, const QRect &rect, int) { + switch (m_type) { + case Horizontal_Integer: + p->drawLine(QLine(rect.x(), rect.y(), rect.x() + m_length, rect.y())); + break; + case Diagonal_Integer: + p->drawLine(QLine(rect.x(), rect.y(), rect.x() + m_length, rect.y() + m_length)); + break; + case Vertical_Integer: + p->drawLine(QLine(rect.x() + 4, rect.y(), rect.x() + 4, rect.y() + m_length)); + break; + case Horizontal_Float: + p->drawLine(QLineF(rect.x(), rect.y(), rect.x() + m_length, rect.y())); + break; + case Diagonal_Float: + p->drawLine(QLineF(rect.x(), rect.y(), rect.x() + m_length, rect.y() + m_length)); + break; + case Vertical_Float: + p->drawLine(QLineF(rect.x() + 4, rect.y(), rect.x() + 4, rect.y() + m_length)); + break; + } + } + + virtual QString name() const { + const char *names[] = { + "Hor_I", + "Diag_I", + "Ver_I", + "Hor_F", + "Diag_F", + "Ver_F" + }; + return QString::fromLatin1("drawLine(size=%1,type=%2)").arg(m_length).arg(names[m_type]); + } + + LineType m_type; + int m_length; +}; + +#endif // BENCHMARKTESTS_H diff --git a/tests/benchmarks/gui/painting/qtbench/qtbench.pro b/tests/benchmarks/gui/painting/qtbench/qtbench.pro new file mode 100644 index 0000000..91f416d --- /dev/null +++ b/tests/benchmarks/gui/painting/qtbench/qtbench.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_qtbench + +SOURCES += tst_qtbench.cpp + diff --git a/tests/benchmarks/gui/painting/qtbench/tst_qtbench.cpp b/tests/benchmarks/gui/painting/qtbench/tst_qtbench.cpp new file mode 100644 index 0000000..8eef472 --- /dev/null +++ b/tests/benchmarks/gui/painting/qtbench/tst_qtbench.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +#include "benchmarktests.h" + +//TESTED_FILES= + +class BenchWidget : public QWidget +{ +public: + BenchWidget(Benchmark *benchmark); + + void paintEvent(QPaintEvent *event); + + bool done() const { return m_done; } + qreal result() const { return m_result; } + +public: + QTime timer; + + Benchmark *m_benchmark; + + bool m_done; + qreal m_result; + + uint m_total; + uint m_iteration; + + QVector iterationTimes; +}; + +void BenchWidget::paintEvent(QPaintEvent *) +{ + if (m_done) + return; + + QPainter p(this); + + m_benchmark->begin(&p, 100); + + PaintingRectAdjuster adjuster; + adjuster.setNewBenchmark(m_benchmark); + adjuster.reset(rect()); + + for (int i = 0; i < 100; ++i) + m_benchmark->draw(&p, adjuster.newPaintingRect(), i); + + m_benchmark->end(&p); + + ++m_iteration; + + uint currentElapsed = timer.isNull() ? 0 : timer.elapsed(); + timer.restart(); + + m_total += currentElapsed; + + // warm up for at most 5 iterations or half a second + if (m_iteration >= 5 || m_total >= 500) { + iterationTimes << currentElapsed; + + if (iterationTimes.size() >= 5) { + qreal mean = 0; + qreal stddev = 0; + uint min = INT_MAX; + + for (int i = 0; i < iterationTimes.size(); ++i) { + mean += iterationTimes.at(i); + min = qMin(min, iterationTimes.at(i)); + } + + mean /= qreal(iterationTimes.size()); + + for (int i = 0; i < iterationTimes.size(); ++i) { + qreal delta = iterationTimes.at(i) - mean; + stddev += delta * delta; + } + + stddev = qSqrt(stddev / iterationTimes.size()); + + stddev = 100 * stddev / mean; + // do 50 iterations, break earlier if we spend more than 5 seconds or have a low std deviation after 2 seconds + if (iterationTimes.size() >= 50 || m_total >= 5000 || (m_total >= 2000 && stddev < 4)) { + m_result = min; + m_done = true; + return; + } + } + } +} + +BenchWidget::BenchWidget(Benchmark *benchmark) + : m_benchmark(benchmark) + , m_done(false) + , m_result(0) + , m_total(0) + , m_iteration(0) +{ + setWindowTitle(benchmark->name()); + resize(640, 480); +} + +class tst_QtBench : public QObject +{ + Q_OBJECT + +private slots: + void qtBench(); + void qtBench_data(); +}; + +QString makeString(int length) +{ + const char chars[] = "abcd efgh ijkl mnop qrst uvwx yz!$. ABCD 1234"; + int len = strlen(chars); + + QString ret; + for (int j = 0; j < length; j++) { + ret += QChar(chars[(j * 97) % len]); + } + + return ret; +} + +void tst_QtBench::qtBench_data() +{ + QTest::addColumn("benchmark"); + + QString shortString = makeString(5); + QString middleString = makeString(50); + QString longString = makeString(35) + "\n" + + makeString(45) + "\n" + + makeString(75); + QString superLongString = "Lorem ipsum dolor sit am\n" + "et, consectetur adipisci\n" + "ng elit. Integer mi leo,\n" + "interdum ut congue at, p\n" + "ulvinar et tellus. Quisq\n" + "ue pretium eleifend laci\n" + "nia. Ut semper gravida l\n" + "ectus in commodo. Vestib\n" + "ulum pharetra arcu in en\n" + "im ultrices hendrerit. P\n" + "ellentesque habitant mor\n" + "bi tristique senectus et\n" + "netus et malesuada fames\n" + "ac turpis egestas. Ut er\n" + "os sem, feugiat in eleme\n" + "ntum in, porta sit amet \n" + "neque. Fusce mi tellus, \n" + "congue non dapibus eget,\n" + "pharetra quis quam. Duis\n" + "dui massa, pulvinar ac s\n" + "odales pharetra, dictum \n" + "in enim. Phasellus a nis\n" + "i erat, sed pellentesque\n" + "mi. Curabitur sed."; + + QList benchmarks; + benchmarks << (new DrawText(shortString, DrawText::PainterMode)); + benchmarks << (new DrawText(middleString, DrawText::PainterMode)); + benchmarks << (new DrawText(longString, DrawText::PainterMode)); + benchmarks << (new DrawText(superLongString, DrawText::PainterMode)); + + benchmarks << (new DrawText(shortString, DrawText::PainterQPointMode)); + benchmarks << (new DrawText(middleString, DrawText::PainterQPointMode)); + benchmarks << (new DrawText(longString, DrawText::PainterQPointMode)); + benchmarks << (new DrawText(superLongString, DrawText::PainterQPointMode)); + + benchmarks << (new DrawText(shortString, DrawText::PixmapMode)); + benchmarks << (new DrawText(middleString, DrawText::PixmapMode)); + benchmarks << (new DrawText(longString, DrawText::PixmapMode)); + benchmarks << (new DrawText(superLongString, DrawText::PixmapMode)); + +#if QT_VERSION >= 0x040700 + benchmarks << (new DrawText(shortString, DrawText::StaticTextMode)); + benchmarks << (new DrawText(middleString, DrawText::StaticTextMode)); + benchmarks << (new DrawText(longString, DrawText::StaticTextMode)); + benchmarks << (new DrawText(superLongString, DrawText::StaticTextMode)); + + benchmarks << (new DrawText(shortString, DrawText::StaticTextWithMaximumSizeMode)); + benchmarks << (new DrawText(middleString, DrawText::StaticTextWithMaximumSizeMode)); + benchmarks << (new DrawText(longString, DrawText::StaticTextWithMaximumSizeMode)); + benchmarks << (new DrawText(superLongString, DrawText::StaticTextWithMaximumSizeMode)); + + benchmarks << (new DrawText(shortString, DrawText::StaticTextBackendOptimizations)); + benchmarks << (new DrawText(middleString, DrawText::StaticTextBackendOptimizations)); + benchmarks << (new DrawText(longString, DrawText::StaticTextBackendOptimizations)); + benchmarks << (new DrawText(superLongString, DrawText::StaticTextBackendOptimizations)); +#endif + + foreach (Benchmark *benchmark, benchmarks) + QTest::newRow(qPrintable(benchmark->name())) << reinterpret_cast(benchmark); +} + +void tst_QtBench::qtBench() +{ + QFETCH(void *, benchmark); + + BenchWidget widget(reinterpret_cast(benchmark)); + widget.show(); + QTest::qWaitForWindowShown(&widget); + + while (!widget.done()) { + widget.update(); + QApplication::processEvents(); + } + + QTest::setBenchmarkResult(widget.result(), QTest::WalltimeMilliseconds); +} + +QTEST_MAIN(tst_QtBench) +#include "tst_qtbench.moc" -- cgit v0.12 From 7b500ed543bb86429ef6fdb82b103784d6cc6a0c Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Fri, 5 Mar 2010 16:59:01 +0100 Subject: Fixed vector-effect property output from QSvgGenerator. The vector-effect property is not inherited by default, so it must be set on each item with a stroke. It is not sufficient to set it on G nodes. Task-number: QTBUG-8733 Reviewed-by: Trond --- src/svg/qsvggenerator.cpp | 27 +++++++++------------- .../referenceSvgs/fileName_output.svg | 6 ++--- .../referenceSvgs/radial_gradient.svg | 10 ++++---- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/svg/qsvggenerator.cpp b/src/svg/qsvggenerator.cpp index 4a8fc0b..cb9086c 100644 --- a/src/svg/qsvggenerator.cpp +++ b/src/svg/qsvggenerator.cpp @@ -310,7 +310,6 @@ public: { *d_func()->stream << QLatin1String("fill=\"none\" "); *d_func()->stream << QLatin1String("stroke=\"black\" "); - *d_func()->stream << QLatin1String("vector-effect=\"non-scaling-stroke\" "); *d_func()->stream << QLatin1String("stroke-width=\"1\" "); *d_func()->stream << QLatin1String("fill-rule=\"evenodd\" "); *d_func()->stream << QLatin1String("stroke-linecap=\"square\" "); @@ -380,13 +379,10 @@ public: break; } - if (spen.widthF() == 0) { - width = QLatin1String("1"); - stream() << "vector-effect=\"non-scaling-stroke\" "; - } + if (spen.widthF() == 0) + stream() <<"stroke-width=\"1\" "; else - width = QString::number(spen.widthF()); - stream() <<"stroke-width=\""<stream << "stream << "\"nonzero\" "; - - *d->stream << "d=\""; + *d->stream << "pen().isCosmetic() ? "non-scaling-stroke" : "none") + << "\" fill-rule=\"" + << (p.fillRule() == Qt::OddEvenFill ? "evenodd" : "nonzero") + << "\" d=\""; for (int i=0; ipen().isCosmetic() ? "non-scaling-stroke" : "none") + << "\" points=\""; for (int i = 0; i < pointCount; ++i) { const QPointF &pt = points[i]; stream() << pt.x() << ',' << pt.y() << ' '; diff --git a/tests/auto/qsvggenerator/referenceSvgs/fileName_output.svg b/tests/auto/qsvggenerator/referenceSvgs/fileName_output.svg index 99926b3..f1f9e35 100644 --- a/tests/auto/qsvggenerator/referenceSvgs/fileName_output.svg +++ b/tests/auto/qsvggenerator/referenceSvgs/fileName_output.svg @@ -4,12 +4,12 @@ Generated with Qt - + - + diff --git a/tests/auto/qsvggenerator/referenceSvgs/radial_gradient.svg b/tests/auto/qsvggenerator/referenceSvgs/radial_gradient.svg index f61dd40..84afbf3 100644 --- a/tests/auto/qsvggenerator/referenceSvgs/radial_gradient.svg +++ b/tests/auto/qsvggenerator/referenceSvgs/radial_gradient.svg @@ -13,18 +13,18 @@ - + - + - + -- cgit v0.12 From ea9f2aeba6f18d19d6a82b3a829373ec032af14f Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Mon, 8 Mar 2010 16:15:44 +0100 Subject: Fixed bounds calculation in QtSvg. This commit fixes an auto-test failure after fixing QTBUG-8733. The calculated element bounds should now be much tighter and respect the width of non-cosmetic pens. Task-number: QTBUG-8733 Reviewed-by: Trond --- src/svg/qsvggraphics.cpp | 178 +++++++++------------------ src/svg/qsvggraphics_p.h | 40 +++--- src/svg/qsvgnode.cpp | 65 ++++++---- src/svg/qsvgnode_p.h | 14 ++- src/svg/qsvgstructure.cpp | 13 +- src/svg/qsvgstructure_p.h | 3 +- src/svg/qsvgstyle.cpp | 46 +++---- src/svg/qsvgstyle_p.h | 28 ++--- src/svg/qsvgtinydocument.cpp | 7 +- src/svg/qsvgtinydocument_p.h | 5 +- tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp | 31 +++++ 11 files changed, 204 insertions(+), 226 deletions(-) diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp index cd0e1ac..a29764a 100644 --- a/src/svg/qsvggraphics.cpp +++ b/src/svg/qsvggraphics.cpp @@ -78,33 +78,29 @@ void QSvgAnimation::draw(QPainter *, QSvgExtraStates &) qWarning(" no implemented"); } -static inline QRectF boundsOnStroke(const QPainterPath &path, qreal width) +static inline QRectF boundsOnStroke(QPainter *p, const QPainterPath &path, qreal width) { QPainterPathStroker stroker; stroker.setWidth(width); QPainterPath stroke = stroker.createStroke(path); - return stroke.boundingRect(); + return p->transform().map(stroke).boundingRect(); } -QSvgCircle::QSvgCircle(QSvgNode *parent, const QRectF &rect) +QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect) : QSvgNode(parent), m_bounds(rect) { } -QRectF QSvgCircle::bounds() const +QRectF QSvgEllipse::bounds(QPainter *p, QSvgExtraStates &) const { - qreal sw = strokeWidth(); - if (qFuzzyIsNull(sw)) - return m_bounds; - else { - QPainterPath path; - path.addRect(m_bounds); - return boundsOnStroke(path, sw); - } + QPainterPath path; + path.addEllipse(m_bounds); + qreal sw = strokeWidth(p); + return qFuzzyIsNull(sw) ? p->transform().map(path).boundingRect() : boundsOnStroke(p, path, sw); } -void QSvgCircle::draw(QPainter *p, QSvgExtraStates &states) +void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states) { applyStyle(p, states); QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds)); @@ -112,9 +108,8 @@ void QSvgCircle::draw(QPainter *p, QSvgExtraStates &states) } QSvgArc::QSvgArc(QSvgNode *parent, const QPainterPath &path) - : QSvgNode(parent), cubic(path) + : QSvgNode(parent), m_path(path) { - m_cachedBounds = path.boundingRect(); } void QSvgArc::draw(QPainter *p, QSvgExtraStates &states) @@ -123,36 +118,12 @@ void QSvgArc::draw(QPainter *p, QSvgExtraStates &states) if (p->pen().widthF() != 0) { qreal oldOpacity = p->opacity(); p->setOpacity(oldOpacity * states.strokeOpacity); - p->drawPath(cubic); + p->drawPath(m_path); p->setOpacity(oldOpacity); } revertStyle(p, states); } -QSvgEllipse::QSvgEllipse(QSvgNode *parent, const QRectF &rect) - : QSvgNode(parent), m_bounds(rect) -{ -} - -QRectF QSvgEllipse::bounds() const -{ - qreal sw = strokeWidth(); - if (qFuzzyIsNull(sw)) - return m_bounds; - else { - QPainterPath path; - path.addEllipse(m_bounds); - return boundsOnStroke(path, sw); - } -} - -void QSvgEllipse::draw(QPainter *p, QSvgExtraStates &states) -{ - applyStyle(p, states); - QT_SVG_DRAW_SHAPE(p->drawEllipse(m_bounds)); - revertStyle(p, states); -} - QSvgImage::QSvgImage(QSvgNode *parent, const QImage &image, const QRect &bounds) : QSvgNode(parent), m_image(image), @@ -173,7 +144,7 @@ void QSvgImage::draw(QPainter *p, QSvgExtraStates &states) QSvgLine::QSvgLine(QSvgNode *parent, const QLineF &line) - : QSvgNode(parent), m_bounds(line) + : QSvgNode(parent), m_line(line) { } @@ -184,7 +155,7 @@ void QSvgLine::draw(QPainter *p, QSvgExtraStates &states) if (p->pen().widthF() != 0) { qreal oldOpacity = p->opacity(); p->setOpacity(oldOpacity * states.strokeOpacity); - p->drawLine(m_bounds); + p->drawLine(m_line); p->setOpacity(oldOpacity); } revertStyle(p, states); @@ -203,19 +174,11 @@ void QSvgPath::draw(QPainter *p, QSvgExtraStates &states) revertStyle(p, states); } -QRectF QSvgPath::bounds() const +QRectF QSvgPath::bounds(QPainter *p, QSvgExtraStates &) const { - qreal sw = strokeWidth(); - if (qFuzzyIsNull(sw)) { - if (m_cachedBounds.isNull()) - //m_cachedBounds = m_path.controlPointRect(); - m_cachedBounds = m_path.boundingRect(); - - return m_cachedBounds; - } - else { - return boundsOnStroke(m_path, sw); - } + qreal sw = strokeWidth(p); + return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect() + : boundsOnStroke(p, m_path, sw); } QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly) @@ -223,15 +186,15 @@ QSvgPolygon::QSvgPolygon(QSvgNode *parent, const QPolygonF &poly) { } -QRectF QSvgPolygon::bounds() const +QRectF QSvgPolygon::bounds(QPainter *p, QSvgExtraStates &) const { - qreal sw = strokeWidth(); - if (qFuzzyIsNull(sw)) - return m_poly.boundingRect(); - else { + qreal sw = strokeWidth(p); + if (qFuzzyIsNull(sw)) { + return p->transform().map(m_poly).boundingRect(); + } else { QPainterPath path; path.addPolygon(m_poly); - return boundsOnStroke(path, sw); + return boundsOnStroke(p, path, sw); } } @@ -274,15 +237,15 @@ QSvgRect::QSvgRect(QSvgNode *node, const QRectF &rect, int rx, int ry) { } -QRectF QSvgRect::bounds() const +QRectF QSvgRect::bounds(QPainter *p, QSvgExtraStates &) const { - qreal sw = strokeWidth(); - if (qFuzzyIsNull(sw)) - return m_rect; - else { + qreal sw = strokeWidth(p); + if (qFuzzyIsNull(sw)) { + return p->transform().mapRect(m_rect); + } else { QPainterPath path; path.addRect(m_rect); - return boundsOnStroke(path, sw); + return boundsOnStroke(p, path, sw); } } @@ -322,7 +285,7 @@ void QSvgText::setTextArea(const QSizeF &size) m_type = TEXTAREA; } -//QRectF QSvgText::bounds() const {} +//QRectF QSvgText::bounds(QPainter *p, QSvgExtraStates &) const {} void QSvgText::draw(QPainter *p, QSvgExtraStates &states) { @@ -593,80 +556,57 @@ QSvgNode::Type QSvgVideo::type() const return VIDEO; } -QRectF QSvgUse::bounds() const -{ - if (m_link && m_bounds.isEmpty()) { - m_bounds = m_link->bounds(); - m_bounds = QRectF(m_bounds.x()+m_start.x(), - m_bounds.y()+m_start.y(), - m_bounds.width(), - m_bounds.height()); - - return m_bounds; - } - return m_bounds; -} - -QRectF QSvgUse::transformedBounds(const QTransform &transform) const +QRectF QSvgUse::bounds(QPainter *p, QSvgExtraStates &states) const { QRectF bounds; - QTransform t = transform; - - if (m_link) { - QSvgTransformStyle *transStyle = m_style.transform; - if (transStyle) { - t = transStyle->qtransform() * t; - } - t.translate(m_start.x(), m_start.y()); - - bounds = m_link->transformedBounds(t); - - return bounds; + if (m_link) { + p->translate(m_start); + bounds = m_link->transformedBounds(p, states); + p->translate(-m_start); } return bounds; } -QRectF QSvgPolyline::bounds() const +QRectF QSvgPolyline::bounds(QPainter *p, QSvgExtraStates &) const { - qreal sw = strokeWidth(); - if (qFuzzyIsNull(sw)) - return m_poly.boundingRect(); - else { + qreal sw = strokeWidth(p); + if (qFuzzyIsNull(sw)) { + return p->transform().map(m_poly).boundingRect(); + } else { QPainterPath path; path.addPolygon(m_poly); - return boundsOnStroke(path, sw); + return boundsOnStroke(p, path, sw); } } -QRectF QSvgArc::bounds() const +QRectF QSvgArc::bounds(QPainter *p, QSvgExtraStates &) const { - qreal sw = strokeWidth(); - if (qFuzzyIsNull(sw)) - return m_cachedBounds; - else { - return boundsOnStroke(cubic, sw); - } + qreal sw = strokeWidth(p); + return qFuzzyIsNull(sw) ? p->transform().map(m_path).boundingRect() + : boundsOnStroke(p, m_path, sw); } -QRectF QSvgImage::bounds() const +QRectF QSvgImage::bounds(QPainter *p, QSvgExtraStates &) const { - return m_bounds; + return p->transform().mapRect(m_bounds); } -QRectF QSvgLine::bounds() const +QRectF QSvgLine::bounds(QPainter *p, QSvgExtraStates &) const { - qreal sw = strokeWidth(); + qreal sw = strokeWidth(p); if (qFuzzyIsNull(sw)) { - qreal minX = qMin(m_bounds.x1(), m_bounds.x2()); - qreal minY = qMin(m_bounds.y1(), m_bounds.y2()); - qreal maxX = qMax(m_bounds.x1(), m_bounds.x2()); - qreal maxY = qMax(m_bounds.y1(), m_bounds.y2()); - return QRectF(minX, minY, maxX-minX, maxY-minY); + QPointF p1 = p->transform().map(m_line.p1()); + QPointF p2 = p->transform().map(m_line.p2()); + qreal minX = qMin(p1.x(), p2.x()); + qreal minY = qMin(p1.y(), p2.y()); + qreal maxX = qMax(p1.x(), p2.x()); + qreal maxY = qMax(p1.y(), p2.y()); + return QRectF(minX, minY, maxX - minX, maxY - minY); } else { QPainterPath path; - path.moveTo(m_bounds.x1(), m_bounds.y1()); - path.lineTo(m_bounds.x2(), m_bounds.y2()); - return boundsOnStroke(path, sw); + path.moveTo(m_line.p1()); + path.lineTo(m_line.p2()); + return boundsOnStroke(p, path, sw); } } diff --git a/src/svg/qsvggraphics_p.h b/src/svg/qsvggraphics_p.h index ca06777..fdc770a 100644 --- a/src/svg/qsvggraphics_p.h +++ b/src/svg/qsvggraphics_p.h @@ -80,32 +80,27 @@ public: QSvgArc(QSvgNode *parent, const QPainterPath &path); virtual void draw(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - virtual QRectF bounds() const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; private: - QPainterPath cubic; - QRectF m_cachedBounds; + QPainterPath m_path; }; -class QSvgCircle : public QSvgNode +class QSvgEllipse : public QSvgNode { public: - QSvgCircle(QSvgNode *parent, const QRectF &rect); + QSvgEllipse(QSvgNode *parent, const QRectF &rect); virtual void draw(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - virtual QRectF bounds() const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; private: QRectF m_bounds; }; -class QSvgEllipse : public QSvgNode +class QSvgCircle : public QSvgEllipse { public: - QSvgEllipse(QSvgNode *parent, const QRectF &rect); - virtual void draw(QPainter *p, QSvgExtraStates &states); + QSvgCircle(QSvgNode *parent, const QRectF &rect) : QSvgEllipse(parent, rect) { } virtual Type type() const; - virtual QRectF bounds() const; -private: - QRectF m_bounds; }; class QSvgImage : public QSvgNode @@ -115,7 +110,7 @@ public: const QRect &bounds); virtual void draw(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - virtual QRectF bounds() const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; private: QImage m_image; QRect m_bounds; @@ -127,9 +122,9 @@ public: QSvgLine(QSvgNode *parent, const QLineF &line); virtual void draw(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - virtual QRectF bounds() const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; private: - QLineF m_bounds; + QLineF m_line; }; class QSvgPath : public QSvgNode @@ -138,14 +133,13 @@ public: QSvgPath(QSvgNode *parent, const QPainterPath &qpath); virtual void draw(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - virtual QRectF bounds() const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; QPainterPath *qpath() { return &m_path; } private: QPainterPath m_path; - mutable QRectF m_cachedBounds; }; class QSvgPolygon : public QSvgNode @@ -154,7 +148,7 @@ public: QSvgPolygon(QSvgNode *parent, const QPolygonF &poly); virtual void draw(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - virtual QRectF bounds() const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; private: QPolygonF m_poly; }; @@ -165,7 +159,7 @@ public: QSvgPolyline(QSvgNode *parent, const QPolygonF &poly); virtual void draw(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - virtual QRectF bounds() const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; private: QPolygonF m_poly; }; @@ -176,7 +170,7 @@ public: QSvgRect(QSvgNode *paren, const QRectF &rect, int rx=0, int ry=0); virtual Type type() const; virtual void draw(QPainter *p, QSvgExtraStates &states); - virtual QRectF bounds() const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; private: QRectF m_rect; int m_rx, m_ry; @@ -205,7 +199,7 @@ public: void addLineBreak() {m_tspans.append(LINEBREAK);} void setWhitespaceMode(WhitespaceMode mode) {m_mode = mode;} - //virtual QRectF bounds() const; + //virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; private: static QSvgTspan * const LINEBREAK; @@ -248,13 +242,11 @@ public: QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *link); virtual void draw(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - virtual QRectF bounds() const; - virtual QRectF transformedBounds(const QTransform &transform) const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; private: QSvgNode *m_link; QPointF m_start; - mutable QRectF m_bounds; }; class QSvgVideo : public QSvgNode diff --git a/src/svg/qsvgnode.cpp b/src/svg/qsvgnode.cpp index 86f2af5..f6bc1c0 100644 --- a/src/svg/qsvgnode.cpp +++ b/src/svg/qsvgnode.cpp @@ -45,6 +45,7 @@ #ifndef QT_NO_SVG #include "qdebug.h" +#include "qstack.h" QT_BEGIN_NAMESPACE @@ -114,12 +115,12 @@ void QSvgNode::appendStyleProperty(QSvgStyleProperty *prop, const QString &id) } } -void QSvgNode::applyStyle(QPainter *p, QSvgExtraStates &states) +void QSvgNode::applyStyle(QPainter *p, QSvgExtraStates &states) const { - m_style.apply(p, bounds(), this, states); + m_style.apply(p, this, states); } -void QSvgNode::revertStyle(QPainter *p, QSvgExtraStates &states) +void QSvgNode::revertStyle(QPainter *p, QSvgExtraStates &states) const { m_style.revert(p, states); } @@ -195,11 +196,40 @@ QSvgFillStyleProperty * QSvgNode::styleProperty(const QString &id) const return doc ? doc->namedStyle(rid) : 0; } -QRectF QSvgNode::bounds() const +QRectF QSvgNode::bounds(QPainter *, QSvgExtraStates &) const { return QRectF(0, 0, 0, 0); } +QRectF QSvgNode::transformedBounds() const +{ + if (!m_cachedBounds.isEmpty()) + return m_cachedBounds; + + QImage dummy(1, 1, QImage::Format_RGB32); + QPainter p(&dummy); + QSvgExtraStates states; + + QPen pen(Qt::NoBrush, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin); + pen.setMiterLimit(4); + p.setPen(pen); + + QStack parentApplyStack; + QSvgNode *parent = m_parent; + while (parent) { + parentApplyStack.push(parent); + parent = parent->parent(); + } + + for (int i = parentApplyStack.size() - 1; i >= 0; --i) + parentApplyStack[i]->applyStyle(&p, states); + + p.setWorldTransform(QTransform()); + + m_cachedBounds = transformedBounds(&p, states); + return m_cachedBounds; +} + QSvgTinyDocument * QSvgNode::document() const { QSvgTinyDocument *doc = 0; @@ -274,19 +304,11 @@ void QSvgNode::setVisible(bool visible) m_visible = visible; } -QRectF QSvgNode::transformedBounds(const QTransform &transform) const +QRectF QSvgNode::transformedBounds(QPainter *p, QSvgExtraStates &states) const { - QTransform t = transform; - - QSvgTransformStyle *transStyle = m_style.transform; - if (transStyle) { - t = transStyle->qtransform() * t; - } - - QRectF rect = bounds(); - - rect = t.mapRect(rect); - + applyStyle(p, states); + QRectF rect = bounds(p, states); + revertStyle(p, states); return rect; } @@ -310,15 +332,12 @@ QSvgNode::DisplayMode QSvgNode::displayMode() const return m_displayMode; } -qreal QSvgNode::strokeWidth() const +qreal QSvgNode::strokeWidth(QPainter *p) { - QSvgStrokeStyle *stroke = static_cast( - styleProperty(QSvgStyleProperty::STROKE)); - if (!stroke) - return 0; - if (stroke->stroke().brush().style() == Qt::NoBrush) + QPen pen = p->pen(); + if (pen.style() == Qt::NoPen || pen.brush().style() == Qt::NoBrush || pen.isCosmetic()) return 0; - return stroke->width(); + return pen.widthF(); } QT_END_NAMESPACE diff --git a/src/svg/qsvgnode_p.h b/src/svg/qsvgnode_p.h index 15466f2..a34c7c0 100644 --- a/src/svg/qsvgnode_p.h +++ b/src/svg/qsvgnode_p.h @@ -118,16 +118,17 @@ public: QSvgNode *parent() const; void appendStyleProperty(QSvgStyleProperty *prop, const QString &id); - void applyStyle(QPainter *p, QSvgExtraStates &states); - void revertStyle(QPainter *p, QSvgExtraStates &states); + void applyStyle(QPainter *p, QSvgExtraStates &states) const; + void revertStyle(QPainter *p, QSvgExtraStates &states) const; QSvgStyleProperty *styleProperty(QSvgStyleProperty::Type type) const; QSvgFillStyleProperty *styleProperty(const QString &id) const; QSvgTinyDocument *document() const; virtual Type type() const =0; - virtual QRectF bounds() const; - virtual QRectF transformedBounds(const QTransform &transform) const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; + virtual QRectF transformedBounds(QPainter *p, QSvgExtraStates &states) const; + QRectF transformedBounds() const; void setRequiredFeatures(const QStringList &lst); const QStringList & requiredFeatures() const; @@ -156,9 +157,9 @@ public: QString xmlClass() const; void setXmlClass(const QString &str); protected: - QSvgStyle m_style; + mutable QSvgStyle m_style; - qreal strokeWidth() const; + static qreal strokeWidth(QPainter *p); private: QSvgNode *m_parent; @@ -174,6 +175,7 @@ private: QString m_class; DisplayMode m_displayMode; + mutable QRectF m_cachedBounds; friend class QSvgTinyDocument; }; diff --git a/src/svg/qsvgstructure.cpp b/src/svg/qsvgstructure.cpp index 34426b7..db5cb9e 100644 --- a/src/svg/qsvgstructure.cpp +++ b/src/svg/qsvgstructure.cpp @@ -357,15 +357,12 @@ void QSvgSwitch::init() m_systemLanguagePrefix = m_systemLanguage.mid(0, idx); } -QRectF QSvgStructureNode::bounds() const +QRectF QSvgStructureNode::bounds(QPainter *p, QSvgExtraStates &states) const { - if (m_bounds.isEmpty()) { - foreach(QSvgNode *node, m_renderers) { - m_bounds |= node->transformedBounds(QTransform()); - } - } - - return m_bounds; + QRectF bounds; + foreach(QSvgNode *node, m_renderers) + bounds |= node->transformedBounds(p, states); + return bounds; } QSvgNode * QSvgStructureNode::previousSiblingNode(QSvgNode *n) const diff --git a/src/svg/qsvgstructure_p.h b/src/svg/qsvgstructure_p.h index fd6eb0a..dd82fc0 100644 --- a/src/svg/qsvgstructure_p.h +++ b/src/svg/qsvgstructure_p.h @@ -74,14 +74,13 @@ public: ~QSvgStructureNode(); QSvgNode *scopeNode(const QString &id) const; void addChild(QSvgNode *child, const QString &id); - virtual QRectF bounds() const; + virtual QRectF bounds(QPainter *p, QSvgExtraStates &states) const; QSvgNode *previousSiblingNode(QSvgNode *n) const; QList renderers() const { return m_renderers; } protected: QList m_renderers; QHash m_scope; QList m_linkedScopes; - mutable QRectF m_bounds; }; class QSvgG : public QSvgStructureNode diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp index 2b12c49..0d1bad9 100644 --- a/src/svg/qsvgstyle.cpp +++ b/src/svg/qsvgstyle.cpp @@ -73,7 +73,7 @@ QSvgStyleProperty::~QSvgStyleProperty() { } -void QSvgFillStyleProperty::apply(QPainter *, const QRectF &, QSvgNode *, QSvgExtraStates &) +void QSvgFillStyleProperty::apply(QPainter *, const QSvgNode *, QSvgExtraStates &) { Q_ASSERT(!"This should not be called!"); } @@ -89,7 +89,7 @@ QSvgQualityStyle::QSvgQualityStyle(int color) { } -void QSvgQualityStyle::apply(QPainter *, const QRectF &, QSvgNode *, QSvgExtraStates &) +void QSvgQualityStyle::apply(QPainter *, const QSvgNode *, QSvgExtraStates &) { } @@ -136,7 +136,7 @@ void QSvgFillStyle::setBrush(QBrush brush) m_fillSet = 1; } -void QSvgFillStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states) +void QSvgFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) { m_oldFill = p->brush(); m_oldFillRule = states.fillRule; @@ -169,7 +169,7 @@ QSvgViewportFillStyle::QSvgViewportFillStyle(const QBrush &brush) { } -void QSvgViewportFillStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) +void QSvgViewportFillStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) { m_oldFill = p->brush(); p->setBrush(m_viewportFill); @@ -224,7 +224,7 @@ int QSvgFontStyle::SVGToQtWeight(int weight) { return QFont::Normal; } -void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states) +void QSvgFontStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) { m_oldQFont = p->font(); m_oldSvgFont = states.svgFont; @@ -292,7 +292,7 @@ QSvgStrokeStyle::QSvgStrokeStyle() { } -void QSvgStrokeStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states) +void QSvgStrokeStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &states) { m_oldStroke = p->pen(); m_oldStrokeOpacity = states.strokeOpacity; @@ -443,7 +443,7 @@ QSvgTransformStyle::QSvgTransformStyle(const QTransform &trans) { } -void QSvgTransformStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) +void QSvgTransformStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) { m_oldWorldTransform = p->worldTransform(); p->setWorldTransform(m_transform, true); @@ -501,7 +501,7 @@ QSvgCompOpStyle::QSvgCompOpStyle(QPainter::CompositionMode mode) } -void QSvgCompOpStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) +void QSvgCompOpStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) { m_oldMode = p->compositionMode(); p->setCompositionMode(m_mode); @@ -521,34 +521,34 @@ QSvgStyle::~QSvgStyle() { } -void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtraStates &states) +void QSvgStyle::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) { if (quality) { - quality->apply(p, rect, node, states); + quality->apply(p, node, states); } if (fill) { - fill->apply(p, rect, node, states); + fill->apply(p, node, states); } if (viewportFill) { - viewportFill->apply(p, rect, node, states); + viewportFill->apply(p, node, states); } if (font) { - font->apply(p, rect, node, states); + font->apply(p, node, states); } if (stroke) { - stroke->apply(p, rect, node, states); + stroke->apply(p, node, states); } if (transform) { - transform->apply(p, rect, node, states); + transform->apply(p, node, states); } if (animateColor) { - animateColor->apply(p, rect, node, states); + animateColor->apply(p, node, states); } //animated transforms have to be applied @@ -572,16 +572,16 @@ void QSvgStyle::apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtra // Apply the animateTransforms after and including the last one with additive="replace". for (; itr != animateTransforms.constEnd(); ++itr) { if ((*itr)->animActive(totalTimeElapsed)) - (*itr)->apply(p, rect, node, states); + (*itr)->apply(p, node, states); } } if (opacity) { - opacity->apply(p, rect, node, states); + opacity->apply(p, node, states); } if (compop) { - compop->apply(p, rect, node, states); + compop->apply(p, node, states); } } @@ -655,7 +655,7 @@ void QSvgAnimateTransform::setArgs(TransformType type, Additive additive, const m_count = args.count() / 3; } -void QSvgAnimateTransform::apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &) +void QSvgAnimateTransform::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &) { m_oldWorldTransform = p->worldTransform(); resolveMatrix(node); @@ -669,7 +669,7 @@ void QSvgAnimateTransform::revert(QPainter *p, QSvgExtraStates &) m_transformApplied = false; } -void QSvgAnimateTransform::resolveMatrix(QSvgNode *node) +void QSvgAnimateTransform::resolveMatrix(const QSvgNode *node) { static const qreal deg2rad = qreal(0.017453292519943295769); qreal totalTimeElapsed = node->document()->currentElapsed(); @@ -834,7 +834,7 @@ void QSvgAnimateColor::setRepeatCount(qreal repeatCount) m_repeatCount = repeatCount; } -void QSvgAnimateColor::apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &) +void QSvgAnimateColor::apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &) { qreal totalTimeElapsed = node->document()->currentElapsed(); if (totalTimeElapsed < m_from || m_finished) @@ -912,7 +912,7 @@ QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity) } -void QSvgOpacityStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) +void QSvgOpacityStyle::apply(QPainter *p, const QSvgNode *, QSvgExtraStates &) { m_oldOpacity = p->opacity(); p->setOpacity(m_opacity * m_oldOpacity); diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h index 202de93..af3b4e5 100644 --- a/src/svg/qsvgstyle_p.h +++ b/src/svg/qsvgstyle_p.h @@ -172,7 +172,7 @@ public: }; public: virtual ~QSvgStyleProperty(); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states) =0; + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states) = 0; virtual void revert(QPainter *p, QSvgExtraStates &states) =0; virtual Type type() const=0; }; @@ -181,7 +181,7 @@ class QSvgFillStyleProperty : public QSvgStyleProperty { public: virtual QBrush brush(QPainter *p, QSvgExtraStates &states) = 0; - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); }; @@ -189,7 +189,7 @@ class QSvgQualityStyle : public QSvgStyleProperty { public: QSvgQualityStyle(int color); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; private: @@ -221,7 +221,7 @@ class QSvgOpacityStyle : public QSvgStyleProperty { public: QSvgOpacityStyle(qreal opacity); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; private: @@ -233,7 +233,7 @@ class QSvgFillStyle : public QSvgStyleProperty { public: QSvgFillStyle(); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; @@ -306,7 +306,7 @@ class QSvgViewportFillStyle : public QSvgStyleProperty { public: QSvgViewportFillStyle(const QBrush &brush); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; @@ -330,7 +330,7 @@ public: QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc); QSvgFontStyle(); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; @@ -410,7 +410,7 @@ class QSvgStrokeStyle : public QSvgStyleProperty { public: QSvgStrokeStyle(); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; @@ -617,7 +617,7 @@ class QSvgTransformStyle : public QSvgStyleProperty { public: QSvgTransformStyle(const QTransform &transform); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; @@ -654,7 +654,7 @@ public: void setArgs(TransformType type, Additive additive, const QVector &args); void setFreeze(bool freeze); void setRepeatCount(qreal repeatCount); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; QSvgAnimateTransform::Additive additiveType() const @@ -688,7 +688,7 @@ public: } protected: - void resolveMatrix(QSvgNode *node); + void resolveMatrix(const QSvgNode *node); private: qreal m_from, m_to, m_by; qreal m_totalRunningTime; @@ -712,7 +712,7 @@ public: void setArgs(bool fill, const QList &colors); void setFreeze(bool freeze); void setRepeatCount(qreal repeatCount); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; private: @@ -732,7 +732,7 @@ class QSvgCompOpStyle : public QSvgStyleProperty { public: QSvgCompOpStyle(QPainter::CompositionMode mode); - virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); + virtual void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; @@ -766,7 +766,7 @@ public: {} ~QSvgStyle(); - void apply(QPainter *p, const QRectF &rect, QSvgNode *node, QSvgExtraStates &states); + void apply(QPainter *p, const QSvgNode *node, QSvgExtraStates &states); void revert(QPainter *p, QSvgExtraStates &states); QSvgRefCounter quality; QSvgRefCounter fill; diff --git a/src/svg/qsvgtinydocument.cpp b/src/svg/qsvgtinydocument.cpp index 17618f7..b21b99f 100644 --- a/src/svg/qsvgtinydocument.cpp +++ b/src/svg/qsvgtinydocument.cpp @@ -277,7 +277,7 @@ void QSvgTinyDocument::draw(QPainter *p, const QString &id, p->save(); - const QRectF elementBounds = node->transformedBounds(QTransform()); + const QRectF elementBounds = node->transformedBounds(); mapSourceToTarget(p, bounds, elementBounds); QTransform originalTransform = p->worldTransform(); @@ -299,7 +299,7 @@ void QSvgTinyDocument::draw(QPainter *p, const QString &id, for (int i = parentApplyStack.size() - 1; i >= 0; --i) parentApplyStack[i]->applyStyle(p, m_states); - + // Reset the world transform so that our parents don't affect // the position QTransform currentTransform = p->worldTransform(); @@ -432,8 +432,7 @@ QRectF QSvgTinyDocument::boundsOnElement(const QString &id) const const QSvgNode *node = scopeNode(id); if (!node) node = this; - - return node->transformedBounds(QTransform()); + return node->transformedBounds(); } bool QSvgTinyDocument::elementExists(const QString &id) const diff --git a/src/svg/qsvgtinydocument_p.h b/src/svg/qsvgtinydocument_p.h index c03c798..3b40770 100644 --- a/src/svg/qsvgtinydocument_p.h +++ b/src/svg/qsvgtinydocument_p.h @@ -173,9 +173,8 @@ inline bool QSvgTinyDocument::heightPercent() const inline QRectF QSvgTinyDocument::viewBox() const { - if (m_viewBox.isNull()) { - m_viewBox = transformedBounds(QTransform()); - } + if (m_viewBox.isNull()) + m_viewBox = transformedBounds(); return m_viewBox; } diff --git a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp index 16dd2b4..106fd8c 100644 --- a/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +++ b/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp @@ -74,6 +74,7 @@ private slots: void nestedQXmlStreamReader() const; void stylePropagation() const; void matrixForElement() const; + void boundsOnElement() const; void gradientStops() const; void gradientRefs(); void fillRule(); @@ -470,6 +471,36 @@ void tst_QSvgRenderer::matrixForElement() const compareTransforms(QTransform(painter.worldMatrix()), QTransform(renderer.matrixForElement(QLatin1String("firkant")))); } +void tst_QSvgRenderer::boundsOnElement() const +{ + QByteArray data("" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""); + + qreal sqrt2 = qSqrt(2); + QSvgRenderer renderer(data); + QCOMPARE(renderer.boundsOnElement(QLatin1String("sjokade")), QRectF(-10 * sqrt2, -10 * sqrt2, 20 * sqrt2, 20 * sqrt2)); + QCOMPARE(renderer.boundsOnElement(QLatin1String("kaviar")), QRectF(-10 * sqrt2, -10 * sqrt2, 20 * sqrt2, 20 * sqrt2)); + QCOMPARE(renderer.boundsOnElement(QLatin1String("nugatti")), QRectF(-11, -11, 22, 22)); + QCOMPARE(renderer.boundsOnElement(QLatin1String("nutella")), QRectF(-10 * sqrt2, -10 * sqrt2, 20 * sqrt2, 20 * sqrt2)); + QCOMPARE(renderer.boundsOnElement(QLatin1String("baconost")), QRectF(-10 * sqrt2, -10 * sqrt2, 20 * sqrt2, 20 * sqrt2)); + QCOMPARE(renderer.boundsOnElement(QLatin1String("hapaa")), QRectF(-13, -9, 22, 22)); + QCOMPARE(renderer.boundsOnElement(QLatin1String("prim")), QRectF(-10 * sqrt2 - 3, -10 * sqrt2 + 1, 20 * sqrt2, 20 * sqrt2)); +} + void tst_QSvgRenderer::gradientStops() const { { -- cgit v0.12