Rendering NSView containing some CALayers to an NSImage
Asked Answered
N

5

7

I have an NSView that contains an NSScrollView containing a CALayer-backed NSView. I've tried all the usual methods of capturing an NSView into an NSImage (using -dataWithPDFInsideRect, NSBitmapImageRep's -initWithFocusedViewRect, etc.) However, all these methods treat the CALayer-backed NSView as if it doesn't exist. I've already seen this StackOverflow post, but it was a question about rendering just a CALayer tree to an image, not an NSView containing both regular NSView's and layer-backed views.

Any help is appreciated, thanks :)

Numeration answered 23/12, 2010 at 5:45 Comment(0)
I
6

The only way I found to do this is to use the CGWindow API's, something like:

CGImageRef cgimg = CGWindowListCreateImage(CGRectZero, kCGWindowListOptionIncludingWindow, [theWindow windowNumber], kCGWindowImageDefault);

then clip out the part of that CGImage that corresponds to your view with

-imageByCroppingToRect.

Then make a NSImage from the cropped CGImage. Be aware this won't work well if parts of that window are offscreen.

Ingleside answered 30/12, 2010 at 19:27 Comment(2)
Instead of doing the crop afterwards, you can use the first argument in CGWindowListCreateImage to capture just a specific rectangle.Medlin
@Medlin I tried that but the result was a blank image.Vie
M
3

This works to draw a view directly to an NSImage, though I haven't tried it with a layer-backed view:

NSImage * i = [[NSImage alloc] initWithSize:[view frame].size];
[i lockFocus];
if ([view lockFocusIfCanDrawInContext:[NSGraphicsContext currentContext]]) {
  [view displayRectIgnoringOpacity:[view frame] inContext:[NSGraphicsContext currentContext]];
  [view unlockFocus];
}
[i unlockFocus];

NSData * d = [i TIFFRepresentation];
[d writeToFile:@"/path/to/my/test.tiff" atomically:YES];
[i release];
Moxa answered 3/1, 2011 at 20:36 Comment(1)
Warning once: -[NSView lockFocusIfCanDrawInContext:] is declared but not yet implemented in this AppKit build. nothing happensElectrodynamometer
B
0

Have you looked at the suggestions in the Cocoa Drawing Guide? ("Creating a Bitmap")

To draw directly into a bitmap, create a new NSBitmapImageRep object with the parameters you want and use the graphicsContextWithBitmapImageRep: method of NSGraphicsContext to create a drawing context. Make the new context the current context and draw. This technique is available only in Mac OS X v10.4 and later. Alternatively, you can create an NSImage object (or an offscreen window), draw into it, and then capture the image contents. This technique is supported in all versions of Mac OS X.

That sounds similar to the iOS solution I'm familiar with (using UIGraphicsBeginImageContext and UIGraphicsGetImageFromCurrentImageContext) so I'd expect it to work for your view.

Barathea answered 3/1, 2011 at 18:48 Comment(0)
S
0

Please have a look at the answer on this post: How do I render a view which contains Core Animation layers to a bitmap?

That approach worked for me under similar circumstances to your own.

Scrophulariaceous answered 1/2, 2011 at 11:55 Comment(0)
H
0

Modern solution:

let bitmap = hostingView.bitmapImageRepForCachingDisplay(in: pointRect)!
hostingView.cacheDisplay(in: centeredRect, to: bitmap)
let image = NSImage(cgImage: bitmap.cgImage!, size: pointSize)

Make sure that rects are relevant to your case. For some reason in cacheDisplay(in:to:) we have to pass the rect with x and y in the center of the view. I'm not so good at macOS drawing so rects adjusting may be required from your side.

Himeji answered 10/1 at 19:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.