qt6windows7/tests/benchmarks/gui/painting/qtbench/benchmarktests.h

795 lines
22 KiB
C
Raw Normal View History

2023-10-30 06:33:08 +08:00
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef BENCHMARKTESTS_H
#define BENCHMARKTESTS_H
#include <QApplication>
#include <QTextDocument>
#include <QTextLayout>
#include <QFontMetrics>
#include <QDebug>
#include <QStaticText>
#include <QPainter>
#include <QPainterPath>
#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))
{
}
void draw(QPainter *p, const QRect &rect, int iterationCount) override
{
p->fillRect(rect, randomColor(iterationCount));
}
QString name() const override
{
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);
}
void draw(QPainter *p, const QRect &rect, int) override { p->fillRect(rect, m_brush); }
QString name() const override
{
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))
{
}
void begin(QPainter *p, int) override
{
p->setPen(Qt::NoPen);
p->setBrush(randomColor(m_size.width()));
}
void draw(QPainter *p, const QRect &rect, int) override { p->drawRect(rect); }
QString name() const override
{
return QString::fromLatin1("drawRect(%1)").arg(m_size.width());
}
};
class DrawRectWithBrushChangeBenchmark : public Benchmark
{
public:
DrawRectWithBrushChangeBenchmark(int size)
: Benchmark(QSize(size, size))
{
}
void begin(QPainter *p, int) override { p->setPen(Qt::NoPen); }
void draw(QPainter *p, const QRect &rect, int i) override
{
p->setBrush(randomColor(i));
p->drawRect(rect);
}
QString name() const override
{
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.;
}
void begin(QPainter *p, int) override
{
p->setPen(Qt::NoPen);
p->setBrush(Qt::red);
}
void draw(QPainter *p, const QRect &rect, int) override
{
p->drawRoundedRect(rect, m_roundness, m_roundness);
}
QString name() const override
{
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)
{
}
void begin(QPainter *p, int) override
{
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);
}
void draw(QPainter *p, const QRect &rect, int) override
{
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;
}
}
QString name() const override
{
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)
{
}
void begin(QPainter *p, int) override { p->scale(m_scale, m_scale); }
void draw(QPainter *p, const QRect &rect, int) override
{
if (m_as_pixmap)
p->drawPixmap(rect.topLeft(), m_pixmap);
else
p->drawImage(rect.topLeft(), m_image);
}
QString name() const override
{
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)
{
}
void draw(QPainter *p, const QRect &rect, int) override
{
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);
}
QString name() const override
{
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)
{
}
void draw(QPainter *p, const QRect &rect, int) override
{
if (m_as_pixmap)
p->drawPixmap(rect.topLeft(), m_pixmap);
else
p->drawImage(rect.topLeft(), m_image);
}
QString name() const override
{
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)
{
}
void begin(QPainter *p, int iterations) override
{
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.size()), fm.height());
break;
}
case PainterQPointMode: {
QFontMetrics fm(p->font());
m_size = QSize(fm.horizontalAdvance(m_text, m_text.size()), fm.height());
break;
}
}
}
void draw(QPainter *p, const QRect &rect, int) override
{
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;
}
}
QString name() const override
{
int letters = m_text.size();
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)
{
}
void begin(QPainter *p, int) override
{
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;
}
}
void draw(QPainter *p, const QRect &rect, int) override { p->drawRect(rect); }
QString name() const override
{
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)
{
}
void draw(QPainter *p, const QRect &rect, int) override
{
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;
}
}
QString name() const override
{
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