2

How does [NSView cacheDisplayInRect:(NSRect)rect toBitmapImageRep:(NSBitmapImageRep *)bitmapImageRep] work internally when writes the bitmap data to the bitmapImageRep?

E.g. does it traverse and access the CALayer layer of all the subviews and combine them when ...cacheDisplayInRect... is called or does it simply write it's own layer (which then is already contains the representation data for all subviews).

Background to my question: I use a native control (WKWebView) that is broken when it comes to screenshots (example SO question), and instead of reinventing the wheel to take an screenshot of the whole app including the WKWebView I would like to fix the method that Cocoa uses for bitmap representations.

Community
  • 1
  • 1
Robin Andersson
  • 5,150
  • 3
  • 25
  • 44

1 Answers1

0

I did some trial and error and figured out that both ...cacheDisplayInRect... and ...drawInContext... calls - [NSView drawRect:] internally to render the elements.

- [CALayer drawInContext:context] call stack

#0  0x000000010000e28c in -[DXSecureWKWebView drawRect:] at /Users/x/dev/X/XSecureWKWebView.m:34
#1  0x00007fff8d386749 in -[NSView(NSInternal) _recursive:displayRectIgnoringOpacity:inGraphicsContext:CGContext:topView:shouldChangeFontReferenceColor:] ()
#2  0x00007fff8d386afe in -[NSView(NSInternal) _recursive:displayRectIgnoringOpacity:inGraphicsContext:CGContext:topView:shouldChangeFontReferenceColor:] ()
#3  0x00007fff8d386afe in -[NSView(NSInternal) _recursive:displayRectIgnoringOpacity:inGraphicsContext:CGContext:topView:shouldChangeFontReferenceColor:] ()
#4  0x00007fff8d386168 in __46-[NSView(NSLayerKitGlue) drawLayer:inContext:]_block_invoke ()
#5  0x00007fff8d385e11 in -[NSView(NSLayerKitGlue) _drawViewBackingLayer:inContext:drawingHandler:] ()
#6  0x00007fff8d385493 in -[NSView(NSLayerKitGlue) drawLayer:inContext:] ()

- [NSView cacheDisplayInRect:toBitmapImageRep:] call stack

#0  0x000000010000e2dc in -[XSecureWKWebView drawRect:] at /Users/x/dev/x/XSecureWKWebView.m:34
#1  0x00007fff8d386749 in -[NSView(NSInternal) _recursive:displayRectIgnoringOpacity:inGraphicsContext:CGContext:topView:shouldChangeFontReferenceColor:] ()
#2  0x00007fff8d65fe1c in __50-[_NSViewBackingLayer _renderForegroundInContext:]_block_invoke ()
#3  0x00007fff8d385e11 in -[NSView(NSLayerKitGlue) _drawViewBackingLayer:inContext:drawingHandler:] ()
#4  0x00007fff8d50b740 in -[_NSViewBackingLayer _renderForegroundInContext:] ()
#5  0x00007fff934f5ce1 in -[CALayer renderInContext:] ()
#6  0x00007fff934f7aaf in -[CALayer _renderSublayersInContext:] ()
#7  0x00007fff8d50b807 in -[_NSViewBackingLayer _renderSublayersInContext:] ()
#8  0x00007fff934f5cf0 in -[CALayer renderInContext:] ()
#9  0x00007fff934f7aaf in -[CALayer _renderSublayersInContext:] ()
#10 0x00007fff8d50b807 in -[_NSViewBackingLayer _renderSublayersInContext:] ()
#11 0x00007fff934f5cf0 in -[CALayer renderInContext:] ()
#12 0x00007fff934f7aaf in -[CALayer _renderSublayersInContext:] ()
#13 0x00007fff8d50b807 in -[_NSViewBackingLayer _renderSublayersInContext:] ()
#14 0x00007fff934f5cf0 in -[CALayer renderInContext:] ()
#15 0x00007fff934f7aaf in -[CALayer _renderSublayersInContext:] ()
#16 0x00007fff8d50b807 in -[_NSViewBackingLayer _renderSublayersInContext:] ()
#17 0x00007fff934f5cf0 in -[CALayer renderInContext:] ()
#18 0x00007fff934f7aaf in -[CALayer _renderSublayersInContext:] ()
#19 0x00007fff8d50b807 in -[_NSViewBackingLayer _renderSublayersInContext:] ()
#20 0x00007fff934f5cf0 in -[CALayer renderInContext:] ()
#21 0x00007fff934f7aaf in -[CALayer _renderSublayersInContext:] ()
#22 0x00007fff8d50b807 in -[_NSViewBackingLayer _renderSublayersInContext:] ()
#23 0x00007fff934f5cf0 in -[CALayer renderInContext:] ()
#24 0x00007fff8d50b11d in -[NSView _layerBackedDisplayRectIgnoringOpacity:inContext:isRootView:flipContextIfNeedeed:] ()
#25 0x00007fff8d50ac74 in -[NSView displayRectIgnoringOpacity:inContext:] ()
#26 0x00007fff8d65f685 in __62-[NSView cacheDisplayInRect:toBitmapImageRep:includeSubviews:]_block_invoke ()
#27 0x00007fff8d65f3dc in __36-[NSBitmapImageRep _captureDrawing:]_block_invoke ()
#28 0x00007fff8d65f14c in -[NSBitmapImageRep _captureDrawing:] ()
#29 0x00007fff8d65f079 in -[NSView cacheDisplayInRect:toBitmapImageRep:includeSubviews:] ()
Robin Andersson
  • 5,150
  • 3
  • 25
  • 44
  • Do you by any chance know, how slow or fast `cacheDisplay(in:to:)` is. I am using it to convert SwiftUI views (via NSView) into an NSImage to place it into the mouse cursor. But my app is super slow and I am having trouble finding out why. Would it make sense to use `NSView draw()` to manually transform NSView (converted from SwiftUI View) into an NSImage? – jns_ai_unr Jan 23 '20 at 17:17
  • @j.unruh I am afraid that I have no idea, it was a few years ago last time that I dug into iOS library internals. Hope you have figured it out in some other way. – Robin Andersson Jan 28 '20 at 10:17
  • If someone else is reading this at some point and has a similar issue: Using Cocoa Drawing and NSViews instead of SwiftUI View did the trick for me. Converting SwiftUI Views via `cacheDisplay(in:to:)` to NSImage was super slow for me. Creating NSViews with Cocoa Drawing and converting them to NSImage via `dataWithPDF(inside:)` sped up the NSImage creation by a factor of ~10. – jns_ai_unr Jan 28 '20 at 17:44