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 + src/opengl/qgl.cpp | 4 +- src/opengl/qgl_qpa.cpp | 5 +- src/plugins/platforms/cocoa/qcocoabackingstore.h | 1 + src/plugins/platforms/cocoa/qcocoabackingstore.mm | 26 +- src/plugins/platforms/cocoa/qcocoaglcontext.mm | 12 + src/plugins/platforms/cocoa/qcocoaintegration.h | 1 + src/plugins/platforms/cocoa/qcocoaintegration.mm | 12 + src/plugins/platforms/cocoa/qcocoawindow.h | 2 + src/plugins/platforms/cocoa/qcocoawindow.mm | 15 + src/plugins/platforms/cocoa/qnsview.mm | 31 +- src/widgets/itemviews/qitemdelegate.cpp | 10 +- src/widgets/kernel/qwidget.cpp | 29 +- src/widgets/kernel/qwidget_p.h | 1 + src/widgets/styles/qfusionstyle.cpp | 100 +++--- src/widgets/styles/qmacstyle_mac.mm | 32 +- src/widgets/styles/qstyle.cpp | 5 +- src/widgets/styles/qstyle_p.h | 11 +- src/widgets/widgets/qlabel.cpp | 9 +- tests/manual/highdpi/highdpi.pro | 12 + tests/manual/highdpi/highdpi.qrc | 7 + tests/manual/highdpi/main.cpp | 366 ++++++++++++++++++++++ tests/manual/highdpi/qticon.png | Bin 0 -> 6474 bytes tests/manual/highdpi/qticon@2x.png | Bin 0 -> 17168 bytes tests/manual/highdpi/qticon_large.png | Bin 0 -> 17168 bytes tests/manual/lance/main.cpp | 18 +- 50 files changed, 933 insertions(+), 118 deletions(-) create mode 100644 tests/manual/highdpi/highdpi.pro create mode 100644 tests/manual/highdpi/highdpi.qrc create mode 100644 tests/manual/highdpi/main.cpp create mode 100644 tests/manual/highdpi/qticon.png create mode 100644 tests/manual/highdpi/qticon@2x.png create mode 100644 tests/manual/highdpi/qticon_large.png 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); diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 8c98a0ea3a..69f4871c6b 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -4100,7 +4100,9 @@ void QGLWidget::glDraw() #endif if (!d->glcx->initialized()) { glInit(); - resizeGL(d->glcx->device()->width(), d->glcx->device()->height()); // New context needs this "resize" + const qreal scaleFactor = (window() && window()->windowHandle()) ? + window()->windowHandle()->devicePixelRatio() : 1.0; + resizeGL(d->glcx->device()->width() * scaleFactor, d->glcx->device()->height() * scaleFactor); // New context needs this "resize" } paintGL(); if (doubleBuffer()) { diff --git a/src/opengl/qgl_qpa.cpp b/src/opengl/qgl_qpa.cpp index ba07f6121c..0e8b8abb4f 100644 --- a/src/opengl/qgl_qpa.cpp +++ b/src/opengl/qgl_qpa.cpp @@ -370,7 +370,10 @@ void QGLWidget::resizeEvent(QResizeEvent *e) makeCurrent(); if (!d->glcx->initialized()) glInit(); - resizeGL(width(), height()); + const qreal scaleFactor = (window() && window()->windowHandle()) ? + window()->windowHandle()->devicePixelRatio() : 1.0; + + resizeGL(width() * scaleFactor, height() * scaleFactor); } diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index 0e1998170a..192ef00649 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -62,6 +62,7 @@ public: void resize (const QSize &size, const QRegion &); bool scroll(const QRegion &area, int dx, int dy); CGImageRef getBackingStoreCGImage(); + qreal getBackingStoreDevicePixelRatio(); private: QImage m_qImage; diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 7bd7e4ce38..ec3168ce99 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -59,10 +59,22 @@ QCocoaBackingStore::~QCocoaBackingStore() QPaintDevice *QCocoaBackingStore::paintDevice() { - if (m_qImage.size() != m_requestedSize) { + if (m_qImage.size() / m_qImage.devicePixelRatio() != m_requestedSize) { CGImageRelease(m_cgImage); m_cgImage = 0; - m_qImage = QImage(m_requestedSize, QImage::Format_ARGB32_Premultiplied); + + int scaleFactor = 1; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { + QCocoaWindow *cocoaWindow = static_cast(window()->handle()); + if (cocoaWindow && cocoaWindow->m_contentView) { + scaleFactor = int([[cocoaWindow->m_contentView window] backingScaleFactor]); + } + } +#endif + + m_qImage = QImage(m_requestedSize * scaleFactor, QImage::Format_ARGB32_Premultiplied); + m_qImage.setDevicePixelRatio(scaleFactor); } return &m_qImage; } @@ -90,10 +102,11 @@ void QCocoaBackingStore::resize(const QSize &size, const QRegion &) bool QCocoaBackingStore::scroll(const QRegion &area, int dx, int dy) { extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); - QPoint qpoint(dx, dy); + const qreal devicePixelRatio = m_qImage.devicePixelRatio(); + QPoint qpoint(dx * devicePixelRatio, dy * devicePixelRatio); const QVector qrects = area.rects(); for (int i = 0; i < qrects.count(); ++i) { - const QRect &qrect = qrects.at(i); + const QRect &qrect = QRect(qrects.at(i).topLeft() * devicePixelRatio, qrects.at(i).size() * devicePixelRatio); qt_scrollRectInImage(m_qImage, qrect, qpoint); } return true; @@ -110,4 +123,9 @@ CGImageRef QCocoaBackingStore::getBackingStoreCGImage() return m_cgImage; } +qreal QCocoaBackingStore::getBackingStoreDevicePixelRatio() +{ + return m_qImage.devicePixelRatio(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index d9bb9c60a9..99956a0b60 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -114,6 +114,18 @@ void QCocoaGLContext::setActiveWindow(QWindow *window) cocoaWindow->setCurrentContext(this); [(QNSView *) cocoaWindow->contentView() setQCocoaGLContext:this]; + + // Enable high-dpi OpenGL for retina displays. Enabling has the side + // effect that Cooca will start calling glViewport(0, 0, width, height), + // overriding any glViewport calls in application code. This is usually not a + // problem, except if the applcation wants to have a "custom" viewport. + // (like the hellogl example) +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { + if (cocoaWindow->devicePixelRatio() > 1) + [cocoaWindow->contentView() setWantsBestResolutionOpenGLSurface:YES]; + } +#endif } void QCocoaGLContext::doneCurrent() diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index dae9872566..1bb46ea3ea 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -68,6 +68,7 @@ public: QRect availableGeometry() const { return m_availableGeometry; } int depth() const { return m_depth; } QImage::Format format() const { return m_format; } + qreal devicePixelRatio() const; QSizeF physicalSize() const { return m_physicalSize; } QDpi logicalDpi() const { return m_logicalDpi; } qreal refreshRate() const { return m_refreshRate; } diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 3767fa014d..393c471c25 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -115,6 +115,18 @@ void QCocoaScreen::updateGeometry() QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), availableGeometry()); } +qreal QCocoaScreen::devicePixelRatio() const +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { + return qreal([m_screen backingScaleFactor]); + } else +#endif + { + return 1.0; + } +} + extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev); QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height) const diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index a9ea135b3e..228644c351 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -140,6 +140,8 @@ public: void setMenubar(QCocoaMenuBar *mb); QCocoaMenuBar *menubar() const; + + qreal devicePixelRatio() const; protected: // NSWindow handling. The QCocoaWindow/QNSView can either be displayed // in an existing NSWindow or in one created by Qt. diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 77073d9bc6..c3b2139998 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -39,6 +39,7 @@ ** ****************************************************************************/ #include "qcocoawindow.h" +#include "qcocoaintegration.h" #include "qnswindowdelegate.h" #include "qcocoaautoreleasepool.h" #include "qcocoaeventdispatcher.h" @@ -820,6 +821,20 @@ QCocoaMenuBar *QCocoaWindow::menubar() const return m_menubar; } +qreal QCocoaWindow::devicePixelRatio() const +{ + if (!m_nsWindow) + return 1.0; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { + return qreal([m_nsWindow backingScaleFactor]); + } else +#endif + { + return 1.0; + } +} + QMargins QCocoaWindow::frameMargins() const { NSRect frameW = [m_nsWindow frame]; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index d2a4685872..b608989e43 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -244,7 +244,7 @@ static QTouchDevice *touchDevice = 0; - (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset { m_backingStore = backingStore; - m_backingStoreOffset = offset; + m_backingStoreOffset = offset * m_backingStore->getBackingStoreDevicePixelRatio(); QRect br = region.boundingRect(); [self setNeedsDisplayInRect:NSMakeRect(br.x(), br.y(), br.width(), br.height())]; } @@ -275,33 +275,44 @@ static QTouchDevice *touchDevice = 0; if (!m_backingStore) return; - CGRect dirtyCGRect = NSRectToCGRect(dirtyRect); + // Calculate source and target rects. The target rect is the dirtyRect: + CGRect dirtyWindowRect = NSRectToCGRect(dirtyRect); + + // The backing store source rect will be larger on retina displays. + // Scale dirtyRect by the device pixel ratio: + const qreal devicePixelRatio = m_backingStore->getBackingStoreDevicePixelRatio(); + CGRect dirtyBackingRect = CGRectMake(dirtyRect.origin.x * devicePixelRatio, + dirtyRect.origin.y * devicePixelRatio, + dirtyRect.size.width * devicePixelRatio, + dirtyRect.size.height * devicePixelRatio); + NSGraphicsContext *nsGraphicsContext = [NSGraphicsContext currentContext]; CGContextRef cgContext = (CGContextRef) [nsGraphicsContext graphicsPort]; // Translate coordiate system from CoreGraphics (bottom-left) to NSView (top-left): CGContextSaveGState(cgContext); - int dy = dirtyCGRect.origin.y + CGRectGetMaxY(dirtyCGRect); + int dy = dirtyWindowRect.origin.y + CGRectGetMaxY(dirtyWindowRect); + CGContextTranslateCTM(cgContext, 0, dy); CGContextScaleCTM(cgContext, 1, -1); // If a mask is set, modify the sub image accordingly: CGImageRef subMask = 0; if (m_maskImage) { - subMask = CGImageCreateWithImageInRect(m_maskImage, dirtyCGRect); - CGContextClipToMask(cgContext, dirtyCGRect, subMask); + subMask = CGImageCreateWithImageInRect(m_maskImage, dirtyWindowRect); + CGContextClipToMask(cgContext, dirtyWindowRect, subMask); } // Clip out and draw the correct sub image from the (shared) backingstore: CGRect backingStoreRect = CGRectMake( - dirtyRect.origin.x + m_backingStoreOffset.x(), - dirtyRect.origin.y + m_backingStoreOffset.y(), - dirtyRect.size.width, - dirtyRect.size.height + dirtyBackingRect.origin.x + m_backingStoreOffset.x(), + dirtyBackingRect.origin.y + m_backingStoreOffset.y(), + dirtyBackingRect.size.width, + dirtyBackingRect.size.height ); CGImageRef bsCGImage = m_backingStore->getBackingStoreCGImage(); CGImageRef cleanImg = CGImageCreateWithImageInRect(bsCGImage, backingStoreRect); - CGContextDrawImage(cgContext, dirtyCGRect, cleanImg); + CGContextDrawImage(cgContext, dirtyWindowRect, cleanImg); // Clean-up: CGContextRestoreGState(cgContext); diff --git a/src/widgets/itemviews/qitemdelegate.cpp b/src/widgets/itemviews/qitemdelegate.cpp index e7ca2d0c6c..7631403933 100644 --- a/src/widgets/itemviews/qitemdelegate.cpp +++ b/src/widgets/itemviews/qitemdelegate.cpp @@ -1060,10 +1060,12 @@ QRect QItemDelegate::rect(const QStyleOptionViewItem &option, switch (value.type()) { case QVariant::Invalid: break; - case QVariant::Pixmap: - return QRect(QPoint(0, 0), qvariant_cast(value).size()); - case QVariant::Image: - return QRect(QPoint(0, 0), qvariant_cast(value).size()); + case QVariant::Pixmap: { + const QPixmap &pixmap = qvariant_cast(value); + return QRect(QPoint(0, 0), pixmap.size() / pixmap.devicePixelRatio() ); } + case QVariant::Image: { + const QImage &image = qvariant_cast(value); + return QRect(QPoint(0, 0), image.size() / image.devicePixelRatio() ); } case QVariant::Icon: { QIcon::Mode mode = d->iconMode(option.state); QIcon::State state = d->iconState(option.state); diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index e167e646c0..ace25fa78b 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1794,6 +1794,23 @@ QRegion QWidgetPrivate::clipRegion() const return r; } +void QWidgetPrivate::setSystemClip(QPaintDevice *paintDevice, const QRegion ®ion) +{ +// Transform the system clip region from device-independent pixels to device pixels +// Qt 5.0.0: This is a Mac-only code path for now, can be made cross-platform once +// it has been tested. + QPaintEngine *paintEngine = paintDevice->paintEngine(); +#ifdef Q_OS_MAC + const qreal devicePixelRatio = (paintDevice->physicalDpiX() == 0 || paintDevice->logicalDpiX() == 0) ? + 1.0 : (paintDevice->physicalDpiX() / paintDevice->logicalDpiX()); + QTransform scaleTransform; + scaleTransform.scale(devicePixelRatio, devicePixelRatio); + paintEngine->d_func()->systemClip = scaleTransform.map(region); +#else + paintEngine->d_func()->systemClip = region; +#endif +} + #ifndef QT_NO_GRAPHICSEFFECT void QWidgetPrivate::invalidateGraphicsEffectsRecursively() { @@ -4998,13 +5015,12 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP QWidgetPaintContext context(pdev, rgn, offset, flags, sharedPainter, backingStore); sourced->context = &context; if (!sharedPainter) { - QPaintEngine *paintEngine = pdev->paintEngine(); - paintEngine->d_func()->systemClip = rgn.translated(offset); + setSystemClip(pdev, rgn.translated(offset)); QPainter p(pdev); p.translate(offset); context.painter = &p; graphicsEffect->draw(&p); - paintEngine->d_func()->systemClip = QRegion(); + setSystemClip(pdev, QRegion()); } else { context.painter = sharedPainter; if (sharedPainter->worldTransform() != sourced->lastEffectTransform) { @@ -5061,7 +5077,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP #endif if (sharedPainter) - paintEngine->d_func()->systemClip = toBePainted; + setSystemClip(pdev, toBePainted); else paintEngine->d_func()->systemRect = q->data->crect; @@ -5073,7 +5089,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP } if (!sharedPainter) - paintEngine->d_func()->systemClip = toBePainted.translated(offset); + setSystemClip(pdev, toBePainted.translated(offset)); if (!onScreen && !asRoot && !isOpaque && q->testAttribute(Qt::WA_TintedBackground)) { QPainter p(q); @@ -5108,7 +5124,8 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP paintEngine->d_func()->systemRect = QRect(); else paintEngine->d_func()->currentClipDevice = 0; - paintEngine->d_func()->systemClip = QRegion(); + + setSystemClip(pdev, QRegion()); } q->setAttribute(Qt::WA_WState_InPaintEvent, false); if (q->paintingActive()) diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index 8aba276966..1d183e41f1 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -402,6 +402,7 @@ public: QRect clipRect() const; QRegion clipRegion() const; + void setSystemClip(QPaintDevice *paintDevice, const QRegion ®ion); void subtractOpaqueChildren(QRegion &rgn, const QRect &clipRect) const; void subtractOpaqueSiblings(QRegion &source, bool *hasDirtySiblingsAbove = 0, bool alsoNonOpaque = false) const; diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index ebcdc10d59..623cdb55b9 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -303,7 +303,7 @@ static void qt_fusion_draw_mdibutton(QPainter *painter, const QStyleOptionTitleB gradient.setColorAt(1, mdiButtonGradientStopColor); QColor mdiButtonBorderColor(active ? option->palette.highlight().color().darker(180): dark.darker(110)); - painter->setPen(QPen(mdiButtonBorderColor, 1)); + painter->setPen(QPen(mdiButtonBorderColor)); const QLine lines[4] = { QLine(tmp.left() + 2, tmp.top(), tmp.right() - 2, tmp.top()), QLine(tmp.left() + 2, tmp.bottom(), tmp.right() - 2, tmp.bottom()), @@ -457,7 +457,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, if (const QStyleOptionTabBarBase *tbb = qstyleoption_cast(option)) { painter->save(); - painter->setPen(QPen(outline.lighter(110), 0)); + painter->setPen(QPen(outline.lighter(110))); switch (tbb->shape) { case QTabBar::RoundedNorth: { QRegion region(tbb->rect); @@ -603,7 +603,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, } } break; - case PE_Frame: + case PE_Frame: { if (widget && widget->inherits("QComboBoxPrivateContainer")){ QStyleOption copy = *option; copy.state |= State_Raised; @@ -611,14 +611,16 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, break; } painter->save(); - painter->setPen(outline.lighter(108)); + QPen thePen(outline.lighter(108)); + thePen.setCosmetic(false); + painter->setPen(thePen); painter->drawRect(option->rect.adjusted(0, 0, -1, -1)); - painter->restore(); + painter->restore(); } break; case PE_FrameMenu: painter->save(); { - painter->setPen(QPen(outline, 1)); + painter->setPen(QPen(outline)); painter->drawRect(option->rect.adjusted(0, 0, -1, -1)); QColor frameLight = option->palette.background().color().lighter(160); QColor frameShadow = option->palette.background().color().darker(110); @@ -644,9 +646,9 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, QRect rect= option->rect; painter->setPen(softshadow); painter->drawRect(option->rect.adjusted(0, 0, -1, -1)); - painter->setPen(QPen(option->palette.light(), 0)); + painter->setPen(QPen(option->palette.light(), 1)); painter->drawLine(QPoint(rect.left() + 1, rect.top() + 1), QPoint(rect.left() + 1, rect.bottom() - 1)); - painter->setPen(QPen(option->palette.background().color().darker(120), 0)); + painter->setPen(QPen(option->palette.background().color().darker(120))); painter->drawLine(QPoint(rect.left() + 1, rect.bottom() - 1), QPoint(rect.right() - 2, rect.bottom() - 1)); painter->drawLine(QPoint(rect.right() - 1, rect.top() + 1), QPoint(rect.right() - 1, rect.bottom() - 1)); @@ -680,12 +682,12 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, painter->save(); { QRect rect= option->rect; - painter->setPen(QPen(outline.darker(150), 0)); + painter->setPen(QPen(outline.darker(150))); painter->drawRect(option->rect.adjusted(0, 0, -1, -1)); - painter->setPen(QPen(option->palette.light(), 0)); + painter->setPen(QPen(option->palette.light(), 1)); painter->drawLine(QPoint(rect.left() + 1, rect.top() + 1), QPoint(rect.left() + 1, rect.bottom() - 1)); - painter->setPen(QPen(option->palette.background().color().darker(120), 0)); + painter->setPen(QPen(option->palette.background().color().darker(120))); painter->drawLine(QPoint(rect.left() + 1, rect.bottom() - 1), QPoint(rect.right() - 2, rect.bottom() - 1)); painter->drawLine(QPoint(rect.right() - 1, rect.top() + 1), @@ -705,7 +707,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, painter->translate(0.5, 0.5); // Draw Outline - painter->setPen( QPen(hasFocus ? highlightedOutline : outline, 0)); + painter->setPen( QPen(hasFocus ? highlightedOutline : outline)); painter->setBrush(option->palette.base()); painter->drawRoundedRect(r.adjusted(0, 0, -1, -1), 2, 2); @@ -740,10 +742,10 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, gradient.setColorAt(1, (state & State_Sunken) ? pressedColor : option->palette.base().color()); painter->setBrush((state & State_Sunken) ? QBrush(pressedColor) : gradient); - painter->setPen(QPen(outline.lighter(110), 1)); + painter->setPen(QPen(outline.lighter(110))); if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) - painter->setPen(QPen(highlightedOutline, 1)); + painter->setPen(QPen(highlightedOutline)); painter->drawRect(rect); QColor checkMarkColor = option->palette.text().color().darker(120); @@ -785,9 +787,9 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, painter->setRenderHint(QPainter::Antialiasing, true); QPainterPath circle; circle.addEllipse(rect.center() + QPoint(1.0, 1.0), 6.5, 6.5); - painter->setPen(QPen(option->palette.background().color().darker(150), 1)); + painter->setPen(QPen(option->palette.background().color().darker(150))); if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) - painter->setPen(QPen(highlightedOutline, 1)); + painter->setPen(QPen(highlightedOutline)); painter->drawPath(circle); if (state & (State_On )) { @@ -862,7 +864,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, if (isFlat && !isDown) { if (isDefault) { r = option->rect.adjusted(0, 1, 0, -1); - painter->setPen(QPen(Qt::black, 0)); + painter->setPen(QPen(Qt::black)); const QLine lines[4] = { QLine(QPoint(r.left() + 2, r.top()), QPoint(r.right() - 2, r.top())), @@ -910,7 +912,7 @@ void QFusionStyle::drawPrimitive(PrimitiveElement elem, p->setBrush(Qt::NoBrush); // Outline - p->setPen(!isEnabled ? QPen(darkOutline.lighter(115)) : QPen(darkOutline, 1)); + p->setPen(!isEnabled ? QPen(darkOutline.lighter(115)) : QPen(darkOutline)); p->drawRoundedRect(r, 2.0, 2.0); p->setPen(d->innerContrastLine()); @@ -1300,7 +1302,7 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio painter->drawLine(rect.topLeft() - QPoint(0, 1), rect.topRight() - QPoint(0, 1)); painter->setBrush(option->palette.base()); - painter->setPen(QPen(outline, 0)); + painter->setPen(QPen(outline)); painter->drawRoundedRect(rect.adjusted(0, 0, -1, -1), 2, 2); // Inner shadow @@ -1359,14 +1361,14 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio progressBar.setRect(rect.left(), rect.top(), width - 1, rect.height() - 1); if (!complete) { painter->drawLine(progressBar.topRight() + QPoint(2, 1), progressBar.bottomRight() + QPoint(2, 0)); - painter->setPen(QPen(highlight.darker(140), 0)); + painter->setPen(QPen(highlight.darker(140))); painter->drawLine(progressBar.topRight() + QPoint(1, 1), progressBar.bottomRight() + QPoint(1, 0)); } } else { progressBar.setRect(rect.right() - width - 1, rect.top(), width + 2, rect.height() - 1); if (!complete) { painter->drawLine(progressBar.topLeft() + QPoint(-2, 1), progressBar.bottomLeft() + QPoint(-2, 0)); - painter->setPen(QPen(highlight.darker(140), 0)); + painter->setPen(QPen(highlight.darker(140))); painter->drawLine(progressBar.topLeft() + QPoint(-1, 1), progressBar.bottomLeft() + QPoint(-1, 0)); } } @@ -1376,7 +1378,7 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio if (indeterminate || bar->progress > bar->minimum) { - painter->setPen(QPen(outline, 0)); + painter->setPen(QPen(outline)); QColor highlightedGradientStartColor = highlight.lighter(120); QColor highlightedGradientStopColor = highlight; @@ -1471,7 +1473,7 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio QRect r = option->rect; if (act) { painter->setBrush(option->palette.highlight().color()); - painter->setPen(QPen(highlightOutline, 0)); + painter->setPen(QPen(highlightOutline)); painter->drawRect(r.adjusted(0, 0, -1, -1)); // painter->drawRoundedRect(r.adjusted(1, 1, -1, -1), 2, 2); @@ -1518,7 +1520,7 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio if (selected) { QRect r = option->rect; painter->fillRect(r, highlight); - painter->setPen(QPen(highlightOutline, 0)); + painter->setPen(QPen(highlightOutline)); const QLine lines[4] = { QLine(QPoint(r.left() + 1, r.bottom()), QPoint(r.right() - 1, r.bottom())), QLine(QPoint(r.left() + 1, r.top()), QPoint(r.right() - 1, r.top())), @@ -2141,15 +2143,18 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption QPixmap upArrow = colorizedImage(QLatin1String(":/qt-project.org/styles/commonstyle/images/fusion_arrow.png"), (spinBox->stepEnabled & QAbstractSpinBox::StepUpEnabled) ? arrowColor : disabledColor); - cachePainter.drawPixmap(QRect(upRect.center().x() - upArrow.width() / 4 + 1, - upRect.center().y() - upArrow.height() / 4 + 1, - upArrow.width()/2, upArrow.height()/2), upArrow); + QRectF upArrowRect = QRectF(upRect.center().x() - upArrow.width() / 4.0 + 1.0, + upRect.center().y() - upArrow.height() / 4.0 + 1.0, + upArrow.width() / 2.0, upArrow.height() / 2.0); + + cachePainter.drawPixmap(upArrowRect, upArrow, QRectF(QPointF(0.0, 0.0), upArrow.size())); QPixmap downArrow = colorizedImage(QLatin1String(":/qt-project.org/styles/commonstyle/images/fusion_arrow.png"), (spinBox->stepEnabled & QAbstractSpinBox::StepDownEnabled) ? arrowColor : disabledColor, 180); - cachePainter.drawPixmap(QRect(downRect.center().x() - downArrow.width() / 4 + 1, - downRect.center().y() - downArrow.height() / 4 + 1, - downArrow.width()/2, downArrow.height()/2), downArrow); + QRectF downArrowRect = QRectF(downRect.center().x() - downArrow.width() / 4.0 + 1.0, + downRect.center().y() - downArrow.height() / 4.0 + 1.0, + downArrow.width() / 2.0, downArrow.height() / 2.0); + cachePainter.drawPixmap(downArrowRect, downArrow, QRectF(QPointF(0.0, 0.0), downArrow.size())); } cachePainter.end(); @@ -2486,7 +2491,7 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption // Paint slider if (scrollBar->subControls & SC_ScrollBarSlider) { QRect pixmapRect = scrollBarSlider; - painter->setPen(QPen(alphaOutline, 0)); + painter->setPen(QPen(alphaOutline)); if (option->state & State_Sunken && scrollBar->activeSubControls & SC_ScrollBarSlider) painter->setBrush(midColor2); else if (option->state & State_MouseOver && scrollBar->activeSubControls & SC_ScrollBarSlider) @@ -2521,7 +2526,7 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption painter->setPen(Qt::NoPen); painter->drawRect(scrollBarSubLine.adjusted(horizontal ? 0 : 1, horizontal ? 1 : 0, 0, 0)); - painter->setPen(QPen(alphaOutline, 1)); + painter->setPen(QPen(alphaOutline)); if (option->state & State_Horizontal) { if (option->direction == Qt::RightToLeft) { pixmapRect.setLeft(scrollBarSubLine.left()); @@ -2545,9 +2550,10 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption rotation = option->direction == Qt::LeftToRight ? -90 : 90; QRect upRect = scrollBarSubLine.translated(horizontal ? -2 : -1, 0); QPixmap arrowPixmap = colorizedImage(QLatin1String(":/qt-project.org/styles/commonstyle/images/fusion_arrow.png"), arrowColor, rotation); - painter->drawPixmap(QRect(upRect.center().x() - arrowPixmap.width() / 4 + 2, - upRect.center().y() - arrowPixmap.height() / 4 + 1, - arrowPixmap.width()/2, arrowPixmap.height()/2), arrowPixmap); + painter->drawPixmap(QRectF(upRect.center().x() - arrowPixmap.width() / 4.0 + 2.0, + upRect.center().y() - arrowPixmap.height() / 4.0 + 1.0, + arrowPixmap.width() / 2.0, arrowPixmap.height() / 2.0), + arrowPixmap, QRectF(QPoint(0.0, 0.0), arrowPixmap.size())); } // The AddLine (down/right) button @@ -2584,9 +2590,10 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption rotation = option->direction == Qt::LeftToRight ? 90 : -90; QRect downRect = scrollBarAddLine.translated(-1, 1); QPixmap arrowPixmap = colorizedImage(QLatin1String(":/qt-project.org/styles/commonstyle/images/fusion_arrow.png"), arrowColor, rotation); - painter->drawPixmap(QRect(downRect.center().x() - arrowPixmap.width() / 4 + 2, - downRect.center().y() - arrowPixmap.height() / 4, - arrowPixmap.width()/2, arrowPixmap.height()/2), arrowPixmap); + painter->drawPixmap(QRectF(downRect.center().x() - arrowPixmap.width() / 4.0 + 2.0, + downRect.center().y() - arrowPixmap.height() / 4.0, + arrowPixmap.width() / 2.0, arrowPixmap.height() / 2.0), + arrowPixmap, QRectF(QPoint(0.0, 0.0), arrowPixmap.size())); } } @@ -2640,7 +2647,7 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption downArrowRect.left() - 6: downArrowRect.right() + 6); proxy()->drawPrimitive(PE_PanelButtonCommand, &buttonOption, &cachePainter, widget); cachePainter.restore(); - cachePainter.setPen( QPen(hasFocus ? option->palette.highlight() : outline.lighter(110), 0)); + cachePainter.setPen( QPen(hasFocus ? option->palette.highlight() : outline.lighter(110), 1)); if (!sunken) { int borderSize = 1; @@ -2677,9 +2684,10 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption QColor arrowColor = option->palette.buttonText().color(); arrowColor.setAlpha(220); QPixmap downArrow = colorizedImage(QLatin1String(":/qt-project.org/styles/commonstyle/images/fusion_arrow.png"), arrowColor, 180); - cachePainter.drawPixmap(QRect(downArrowRect.center().x() - downArrow.width() / 4 + 1, - downArrowRect.center().y() - downArrow.height() / 4 + 1, - downArrow.width()/2, downArrow.height()/2), downArrow); + cachePainter.drawPixmap(QRectF(downArrowRect.center().x() - downArrow.width() / 4.0 + 1.0, + downArrowRect.center().y() - downArrow.height() / 4.0 + 1.0, + downArrow.width() / 2.0, downArrow.height() / 2.0), + downArrow, QRectF(QPointF(0.0, 0.0), downArrow.size())); } cachePainter.end(); QPixmapCache::insert(pixmapName, cache); @@ -2730,7 +2738,7 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption gradient.setStart(pixmapRect.left(), pixmapRect.center().y()); gradient.setFinalStop(pixmapRect.right(), pixmapRect.center().y()); } - groovePainter.setPen(QPen(outline, 0)); + groovePainter.setPen(QPen(outline)); gradient.setColorAt(0, grooveColor.darker(110)); gradient.setColorAt(1, grooveColor.lighter(110));//palette.button().color().darker(115)); groovePainter.setBrush(gradient); @@ -2764,7 +2772,7 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption groovePainter.setRenderHint(QPainter::Antialiasing, true); groovePainter.translate(0.5, 0.5); - groovePainter.setPen(QPen(outline, 0)); + groovePainter.setPen(QPen(outline)); gradient.setColorAt(0, activeHighlight); gradient.setColorAt(1, activeHighlight.lighter(130)); groovePainter.setBrush(gradient); @@ -2813,9 +2821,9 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption handlePainter.setBrush(QColor(0, 0, 0, 40)); handlePainter.drawRect(r.adjusted(-1, 2, 1, -2)); - handlePainter.setPen(QPen(d->outline(option->palette), 1)); + handlePainter.setPen(QPen(d->outline(option->palette))); if (option->state & State_HasFocus && option->state & State_KeyboardFocusChange) - handlePainter.setPen(QPen(d->highlightedOutline(option->palette), 1)); + handlePainter.setPen(QPen(d->highlightedOutline(option->palette))); handlePainter.setBrush(gradient); handlePainter.drawRoundedRect(r, 2, 2); diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm index 759b3678d7..89ea4d553a 100644 --- a/src/widgets/styles/qmacstyle_mac.mm +++ b/src/widgets/styles/qmacstyle_mac.mm @@ -462,7 +462,7 @@ class QMacCGContext { CGContextRef context; public: - QMacCGContext(QPainter *p); //qpaintengine_mac.cpp + QMacCGContext(QPainter *p); inline QMacCGContext() { context = 0; } inline QMacCGContext(const QPaintDevice *pdev) { extern CGContextRef qt_mac_cg_context(const QPaintDevice *); @@ -6476,6 +6476,18 @@ void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig } } +// move to QRegion? +void qt_mac_scale_region(QRegion *region, qreal scaleFactor) +{ + QVector scaledRects; + scaledRects.reserve(region->rects().count()); + + foreach (const QRect &rect, region->rects()) { + scaledRects.append(QRect(rect.topLeft(), rect.size() * scaleFactor)); + } + region->setRects(&scaledRects[0], scaledRects.count()); +} + QMacCGContext::QMacCGContext(QPainter *p) { QPaintEngine *pe = p->paintEngine(); @@ -6502,20 +6514,28 @@ QMacCGContext::QMacCGContext(QPainter *p) CGContextScaleCTM(context, 1, -1); if (devType == QInternal::Widget) { - QRegion clip = p->paintEngine()->systemClip(); - QTransform native = p->deviceTransform(); + // Set the clip rect which is an intersection of the system clip + // and the painter clip. To make matters more interesting these + // are in device pixels and device-independent pixels, respectively. + const qreal devicePixelRatio = image->devicePixelRatio(); + + QRegion clip = p->paintEngine()->systemClip(); // get system clip in device pixels + QTransform native = p->deviceTransform(); // get device transform. dx/dy is in device pixels if (p->hasClipping()) { - QRegion r = p->clipRegion(); + QRegion r = p->clipRegion(); // get painter clip, which is in device-independent pixels + qt_mac_scale_region(&r, devicePixelRatio); // scale painter clip to device pixels r.translate(native.dx(), native.dy()); if (clip.isEmpty()) clip = r; else clip &= r; } - qt_mac_clip_cg(context, clip, 0); + qt_mac_clip_cg(context, clip, 0); // clip in device pixels - CGContextTranslateCTM(context, native.dx(), native.dy()); + // Scale the context so that painting happens in device-independet pixels. + CGContextScaleCTM(context, devicePixelRatio, devicePixelRatio); + CGContextTranslateCTM(context, native.dx() / devicePixelRatio, native.dy() / devicePixelRatio); } } else { qDebug() << "QMacCGContext:: Unsupported painter devtype type" << devType; diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index 76d6efadee..ab66cdae9f 100644 --- a/src/widgets/styles/qstyle.cpp +++ b/src/widgets/styles/qstyle.cpp @@ -618,10 +618,11 @@ void QStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, c void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, const QPixmap &pixmap) const { - QRect aligned = alignedRect(QApplication::layoutDirection(), QFlag(alignment), pixmap.size(), rect); + int scale = pixmap.devicePixelRatio(); + QRect aligned = alignedRect(QApplication::layoutDirection(), QFlag(alignment), pixmap.size() / scale, rect); QRect inter = aligned.intersected(rect); - painter->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width(), inter.height()); + painter->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width() * scale, inter.height() *scale); } /*! diff --git a/src/widgets/styles/qstyle_p.h b/src/widgets/styles/qstyle_p.h index 85e8e54b16..78dfc6fed9 100644 --- a/src/widgets/styles/qstyle_p.h +++ b/src/widgets/styles/qstyle_p.h @@ -43,6 +43,7 @@ #define QSTYLE_P_H #include "private/qobject_p.h" +#include #include QT_BEGIN_NAMESPACE @@ -74,12 +75,18 @@ public: inline QImage styleCacheImage(const QSize &size) { - return QImage(size, QImage::Format_ARGB32_Premultiplied); + const qreal pixelRatio = qApp->devicePixelRatio(); + QImage cacheImage = QImage(size * pixelRatio, QImage::Format_ARGB32_Premultiplied); + cacheImage.setDevicePixelRatio(pixelRatio); + return cacheImage; } inline QPixmap styleCachePixmap(const QSize &size) { - return QPixmap(size); + const qreal pixelRatio = qApp->devicePixelRatio(); + QPixmap cachePixmap = QPixmap(size * pixelRatio); + cachePixmap.setDevicePixelRatio(pixelRatio); + return cachePixmap; } #define BEGIN_STYLE_PIXMAPCACHE(a) \ diff --git a/src/widgets/widgets/qlabel.cpp b/src/widgets/widgets/qlabel.cpp index a133b9c310..3b3d15f6d0 100644 --- a/src/widgets/widgets/qlabel.cpp +++ b/src/widgets/widgets/qlabel.cpp @@ -562,17 +562,18 @@ QSize QLabelPrivate::sizeForWidth(int w) const int vextra = hextra; QFontMetrics fm = q->fontMetrics(); - if (pixmap && !pixmap->isNull()) + if (pixmap && !pixmap->isNull()) { br = pixmap->rect(); + br.setSize(br.size() / pixmap->devicePixelRatio()); #ifndef QT_NO_PICTURE - else if (picture && !picture->isNull()) + } else if (picture && !picture->isNull()) { br = picture->boundingRect(); #endif #ifndef QT_NO_MOVIE - else if (movie && !movie->currentPixmap().isNull()) + } else if (movie && !movie->currentPixmap().isNull()) { br = movie->currentPixmap().rect(); #endif - else if (isTextLabel) { + } else if (isTextLabel) { int align = QStyle::visualAlignment(textDirection(), QFlag(this->align)); // Add indentation int m = indent; diff --git a/tests/manual/highdpi/highdpi.pro b/tests/manual/highdpi/highdpi.pro new file mode 100644 index 0000000000..635ba37a38 --- /dev/null +++ b/tests/manual/highdpi/highdpi.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +TARGET = highdpi +DEPENDPATH += . +INCLUDEPATH += . +QT += widgets + +# Input +SOURCES += main.cpp + +RESOURCES += \ + highdpi.qrc + diff --git a/tests/manual/highdpi/highdpi.qrc b/tests/manual/highdpi/highdpi.qrc new file mode 100644 index 0000000000..b43c2c07ad --- /dev/null +++ b/tests/manual/highdpi/highdpi.qrc @@ -0,0 +1,7 @@ + + + qticon.png + qticon@2x.png + qticon_large.png + + diff --git a/tests/manual/highdpi/main.cpp b/tests/manual/highdpi/main.cpp new file mode 100644 index 0000000000..ee2d8ed29e --- /dev/null +++ b/tests/manual/highdpi/main.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** + ** + ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). + ** Contact: http://www.qt-project.org/legal + ** + ** This file is part of the QtGui module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** 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 Digia. For licensing terms and + ** conditions see http://qt.digia.com/licensing. For further information + ** use the contact form at http://qt.digia.com/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 2.1 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPL included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 2.1 requirements + ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Digia gives you certain additional + ** rights. These rights are described in the Digia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 3.0 as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU General Public License version 3.0 requirements will be + ** met: http://www.gnu.org/copyleft/gpl.html. + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include +#include +#include + + +class PixmapPainter : public QWidget +{ +public: + PixmapPainter(); + void paintEvent(QPaintEvent *event); + + QPixmap pixmap1X; + QPixmap pixmap2X; + QPixmap pixmapLarge; + QImage image1X; + QImage image2X; + QImage imageLarge; + QIcon qtIcon; +}; + + +PixmapPainter::PixmapPainter() +{ + pixmap1X = QPixmap(":/qticon.png"); + pixmap2X = QPixmap(":/qticon@2x.png"); + pixmapLarge = QPixmap(":/qticon_large.png"); + + image1X = QImage(":/qticon.png"); + image2X = QImage(":/qticon@2x.png"); + imageLarge = QImage(":/qticon_large.png"); + + qtIcon.addFile(":/qticon.png"); + qtIcon.addFile(":/qticon@2x.png"); +} + +void PixmapPainter::paintEvent(QPaintEvent *event) +{ + QPainter p(this); + p.fillRect(QRect(QPoint(0, 0), size()), QBrush(Qt::gray)); + + int pixmapPointSize = 64; + int y = 30; + int dy = 150; + + int x = 10; + int dx = 80; + // draw at point +// qDebug() << "paint pixmap" << pixmap1X.devicePixelRatio(); + p.drawPixmap(x, y, pixmap1X); + x+=dx;p.drawPixmap(x, y, pixmap2X); + x+=dx;p.drawPixmap(x, y, pixmapLarge); + x+=dx*2;p.drawPixmap(x, y, qtIcon.pixmap(QSize(pixmapPointSize, pixmapPointSize))); + x+=dx;p.drawImage(x, y, image1X); + x+=dx;p.drawImage(x, y, image2X); + x+=dx;p.drawImage(x, y, imageLarge); + + // draw at 64x64 rect + y+=dy; + x = 10; + p.drawPixmap(QRect(x, y, pixmapPointSize, pixmapPointSize), pixmap1X); + x+=dx;p.drawPixmap(QRect(x, y, pixmapPointSize, pixmapPointSize), pixmap2X); + x+=dx;p.drawPixmap(QRect(x, y, pixmapPointSize, pixmapPointSize), pixmapLarge); + x+=dx;p.drawPixmap(QRect(x, y, pixmapPointSize, pixmapPointSize), qtIcon.pixmap(QSize(pixmapPointSize, pixmapPointSize))); + x+=dx;p.drawImage(QRect(x, y, pixmapPointSize, pixmapPointSize), image1X); + x+=dx;p.drawImage(QRect(x, y, pixmapPointSize, pixmapPointSize), image2X); + x+=dx;p.drawImage(QRect(x, y, pixmapPointSize, pixmapPointSize), imageLarge); + + + // draw at 128x128 rect + y+=dy - 50; + x = 10; + p.drawPixmap(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), pixmap1X); + x+=dx * 2; p.drawPixmap(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), pixmap2X); + x+=dx * 2; p.drawPixmap(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), pixmapLarge); + x+=dx * 2; p.drawPixmap(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), qtIcon.pixmap(QSize(pixmapPointSize, pixmapPointSize))); + x+=dx * 2; p.drawImage(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), image1X); + x+=dx * 2; p.drawImage(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), image2X); + x+=dx * 2; p.drawImage(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), imageLarge); + } + +class Labels : public QWidget +{ +public: + Labels(); + + QPixmap pixmap1X; + QPixmap pixmap2X; + QPixmap pixmapLarge; + QIcon qtIcon; +}; + +Labels::Labels() +{ + pixmap1X = QPixmap(":/qticon.png"); + pixmap2X = QPixmap(":/qticon@2x.png"); + pixmapLarge = QPixmap(":/qticon_large.png"); + + qtIcon.addFile(":/qticon.png"); + qtIcon.addFile(":/qticon@2x.png"); + setWindowIcon(qtIcon); + setWindowTitle("Labels"); + + QLabel *label1x = new QLabel(); + label1x->setPixmap(pixmap1X); + QLabel *label2x = new QLabel(); + label2x->setPixmap(pixmap2X); + QLabel *labelIcon = new QLabel(); + labelIcon->setPixmap(qtIcon.pixmap(QSize(64,64))); + QLabel *labelLarge = new QLabel(); + labelLarge->setPixmap(pixmapLarge); + + QHBoxLayout *layout = new QHBoxLayout(this); +// layout->addWidget(label1x); //expected low-res on high-dpi displays + layout->addWidget(label2x); +// layout->addWidget(labelIcon); +// layout->addWidget(labelLarge); // expected large size and low-res + setLayout(layout); +} + +class MainWindow : public QMainWindow +{ +public: + MainWindow(); + + QIcon qtIcon; + QIcon qtIcon1x; + QIcon qtIcon2x; + + QToolBar *fileToolBar; +}; + +MainWindow::MainWindow() +{ + qtIcon.addFile(":/qticon.png"); + qtIcon.addFile(":/qticon@2x.png"); + qtIcon1x.addFile(":/qticon.png"); + qtIcon2x.addFile(":/qticon@2x.png"); + setWindowIcon(qtIcon); + setWindowTitle("MainWindow"); + + fileToolBar = addToolBar(tr("File")); +// fileToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + fileToolBar->addAction(new QAction(qtIcon, QString("1x and 2x"), this)); + fileToolBar->addAction(new QAction(qtIcon1x, QString("1x"), this)); + fileToolBar->addAction(new QAction(qtIcon2x, QString("2x"), this)); +} + +class StandardIcons : public QWidget +{ +public: + void paintEvent(QPaintEvent *event) + { + int x = 10; + int y = 10; + int dx = 50; + int dy = 50; + int maxX = 500; + + for (int iconIndex = QStyle::SP_TitleBarMenuButton; iconIndex < QStyle::SP_MediaVolumeMuted; ++iconIndex) { + QIcon icon = qApp->style()->standardIcon(QStyle::StandardPixmap(iconIndex)); + QPainter p(this); + p.drawPixmap(x, y, icon.pixmap(dx - 5, dy - 5)); + if (x + dx > maxX) + y+=dy; + x = ((x + dx) % maxX); + } + }; +}; + +class Caching : public QWidget +{ +public: + void paintEvent(QPaintEvent *event) + { + QSize layoutSize(75, 75); + + QPainter widgetPainter(this); + widgetPainter.fillRect(QRect(QPoint(0, 0), this->size()), Qt::gray); + + { + const qreal devicePixelRatio = this->windowHandle()->devicePixelRatio(); + QPixmap cache(layoutSize * devicePixelRatio); + cache.setDevicePixelRatio(devicePixelRatio); + + QPainter cachedPainter(&cache); + cachedPainter.fillRect(QRect(0,0, 75, 75), Qt::blue); + cachedPainter.fillRect(QRect(10,10, 55, 55), Qt::red); + cachedPainter.drawEllipse(QRect(10,10, 55, 55)); + + QPainter widgetPainter(this); + widgetPainter.drawPixmap(QPoint(10, 10), cache); + } + + { + const qreal devicePixelRatio = this->windowHandle()->devicePixelRatio(); + QImage cache = QImage(layoutSize * devicePixelRatio, QImage::QImage::Format_ARGB32_Premultiplied); + cache.setDevicePixelRatio(devicePixelRatio); + + QPainter cachedPainter(&cache); + cachedPainter.fillRect(QRect(0,0, 75, 75), Qt::blue); + cachedPainter.fillRect(QRect(10,10, 55, 55), Qt::red); + cachedPainter.drawEllipse(QRect(10,10, 55, 55)); + + QPainter widgetPainter(this); + widgetPainter.drawImage(QPoint(95, 10), cache); + } + + } +}; + +class Style : public QWidget { +public: + QPushButton *button; + QLineEdit *lineEdit; + QSlider *slider; + QHBoxLayout *row1; + + Style() { + row1 = new QHBoxLayout(); + setLayout(row1); + + button = new QPushButton(); + button->setText("Test Button"); + row1->addWidget(button); + + lineEdit = new QLineEdit(); + lineEdit->setText("Test Lineedit"); + row1->addWidget(lineEdit); + + slider = new QSlider(); + row1->addWidget(slider); + + row1->addWidget(new QSpinBox); + row1->addWidget(new QScrollBar); + + QTabBar *tab = new QTabBar(); + tab->addTab("Foo"); + tab->addTab("Bar"); + row1->addWidget(tab); + } +}; + +class Fonts : public QWidget +{ +public: + void paintEvent(QPaintEvent *event) + { + QPainter painter(this); + int y = 40; + for (int fontSize = 2; fontSize < 18; fontSize += 2) { + QFont font; + font.setPointSize(fontSize); + QString string = QString(QStringLiteral("%1 The quick brown fox jumped over the lazy Doug.")).arg(fontSize); + painter.setFont(font); + painter.drawText(10, y, string); + y += (fontSize * 2.5); + } + } +}; + + +template +void apiTestdevicePixelRatioGetter() +{ + if (0) { + T *t = 0; + t->devicePixelRatio(); + } +} + +template +void apiTestdevicePixelRatioSetter() +{ + if (0) { + T *t = 0; + t->setDevicePixelRatio(2.0); + } +} + +void apiTest() +{ + // compile call to devicePixelRatio getter and setter (verify spelling) + apiTestdevicePixelRatioGetter(); + apiTestdevicePixelRatioGetter(); + apiTestdevicePixelRatioGetter(); + + apiTestdevicePixelRatioGetter(); + apiTestdevicePixelRatioSetter(); + apiTestdevicePixelRatioGetter(); + apiTestdevicePixelRatioSetter(); +} + +int main(int argc, char **argv) +{ + qputenv("QT_HIGHDPI_AWARE", "1"); + QApplication app(argc, argv); + + PixmapPainter pixmapPainter; + +// Enable for lots of pixmap drawing + pixmapPainter.show(); + + Labels label; + label.resize(200, 200); + label.show(); + + MainWindow mainWindow; + mainWindow.show(); + + StandardIcons icons; + icons.resize(510, 510); + icons.show(); + + Caching caching; + caching.resize(300, 300); + caching.show(); + + Style style; + style.show(); + + Fonts fonts; + fonts.show(); + + return app.exec(); +} diff --git a/tests/manual/highdpi/qticon.png b/tests/manual/highdpi/qticon.png new file mode 100644 index 0000000000..76f02c6c96 Binary files /dev/null and b/tests/manual/highdpi/qticon.png differ diff --git a/tests/manual/highdpi/qticon@2x.png b/tests/manual/highdpi/qticon@2x.png new file mode 100644 index 0000000000..0b00c00c96 Binary files /dev/null and b/tests/manual/highdpi/qticon@2x.png differ diff --git a/tests/manual/highdpi/qticon_large.png b/tests/manual/highdpi/qticon_large.png new file mode 100644 index 0000000000..0b00c00c96 Binary files /dev/null and b/tests/manual/highdpi/qticon_large.png differ diff --git a/tests/manual/lance/main.cpp b/tests/manual/lance/main.cpp index 97197f0509..c9458f5657 100644 --- a/tests/manual/lance/main.cpp +++ b/tests/manual/lance/main.cpp @@ -121,6 +121,7 @@ static void printHelp() " -commands Displays all available commands\n" " -w Width of the paintdevice\n" " -h Height of the paintdevice\n" + " -scalefactor Scale factor (device pixel ratio) of the paintdevice\n" " -cmp Show the reference picture\n" " -bg-white No checkers background\n"); } @@ -238,6 +239,8 @@ int main(int argc, char **argv) bool highres = false; bool show_cmp = false; int width = 800, height = 800; + int scaledWidth = width, scaledHeight = height; + qreal scalefactor = 1.0; bool verboseMode = false; #ifndef QT_NO_OPENGL @@ -328,6 +331,9 @@ int main(int argc, char **argv) } else if (option == "h") { Q_ASSERT_X(i + 1 < argc, "main", "-h must be followed by a value"); height = atoi(argv[++i]); + } else if (option == "scalefactor") { + Q_ASSERT_X(i + 1 < argc, "main", "-scalefactor must be followed by a value"); + scalefactor = atof(argv[++i]); } else if (option == "cmp") { show_cmp = true; } else if (option == "bg-white") { @@ -350,6 +356,8 @@ int main(int argc, char **argv) #endif } } + scaledWidth = width * scalefactor; + scaledHeight = height * scalefactor; PaintCommands pcmd(QStringList(), 800, 800); pcmd.setVerboseMode(verboseMode); @@ -514,7 +522,8 @@ int main(int argc, char **argv) #endif case PixmapType: { - QPixmap pixmap(width, height); + QPixmap pixmap(scaledWidth, scaledHeight); + pixmap.setDevicePixelRatio(scalefactor); pixmap.fill(Qt::white); QPainter pt(&pixmap); pcmd.setPainter(&pt); @@ -527,7 +536,8 @@ int main(int argc, char **argv) case BitmapType: { - QBitmap bitmap(width, height); + QBitmap bitmap(scaledWidth, scaledHeight); + bitmap.setDevicePixelRatio(scalefactor); QPainter pt(&bitmap); pcmd.setPainter(&pt); pcmd.setFilePath(fileinfo.absolutePath()); @@ -547,9 +557,10 @@ int main(int argc, char **argv) case ImageType: { qDebug() << "Creating image"; - QImage image(width, height, type == ImageMonoType + QImage image(scaledWidth, scaledHeight, type == ImageMonoType ? QImage::Format_MonoLSB : imageFormat); + image.setDevicePixelRatio(scalefactor); image.fill(0); QPainter pt(&image); pcmd.setPainter(&pt); @@ -557,6 +568,7 @@ int main(int argc, char **argv) pcmd.runCommands(); pt.end(); image.convertToFormat(QImage::Format_ARGB32).save("output_image.png", "PNG"); + image.setDevicePixelRatio(1.0); // reset scale factor: display "large" image. #ifndef CONSOLE_APPLICATION QLabel *label = createLabel(); label->setPixmap(QPixmap::fromImage(image)); -- cgit v1.2.3