macOS: Streamline QImage to NSImage conversion

The conversion uses NSBitmapImageRep and correctly sets the display
pixel ratio and size of the resulting image, reducing the need for
clients to deal with this.

The function returns an auto-released object, as is customary for
class-functions like these.

Change-Id: I5124d1d8145a7f5266921b22fda1987798771ec1
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Tor Arne Vestbø 2020-03-09 18:05:14 +01:00 committed by Tor Arne Vestbø
parent 28d3d82a0d
commit 52e9c8b4be
8 changed files with 45 additions and 41 deletions

View File

@ -47,6 +47,8 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qoperatingsystemversion.h>
QT_USE_NAMESPACE
QT_BEGIN_NAMESPACE
// ---------------------- Images ----------------------
@ -124,23 +126,34 @@ QImage qt_mac_toQImage(CGImageRef image)
#ifdef Q_OS_MACOS
static NSImage *qt_mac_cgimage_to_nsimage(CGImageRef image)
{
NSImage *newImage = [[NSImage alloc] initWithCGImage:image size:NSZeroSize];
return newImage;
}
QT_END_NAMESPACE
NSImage *qt_mac_create_nsimage(const QPixmap &pm)
@implementation NSImage (QtExtras)
+ (instancetype)imageFromQImage:(const QImage &)image
{
if (pm.isNull())
return 0;
QImage image = pm.toImage();
CGImageRef cgImage = qt_mac_toCGImage(image);
NSImage *nsImage = qt_mac_cgimage_to_nsimage(cgImage);
nsImage.size = (pm.size() / pm.devicePixelRatioF()).toCGSize();
CGImageRelease(cgImage);
return nsImage;
if (image.isNull())
return nil;
QCFType<CGImageRef> cgImage = image.toCGImage();
if (!cgImage)
return nil;
// We set up the NSImage using an explicit NSBitmapImageRep, instead of
// [NSImage initWithCGImage:size:], as the former allows us to correctly
// set the size of the representation to account for the device pixel
// ratio of the original image, which in turn will be reflected by the
// NSImage.
auto nsImage = [[NSImage alloc] initWithSize:NSZeroSize];
auto *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
imageRep.size = (image.size() / image.devicePixelRatioF()).toCGSize();
[nsImage addRepresentation:[imageRep autorelease]];
Q_ASSERT(CGSizeEqualToSize(nsImage.size, imageRep.size));
return [nsImage autorelease];
}
@end
QT_BEGIN_NAMESPACE
NSImage *qt_mac_create_nsimage(const QIcon &icon, int defaultSize)
{

View File

@ -69,9 +69,15 @@ QT_BEGIN_NAMESPACE
Q_GUI_EXPORT CGBitmapInfo qt_mac_bitmapInfoForImage(const QImage &image);
#ifdef HAVE_APPKIT
Q_GUI_EXPORT NSImage *qt_mac_create_nsimage(const QPixmap &pm);
Q_GUI_EXPORT NSImage *qt_mac_create_nsimage(const QIcon &icon, int defaultSize = 0);
Q_GUI_EXPORT QPixmap qt_mac_toQPixmap(const NSImage *image, const QSizeF &size);
QT_END_NAMESPACE
@interface NSImage (QtExtras)
+ (instancetype)imageFromQImage:(const QT_PREPEND_NAMESPACE(QImage) &)image;
@end
QT_BEGIN_NAMESPACE
#endif
Q_GUI_EXPORT CGImageRef qt_mac_toCGImage(const QImage &qImage);
Q_GUI_EXPORT CGImageRef qt_mac_toCGImageMask(const QImage &qImage);

View File

@ -340,19 +340,12 @@ NSCursor *QCocoaCursor::createCursorFromPixmap(const QPixmap pixmap, const QPoin
QSize layoutSize = pixmap.size() / pixmap.devicePixelRatio();
QPixmap scaledPixmap = pixmap.scaled(layoutSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
scaledPixmap.setDevicePixelRatio(1.0);
nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(scaledPixmap));
CGImageRef cgImage = qt_mac_toCGImage(pixmap.toImage());
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
[nsimage addRepresentation:imageRep];
[imageRep release];
CGImageRelease(cgImage);
nsimage = [NSImage imageFromQImage:scaledPixmap.toImage()];
} else {
nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
nsimage = [NSImage imageFromQImage:pixmap.toImage()];
}
NSCursor *nsCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot];
[nsimage release];
return nsCursor;
return [[NSCursor alloc] initWithImage:nsimage hotSpot:hotSpot];
}
QT_END_NAMESPACE

View File

@ -130,9 +130,8 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o)
QPoint hotSpot = m_drag->hotSpot();
QPixmap pm = dragPixmap(m_drag, hotSpot);
QSize pmDeviceIndependentSize = pm.size() / pm.devicePixelRatio();
NSImage *nsimage = qt_mac_create_nsimage(pm);
[nsimage setSize:NSSizeFromCGSize(pmDeviceIndependentSize.toCGSize())];
NSImage *dragImage = [NSImage imageFromQImage:pm.toImage()];
Q_ASSERT(dragImage);
QMacPasteboard dragBoard(CFStringRef(NSPasteboardNameDrag), QMacInternalPasteboardMime::MIME_DND);
m_drag->mimeData()->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray("dummy"));
@ -142,12 +141,12 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o)
NSWindow *theWindow = [m_lastEvent window];
Q_ASSERT(theWindow);
event_location.x -= hotSpot.x();
CGFloat flippedY = pmDeviceIndependentSize.height() - hotSpot.y();
CGFloat flippedY = dragImage.size.height - hotSpot.y();
event_location.y -= flippedY;
NSSize mouseOffset_unused = NSMakeSize(0.0, 0.0);
NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSPasteboardNameDrag];
[theWindow dragImage:nsimage
[theWindow dragImage:dragImage
at:event_location
offset:mouseOffset_unused
event:m_lastEvent
@ -155,8 +154,6 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o)
source:m_lastView
slideBack:YES];
[nsimage release];
m_drag = nullptr;
return m_executed_drop_action;
}

View File

@ -476,10 +476,9 @@ void QCocoaIntegration::setApplicationIcon(const QIcon &icon) const
if (!icon.isNull()) {
NSSize size = [[[NSApplication sharedApplication] dockTile] size];
QPixmap pixmap = icon.pixmap(size.width, size.height);
image = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
image = [NSImage imageFromQImage:pixmap.toImage()];
}
[[NSApplication sharedApplication] setApplicationIconImage:image];
[image release];
}
void QCocoaIntegration::beep() const

View File

@ -228,10 +228,9 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
p.drawPixmap(r, pixmap);
}
NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(fullHeightPixmap));
auto *nsimage = [NSImage imageFromQImage:fullHeightPixmap.toImage()];
[nsimage setTemplate:icon.isMask()];
[(NSImageView*)[[m_sys->item item] view] setImage: nsimage];
[nsimage release];
}
void QCocoaSystemTrayIcon::updateMenu(QPlatformMenu *menu)

View File

@ -899,8 +899,7 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon)
[iconButton setImage:[workspace iconForFile:m_view.window.representedFilename]];
} else {
QPixmap pixmap = icon.pixmap(QSize(22, 22));
NSImage *image = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap));
[iconButton setImage:[image autorelease]];
iconButton.image = [NSImage imageFromQImage:pixmap.toImage()];
}
}

View File

@ -150,10 +150,8 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin
break;
}
} else {
NSImage *nsimage = qt_mac_create_nsimage(pixmapCursor);
nsimage.size = NSSizeFromCGSize((pixmapCursor.size() / pixmapCursor.devicePixelRatioF()).toCGSize());
auto *nsimage = [NSImage imageFromQImage:pixmapCursor.toImage()];
nativeCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSZeroPoint];
[nsimage release];
}
// Change the cursor