Mandelbrot Example: Use High DPI scaling
Create the pixmap with a device pixel ratio set. Change-Id: I7f7e90aec4d117304852f050be70e14a0c6bf69d Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
parent
2c390e85cf
commit
a719c630f1
@ -187,6 +187,10 @@
|
||||
generate more and more precise (and computationally expensive)
|
||||
approximations of the fractal.
|
||||
|
||||
We create a high resolution pixmap by applying the device
|
||||
pixel ratio to the target size (see
|
||||
\l{Drawing High Resolution Versions of Pixmaps and Images}).
|
||||
|
||||
If we discover inside the loop that \c restart has been set to \c
|
||||
true (by \c render()), we break out of the loop immediately, so
|
||||
that the control quickly returns to the very top of the outer
|
||||
@ -273,12 +277,21 @@
|
||||
\snippet threads/mandelbrot/mandelbrotwidget.cpp 8
|
||||
|
||||
If the pixmap has the right scale factor, we draw the pixmap directly onto
|
||||
the widget. Otherwise, we scale and translate the \l{Coordinate
|
||||
System}{coordinate system} before we draw the pixmap. By reverse mapping
|
||||
the widget's rectangle using the scaled painter matrix, we also make sure
|
||||
that only the exposed areas of the pixmap are drawn. The calls to
|
||||
QPainter::save() and QPainter::restore() make sure that any painting
|
||||
performed afterwards uses the standard coordinate system.
|
||||
the widget.
|
||||
|
||||
Otherwise, we create a preview pixmap to be shown until the calculation
|
||||
finishes and translate the \l{Coordinate System}{coordinate system}
|
||||
accordingly.
|
||||
|
||||
Since we are going to use transformations on the painter
|
||||
and use an overload of QPainter::drawPixmap() that does not support
|
||||
high resolution pixmaps in that case, we create a pixmap with device pixel
|
||||
ratio 1.
|
||||
|
||||
By reverse mapping the widget's rectangle using the scaled painter matrix,
|
||||
we also make sure that only the exposed areas of the pixmap are drawn.
|
||||
The calls to QPainter::save() and QPainter::restore() make sure that any
|
||||
painting performed afterwards uses the standard coordinate system.
|
||||
|
||||
\snippet threads/mandelbrot/mandelbrotwidget.cpp 9
|
||||
|
||||
|
@ -55,6 +55,8 @@
|
||||
//! [0]
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
QApplication app(argc, argv);
|
||||
MandelbrotWidget widget;
|
||||
widget.show();
|
||||
|
@ -107,18 +107,22 @@ void MandelbrotWidget::paintEvent(QPaintEvent * /* event */)
|
||||
//! [6] //! [7]
|
||||
} else {
|
||||
//! [7] //! [8]
|
||||
auto previewPixmap = qFuzzyCompare(pixmap.devicePixelRatioF(), qreal(1))
|
||||
? pixmap
|
||||
: pixmap.scaled(pixmap.size() / pixmap.devicePixelRatioF(), Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
double scaleFactor = pixmapScale / curScale;
|
||||
int newWidth = int(pixmap.width() * scaleFactor);
|
||||
int newHeight = int(pixmap.height() * scaleFactor);
|
||||
int newX = pixmapOffset.x() + (pixmap.width() - newWidth) / 2;
|
||||
int newY = pixmapOffset.y() + (pixmap.height() - newHeight) / 2;
|
||||
int newWidth = int(previewPixmap.width() * scaleFactor);
|
||||
int newHeight = int(previewPixmap.height() * scaleFactor);
|
||||
int newX = pixmapOffset.x() + (previewPixmap.width() - newWidth) / 2;
|
||||
int newY = pixmapOffset.y() + (previewPixmap.height() - newHeight) / 2;
|
||||
|
||||
painter.save();
|
||||
painter.translate(newX, newY);
|
||||
painter.scale(scaleFactor, scaleFactor);
|
||||
|
||||
QRectF exposed = painter.transform().inverted().mapRect(rect()).adjusted(-1, -1, 1, 1);
|
||||
painter.drawPixmap(exposed, pixmap, exposed);
|
||||
painter.drawPixmap(exposed, previewPixmap, exposed);
|
||||
painter.restore();
|
||||
}
|
||||
//! [8] //! [9]
|
||||
@ -139,7 +143,7 @@ void MandelbrotWidget::paintEvent(QPaintEvent * /* event */)
|
||||
//! [10]
|
||||
void MandelbrotWidget::resizeEvent(QResizeEvent * /* event */)
|
||||
{
|
||||
thread.render(centerX, centerY, curScale, size());
|
||||
thread.render(centerX, centerY, curScale, size(), devicePixelRatioF());
|
||||
}
|
||||
//! [10]
|
||||
|
||||
@ -208,8 +212,9 @@ void MandelbrotWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
pixmapOffset += event->pos() - lastDragPos;
|
||||
lastDragPos = QPoint();
|
||||
|
||||
int deltaX = (width() - pixmap.width()) / 2 - pixmapOffset.x();
|
||||
int deltaY = (height() - pixmap.height()) / 2 - pixmapOffset.y();
|
||||
const auto pixmapSize = pixmap.size() / pixmap.devicePixelRatioF();
|
||||
int deltaX = (width() - pixmapSize.width()) / 2 - pixmapOffset.x();
|
||||
int deltaY = (height() - pixmapSize.height()) / 2 - pixmapOffset.y();
|
||||
scroll(deltaX, deltaY);
|
||||
}
|
||||
}
|
||||
@ -234,7 +239,7 @@ void MandelbrotWidget::zoom(double zoomFactor)
|
||||
{
|
||||
curScale *= zoomFactor;
|
||||
update();
|
||||
thread.render(centerX, centerY, curScale, size());
|
||||
thread.render(centerX, centerY, curScale, size(), devicePixelRatioF());
|
||||
}
|
||||
//! [17]
|
||||
|
||||
@ -244,6 +249,6 @@ void MandelbrotWidget::scroll(int deltaX, int deltaY)
|
||||
centerX += deltaX * curScale;
|
||||
centerY += deltaY * curScale;
|
||||
update();
|
||||
thread.render(centerX, centerY, curScale, size());
|
||||
thread.render(centerX, centerY, curScale, size(), devicePixelRatioF());
|
||||
}
|
||||
//! [18]
|
||||
|
@ -76,13 +76,14 @@ RenderThread::~RenderThread()
|
||||
|
||||
//! [2]
|
||||
void RenderThread::render(double centerX, double centerY, double scaleFactor,
|
||||
QSize resultSize)
|
||||
QSize resultSize, double devicePixelRatio)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
|
||||
this->centerX = centerX;
|
||||
this->centerY = centerY;
|
||||
this->scaleFactor = scaleFactor;
|
||||
this->devicePixelRatio = devicePixelRatio;
|
||||
this->resultSize = resultSize;
|
||||
|
||||
if (!isRunning()) {
|
||||
@ -99,8 +100,10 @@ void RenderThread::run()
|
||||
{
|
||||
forever {
|
||||
mutex.lock();
|
||||
const QSize resultSize = this->resultSize;
|
||||
const double scaleFactor = this->scaleFactor;
|
||||
const double devicePixelRatio = this->devicePixelRatio;
|
||||
const QSize resultSize = this->resultSize * devicePixelRatio;
|
||||
const double requestedScaleFactor = this->scaleFactor;
|
||||
const double scaleFactor = requestedScaleFactor / devicePixelRatio;
|
||||
const double centerX = this->centerX;
|
||||
const double centerY = this->centerY;
|
||||
mutex.unlock();
|
||||
@ -111,6 +114,7 @@ void RenderThread::run()
|
||||
//! [4] //! [5]
|
||||
int halfHeight = resultSize.height() / 2;
|
||||
QImage image(resultSize, QImage::Format_RGB32);
|
||||
image.setDevicePixelRatio(devicePixelRatio);
|
||||
|
||||
const int NumPasses = 8;
|
||||
int pass = 0;
|
||||
@ -162,7 +166,7 @@ void RenderThread::run()
|
||||
pass = 4;
|
||||
} else {
|
||||
if (!restart)
|
||||
emit renderedImage(image, scaleFactor);
|
||||
emit renderedImage(image, requestedScaleFactor);
|
||||
//! [5] //! [6]
|
||||
++pass;
|
||||
}
|
||||
|
@ -69,7 +69,8 @@ public:
|
||||
RenderThread(QObject *parent = nullptr);
|
||||
~RenderThread();
|
||||
|
||||
void render(double centerX, double centerY, double scaleFactor, QSize resultSize);
|
||||
void render(double centerX, double centerY, double scaleFactor, QSize resultSize,
|
||||
double devicePixelRatio);
|
||||
|
||||
signals:
|
||||
void renderedImage(const QImage &image, double scaleFactor);
|
||||
@ -85,6 +86,7 @@ private:
|
||||
double centerX;
|
||||
double centerY;
|
||||
double scaleFactor;
|
||||
double devicePixelRatio;
|
||||
QSize resultSize;
|
||||
bool restart = false;
|
||||
bool abort = false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user