15

I'm attempting to create an image "loupe" for my application that can be used to inspect images at different magnification levels, and I have run into a bit of a road bump. I'm using Quartz to create a CGImageRef snapshot of a selected portion of the display my app's window is on. The problem is, the nomenclature used by all of the different OS X technologies has me really confused.

The function I'm using is CGDisplayCreateImageForRect(CGDirectDisplayID display, CGRect rect). The documentation states the CGRect rect parameter is the "rectangle, specified in display space, for the portion of the display being copied into the image." The problem is, the output I'm getting in my console isn't what I was expecting. I've tried so many different conversions, transformations, coord flips, etc., that I'm 100% confused now.

So, I guess my question boils down to this: Does screen space == device space == display space, and how does one properly convert his or her view's frame (which happens to be a custom borderless window) coordinates (bottom-left origin) to match the display's coordinates (top-left origin)?

If someone could set me straight, or point me in the right direction, I'd be forever appreciative. Thanks!

Ben Stock
  • 1,986
  • 1
  • 22
  • 30

1 Answers1

26

Quartz uses a coordinate space where the origin (0, 0) is at the top-left of the primary display. Increasing y goes down.

Cocoa uses a coordinate space where the origin (0, 0) is the bottom-left of the primary display and increasing y goes up.

You can convert a window's frame from Cocoa to Quartz like so:

NSRect frame = window.frame;
frame.origin.y = NSMaxY(NSScreen.screens[0].frame) - NSMaxY(frame);
CGRect quartzRect = NSRectToCGRect(frame);

Note: you don't use the window's -screen, you always use the primary screen.

This is for a rect in window coordinates. For a rect in a view, first convert from view coordinates to window coordinates using -[NSView convertRect:toView:] with nil as the destination view signifying the window. This would do the right thing for someView.bounds but not for someView.frame, since a view's frame is in the superview's coordinate system.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Thanks, Ken. I've since fixed my issue by commenting everything out (including my thousands of `NSLog()` calls), and starting from scratch. I think my biggest issue was that I had so many conversions and coordinate flips going on, one would cancel out the other. I really appreciate your breakdown though. I never thought about the bounds/frame difference. That's probably where I went wrong. My only question remaining is: does "screen space" equal "device space?" And are they synonymous with "global display space?" I'm starting to think I have a big space in between my ears. :-( – Ben Stock Nov 12 '13 at 20:39
  • 5
    "Device space" refers to pixels. Its origin is typically specific to the device. "Local display space" uses points but its origin is at the top-left of the specific display. "Global display space" is a unified coordinate space in which all displays are situated. Its origin is at the top-left of the primary display. "Screen space" is the Cocoa coordinate space with its origin at the bottom-left of the primary display. – Ken Thomases Nov 13 '13 at 02:39
  • 1
    Well, you can't be more helpful than that! Your explanations are exactly what I've been looking for. Thanks a million, man. – Ben Stock Nov 14 '13 at 03:29
  • It should be `frame.origin.y` instead of `frame.y` – vaughan Mar 09 '14 at 17:55
  • @KenThomases - How do we convert a point with an inverted 'cocoa Y' to normal 'Quartz y' ? I tried ur logic but the Y coordinate is going negative................. nsRect.origin.y = NSMaxY(NSScreen.screens[0].frame) - NSMaxY(nsRect); CGRect cropRect = NSRectToCGRect(nsRect); – KamyFC Jul 24 '18 at 17:08
  • @kamyFC The conversion works just the same either way, so what you showed seems right. A negative Y coordinate might be correct, depending on what your `nsRect` is meant to represent. It just means a rectangle above the top of the primary screen. That might be a secondary screen or just something that's partially off the top of the screen. Is your `nsRect` in screen space (as opposed to, for example, relative to a view in a window)? – Ken Thomases Jul 24 '18 at 19:18
  • @KenThomases Yes the Y from point comes from this code - NSPoint currentPoint = [self.view convertPoint:[theEvent locationInWindow] fromView:nil]; so this seems to be screen space right? – KamyFC Jul 25 '18 at 05:14
  • @KenThomases also after using the formulae - If i click near top edge of the view, i get flippedY = 510 to 515 If i click near bottom edge of the view, i get flippedY = 965 to 970 At the top edge it should be between 0 to 10. This is strange :-| – KamyFC Jul 25 '18 at 06:24
  • I've seen a handful of other posts mention that the coordinate system is relative to the "main" display. Just to prevent other folks like me, this is _not_ the same as `NSScreen.main`, which is the screen that the keyboard currently has focus on. – Salem May 05 '21 at 00:51
  • What is the purpose of `NSRectToCGRect`? `NSRect` is just a `typealias` to `CGRect` – user16217248 Mar 29 '23 at 22:11