qt5base-lts/tests/benchmarks/gui/painting/qtbench/benchmarktests.h
Eskil Abrahamsen Blomfeldt 4d88d79aa5 Update usage of QFontMetrics::width() to new API
QFontMetrics(F)::width() has been deprecated and is replaced by
horizontalAdvance(). This updates all usage of it in tests and
documentation.

It is worth noting that many or most of the usages of
QFontMetrics::width() probably intended to use boundingRect().width(),
but since it currently works, I have not looked into that, just
replaced the function name mechanically.

Change-Id: Iec382e5bad0b50f37a6cfff841bfb46ed4d4555f
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
2017-12-08 15:06:32 +00:00

807 lines
23 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the FOO module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef BENCHMARKTESTS_H
#define BENCHMARKTESTS_H
#include <QApplication>
#include <QTextDocument>
#include <QDesktopWidget>
#include <QTextLayout>
#include <QFontMetrics>
#include <QDebug>
#include <QStaticText>
#include <QPainter>
#include <QRandomGenerator>
class Benchmark
{
public:
virtual ~Benchmark() {}
Benchmark(const QSize &size)
: m_size(size)
{
for (int i=0; i<16; ++i) {
m_colors[i] = QColor::fromRgbF((QRandomGenerator::global()->bounded(4)) / 3.0,
(QRandomGenerator::global()->bounded(4)) / 3.0,
(QRandomGenerator::global()->bounded(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 = QRandomGenerator::global()->bounded(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(asPixmap ? "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(asPixmap ? "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(asPixmap ? "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,
StaticTextMode,
StaticTextWithMaximumSizeMode,
StaticTextBackendOptimizations
};
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) {
m_staticTexts.clear();
m_currentStaticText = 0;
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<lines.size(); ++i) {
QTextLine textLine = m_layout.createLine();
if (textLine.isValid()) {
textLine.setLineWidth(r.width());
textLine.setPosition(QPointF(0, height));
height += leading + textLine.height();
}
}
m_layout.endLayout();
m_layout.setCacheEnabled(true);
m_size = m_layout.boundingRect().toRect().size();
break; }
case StaticTextWithMaximumSizeMode: {
QStaticText staticText;
m_size = (p->boundingRect(m_bounds, 0, m_text)).size();
staticText.setTextWidth(m_size.width() + 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; i<iterations; ++i) {
QStaticText staticText;
staticText.setPerformanceHint(QStaticText::AggressiveCaching);
staticText.setTextWidth(m_size.width() + 10);
staticText.setText(m_text);
staticText.prepare(p->transform(), 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.horizontalAdvance(m_text, m_text.length()), fm.height());
break;
}
case PainterQPointMode: {
QFontMetrics fm(p->font());
m_size = QSize(fm.horizontalAdvance(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;
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;
}
}
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;
case StaticTextMode: type = "drawStaticText()"; break;
case StaticTextWithMaximumSizeMode: type = "drawStaticText() w/ maxsize"; break;
case StaticTextBackendOptimizations: type = "drawStaticText() w/ backend optimizations"; break;
}
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<QPixmap> m_pixmaps;
int m_currentPixmap;
int m_currentStaticText;
QList<QStaticText> m_staticTexts;
};
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