Index: src/corelib/global/qnamespace.qdoc =================================================================== --- src/corelib/global/qnamespace.qdoc (revision 3880) +++ src/corelib/global/qnamespace.qdoc (revision 3881) @@ -180,6 +180,14 @@ be set before QApplication is constructed. This attribute is only supported in Symbian platform. + \value AA_UseHighDpiPixmaps Make QIcon::pixmap() generate high-dpi pixmaps + that can be larger than the requested size. Such pixmaps will have + devicePixelRatio set to a value higher than 1. + + After setting this attribute application code that uses pixmap + sizes in layout geometry calculations should typically divide by + QPixmap::devicePixelRatio() to get device-independent layout geometry. + \omitvalue AA_AttributeCount */ Index: src/corelib/global/qnamespace.h =================================================================== --- src/corelib/global/qnamespace.h (revision 3880) +++ src/corelib/global/qnamespace.h (revision 3881) @@ -547,6 +547,7 @@ AA_S60DisablePartialScreenInputMode = 9, AA_X11InitThreads = 10, AA_CaptureMultimediaKeys = 11, + AA_UseHighDpiPixmaps = 12, // Add new attributes before this line AA_AttributeCount Index: src/gui/kernel/qwidget.h =================================================================== --- src/gui/kernel/qwidget.h (revision 3880) +++ src/gui/kernel/qwidget.h (revision 3881) @@ -294,6 +294,8 @@ void setMinimumHeight(int minh); void setMaximumWidth(int maxw); void setMaximumHeight(int maxh); + + qreal devicePixelRatio() const; #ifdef Q_QDOC void setupUi(QWidget *widget); Index: src/gui/kernel/qwidget.cpp =================================================================== --- src/gui/kernel/qwidget.cpp (revision 3880) +++ src/gui/kernel/qwidget.cpp (revision 3881) @@ -2934,6 +2934,24 @@ } /*! + 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 QApplication::devicePixelRatio() +*/ +qreal QWidget::devicePixelRatio() const +{ +#ifndef Q_WS_MAC + return 1.0; +#else + return qt_mac_get_scalefactor(this); +#endif +} + +/*! \fn bool QWidget::underMouse() const Returns true if the widget is under the mouse cursor; otherwise Index: src/gui/kernel/qapplication.h =================================================================== --- src/gui/kernel/qapplication.h (revision 3880) +++ src/gui/kernel/qapplication.h (revision 3881) @@ -166,8 +166,9 @@ static void setWindowIcon(const QIcon &icon); static QIcon windowIcon(); + + qreal devicePixelRatio() const; - #ifdef QT3_SUPPORT static QT3_SUPPORT QWidget *mainWidget(); static QT3_SUPPORT void setMainWidget(QWidget *); Index: src/gui/styles/qmacstyle_mac.mm =================================================================== --- src/gui/styles/qmacstyle_mac.mm (revision 3880) +++ src/gui/styles/qmacstyle_mac.mm (revision 3881) @@ -3257,9 +3257,9 @@ QPixmap pixmap = header->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize), mode); QRect pixr = header->rect; - pixr.setY(header->rect.center().y() - (pixmap.height() - 1) / 2); + pixr.setY(header->rect.center().y() - (pixmap.height() / pixmap.devicePixelRatio() - 1) / 2); proxy()->drawItemPixmap(p, pixr, Qt::AlignVCenter, pixmap); - textr.translate(pixmap.width() + 2, 0); + textr.translate(pixmap.width() / pixmap.devicePixelRatio() + 2, 0); } proxy()->drawItemText(p, textr, header->textAlignment | Qt::AlignVCenter, header->palette, @@ -3311,15 +3311,15 @@ if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) { QMainWindow *mw = qobject_cast(w->window()); if (mw && mw->unifiedTitleAndToolBarOnMac()) { - pr.setHeight(pixmap.size().height()); + pr.setHeight(pixmap.size().height() / pixmap.devicePixelRatio()); cr.adjust(0, pr.bottom() + 1, 0, 1); } else { - pr.setHeight(pixmap.size().height() + 6); + pr.setHeight(pixmap.size().height() / pixmap.devicePixelRatio() + 6); cr.adjust(0, pr.bottom(), 0, -3); } alignment |= Qt::AlignCenter; } else { - pr.setWidth(pixmap.width() + 8); + pr.setWidth(pixmap.width() / pixmap.devicePixelRatio() + 8); cr.adjust(pr.right(), 0, 0, 0); alignment |= Qt::AlignLeft | Qt::AlignVCenter; } @@ -3510,10 +3510,12 @@ if (btn->state & State_On) state = QIcon::On; QPixmap pixmap = btn->icon.pixmap(btn->iconSize, mode, state); - contentW += pixmap.width() + QMacStylePrivate::PushButtonContentPadding; + int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio(); + int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio(); + contentW += pixmapWidth + QMacStylePrivate::PushButtonContentPadding; int iconLeftOffset = freeContentRect.x() + (freeContentRect.width() - contentW) / 2; - int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmap.height()) / 2; - QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmap.width(), pixmap.height()); + int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapHeight) / 2; + QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapWidth, pixmapHeight); QRect visualIconDestRect = visualRect(btn->direction, freeContentRect, iconDestRect); proxy()->drawItemPixmap(p, visualIconDestRect, Qt::AlignLeft | Qt::AlignVCenter, pixmap); int newOffset = iconDestRect.x() + iconDestRect.width() @@ -3954,8 +3956,8 @@ iconSize = comboBox->iconSize(); } QPixmap pixmap = mi->icon.pixmap(iconSize, mode); - int pixw = pixmap.width(); - int pixh = pixmap.height(); + int pixw = pixmap.width() / pixmap.devicePixelRatio(); + int pixh = pixmap.height() / pixmap.devicePixelRatio(); QRect cr(xpos, contentRect.y(), checkcol, contentRect.height()); QRect pmr(0, 0, pixw, pixh); pmr.moveCenter(cr.center()); @@ -6036,6 +6038,7 @@ QPixmap pixmap(qt_mac_toolbar_ext); if (standardIcon == SP_ToolBarVerticalExtensionButton) { QPixmap pix2(pixmap.height(), pixmap.width()); + pix2.setDevicePixelRatio(pixmap.devicePixelRatio()); pix2.fill(Qt::transparent); QPainter p(&pix2); p.translate(pix2.width(), 0); Index: src/gui/styles/qstyle.cpp =================================================================== --- src/gui/styles/qstyle.cpp (revision 3880) +++ src/gui/styles/qstyle.cpp (revision 3881) @@ -555,10 +555,11 @@ 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); } /*! Index: src/gui/widgets/qlabel.cpp =================================================================== --- src/gui/widgets/qlabel.cpp (revision 3880) +++ src/gui/widgets/qlabel.cpp (revision 3881) @@ -644,10 +644,11 @@ 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 Index: src/gui/kernel/qwidget_mac.mm =================================================================== --- src/gui/kernel/qwidget_mac.mm (revision 3880) +++ src/gui/kernel/qwidget_mac.mm (revision 3881) @@ -3279,8 +3279,7 @@ if (icon.isNull()) { [iconButton setImage:nil]; } else { - QPixmap scaled = pm->scaled(QSize(16,16), Qt::KeepAspectRatio, Qt::SmoothTransformation); - NSImage *image = static_cast(qt_mac_create_nsimage(scaled)); + NSImage *image = static_cast(qt_mac_create_nsimage(*pm)); [iconButton setImage:image]; [image release]; } Index: src/gui/kernel/qt_cocoa_helpers_mac_p.h =================================================================== --- src/gui/kernel/qt_cocoa_helpers_mac_p.h (revision 3880) +++ src/gui/kernel/qt_cocoa_helpers_mac_p.h (revision 3881) @@ -204,7 +204,7 @@ void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow); -CGFloat qt_mac_get_scalefactor(); +Q_GUI_EXPORT CGFloat qt_mac_get_scalefactor(const QWidget *window = 0); QString qt_mac_get_pasteboardString(OSPasteboardRef paste); #ifdef __OBJC__ Index: src/gui/kernel/qt_cocoa_helpers_mac.mm =================================================================== --- src/gui/kernel/qt_cocoa_helpers_mac.mm (revision 3880) +++ src/gui/kernel/qt_cocoa_helpers_mac.mm (revision 3881) @@ -1551,19 +1551,44 @@ #endif } -CGFloat qt_mac_get_scalefactor() +#ifndef QT_MAC_USE_COCOA +CGFloat qt_mac_get_scalefactor(const QWidget *window) { -#ifndef QT_MAC_USE_COCOA + Q_UNUSED(window); return HIGetScaleFactor(); +} #endif +#ifdef QT_MAC_USE_COCOA +CGFloat qt_mac_get_scalefactor(const QWidget *window) + { + // No high-dpi support on 10.6 and below #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) - NSScreen *mainScreen = [NSScreen mainScreen]; - if ([mainScreen respondsToSelector:@selector(backingScaleFactor)]) - return [mainScreen backingScaleFactor]; + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { + if (window == 0) { + // If there is no window given we answer the question + // "Are there any HiDPI screens connected?" by returning + // the highest scale factor found. + CGFloat highestScaleFactor = 1.0; + NSArray *screens = [NSScreen screens]; + for (id screen in screens) { + highestScaleFactor = qMax(highestScaleFactor, [screen backingScaleFactor]); + } + return highestScaleFactor; + } else { + if (window->inherits("QGLWidget") && ![qt_mac_nativeview_for(window) wantsBestResolutionOpenGLSurface]) { + return 1.0; + } else { + return [qt_mac_window_for(window) backingScaleFactor]; + } + } + } else #endif - return 1.0; + { + return 1.0; // return 1.0 when compiled on or running on 10.6 and lower. + } } +#endif QString qt_mac_get_pasteboardString(OSPasteboardRef paste) { Index: src/gui/painting/qpainter.cpp =================================================================== --- src/gui/painting/qpainter.cpp (revision 3880) +++ src/gui/painting/qpainter.cpp (revision 3881) @@ -5389,7 +5389,8 @@ 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)); } } @@ -5419,6 +5420,11 @@ 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 int pmscale = pm.devicePixelRatio(); + // Sanity-check clipping if (sw <= 0) sw = pm.width() - sx; @@ -5427,9 +5433,9 @@ 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; @@ -5518,7 +5524,7 @@ x += d->state->matrix.dx(); y += d->state->matrix.dy(); } - d->engine->drawPixmap(QRectF(x, y, w, h), pm, QRectF(sx, sy, sw, sh)); + d->engine->drawPixmap(QRectF(x, y, w , h), pm, QRectF(sx, sy, sw, sh)); } } @@ -5677,7 +5683,8 @@ y += d->state->matrix.dy(); } - d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(0, 0, w, h), Qt::AutoColor); + int scale = image.devicePixelRatio(); + 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, @@ -5696,6 +5703,7 @@ qreal sy = sourceRect.y(); qreal sw = sourceRect.width(); qreal sh = sourceRect.height(); + int imageScale = image.devicePixelRatio(); // Sanity-check clipping if (sw <= 0) @@ -5705,9 +5713,9 @@ 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; Index: src/gui/itemviews/qstyleditemdelegate.cpp =================================================================== --- src/gui/itemviews/qstyleditemdelegate.cpp (revision 3880) +++ src/gui/itemviews/qstyleditemdelegate.cpp (revision 3881) @@ -356,7 +356,8 @@ else mode = QIcon::Normal; QIcon::State state = option->state & QStyle::State_Open ? QIcon::On : QIcon::Off; - v4->decorationSize = v4->icon.actualSize(option->decorationSize, mode, state); + QSize actualSize = v4->icon.actualSize(option->decorationSize, mode, state); + v4->decorationSize = QSize(qMin(v4->decorationSize.width(), actualSize.width()), qMin(v4->decorationSize.height(), actualSize.height())); break; } case QVariant::Color: { Index: src/gui/kernel/qapplication.cpp =================================================================== --- src/gui/kernel/qapplication.cpp (revision 3880) +++ src/gui/kernel/qapplication.cpp (revision 3881) @@ -2125,6 +2125,33 @@ } /*! + 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 QWidget::devicePixelRatio() instead. + + \sa QWidget::devicePixelRatio() +*/ +qreal QApplication::devicePixelRatio() const +{ + // Cache topDevicePixelRatio, iterate through the screen list once only. + static qreal topDevicePixelRatio = 0.0; + if (!qFuzzyIsNull(topDevicePixelRatio)) { + return topDevicePixelRatio; + } + +#ifndef Q_WS_MAC + topDevicePixelRatio = 1.0; +#else + topDevicePixelRatio = qt_mac_get_scalefactor(); +#endif + + return topDevicePixelRatio; +} + +/*! Returns a list of the top-level widgets (windows) in the application. \note Some of the top-level widgets may be hidden, for example a tooltip if Index: src/gui/itemviews/qitemdelegate.cpp =================================================================== --- src/gui/itemviews/qitemdelegate.cpp (revision 3880) +++ src/gui/itemviews/qitemdelegate.cpp (revision 3881) @@ -1084,10 +1084,12 @@ 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); Index: src/gui/image/qimage.cpp =================================================================== --- src/gui/image/qimage.cpp (revision 3880) +++ src/gui/image/qimage.cpp (revision 3881) @@ -127,7 +127,7 @@ 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), #ifdef QT3_SUPPORT jumptable(0), #endif @@ -1500,6 +1500,7 @@ 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; #ifndef QT_NO_IMAGE_TEXT @@ -1728,8 +1729,51 @@ return d ? d->colortable : QVector(); } +/*! + Returns the current dpi scale factor for the image. + Common values for the scale factor is 1.0 (the default), + and 2.0 for images intended for display on High DPI displays. + + Use this function when calculating layouts based on the + image size. Layout size is pixel size divided by the scale + factor. + + \sa setScaleFactor() +*/ +qreal QImage::devicePixelRatio() const +{ + if (!d) + return qreal(1.0); + return d->devicePixelRatio; +} + /*! + Sets the dpi scale factor for the image. + + The scale factor is typically set to 2.0 when producing + images for high-dpi displays. This informs layout code + paths in Qt which use the image size that the image is + a high-resolution image, and not a large image. + + Qt supports using the "@2x" suffix when loading + images from files. Loading "myicon@2x.png" will result + in an image with a 2x scale factor. + + Setting the scale factor will also change the dpi information + returned by QPaindevice::metric(): Physical dpi will logical dpi + multiplied by the scale factor. + + \sa scaleFactor() +*/ +void QImage::setDevicePixelRatio(qreal scale) +{ + detach(); + d->devicePixelRatio = scale; +} + + +/*! \obsolete Returns the number of bytes occupied by the image data. @@ -3894,6 +3938,7 @@ image.setDotsPerMeterY(dotsPerMeterY()); image.setDotsPerMeterX(dotsPerMeterX()); + image.setDevicePixelRatio(devicePixelRatio()); #if !defined(QT_NO_IMAGE_TEXT) image.d->text = d->text; @@ -3943,6 +3988,7 @@ const QVector &clut) { QImage dest(src.size(), format); dest.setColorTable(clut); + dest.setDevicePixelRatio(src.devicePixelRatio()); #if !defined(QT_NO_IMAGE_TEXT) QString textsKeys = src.text(); @@ -4299,6 +4345,7 @@ image.setDotsPerMeterX(dotsPerMeterX()); image.setDotsPerMeterY(dotsPerMeterY()); + image.setDevicePixelRatio(devicePixelRatio()); image.d->colortable = d->colortable; return image; @@ -4796,6 +4843,7 @@ result.d->colortable = d->colortable; result.d->has_alpha_clut = d->has_alpha_clut; + result.d->devicePixelRatio = d->devicePixelRatio; if (depth() == 1) w = (w+7)/8; @@ -5838,11 +5886,11 @@ 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: @@ -6627,6 +6675,7 @@ dImage.d->dpmx = dotsPerMeterX(); dImage.d->dpmy = dotsPerMeterY(); + dImage.d->devicePixelRatio = devicePixelRatio(); switch (bpp) { // initizialize the data Index: src/opengl/qgl.cpp =================================================================== --- src/opengl/qgl.cpp (revision 3880) +++ src/opengl/qgl.cpp (revision 3881) @@ -840,6 +840,17 @@ return d->swapInterval; } +void QGLFormat::setDevicePixelRatio(qreal scaleFactor) +{ + detach(); + d->devicePixelRatio = scaleFactor; +} + +qreal QGLFormat::devicePixelRatio() const +{ + return d->devicePixelRatio; +} + /*! \fn bool QGLFormat::hasOverlay() const @@ -1555,6 +1566,7 @@ && a.d->blueSize == b.d->blueSize && a.d->numSamples == b.d->numSamples && a.d->swapInterval == b.d->swapInterval + && a.d->devicePixelRatio == b.d->devicePixelRatio && a.d->majorVersion == b.d->majorVersion && a.d->minorVersion == b.d->minorVersion && a.d->profile == b.d->profile); @@ -1577,6 +1589,7 @@ << ", alphaBufferSize " << d->alphaSize << ", samples " << d->numSamples << ", swapInterval " << d->swapInterval + << ", devicePixelRatio " << d->devicePixelRatio << ", majorVersion " << d->majorVersion << ", minorVersion " << d->minorVersion << ", profile " << d->profile Index: src/gui/image/qicon.cpp =================================================================== --- src/gui/image/qicon.cpp (revision 3880) +++ src/gui/image/qicon.cpp (revision 3881) @@ -115,6 +115,34 @@ qtIconCache()->clear(); } +/*! \internal + + Returns the effective device pixel ratio. + New public API should set a QWindow pointer, and will get the + the deivcePixelRatio for that window. + + Old API won't have a window pointer and qApp->devicePixelRatio() + will be used, iff Qt::AA_UseHighDpiPixmaps is set to prevent + breaking old code. +*/ +static qreal qt_effective_device_pixel_ratio() +{ + bool enableHighdpi = !qgetenv("QT_HIGHDPI_AWARE").isEmpty(); + static bool hasWarned = false; + if (!hasWarned && enableHighdpi) { + qWarning("QT_HIGHDPI_AWARE is deprecated, use qApp->setAttribute(Qt::AA_UseHighDpiPixmaps) instead."); + hasWarned = true; + } + +#ifdef Q_WS_MAC + if (enableHighdpi || qApp->testAttribute(Qt::AA_UseHighDpiPixmaps)) { + return qt_mac_get_scalefactor(); + } +#endif + + return qreal(1.0); +} + QIconPrivate::QIconPrivate() : engine(0), ref(1), serialNum(serialNumCounter.fetchAndAddRelaxed(1)), @@ -139,11 +167,8 @@ void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) { - QSize pixmapSize = rect.size(); -#if defined(Q_WS_MAC) - pixmapSize *= qt_mac_get_scalefactor(); -#endif - painter->drawPixmap(rect, pixmap(pixmapSize, mode, state)); + QSize targetRectSize = rect.size(); + painter->drawPixmap(rect, pixmap(targetRectSize, mode, state)); } static inline int area(const QSize &s) { return s.width() * s.height(); } @@ -238,9 +263,14 @@ return pe; } -QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +QPixmap QPixmapIconEngine::pixmap(const QSize &inSize, QIcon::Mode mode, QIcon::State state) { QPixmap pm; + QSize size = inSize; +#ifdef Q_WS_MAC + size *= qt_effective_device_pixel_ratio(); +#endif + QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, false); if (pe) pm = pe->pixmap; @@ -294,11 +324,20 @@ } QPixmapCache::insert(key % HexString(mode), pm); } + +#ifdef Q_WS_MAC + if (qt_effective_device_pixel_ratio() > 1 && pm.size().width() > inSize.width()) // detect high-dpi pixmap + pm.setDevicePixelRatio(2.0); +#endif return pm; } -QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) +QSize QPixmapIconEngine::actualSize(const QSize &inSize, QIcon::Mode mode, QIcon::State state) { + QSize size = inSize; +#ifdef Q_WS_MAC + size *= qt_effective_device_pixel_ratio(); +#endif QSize actualSize; if (QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, true)) actualSize = pe->size; @@ -343,6 +382,9 @@ } if (pe->size == QSize() && pe->pixmap.isNull()) { pe->pixmap = QPixmap(pe->fileName); + // Reset the devicePixelRatio. The pixmap may be loaded from a @2x file, + // but be used as a 1x pixmap by QIcon. + pe->pixmap.setDevicePixelRatio(1.0); pe->size = pe->pixmap.size(); } if(pe->size == size) { @@ -563,6 +605,18 @@ : d(0) { addFile(fileName); + +#ifdef Q_WS_MAC + if (qt_effective_device_pixel_ratio() > 1.0) { + // Check if a "@2x" file exists and add that as well. + QString at2xfileName = fileName; + int dotIndex = at2xfileName.lastIndexOf("."); + at2xfileName.insert(dotIndex, "@2x"); + if (QFile::exists(at2xfileName)) { + addFile(at2xfileName); + } + } +#endif } @@ -672,16 +726,23 @@ /*! Returns a pixmap with the requested \a size, \a mode, and \a - state, generating one if necessary. The pixmap might be smaller than - requested, but never larger. + state, generating one if necessary. - \sa actualSize(), paint() + This function has two modes. By default, the returned pixmap might + be smaller than requested, but never larger. Setting the Qt::AA_UseHighDpiPixmaps + application attribute enables support for high-dpi pixmaps and this function + may then return pixmaps that are larger than the requested size, + with a corresponding dpi scale factor. + + \sa actualSize(), paint(), QPixmap::devicePixelRatio() */ QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const { if (!d) return QPixmap(); - return d->engine->pixmap(size, mode, state); + + QPixmap pm = d->engine->pixmap(size, mode, state); + return pm; } /*! @@ -691,6 +752,10 @@ Returns a pixmap of size QSize(\a w, \a h). The pixmap might be smaller than requested, but never larger. + + Setting the Qt::AA_UseHighDpiPixmaps application attribute enables this + function to return pixmaps that are larger than the requested size. Such + images will have a devicePixelRatio larger than 1. */ /*! @@ -700,12 +765,20 @@ Returns a pixmap of size QSize(\a extent, \a extent). The pixmap might be smaller than requested, but never larger. + + Setting the Qt::AA_UseHighDpiPixmaps application attribute enables this + function to return pixmaps that are larger than the requested size. Such + images will have a devicePixelRatio larger than 1. */ -/*! Returns the actual size of the icon for the requested \a size, \a +/*! + Returns the actual size of the icon for the requested \a size, \a mode, and \a state. The result might be smaller than requested, but never larger. + Setting the Qt::AA_UseHighDpiPixmaps application attribute enables this + function to return sizes that are larger than the requested size. + \sa pixmap(), paint() */ QSize QIcon::actualSize(const QSize &size, Mode mode, State state) const @@ -726,7 +799,15 @@ { if (!d || !painter) return; - QRect alignedRect = QStyle::alignedRect(painter->layoutDirection(), alignment, d->engine->actualSize(rect.size(), mode, state), rect); + + // High-dpi pixmaps do not need aligmnent. Clamp actualSize to the + // passed in destination rect. + QSize actualSize = d->engine->actualSize(rect.size(), mode, state); + actualSize.setWidth(qMin(rect.width(), actualSize.width())); + actualSize.setHeight(qMin(rect.width(), actualSize.height())); + + QRect alignedRect = QStyle::alignedRect(painter->layoutDirection(), alignment, actualSize , rect); + d->engine->paint(painter, alignedRect, mode, state); } @@ -868,6 +949,19 @@ detach(); } d->engine->addFile(fileName, size, mode, state); + +#ifdef Q_WS_MAC + // Check if a "@2x" file exists and add it. + if (qt_effective_device_pixel_ratio() > 1.0) { + int dotIndex = fileName.lastIndexOf(QLatin1Char('.')); + if (dotIndex != -1) { + QString at2xfileName = fileName; + at2xfileName.insert(dotIndex, QLatin1String("@2x")); + if (QFile::exists(at2xfileName)) + d->engine->addFile(at2xfileName, size, mode, state); + } + } +#endif } /*! @@ -1161,7 +1255,8 @@ int i = 0; if (which == QIcon::Large) i = 1; - return QSize(widths[i], heights[i]); + return QSize(widths[i] * qt_effective_device_pixel_ratio(), + heights[i] * qt_effective_device_pixel_ratio()); } /*! Index: src/opengl/qgl_mac.mm =================================================================== --- src/opengl/qgl_mac.mm (revision 3880) +++ src/opengl/qgl_mac.mm (revision 3881) @@ -695,7 +695,14 @@ //get control information QWidget *w = (QWidget *)d->paintDevice; NSView *view = qt_mac_nativeview_for(w); - + + if (view) { + if (d->reqFormat.devicePixelRatio() > 1.0 && [[view window] backingScaleFactor] > 1.0) { + [view setWantsBestResolutionOpenGLSurface:YES]; + d->glFormat.setDevicePixelRatio(2.0); + } + } + // Trying to attach the GL context to the NSView will fail with // "invalid drawable" if done too soon, but we have to make sure // the connection is made before the first paint event. Using @@ -915,7 +922,8 @@ float scale = qt_mac_get_scale_factor(this); resizeGL(width() * scale, height() * scale); #else - resizeGL(width(), height()); + float scale = devicePixelRatio(); + resizeGL(width() * scale, height() * scale); #endif } Index: src/opengl/qgl_p.h =================================================================== --- src/opengl/qgl_p.h (revision 3880) +++ src/opengl/qgl_p.h (revision 3881) @@ -126,6 +126,7 @@ depthSize = accumSize = stencilSize = redSize = greenSize = blueSize = alphaSize = -1; numSamples = -1; swapInterval = -1; + devicePixelRatio = 1.0; majorVersion = 1; minorVersion = 0; profile = QGLFormat::NoProfile; @@ -143,6 +144,7 @@ alphaSize(other->alphaSize), numSamples(other->numSamples), swapInterval(other->swapInterval), + devicePixelRatio(other->devicePixelRatio), majorVersion(other->majorVersion), minorVersion(other->minorVersion), profile(other->profile) @@ -160,6 +162,7 @@ int alphaSize; int numSamples; int swapInterval; + qreal devicePixelRatio; int majorVersion; int minorVersion; QGLFormat::OpenGLContextProfile profile; Index: src/opengl/qgl.h =================================================================== --- src/opengl/qgl.h (revision 3880) +++ src/opengl/qgl.h (revision 3881) @@ -210,6 +210,9 @@ void setSwapInterval(int interval); int swapInterval() const; + + void setDevicePixelRatio(qreal scaleFactor); + qreal devicePixelRatio() const; bool doubleBuffer() const; void setDoubleBuffer(bool enable); Index: src/gui/image/qpixmap.cpp =================================================================== --- src/gui/image/qpixmap.cpp (revision 3880) +++ src/gui/image/qpixmap.cpp (revision 3881) @@ -779,6 +779,49 @@ data->setMask(mask); } +/*! + Returns the current dpi scale factor for the pixmap. + + Common values for the scale factor is 1.0 (the default), + and 2.0 for images intended for display on High DPI displays. + + Use this function when calculating layouts based on the + pixmap size. Layout size is pixel size divided by the scale + factor. + + \sa setScaleFactor(), QIcon::pixmap() +*/ +qreal QPixmap::devicePixelRatio() const +{ + if (!data) + return qreal(1.0); + return data->devicePixelRatio; +} + +/*! + Sets the dpi scale factor for the pixmap. + + The scale factor is typically set to 2.0 when producing + pixmap for high-dpi displays. This informs layout code + paths in Qt which use the image size that the image is + a high-resolution image, and not a large image. + + Qt supports using the "@2x" suffix when loading + pixmap from files. Loading "myicon@2x.png" will result + in an image with a 2x scale factor. + + Setting the scale factor will also change the dpi information + returned by QPainDevice::metric(): Physical dpi is logical dpi + multiplied by the scale factor. + + \sa scaleFactor() +*/ +void QPixmap::setDevicePixelRatio(qreal scaleFactor) +{ + detach(); + data->devicePixelRatio = scaleFactor; +} + #ifndef QT_NO_IMAGE_HEURISTIC_MASK /*! Creates and returns a heuristic mask for this pixmap. Index: src/gui/image/qpixmap_mac.cpp =================================================================== --- src/gui/image/qpixmap_mac.cpp (revision 3880) +++ src/gui/image/qpixmap_mac.cpp (revision 3881) @@ -56,6 +56,10 @@ #include #include +#ifdef Q_WS_MAC +#include +#endif + #include #include @@ -235,6 +239,7 @@ h = img.height(); is_null = (w <= 0 || h <= 0); d = (pixelType() == BitmapType ? 1 : img.depth()); + devicePixelRatio = img.devicePixelRatio(); QImage image = img; int dd = QPixmap::defaultDepth(); @@ -379,6 +384,11 @@ QImage::Format_RGB32); QImage image(w, h, format); + // exit if image was not created (out of memory) + if (image.isNull()) + return image; + image.setDevicePixelRatio(devicePixelRatio); + quint32 *sptr = pixels, *srow; const uint sbpr = bytesPerRow; if (format == QImage::Format_MonoLSB) { @@ -475,6 +485,10 @@ int QMacPixmapData::metric(QPaintDevice::PaintDeviceMetric theMetric) const { + + + extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpps + extern float qt_mac_defaultDpi_y(); //qpaintdevice_mac.cpp switch (theMetric) { case QPaintDevice::PdmWidth: return w; @@ -487,14 +501,14 @@ case QPaintDevice::PdmNumColors: return 1 << d; case QPaintDevice::PdmDpiX: + return int(qt_mac_defaultDpi_x()); case QPaintDevice::PdmPhysicalDpiX: { - extern float qt_mac_defaultDpi_x(); //qpaintdevice_mac.cpp - return int(qt_mac_defaultDpi_x()); + return int(qt_mac_defaultDpi_x() * devicePixelRatio); } case QPaintDevice::PdmDpiY: + return int(qt_mac_defaultDpi_x()); case QPaintDevice::PdmPhysicalDpiY: { - extern float qt_mac_defaultDpi_y(); //qpaintdevice_mac.cpp - return int(qt_mac_defaultDpi_y()); + return int(qt_mac_defaultDpi_y() * devicePixelRatio); } case QPaintDevice::PdmDepth: return d; @@ -863,7 +877,6 @@ const CGRect cgRect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()); const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount); - extern CGFloat qt_mac_get_scalefactor(); QRect scaledRect = QRect(rect.topLeft(), rect.size() * qt_mac_get_scalefactor()); if (err && displayCount == 0) @@ -1173,6 +1186,7 @@ has_alpha = macData->has_alpha; has_mask = macData->has_mask; uninit = false; + devicePixelRatio = macData->devicePixelRatio; const int x = rect.x(); const int y = rect.y(); Index: src/gui/image/qimagereader.cpp =================================================================== --- src/gui/image/qimagereader.cpp (revision 3880) +++ src/gui/image/qimagereader.cpp (revision 3881) @@ -128,6 +128,7 @@ #include #include #include +#include // factory loader #include @@ -1263,6 +1264,11 @@ } } + // successful read; check for "@2x" suffix and set scale factor + if (QFileInfo(fileName()).baseName().endsWith("@2x")) { + image->setDevicePixelRatio(2.0); + } + return true; } Index: src/gui/image/qpixmapdata_p.h =================================================================== --- src/gui/image/qpixmapdata_p.h (revision 3880) +++ src/gui/image/qpixmapdata_p.h (revision 3881) @@ -155,6 +155,7 @@ int h; int d; bool is_null; + qreal devicePixelRatio; private: friend class QPixmap; Index: src/gui/image/qimage_p.h =================================================================== --- src/gui/image/qimage_p.h (revision 3880) +++ src/gui/image/qimage_p.h (revision 3881) @@ -77,6 +77,7 @@ int height; int depth; int nbytes; // number of bytes data + qreal devicePixelRatio; QVector colortable; uchar *data; #ifdef QT3_SUPPORT Index: src/gui/image/qimage.h =================================================================== --- src/gui/image/qimage.h (revision 3880) +++ src/gui/image/qimage.h (revision 3881) @@ -215,6 +215,9 @@ QVector colorTable() const; void setColorTable(const QVector colors); + qreal devicePixelRatio() const; + void setDevicePixelRatio(qreal scale); + void fill(uint pixel); void fill(const QColor &color); void fill(Qt::GlobalColor color); Index: src/gui/image/qpixmapdata.cpp =================================================================== --- src/gui/image/qpixmapdata.cpp (revision 3880) +++ src/gui/image/qpixmapdata.cpp (revision 3881) @@ -70,6 +70,7 @@ h(0), d(0), is_null(true), + devicePixelRatio(1.0), ref(0), detach_no(0), type(pixelType), Index: src/gui/image/qpixmap_mac_p.h =================================================================== --- src/gui/image/qpixmap_mac_p.h (revision 3880) +++ src/gui/image/qpixmap_mac_p.h (revision 3881) @@ -126,6 +126,7 @@ friend void qt_mac_cgimage_data_free(void *, const void*, size_t); friend IconRef qt_mac_create_iconref(const QPixmap&); friend CGContextRef qt_mac_cg_context(const QPaintDevice*); + friend void qt_mac_set_pixmap_scale(QPixmap *pixmap, int devicePixelRatio); friend QColor qcolorForThemeTextColor(ThemeTextColor themeColor); }; Index: src/gui/image/qpixmap.h =================================================================== --- src/gui/image/qpixmap.h (revision 3880) +++ src/gui/image/qpixmap.h (revision 3881) @@ -109,6 +109,9 @@ QBitmap mask() const; void setMask(const QBitmap &); + qreal devicePixelRatio() const; + void setDevicePixelRatio(qreal scaleFactor); + #ifdef QT_DEPRECATED QT_DEPRECATED QPixmap alphaChannel() const; QT_DEPRECATED void setAlphaChannel(const QPixmap &); @@ -271,6 +274,7 @@ friend IconRef qt_mac_create_iconref(const QPixmap&); friend quint32 *qt_mac_pixmap_get_base(const QPixmap*); friend int qt_mac_pixmap_get_bytes_per_line(const QPixmap*); + friend void qt_mac_set_pixmap_scale(QPixmap *pixmap, int scale); #endif friend class QPixmapData; friend class QX11PixmapData; Index: src/gui/styles/qstylehelper.cpp =================================================================== --- src/gui/styles/qstylehelper.cpp (revision 3880) +++ src/gui/styles/qstylehelper.cpp (revision 3881) @@ -91,8 +91,6 @@ ReleaseDC(0, hdcScreen); scale = dpi/96.0; } -#elif defined(Q_WS_MAC) - scale = qt_mac_get_scalefactor(); #endif } return value * scale; Index: src/gui/styles/qcommonstyle.cpp =================================================================== --- src/gui/styles/qcommonstyle.cpp (revision 3880) +++ src/gui/styles/qcommonstyle.cpp (revision 3881) @@ -1172,6 +1172,8 @@ QSize tabIconSize = opt->icon.actualSize(iconSize, (opt->state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled, (opt->state & QStyle::State_Selected) ? QIcon::On : QIcon::Off ); + // High-dpi icons do not need adjustmet; make sure tabIconSize is not larger than iconSize + tabIconSize = QSize(qMin(tabIconSize.width(), iconSize.width()), qMin(tabIconSize.height(), iconSize.width())); *iconRect = QRect(tr.left(), tr.center().y() - tabIconSize.height() / 2, tabIconSize.width(), tabIconSize .height()); @@ -1253,8 +1255,11 @@ state = QIcon::On; QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state); - int labelWidth = pixmap.width(); - int labelHeight = pixmap.height(); + + int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio(); + int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio(); + int labelWidth = pixmapWidth; + int labelHeight = pixmapHeight; int iconSpacing = 4;//### 4 is currently hardcoded in QPushButton::sizeHint() int textWidth = button->fontMetrics.boundingRect(opt->rect, tf, button->text).width(); if (!button->text.isEmpty()) @@ -1262,7 +1267,7 @@ iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2, textRect.y() + (textRect.height() - labelHeight) / 2, - pixmap.width(), pixmap.height()); + pixmapWidth, pixmapHeight); iconRect = visualRect(button->direction, textRect, iconRect); @@ -1534,9 +1539,9 @@ if (!header->icon.isNull()) { QPixmap pixmap = header->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize), (header->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled); - int pixw = pixmap.width(); + int pixw = pixmap.width() / pixmap.devicePixelRatio(); - QRect aligned = alignedRect(header->direction, QFlag(header->iconAlignment), pixmap.size(), rect); + QRect aligned = alignedRect(header->direction, QFlag(header->iconAlignment), pixmap.size() / pixmap.devicePixelRatio(), rect); QRect inter = aligned.intersected(rect); p->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width(), inter.height()); @@ -1591,7 +1596,7 @@ mode = QIcon::Normal; pm = toolbutton->icon.pixmap(toolbutton->rect.size().boundedTo(toolbutton->iconSize), mode, state); - pmSize = pm.size(); + pmSize = pm.size() / pm.devicePixelRatio(); } if (toolbutton->toolButtonStyle != Qt::ToolButtonIconOnly) { @@ -1814,8 +1819,8 @@ tr = cr; tr.adjust(4, 0, -8, 0); } else { - int iw = pm.width() + 4; - ih = pm.height(); + int iw = pm.width() / pm.devicePixelRatio() + 4; + ih = pm.height()/ pm.devicePixelRatio(); ir = QRect(cr.left() + 4, cr.top(), iw + 2, ih); tr = QRect(ir.right(), cr.top(), cr.width() - ir.right() - 4, cr.height()); }