Brush up the mandelbrot example

The example refines the image by running a number of passes
with increasing number of iterations, which is not really
visible to the user. Set an informational text string on
the generated image which provides this information
along with the elapsed time.
The idea is to do the same to the corresponding
Qt for Python example to have some sort of speed comparison
for number crunching.

Add a command line option for the number of passes.

Make the window a bit larger to accommodate the
information.

Change-Id: I2afc1009ab53b580123d82a6aa645d9ffaa63ea2
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
This commit is contained in:
Friedemann Kleint 2021-04-07 22:14:23 +02:00
parent 2df3d8ed41
commit 0e69349f6f
5 changed files with 77 additions and 12 deletions

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the examples of the Qt Toolkit. ** This file is part of the examples of the Qt Toolkit.
@ -52,11 +52,43 @@
#include <QApplication> #include <QApplication>
#include <QScreen>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QDebug>
#include <QRect>
//! [0] //! [0]
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
QCommandLineParser parser;
parser.setApplicationDescription("Qt Mandelbrot Example");
parser.addHelpOption();
parser.addVersionOption();
QCommandLineOption passesOption("passes", "Number of passes (1-8)", "passes");
parser.addOption(passesOption);
parser.process(app);
if (parser.isSet(passesOption)) {
const auto passesStr = parser.value(passesOption);
bool ok;
const int passes = passesStr.toInt(&ok);
if (!ok || passes < 1 || passes > 8) {
qWarning() << "Invalid value:" << passesStr;
return -1;
}
RenderThread::setNumPasses(passes);
}
MandelbrotWidget widget; MandelbrotWidget widget;
const auto geometry = widget.screen()->availableGeometry();
widget.resize((2 * geometry.size()) / 3);
const auto pos = (geometry.size() - widget.size()) / 2;
widget.move(geometry.topLeft() + QPoint(pos.width(), pos.height()));
widget.show(); widget.show();
return app.exec(); return app.exec();
} }

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the examples of the Qt Toolkit. ** This file is part of the examples of the Qt Toolkit.
@ -73,6 +73,8 @@ MandelbrotWidget::MandelbrotWidget(QWidget *parent) :
pixmapScale(DefaultScale), pixmapScale(DefaultScale),
curScale(DefaultScale) curScale(DefaultScale)
{ {
help = tr("Use mouse wheel or the '+' and '-' keys to zoom. "
"Press and hold left mouse button to scroll.");
connect(&thread, &RenderThread::renderedImage, connect(&thread, &RenderThread::renderedImage,
this, &MandelbrotWidget::updatePixmap); this, &MandelbrotWidget::updatePixmap);
@ -80,8 +82,6 @@ MandelbrotWidget::MandelbrotWidget(QWidget *parent) :
#if QT_CONFIG(cursor) #if QT_CONFIG(cursor)
setCursor(Qt::CrossCursor); setCursor(Qt::CrossCursor);
#endif #endif
resize(550, 400);
} }
//! [1] //! [1]
@ -127,8 +127,9 @@ void MandelbrotWidget::paintEvent(QPaintEvent * /* event */)
} }
//! [8] //! [9] //! [8] //! [9]
QString text = tr("Use mouse wheel or the '+' and '-' keys to zoom. " QString text = help;
"Press and hold left mouse button to scroll."); if (!info.isEmpty())
text += ' ' + info;
QFontMetrics metrics = painter.fontMetrics(); QFontMetrics metrics = painter.fontMetrics();
int textWidth = metrics.horizontalAdvance(text); int textWidth = metrics.horizontalAdvance(text);
@ -169,6 +170,9 @@ void MandelbrotWidget::keyPressEvent(QKeyEvent *event)
case Qt::Key_Up: case Qt::Key_Up:
scroll(0, +ScrollStep); scroll(0, +ScrollStep);
break; break;
case Qt::Key_Q:
close();
break;
default: default:
QWidget::keyPressEvent(event); QWidget::keyPressEvent(event);
} }
@ -226,6 +230,8 @@ void MandelbrotWidget::updatePixmap(const QImage &image, double scaleFactor)
if (!lastDragPos.isNull()) if (!lastDragPos.isNull())
return; return;
info = image.text(RenderThread::infoKey());
pixmap = QPixmap::fromImage(image); pixmap = QPixmap::fromImage(image);
pixmapOffset = QPoint(); pixmapOffset = QPoint();
lastDragPos = QPoint(); lastDragPos = QPoint();

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the examples of the Qt Toolkit. ** This file is part of the examples of the Qt Toolkit.
@ -86,6 +86,8 @@ private:
QPixmap pixmap; QPixmap pixmap;
QPoint pixmapOffset; QPoint pixmapOffset;
QPoint lastDragPos; QPoint lastDragPos;
QString help;
QString info;
double centerX; double centerX;
double centerY; double centerY;
double pixmapScale; double pixmapScale;

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the examples of the Qt Toolkit. ** This file is part of the examples of the Qt Toolkit.
@ -51,8 +51,14 @@
#include "renderthread.h" #include "renderthread.h"
#include <QImage> #include <QImage>
#include <QElapsedTimer>
#include <QTextStream>
#include <cmath> #include <cmath>
int RenderThread::numPasses = 8;
//! [0] //! [0]
RenderThread::RenderThread(QObject *parent) RenderThread::RenderThread(QObject *parent)
: QThread(parent) : QThread(parent)
@ -98,6 +104,7 @@ void RenderThread::render(double centerX, double centerY, double scaleFactor,
//! [3] //! [3]
void RenderThread::run() void RenderThread::run()
{ {
QElapsedTimer timer;
forever { forever {
mutex.lock(); mutex.lock();
const double devicePixelRatio = this->devicePixelRatio; const double devicePixelRatio = this->devicePixelRatio;
@ -116,13 +123,14 @@ void RenderThread::run()
QImage image(resultSize, QImage::Format_RGB32); QImage image(resultSize, QImage::Format_RGB32);
image.setDevicePixelRatio(devicePixelRatio); image.setDevicePixelRatio(devicePixelRatio);
const int NumPasses = 8;
int pass = 0; int pass = 0;
while (pass < NumPasses) { while (pass < numPasses) {
const int MaxIterations = (1 << (2 * pass + 6)) + 32; const int MaxIterations = (1 << (2 * pass + 6)) + 32;
const int Limit = 4; const int Limit = 4;
bool allBlack = true; bool allBlack = true;
timer.restart();
for (int y = -halfHeight; y < halfHeight; ++y) { for (int y = -halfHeight; y < halfHeight; ++y) {
if (restart) if (restart)
break; break;
@ -165,8 +173,20 @@ void RenderThread::run()
if (allBlack && pass == 0) { if (allBlack && pass == 0) {
pass = 4; pass = 4;
} else { } else {
if (!restart) if (!restart) {
QString message;
QTextStream str(&message);
str << " Pass " << (pass + 1) << '/' << numPasses
<< ", max iterations: " << MaxIterations << ", time: ";
const auto elapsed = timer.elapsed();
if (elapsed > 2000)
str << (elapsed / 1000) << 's';
else
str << elapsed << "ms";
image.setText(infoKey(), message);
emit renderedImage(image, requestedScaleFactor); emit renderedImage(image, requestedScaleFactor);
}
//! [5] //! [6] //! [5] //! [6]
++pass; ++pass;
} }

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the examples of the Qt Toolkit. ** This file is part of the examples of the Qt Toolkit.
@ -72,6 +72,10 @@ public:
void render(double centerX, double centerY, double scaleFactor, QSize resultSize, void render(double centerX, double centerY, double scaleFactor, QSize resultSize,
double devicePixelRatio); double devicePixelRatio);
static void setNumPasses(int n) { numPasses = n; }
static QString infoKey() { return QStringLiteral("info"); }
signals: signals:
void renderedImage(const QImage &image, double scaleFactor); void renderedImage(const QImage &image, double scaleFactor);
@ -88,6 +92,7 @@ private:
double scaleFactor; double scaleFactor;
double devicePixelRatio; double devicePixelRatio;
QSize resultSize; QSize resultSize;
static int numPasses;
bool restart = false; bool restart = false;
bool abort = false; bool abort = false;