summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gui/kernel/qscreen.cpp5
-rw-r--r--src/gui/kernel/qscreen.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm119
4 files changed, 81 insertions, 46 deletions
diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp
index 3fd1548d04..1d11358b3d 100644
--- a/src/gui/kernel/qscreen.cpp
+++ b/src/gui/kernel/qscreen.cpp
@@ -703,10 +703,11 @@ QScreen *QScreen::virtualSiblingAt(QPoint point)
/*!
Creates and returns a pixmap constructed by grabbing the contents
of the given \a window restricted by QRect(\a x, \a y, \a width,
- \a height).
+ \a height). If \a window is 0, then the entire screen will be
+ grabbed.
The arguments (\a{x}, \a{y}) specify the offset in the window,
- whereas (\a{width}, \a{height}) specify the area to be copied. If
+ whereas (\a{width}, \a{height}) specify the area to be copied. If
\a width is negative, the function copies everything to the right
border of the window. If \a height is negative, the function
copies everything to the bottom of the window.
diff --git a/src/gui/kernel/qscreen.h b/src/gui/kernel/qscreen.h
index 039940f196..1bff3f4ec1 100644
--- a/src/gui/kernel/qscreen.h
+++ b/src/gui/kernel/qscreen.h
@@ -144,7 +144,7 @@ public:
bool isPortrait(Qt::ScreenOrientation orientation) const;
bool isLandscape(Qt::ScreenOrientation orientation) const;
- QPixmap grabWindow(WId window, int x = 0, int y = 0, int w = -1, int h = -1);
+ QPixmap grabWindow(WId window = 0, int x = 0, int y = 0, int w = -1, int h = -1);
qreal refreshRate() const;
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h
index 5a5d7fc735..ffbb67b677 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.h
+++ b/src/plugins/platforms/cocoa/qcocoascreen.h
@@ -79,6 +79,7 @@ public:
// ----------------------------------------------------
+ static NSScreen *nativeScreenForDisplayId(CGDirectDisplayID displayId);
NSScreen *nativeScreen() const;
void requestUpdate();
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
index ebf1afb57d..70d02d1a0d 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.mm
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -592,70 +592,99 @@ QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const
return window;
}
+/*!
+ \internal
+
+ Coordinates are in screen coordinates if \a view is 0, otherwise they are in view
+ coordiantes.
+*/
QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) const
{
- // Determine the grab rect. FIXME: The rect should be bounded by the view's
- // geometry, but note that for the pixeltool use case that window will be the
- // desktop widget's view, which currently gets resized to fit one screen
- // only, since its NSWindow has the NSWindowStyleMaskTitled flag set.
- Q_UNUSED(view);
+ /*
+ Grab the grabRect section of the specified display into a pixmap that has
+ sRGB color spec. Once Qt supports a fully color-managed flow and conversions
+ that don't lose the colorspec information, we would want the image to maintain
+ the color spec of the display from which it was grabbed. Ultimately, rendering
+ the returned pixmap on the same display from which it was grabbed should produce
+ identical visual results.
+ */
+ auto grabFromDisplay = [](CGDirectDisplayID displayId, const QRect &grabRect) -> QPixmap {
+ QCFType<CGImageRef> image = CGDisplayCreateImageForRect(displayId, grabRect.toCGRect());
+ const QCFType<CGColorSpaceRef> sRGBcolorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
+ if (CGImageGetColorSpace(image) != sRGBcolorSpace) {
+ qCDebug(lcQpaScreen) << "applying color correction for display" << displayId;
+ image = CGImageCreateCopyWithColorSpace(image, sRGBcolorSpace);
+ }
+ QPixmap pixmap = QPixmap::fromImage(qt_mac_toQImage(image));
+ pixmap.setDevicePixelRatio(nativeScreenForDisplayId(displayId).backingScaleFactor);
+ return pixmap;
+ };
+
QRect grabRect = QRect(x, y, width, height);
qCDebug(lcQpaScreen) << "input grab rect" << grabRect;
- // Find which displays to grab from, or all of them if the grab size is unspecified
+ if (!view) {
+ // coordinates are relative to the screen
+ if (!grabRect.isValid()) // entire screen
+ grabRect = QRect(QPoint(0, 0), geometry().size());
+ else
+ grabRect.translate(-geometry().topLeft());
+ return grabFromDisplay(displayId(), grabRect);
+ }
+
+ // grab the window; grab rect in window coordinates might span multiple screens
+ NSView *nsView = reinterpret_cast<NSView*>(view);
+ NSPoint windowPoint = [nsView convertPoint:NSMakePoint(0, 0) toView:nil];
+ NSRect screenRect = [nsView.window convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)];
+ QPoint position = mapFromNative(screenRect.origin).toPoint();
+ QSize size = QRectF::fromCGRect(NSRectToCGRect(nsView.bounds)).toRect().size();
+ QRect windowRect = QRect(position, size);
+ if (!grabRect.isValid())
+ grabRect = windowRect;
+ else
+ grabRect.translate(windowRect.topLeft());
+
+ // Find which displays to grab from
const int maxDisplays = 128;
CGDirectDisplayID displays[maxDisplays];
CGDisplayCount displayCount;
- CGRect cgRect = (width < 0 || height < 0) ? CGRectInfinite : grabRect.toCGRect();
+ CGRect cgRect = grabRect.isValid() ? grabRect.toCGRect() : CGRectInfinite;
const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
if (err || displayCount == 0)
return QPixmap();
- // If the grab size is not specified, set it to be the bounding box of all screens,
- if (width < 0 || height < 0) {
- QRect windowRect;
- for (uint i = 0; i < displayCount; ++i) {
- QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(displays[i])).toRect();
- // Only include the screen if it is positioned past the x/y position
- if ((displayBounds.x() >= x || displayBounds.right() > x) &&
- (displayBounds.y() >= y || displayBounds.bottom() > y)) {
- windowRect = windowRect.united(displayBounds);
- }
- }
- if (grabRect.width() < 0)
- grabRect.setWidth(windowRect.width());
- if (grabRect.height() < 0)
- grabRect.setHeight(windowRect.height());
- }
-
qCDebug(lcQpaScreen) << "final grab rect" << grabRect << "from" << displayCount << "displays";
// Grab images from each display
- QVector<QImage> images;
+ QVector<QPixmap> pixmaps;
QVector<QRect> destinations;
for (uint i = 0; i < displayCount; ++i) {
auto display = displays[i];
- QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(display)).toRect();
- QRect grabBounds = displayBounds.intersected(grabRect);
+ const QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(display)).toRect();
+ const QRect grabBounds = displayBounds.intersected(grabRect);
if (grabBounds.isNull()) {
destinations.append(QRect());
- images.append(QImage());
+ pixmaps.append(QPixmap());
continue;
}
- QRect displayLocalGrabBounds = QRect(QPoint(grabBounds.topLeft() - displayBounds.topLeft()), grabBounds.size());
- QImage displayImage = qt_mac_toQImage(QCFType<CGImageRef>(CGDisplayCreateImageForRect(display, displayLocalGrabBounds.toCGRect())));
- displayImage.setDevicePixelRatio(displayImage.size().width() / displayLocalGrabBounds.size().width());
- images.append(displayImage);
- QRect destBounds = QRect(QPoint(grabBounds.topLeft() - grabRect.topLeft()), grabBounds.size());
+ const QRect displayLocalGrabBounds = QRect(QPoint(grabBounds.topLeft() - displayBounds.topLeft()), grabBounds.size());
+
+ qCDebug(lcQpaScreen) << "grab display" << i << "global" << grabBounds << "local" << displayLocalGrabBounds;
+ QPixmap displayPixmap = grabFromDisplay(display, displayLocalGrabBounds);
+ // Fast path for when grabbing from a single screen only
+ if (displayCount == 1)
+ return displayPixmap;
+
+ qCDebug(lcQpaScreen) << "grab sub-image size" << displayPixmap.size() << "devicePixelRatio" << displayPixmap.devicePixelRatio();
+ pixmaps.append(displayPixmap);
+ const QRect destBounds = QRect(QPoint(grabBounds.topLeft() - grabRect.topLeft()), grabBounds.size());
destinations.append(destBounds);
- qCDebug(lcQpaScreen) << "grab display" << i << "global" << grabBounds << "local" << displayLocalGrabBounds
- << "grab image size" << displayImage.size() << "devicePixelRatio" << displayImage.devicePixelRatio();
}
// Determine the highest dpr, which becomes the dpr for the returned pixmap.
qreal dpr = 1.0;
for (uint i = 0; i < displayCount; ++i)
- dpr = qMax(dpr, images.at(i).devicePixelRatio());
+ dpr = qMax(dpr, pixmaps.at(i).devicePixelRatio());
// Allocate target pixmap and draw each screen's content
qCDebug(lcQpaScreen) << "Create grap pixmap" << grabRect.size() << "at devicePixelRatio" << dpr;
@@ -664,7 +693,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
windowPixmap.fill(Qt::transparent);
QPainter painter(&windowPixmap);
for (uint i = 0; i < displayCount; ++i)
- painter.drawImage(destinations.at(i), images.at(i));
+ painter.drawPixmap(destinations.at(i), pixmaps.at(i));
return windowPixmap;
}
@@ -754,19 +783,23 @@ QCocoaScreen *QCocoaScreen::get(CFUUIDRef uuid)
return nullptr;
}
-NSScreen *QCocoaScreen::nativeScreen() const
+NSScreen *QCocoaScreen::nativeScreenForDisplayId(CGDirectDisplayID displayId)
{
- if (!m_displayId)
- return nil; // The display has been disconnected
-
for (NSScreen *screen in NSScreen.screens) {
- if (screen.qt_displayId == m_displayId)
+ if (screen.qt_displayId == displayId)
return screen;
}
-
return nil;
}
+NSScreen *QCocoaScreen::nativeScreen() const
+{
+ if (!m_displayId)
+ return nil; // The display has been disconnected
+
+ return nativeScreenForDisplayId(m_displayId);
+}
+
CGPoint QCocoaScreen::mapToNative(const QPointF &pos, QCocoaScreen *screen)
{
Q_ASSERT(screen);