Skip to content
Permalink
Browse files

Basic high-dpi "retina" support for Qt 5.

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 <morten.sorvig@digia.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>
  • Loading branch information
Morten Johan Sørvig The Qt Project
Morten Johan Sørvig authored and The Qt Project committed Nov 20, 2012
1 parent c8dc41b commit 5e61bbe586519c3d9bc636153d32e810da4e59a3
Showing with 933 additions and 118 deletions.
  1. +61 −6 src/gui/image/qimage.cpp
  2. +3 −0 src/gui/image/qimage.h
  3. +5 −2 src/gui/image/qimage_p.h
  4. +5 −0 src/gui/image/qimagereader.cpp
  5. +44 −0 src/gui/image/qpixmap.cpp
  6. +3 −0 src/gui/image/qpixmap.h
  7. +10 −0 src/gui/image/qpixmap_blitter.cpp
  8. +3 −0 src/gui/image/qpixmap_blitter_p.h
  9. +16 −3 src/gui/image/qpixmap_raster.cpp
  10. +3 −0 src/gui/image/qpixmap_raster_p.h
  11. +3 −0 src/gui/image/qplatformpixmap.h
  12. +28 −1 src/gui/kernel/qguiapplication.cpp
  13. +1 −0 src/gui/kernel/qguiapplication.h
  14. +12 −0 src/gui/kernel/qplatformscreen.cpp
  15. +1 −0 src/gui/kernel/qplatformscreen.h
  16. +12 −0 src/gui/kernel/qplatformwindow.cpp
  17. +2 −0 src/gui/kernel/qplatformwindow.h
  18. +14 −0 src/gui/kernel/qscreen.cpp
  19. +2 −0 src/gui/kernel/qscreen.h
  20. +18 −0 src/gui/kernel/qwindow.cpp
  21. +2 −0 src/gui/kernel/qwindow.h
  22. +24 −5 src/gui/painting/qpaintengine_raster.cpp
  23. +2 −2 src/gui/painting/qpaintengineex.cpp
  24. +38 −10 src/gui/painting/qpainter.cpp
  25. +1 −0 src/gui/painting/qpainter_p.h
  26. +3 −1 src/opengl/qgl.cpp
  27. +4 −1 src/opengl/qgl_qpa.cpp
  28. +1 −0 src/plugins/platforms/cocoa/qcocoabackingstore.h
  29. +22 −4 src/plugins/platforms/cocoa/qcocoabackingstore.mm
  30. +12 −0 src/plugins/platforms/cocoa/qcocoaglcontext.mm
  31. +1 −0 src/plugins/platforms/cocoa/qcocoaintegration.h
  32. +12 −0 src/plugins/platforms/cocoa/qcocoaintegration.mm
  33. +2 −0 src/plugins/platforms/cocoa/qcocoawindow.h
  34. +15 −0 src/plugins/platforms/cocoa/qcocoawindow.mm
  35. +21 −10 src/plugins/platforms/cocoa/qnsview.mm
  36. +6 −4 src/widgets/itemviews/qitemdelegate.cpp
  37. +23 −6 src/widgets/kernel/qwidget.cpp
  38. +1 −0 src/widgets/kernel/qwidget_p.h
  39. +54 −46 src/widgets/styles/qfusionstyle.cpp
  40. +26 −6 src/widgets/styles/qmacstyle_mac.mm
  41. +3 −2 src/widgets/styles/qstyle.cpp
  42. +9 −2 src/widgets/styles/qstyle_p.h
  43. +5 −4 src/widgets/widgets/qlabel.cpp
  44. +12 −0 tests/manual/highdpi/highdpi.pro
  45. +7 −0 tests/manual/highdpi/highdpi.qrc
  46. +366 −0 tests/manual/highdpi/main.cpp
  47. BIN tests/manual/highdpi/qticon.png
  48. BIN tests/manual/highdpi/qticon@2x.png
  49. BIN tests/manual/highdpi/qticon_large.png
  50. +15 −3 tests/manual/lance/main.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<QRgb> QImage::colorTable() const
return d ? d->colortable : QVector<QRgb>();
}

/*!
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<QRgb> &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
@@ -211,6 +211,9 @@ class Q_GUI_EXPORT QImage : public QPaintDevice
QVector<QRgb> colorTable() const;
void setColorTable(const QVector<QRgb> colors);

qreal devicePixelRatio() const;
void setDevicePixelRatio(qreal scaleFactor);

void fill(uint pixel);
void fill(const QColor &color);
void fill(Qt::GlobalColor color);
@@ -74,15 +74,18 @@ struct Q_GUI_EXPORT QImageData { // internal image data
int height;
int depth;
int nbytes; // number of bytes data
qreal devicePixelRatio;
QVector<QRgb> colortable;
uchar *data;
QImage::Format format;
int bytes_per_line;
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;
@@ -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;
}

@@ -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.
@@ -102,6 +102,9 @@ class Q_GUI_EXPORT QPixmap : public QPaintDevice
QBitmap mask() const;
void setMask(const QBitmap &);

qreal devicePixelRatio() const;
void setDevicePixelRatio(qreal scaleFactor);

bool hasAlpha() const;
bool hasAlphaChannel() const;

@@ -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) {
@@ -66,6 +66,8 @@ class Q_GUI_EXPORT QBlittablePlatformPixmap : public QPlatformPixmap
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 @@ class Q_GUI_EXPORT QBlittablePlatformPixmap : public QPlatformPixmap
QScopedPointer<QBlitterPaintEngine> m_engine;
QScopedPointer<QBlittable> m_blittable;
bool m_alpha;
qreal m_devicePixelRatio;

#ifdef QT_BLITTER_RASTEROVERLAY
QImage *m_rasterOverlay;
@@ -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 &image;
}

qreal QRasterPlatformPixmap::devicePixelRatio() const
{
return image.devicePixelRatio();
}

void QRasterPlatformPixmap::setDevicePixelRatio(qreal scaleFactor)
{
image.setDevicePixelRatio(scaleFactor);
}

QT_END_NAMESPACE
@@ -79,6 +79,9 @@ class Q_GUI_EXPORT QRasterPlatformPixmap : public QPlatformPixmap
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;
@@ -108,6 +108,9 @@ class Q_GUI_EXPORT QPlatformPixmap
inline PixelType pixelType() const { return type; }
inline ClassId classId() const { return static_cast<ClassId>(id); }

virtual qreal devicePixelRatio() const = 0;
virtual void setDevicePixelRatio(qreal scaleFactor) = 0;

virtual QImage* buffer();

inline int width() const { return w; }
@@ -703,7 +703,34 @@ QList<QScreen *> 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)
{
@@ -104,6 +104,7 @@ class Q_GUI_EXPORT QGuiApplication : public QCoreApplication

static QScreen *primaryScreen();
static QList<QScreen *> screens();
qreal devicePixelRatio() const;

#ifndef QT_NO_CURSOR
static QCursor *overrideCursor();
@@ -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.
@@ -98,6 +98,7 @@ class Q_GUI_EXPORT QPlatformScreen

virtual QSizeF physicalSize() const;
virtual QDpi logicalDpi() const;
virtual qreal devicePixelRatio() const;

virtual qreal refreshRate() const;

0 comments on commit 5e61bbe

Please sign in to comment.
You can’t perform that action at this time.