Uli's Web Site
[ Zathras.de - Uli's Web Site ]
Other Sites: Stories
Abi 2000
Stargate: Resurgence
Lost? Site Map!
     home | blog | moose | programming | articles >> blog

 Blog Topics

15 Most Recent [RSS]

 Less work through Xcode and shell scripts
2011-12-16 @600
 iTunesCantComplain released
2011-10-28 @954
 Dennis Ritchie deceased
2011-10-13 @359
 Thank you, Steve.
2011-10-06 @374
 Cocoa Text System everywhere...
2011-03-27 @788
 Blog migration
2011-01-29 @520
 All you need to know about the Mac keyboard
2010-08-09 @488
 Review: Sherlock
2010-07-31 @978
 Playing with Objective C on Debian
2010-05-08 @456
 Fruit vs. Obst
2010-05-08 @439
 Mixed-language ambiguity
2010-04-15 @994
 Uli's 12:07 AM Law
2010-04-12 @881
 Uli's 1:24 AM Law
2010-04-12 @874
 Uli's 6:28 AM Law
2010-04-12 @869
 Uli's 3:57 PM Law
2010-04-12 @867


Screen shots on the Mac

Recently I needed to do a screen saver that appears to be transparent. Trouble is, ScreenSaverEngine creates the window for you, and it's opaque. So, to work around this, I had to take a screen shot when my screen saver is loaded, and then draw that as my background.

So, how do you do a screenshot from code? Well, the theory is simple: There's a few functions in CGDirectDisplay.h to get the screen's base address, row bytes, size in pixels etc. So, you just use those to copy out your drawings.

The practice is not quite as easy. When you try to directly init an NSData with the base address pointer of your screen, it complains. You'd have to create an NSMutableData and memmove() the data to its mutableBytes. But sadly, this gives you ABGR data on an Intel Mac, which is kind of confusing to Cocoa's NSImage, which expects ARGB. Since there's no flag to tell NSImage to use a different byte ordering, you have to take a detour via CGImage.

Use a preprocessor switch on TARGET_RT_LITTLE_ENDIAN to set the kCGImageAlphaPremultipliedFirst and kCGBitmapByteOrder32Little CGBitmapInfo flags (or whatever is appropriate for the current depth CGDisplayBitsPerPixel gives you). For the "else" case (i.e. big-endian on PPC), specify kCGImageAlphaPremultipliedFirst and default byte order.

Now, the CGImage is created pretty easily, but you'll have to notice that it refers to the screen buffer directly, and thus does "live updates" while you're doing screen shots. So, saving this away is pretty pointless. So, in the end, you'll have to create an NSImage of the appropriate size and use CGContextDrawImage to make an unchanging copy, and that you can save away in a movie or whatever.

If you're a Carbon coder, you might even be able to use the CGImage directly with the QuickTime APIs, but I haven't tried that (yet).

Also, be wary of keeping around the CGImage: you'll get a royal pixel massacre if someone changes the screen depth or size and you then use it again. So, you may want to sign up for some display size change notifications etc.

Reader Comments: (RSS Feed)
ken writes:
NSBitmapImageRep can deal directly with alpha first data. Pass NSAlphaFirstBitmapFormat as the bitmapFormat argument to the most massive init method (longest method in Cocoa!).
Uli Kusterer replies:
It's not the alpha-first that causes the problem. It's the order of the R and B color components (or rather, the byte-order of the pixel data). The pixel data GCDirectDisplay gives you is little-endian, while Cocoa seems to be hard-coded to big-endian for backwards compatibility.
Or E-Mail Uli privately.

Created: 2006-12-12 @960 Last change: 2018-01-23 @462 | Home | Admin | Edit
© Copyright 2003-2018 by M. Uli Kusterer, all rights reserved.