From 5e61bbe586519c3d9bc636153d32e810da4e59a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 20 Nov 2012 11:34:52 +0100 Subject: Basic high-dpi "retina" support for Qt 5. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring Qt 5 on par with Qt 4, prepare for more comprehensive support later on. Introduce device independent pixels (dips), device pixels, and devicePixelRatio. Add high-dpi support to QPainter, QGLWidget, the cocoa platform plugin, mac and fusion styles. Dips are similar to CSS pixels, Apple points and Android density-independent pixels. Device pixels are pixels in the backing store/physical pixels on screen. devicePixelRatio is the ratio between them, which is 1.0 on standard displays and 2.0 on "retina" displays. New API: QImage::devicePixelRatio() and setDevicePixelRatio() QPixmap::devicePixelRatio() and setDevicePixelRatio() QWindow::devicePixelRatio() QScreen::devicePixelRatio() QGuiApplicaiton::devicePixelRatio() Change-Id: If98c3ca9bfdf0e1bdbcf7574cd5b912c9ff63856 Reviewed-by: Morten Johan Sørvig Reviewed-by: Gunnar Sletta --- src/gui/image/qimage.cpp | 67 +++++++++++++++++++++++++++++--- src/gui/image/qimage.h | 3 ++ src/gui/image/qimage_p.h | 7 +++- src/gui/image/qimagereader.cpp | 5 +++ src/gui/image/qpixmap.cpp | 44 +++++++++++++++++++++ src/gui/image/qpixmap.h | 3 ++ src/gui/image/qpixmap_blitter.cpp | 10 +++++ src/gui/image/qpixmap_blitter_p.h | 3 ++ src/gui/image/qpixmap_raster.cpp | 19 +++++++-- src/gui/image/qpixmap_raster_p.h | 3 ++ src/gui/image/qplatformpixmap.h | 3 ++ src/gui/kernel/qguiapplication.cpp | 29 +++++++++++++- src/gui/kernel/qguiapplication.h | 1 + src/gui/kernel/qplatformscreen.cpp | 12 ++++++ src/gui/kernel/qplatformscreen.h | 1 + src/gui/kernel/qplatformwindow.cpp | 12 ++++++ src/gui/kernel/qplatformwindow.h | 2 + src/gui/kernel/qscreen.cpp | 14 +++++++ src/gui/kernel/qscreen.h | 2 + src/gui/kernel/qwindow.cpp | 18 +++++++++ src/gui/kernel/qwindow.h | 2 + src/gui/painting/qpaintengine_raster.cpp | 29 +++++++++++--- src/gui/painting/qpaintengineex.cpp | 4 +- src/gui/painting/qpainter.cpp | 48 ++++++++++++++++++----- src/gui/painting/qpainter_p.h | 1 + 25 files changed, 313 insertions(+), 29 deletions(-) (limited to 'src/gui') diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 9da360bc26..c40ca1545a 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -95,10 +95,12 @@ static QImage rotated270(const QImage &src); QBasicAtomicInt qimage_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1); QImageData::QImageData() - : ref(0), width(0), height(0), depth(0), nbytes(0), data(0), + : ref(0), width(0), height(0), depth(0), nbytes(0), devicePixelRatio(1.0), data(0), format(QImage::Format_ARGB32), bytes_per_line(0), ser_no(qimage_serial_number.fetchAndAddRelaxed(1)), detach_no(0), + ldpmx(qt_defaultDpiX() * 100 / qreal(2.54)), + ldpmy(qt_defaultDpiY() * 100 / qreal(2.54)), dpmx(qt_defaultDpiX() * 100 / qreal(2.54)), dpmy(qt_defaultDpiY() * 100 / qreal(2.54)), offset(0, 0), own_data(true), ro_data(false), has_alpha_clut(false), @@ -1216,6 +1218,7 @@ QImage QImage::copy(const QRect& r) const image.d->dpmx = dotsPerMeterX(); image.d->dpmy = dotsPerMeterY(); + image.d->devicePixelRatio = devicePixelRatio(); image.d->offset = offset(); image.d->has_alpha_clut = d->has_alpha_clut; image.d->text = d->text; @@ -1369,6 +1372,52 @@ QVector QImage::colorTable() const return d ? d->colortable : QVector(); } +/*! + Returns the device pixel ratio for the image. This is the + ratio between image pixels and device-independent pixels. + + Use this function when calculating layout geometry based on + the image size: QSize layoutSize = image.size() / image.devicePixelRatio() + + The default value is 1.0. + + \sa setDevicePixelRatio() +*/ +qreal QImage::devicePixelRatio() const +{ + if (!d) + return 1.0; + return d->devicePixelRatio; +} + +/*! + Sets the the device pixel ratio for the image. This is the + ratio between image pixels and device-independent pixels. + + The default value is 1.0. Setting it to something else has + two effects: + + QPainters that are opened on the image will be scaled. For + example, painting on a 200x200 image if with a ratio of 2.0 + will result in effective (device-independent) painting bounds + of 100x100. + + Code paths in Qt that calculate layout geometry based on the + image size will take the ratio into account: + QSize layoutSize = image.size() / image.devicePixelRatio() + The net effect of this is that the image is displayed as + high-dpi image rather than a large image. + + \sa devicePixelRatio() +*/ +void QImage::setDevicePixelRatio(qreal scaleFactor) +{ + if (!d) + return; + detach(); + d->devicePixelRatio = scaleFactor; +} + /*! \since 4.6 Returns the number of bytes occupied by the image data. @@ -3359,6 +3408,7 @@ QImage QImage::convertToFormat(Format format, Qt::ImageConversionFlags flags) co image.setDotsPerMeterY(dotsPerMeterY()); image.setDotsPerMeterX(dotsPerMeterX()); + image.setDevicePixelRatio(devicePixelRatio()); image.d->text = d->text; @@ -3479,6 +3529,7 @@ QImage QImage::convertToFormat(Format format, const QVector &colorTable, Q QImage image(d->width, d->height, format); QIMAGE_SANITYCHECK_MEMORY(image); + image.setDevicePixelRatio(devicePixelRatio()); image.d->text = d->text; @@ -4932,17 +4983,20 @@ int QImage::metric(PaintDeviceMetric metric) const return d->depth; case PdmDpiX: - return qRound(d->dpmx * 0.0254); + return qRound(d->ldpmx * 0.0254); + break; case PdmDpiY: - return qRound(d->dpmy * 0.0254); + return qRound(d->ldpmy * 0.0254); + break; case PdmPhysicalDpiX: - return qRound(d->dpmx * 0.0254); + return qRound(d->dpmx * 0.0254 * d->devicePixelRatio); + break; case PdmPhysicalDpiY: - return qRound(d->dpmy * 0.0254); - + return qRound(d->dpmy * 0.0254 * d->devicePixelRatio); + break; default: qWarning("QImage::metric(): Unhandled metric type %d", metric); break; @@ -5641,6 +5695,7 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode dImage.d->dpmx = dotsPerMeterX(); dImage.d->dpmy = dotsPerMeterY(); + dImage.d->devicePixelRatio = devicePixelRatio(); switch (bpp) { // initizialize the data diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 0356c1cab1..304d54a378 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -211,6 +211,9 @@ public: QVector colorTable() const; void setColorTable(const QVector colors); + qreal devicePixelRatio() const; + void setDevicePixelRatio(qreal scaleFactor); + void fill(uint pixel); void fill(const QColor &color); void fill(Qt::GlobalColor color); diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index 74a79a8425..2bb989c03b 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -74,6 +74,7 @@ struct Q_GUI_EXPORT QImageData { // internal image data int height; int depth; int nbytes; // number of bytes data + qreal devicePixelRatio; QVector colortable; uchar *data; QImage::Format format; @@ -81,8 +82,10 @@ struct Q_GUI_EXPORT QImageData { // internal image data int ser_no; // serial number int detach_no; - qreal dpmx; // dots per meter X (or 0) - qreal dpmy; // dots per meter Y (or 0) + qreal ldpmx; // logical dots per meter X (or 0) + qreal ldpmy; // logical dots per meter Y (or 0) + qreal dpmx; // device dots per meter X (or 0) + qreal dpmy; // device dots per meter Y (or 0) QPoint offset; // offset in pixels uint own_data : 1; diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 3b0fe33d21..a27a7710f6 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -1234,6 +1234,11 @@ bool QImageReader::read(QImage *image) } } + // successful read; check for "@2x" file name suffix and set device pixel ratio. + if (QFileInfo(fileName()).baseName().endsWith("@2x")) { + image->setDevicePixelRatio(2.0); + } + return true; } diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 113369fd75..8782119b7a 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -638,6 +638,50 @@ void QPixmap::setMask(const QBitmap &mask) data->fromImage(image, Qt::AutoColor); } +/*! + Returns the device pixel ratio for the pixmap. This is the + ratio between pixmap pixels and device-independent pixels. + + Use this function when calculating layout geometry based on + the pixmap size: QSize layoutSize = image.size() / image.devicePixelRatio() + + The default value is 1.0. + + \sa setDevicePixelRatio() +*/ +qreal QPixmap::devicePixelRatio() const +{ + if (!data) + return qreal(1.0); + return data->devicePixelRatio(); +} + +/*! + Sets the the device pixel ratio for the pixmap. This is the + ratio between image pixels and device-independent pixels. + + The default value is 1.0. Setting it to something else has + two effects: + + QPainters that are opened on the pixmap will be scaled. For + example, painting on a 200x200 image if with a ratio of 2.0 + will result in effective (device-independent) painting bounds + of 100x100. + + Code paths in Qt that calculate layout geometry based on the + pixmap size will take the ratio into account: + QSize layoutSize = pixmap.size() / pixmap.devicePixelRatio() + The net effect of this is that the pixmap is displayed as + high-dpi pixmap rather than a large pixmap. + + \sa devicePixelRatio() +*/ +void QPixmap::setDevicePixelRatio(qreal scaleFactor) +{ + detach(); + data->setDevicePixelRatio(scaleFactor); +} + #ifndef QT_NO_IMAGE_HEURISTIC_MASK /*! Creates and returns a heuristic mask for this pixmap. diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index 5f5c28def2..af141cd325 100644 --- a/src/gui/image/qpixmap.h +++ b/src/gui/image/qpixmap.h @@ -102,6 +102,9 @@ public: QBitmap mask() const; void setMask(const QBitmap &); + qreal devicePixelRatio() const; + void setDevicePixelRatio(qreal scaleFactor); + bool hasAlpha() const; bool hasAlphaChannel() const; diff --git a/src/gui/image/qpixmap_blitter.cpp b/src/gui/image/qpixmap_blitter.cpp index 2bc3a67509..2a4576e8a8 100644 --- a/src/gui/image/qpixmap_blitter.cpp +++ b/src/gui/image/qpixmap_blitter.cpp @@ -195,6 +195,16 @@ void QBlittablePlatformPixmap::fromImage(const QImage &image, } } +qreal QBlittablePlatformPixmap::devicePixelRatio() const +{ + return m_devicePixelRatio; +} + +void QBlittablePlatformPixmap::setDevicePixelRatio(qreal scaleFactor) +{ + m_devicePixelRatio = scaleFactor; +} + QPaintEngine *QBlittablePlatformPixmap::paintEngine() const { if (!m_engine) { diff --git a/src/gui/image/qpixmap_blitter_p.h b/src/gui/image/qpixmap_blitter_p.h index c4e0e0a4cc..a0e5f55f69 100644 --- a/src/gui/image/qpixmap_blitter_p.h +++ b/src/gui/image/qpixmap_blitter_p.h @@ -66,6 +66,8 @@ public: QImage toImage() const; bool hasAlphaChannel() const; void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + qreal devicePixelRatio() const; + void setDevicePixelRatio(qreal scaleFactor); QPaintEngine *paintEngine() const; @@ -89,6 +91,7 @@ protected: QScopedPointer m_engine; QScopedPointer m_blittable; bool m_alpha; + qreal m_devicePixelRatio; #ifdef QT_BLITTER_RASTEROVERLAY QImage *m_rasterOverlay; diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp index d30c6eaae7..5c188f0362 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -272,12 +272,14 @@ int QRasterPlatformPixmap::metric(QPaintDevice::PaintDeviceMetric metric) const return d->colortable.size(); case QPaintDevice::PdmDepth: return this->d; - case QPaintDevice::PdmDpiX: // fall-through + case QPaintDevice::PdmDpiX: + return qt_defaultDpiX(); case QPaintDevice::PdmPhysicalDpiX: + return qt_defaultDpiX() * image.devicePixelRatio(); + case QPaintDevice::PdmDpiY: return qt_defaultDpiX(); - case QPaintDevice::PdmDpiY: // fall-through case QPaintDevice::PdmPhysicalDpiY: - return qt_defaultDpiY(); + return qt_defaultDpiY() * image.devicePixelRatio(); default: qWarning("QRasterPlatformPixmap::metric(): Unhandled metric type %d", metric); break; @@ -350,6 +352,7 @@ void QRasterPlatformPixmap::createPixmapForImage(QImage &sourceImage, Qt::ImageC } is_null = (w <= 0 || h <= 0); + image.d->devicePixelRatio = sourceImage.devicePixelRatio(); setSerialNumber(image.cacheKey() >> 32); } @@ -358,4 +361,14 @@ QImage* QRasterPlatformPixmap::buffer() return ℑ } +qreal QRasterPlatformPixmap::devicePixelRatio() const +{ + return image.devicePixelRatio(); +} + +void QRasterPlatformPixmap::setDevicePixelRatio(qreal scaleFactor) +{ + image.setDevicePixelRatio(scaleFactor); +} + QT_END_NAMESPACE diff --git a/src/gui/image/qpixmap_raster_p.h b/src/gui/image/qpixmap_raster_p.h index 7bc007f67f..e560577e17 100644 --- a/src/gui/image/qpixmap_raster_p.h +++ b/src/gui/image/qpixmap_raster_p.h @@ -79,6 +79,9 @@ public: QImage toImage(const QRect &rect) const; QPaintEngine* paintEngine() const; QImage* buffer(); + qreal devicePixelRatio() const; + void setDevicePixelRatio(qreal scaleFactor); + protected: int metric(QPaintDevice::PaintDeviceMetric metric) const; diff --git a/src/gui/image/qplatformpixmap.h b/src/gui/image/qplatformpixmap.h index 9d28721f99..af3abecaf8 100644 --- a/src/gui/image/qplatformpixmap.h +++ b/src/gui/image/qplatformpixmap.h @@ -108,6 +108,9 @@ public: inline PixelType pixelType() const { return type; } inline ClassId classId() const { return static_cast(id); } + virtual qreal devicePixelRatio() const = 0; + virtual void setDevicePixelRatio(qreal scaleFactor) = 0; + virtual QImage* buffer(); inline int width() const { return w; } diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 80e13227ee..fb14490a24 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -703,7 +703,34 @@ QList QGuiApplication::screens() /*! - Returns the top level window at the given position \a pos, if any. + Returns the highest screen device pixel ratio found on + the system. This is the ratio between physical pixels and + device-independent pixels. + + Use this function only when you don't know which window you are targeting. + If you do know the target window use QWindow::devicePixelRatio() instead. + + \sa QWindow::devicePixelRatio(); + \sa QGuiApplicaiton::devicePixelRatio(); +*/ +qreal QGuiApplication::devicePixelRatio() const +{ + // Cache topDevicePixelRatio, iterate through the screen list once only. + static qreal topDevicePixelRatio = 0.0; + if (!qFuzzyIsNull(topDevicePixelRatio)) { + return topDevicePixelRatio; + } + + topDevicePixelRatio = 1.0; // make sure we never return 0. + foreach (QScreen *screen, QGuiApplicationPrivate::screen_list) { + topDevicePixelRatio = qMax(topDevicePixelRatio, screen->devicePixelRatio()); + } + + return topDevicePixelRatio; +} + +/*! + Returns the top level window at the given position, if any. */ QWindow *QGuiApplication::topLevelAt(const QPoint &pos) { diff --git a/src/gui/kernel/qguiapplication.h b/src/gui/kernel/qguiapplication.h index 27ea86ec6e..7c0dbbdcde 100644 --- a/src/gui/kernel/qguiapplication.h +++ b/src/gui/kernel/qguiapplication.h @@ -104,6 +104,7 @@ public: static QScreen *primaryScreen(); static QList screens(); + qreal devicePixelRatio() const; #ifndef QT_NO_CURSOR static QCursor *overrideCursor(); diff --git a/src/gui/kernel/qplatformscreen.cpp b/src/gui/kernel/qplatformscreen.cpp index 211a9650dc..ab2fdfa409 100644 --- a/src/gui/kernel/qplatformscreen.cpp +++ b/src/gui/kernel/qplatformscreen.cpp @@ -160,6 +160,18 @@ QDpi QPlatformScreen::logicalDpi() const 25.4 * s.height() / ps.height()); } +/*! + Reimplement this function in subclass to return the device pixel + ratio for the screen. This is the ratio between physical pixels + and device-independent pixels. + + \sa QPlatformWindow::devicePixelRatio(); +*/ +qreal QPlatformScreen::devicePixelRatio() const +{ + return 1.0; +} + /*! Reimplement this function in subclass to return the vertical refresh rate of the screen, in Hz. diff --git a/src/gui/kernel/qplatformscreen.h b/src/gui/kernel/qplatformscreen.h index 493a1cedd0..91365b1cf0 100644 --- a/src/gui/kernel/qplatformscreen.h +++ b/src/gui/kernel/qplatformscreen.h @@ -98,6 +98,7 @@ public: virtual QSizeF physicalSize() const; virtual QDpi logicalDpi() const; + virtual qreal devicePixelRatio() const; virtual qreal refreshRate() const; diff --git a/src/gui/kernel/qplatformwindow.cpp b/src/gui/kernel/qplatformwindow.cpp index 3bf06c6ab1..25b863c9a3 100644 --- a/src/gui/kernel/qplatformwindow.cpp +++ b/src/gui/kernel/qplatformwindow.cpp @@ -355,6 +355,18 @@ Qt::ScreenOrientation QPlatformWindow::requestWindowOrientation(Qt::ScreenOrient return Qt::PrimaryOrientation; } +/*! + Reimplement this function in subclass to return the device pixel ratio + for the window. This is the ratio between physical pixels + and device-independent pixels. + + \sa QPlatformWindow::devicePixelRatio(); +*/ +qreal QPlatformWindow::devicePixelRatio() const +{ + return 1.0; +} + bool QPlatformWindow::setKeyboardGrabEnabled(bool grab) { Q_UNUSED(grab); diff --git a/src/gui/kernel/qplatformwindow.h b/src/gui/kernel/qplatformwindow.h index 12650d6073..607c8e4035 100644 --- a/src/gui/kernel/qplatformwindow.h +++ b/src/gui/kernel/qplatformwindow.h @@ -117,6 +117,8 @@ public: virtual void handleContentOrientationChange(Qt::ScreenOrientation orientation); virtual Qt::ScreenOrientation requestWindowOrientation(Qt::ScreenOrientation orientation); + virtual qreal devicePixelRatio() const; + virtual bool setKeyboardGrabEnabled(bool grab); virtual bool setMouseGrabEnabled(bool grab); diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp index 0c30de498c..f5467ab742 100644 --- a/src/gui/kernel/qscreen.cpp +++ b/src/gui/kernel/qscreen.cpp @@ -214,6 +214,20 @@ qreal QScreen::logicalDotsPerInch() const return (dpi.first + dpi.second) * qreal(0.5); } +/* + Returns the ratio between physical pixels and device-independent pixels for the screen. + + Common values are 1.0 on normal displays and 2.0 on Apple retina displays. + + \sa QWindow::devicePixelRatio(); + \sa QGuiApplicaiton::devicePixelRatio(); +*/ +qreal QScreen::devicePixelRatio() const +{ + Q_D(const QScreen); + return d->platformScreen->devicePixelRatio(); +} + /*! \property QScreen::physicalSize \brief the screen's physical size (in millimeters) diff --git a/src/gui/kernel/qscreen.h b/src/gui/kernel/qscreen.h index 48eaad94f7..fbbd886755 100644 --- a/src/gui/kernel/qscreen.h +++ b/src/gui/kernel/qscreen.h @@ -109,6 +109,8 @@ public: qreal logicalDotsPerInchY() const; qreal logicalDotsPerInch() const; + qreal devicePixelRatio() const; + QSize availableSize() const; QRect availableGeometry() const; diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index c24609e886..aaf2b25ad4 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -805,6 +805,24 @@ Qt::ScreenOrientation QWindow::orientation() const return d->windowOrientation; } +/*! + Returns the ratio between physical pixels and device-independent pixels + for the window. This value is dependent on the screen the window is on, + and may change when the window is moved. + + Common values are 1.0 on normal displays and 2.0 on Apple "retina" displays. + + \sa QWindow::devicePixelRatio(); + \sa QGuiApplicaiton::devicePixelRatio(); +*/ +qreal QWindow::devicePixelRatio() const +{ + Q_D(const QWindow); + if (!d->platformWindow) + return 1.0; + return d->platformWindow->devicePixelRatio(); +} + /*! \brief set the screen-occupation state of the window diff --git a/src/gui/kernel/qwindow.h b/src/gui/kernel/qwindow.h index e6c9a3736f..229275d7c7 100644 --- a/src/gui/kernel/qwindow.h +++ b/src/gui/kernel/qwindow.h @@ -184,6 +184,8 @@ public: void reportContentOrientationChange(Qt::ScreenOrientation orientation); Qt::ScreenOrientation contentOrientation() const; + qreal devicePixelRatio() const; + bool requestOrientation(Qt::ScreenOrientation orientation); Qt::ScreenOrientation orientation() const; diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 2841a583d5..46648fe297 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -1072,9 +1072,11 @@ void QRasterPaintEnginePrivate::systemStateChanged() exDeviceRect = deviceRect; Q_Q(QRasterPaintEngine); - q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion; - q->state()->fillFlags |= QPaintEngine::DirtyClipRegion; - q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion; + if (q->state()) { + q->state()->strokeFlags |= QPaintEngine::DirtyClipRegion; + q->state()->fillFlags |= QPaintEngine::DirtyClipRegion; + q->state()->pixmapFlags |= QPaintEngine::DirtyClipRegion; + } } void QRasterPaintEnginePrivate::updateMatrixData(QSpanData *spanData, const QBrush &b, const QTransform &m) @@ -2143,9 +2145,10 @@ void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img) Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); + qreal scale = img.devicePixelRatio(); - if (s->matrix.type() > QTransform::TxTranslate) { - drawImage(QRectF(p.x(), p.y(), img.width(), img.height()), + if (scale > 1.0 || s->matrix.type() > QTransform::TxTranslate) { + drawImage(QRectF(p.x(), p.y(), img.width() / scale, img.height() / scale), img, QRectF(0, 0, img.width(), img.height())); } else { @@ -2349,6 +2352,22 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe return; } } else { + // Test for optimized high-dpi case: 2x source on 2x target. (Could be generalized to nX.) + bool sourceRect2x = r.width() * 2 == sr.width() && r.height() * 2 == sr.height(); + bool scale2x = (s->matrix.m11() == qreal(2)) && (s->matrix.m22() == qreal(2)); + if (s->matrix.type() == QTransform::TxScale && sourceRect2x && scale2x) { + SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()]; + if (func) { + QPointF pt(r.x() * 2 + s->matrix.dx(), r.y() * 2 + s->matrix.dy()); + if (!clip) { + d->drawImage(pt, img, func, d->deviceRect, s->intOpacity, sr.toRect()); + return; + } else if (clip->hasRectClip) { + d->drawImage(pt, img, func, clip->clipRect, s->intOpacity, sr.toRect()); + return; + } + } + } SrcOverScaleFunc func = qScaleFunctions[d->rasterBuffer->format][img.format()]; if (func && (!clip || clip->hasRectClip)) { func(d->rasterBuffer->buffer(), d->rasterBuffer->bytesPerLine(), diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index 113cbd8a8e..2c41ab9ff2 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -939,12 +939,12 @@ void QPaintEngineEx::drawPolygon(const QPoint *points, int pointCount, PolygonDr void QPaintEngineEx::drawPixmap(const QPointF &pos, const QPixmap &pm) { - drawPixmap(QRectF(pos, pm.size()), pm, pm.rect()); + drawPixmap(QRectF(pos, pm.size() / pm.devicePixelRatio()), pm, pm.rect()); } void QPaintEngineEx::drawImage(const QPointF &pos, const QImage &image) { - drawImage(QRectF(pos, image.size()), image, image.rect()); + drawImage(QRectF(pos, image.size() / image.devicePixelRatio()), image, image.rect()); } void QPaintEngineEx::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s) diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 0cfe953e43..8ec9c1648f 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -225,6 +225,18 @@ QTransform QPainterPrivate::viewTransform() const return QTransform(); } +QTransform QPainterPrivate::hidpiScaleTransform() const +{ +#ifdef Q_OS_MAC + // Limited feature introduction for Qt 5.0.0, remove ifdef in a later release. + if (device->physicalDpiX() == 0 || device->logicalDpiX() == 0) + return QTransform(); + const qreal deviceScale = (device->physicalDpiX() / device->logicalDpiX()); + if (deviceScale > 1.0) + return QTransform::fromScale(deviceScale, deviceScale); +#endif + return QTransform(); +} /* \internal @@ -641,6 +653,8 @@ void QPainterPrivate::updateMatrix() else state->dirtyFlags |= QPaintEngine::DirtyTransform; + state->matrix *= hidpiScaleTransform(); + // printf("VxF=%d, WxF=%d\n", state->VxF, state->WxF); // qDebug() << " --- using matrix" << state->matrix << redirection_offset; } @@ -1827,7 +1841,14 @@ bool QPainter::begin(QPaintDevice *pd) Q_ASSERT(d->engine->isActive()); - if (!d->state->redirectionMatrix.isIdentity()) +#ifdef Q_OS_MAC + // Limited feature introduction for Qt 5.0.0, remove ifdef in a later release. + const bool isHighDpi = (d->device->physicalDpiX() == 0 || d->device->logicalDpiX() == 0) ? + false : (d->device->physicalDpiX() / d->device->logicalDpiX() > 1); +#else + const bool isHighDpi = false; +#endif + if (!d->state->redirectionMatrix.isIdentity() || isHighDpi) d->updateMatrix(); Q_ASSERT(d->engine->isActive()); @@ -5092,7 +5113,8 @@ void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm) x += d->state->matrix.dx(); y += d->state->matrix.dy(); } - d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(0, 0, w, h)); + int scale = pm.devicePixelRatio(); + d->engine->drawPixmap(QRectF(x, y, w / scale, h / scale), pm, QRectF(0, 0, w, h)); } } @@ -5122,6 +5144,11 @@ void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) qreal sw = sr.width(); qreal sh = sr.height(); + // Get pixmap scale. Use it when calculating the target + // rect size from pixmap size. For example, a 2X 64x64 pixel + // pixmap should result in a 32x32 point target rect. + const qreal pmscale = pm.devicePixelRatio(); + // Sanity-check clipping if (sw <= 0) sw = pm.width() - sx; @@ -5130,9 +5157,9 @@ void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) sh = pm.height() - sy; if (w < 0) - w = sw; + w = sw / pmscale; if (h < 0) - h = sh; + h = sh / pmscale; if (sx < 0) { qreal w_ratio = sx * w/sw; @@ -5345,6 +5372,7 @@ void QPainter::drawImage(const QPointF &p, const QImage &image) int w = image.width(); int h = image.height(); + qreal scale = image.devicePixelRatio(); d->updateState(d->state); @@ -5368,8 +5396,7 @@ void QPainter::drawImage(const QPointF &p, const QImage &image) setBrush(brush); setPen(Qt::NoPen); setBrushOrigin(QPointF(0, 0)); - - drawRect(image.rect()); + drawRect(QRect(QPoint(0, 0), image.size() / scale)); restore(); return; } @@ -5380,7 +5407,7 @@ void QPainter::drawImage(const QPointF &p, const QImage &image) y += d->state->matrix.dy(); } - d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(0, 0, w, h), Qt::AutoColor); + d->engine->drawImage(QRectF(x, y, w / scale, h / scale), image, QRectF(0, 0, w, h), Qt::AutoColor); } void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect, @@ -5399,6 +5426,7 @@ void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QR qreal sy = sourceRect.y(); qreal sw = sourceRect.width(); qreal sh = sourceRect.height(); + qreal imageScale = image.devicePixelRatio(); // Sanity-check clipping if (sw <= 0) @@ -5408,9 +5436,9 @@ void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QR sh = image.height() - sy; if (w < 0) - w = sw; + w = sw / imageScale; if (h < 0) - h = sh; + h = sh / imageScale; if (sx < 0) { qreal w_ratio = sx * w/sw; @@ -8235,7 +8263,7 @@ QTransform QPainter::combinedTransform() const qWarning("QPainter::combinedTransform: Painter not active"); return QTransform(); } - return d->state->worldMatrix * d->viewTransform(); + return d->state->worldMatrix * d->viewTransform() * d->hidpiScaleTransform(); } /*! diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h index 0e46cee4b5..fd5d560141 100644 --- a/src/gui/painting/qpainter_p.h +++ b/src/gui/painting/qpainter_p.h @@ -249,6 +249,7 @@ public: } QTransform viewTransform() const; + QTransform hidpiScaleTransform() const; static bool attachPainterPrivate(QPainter *q, QPaintDevice *pdev); void detachPainterPrivate(QPainter *q); -- cgit v1.2.3