NSBitmapImageRep caching behavior in Snow Leopard
NSBitmapImageRep caching behavior in Snow Leopard
- Subject: NSBitmapImageRep caching behavior in Snow Leopard
- From: John Horigan <email@hidden>
- Date: Fri, 25 Sep 2009 16:22:03 -0700
I have a drawing program that creates an NSBitmapImageRep and an
NSImage:
mImage = [[NSImage alloc] initWithSize: size];
mBitmap = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes: NULL
pixelsWide: (int)size.width
pixelsHigh: (int)size.height
bitsPerSample: 8 samplesPerPixel: 4
hasAlpha: YES isPlanar: NO
colorSpaceName: NSCalibratedRGBColorSpace
bytesPerRow: 0 bitsPerPixel: 0];
[mImage addRepresentation: mBitmap];
later I get the pointer to the bitmap data using the bitmapData method
and hand it over drawing code (AGG) running in another thread.
Periodically the render thread will send a message to the main thread
telling it to draw the current image state to my view:
[mView performSelectorOnMainThread: @selector(redisplayImage:)
withObject: [NSValue valueWithRect:
NSMakeRect(cropX(), cropY(), cropWidth(), cropHeight())]
waitUntilDone: YES];
The redisplayImage method in my view class calls the display method
- (void)redisplayImage:(NSValue*)rectObj
{
mRenderedRect = [rectObj rectValue];
[self display];
}
Note that the render thread is waiting for redisplayImage to complete
and the redisplayImage method is calling the display method, not the
setNeedsDisplay method. The views's drawRect method looks like so:
- (void)drawRect:(NSRect)rect
{
NSSize fSize = [self frame].size;
NSSize rSize = [mBitmap size];
float scale;
if (rSize.width <= fSize.width && rSize.height <= fSize.height) {
// rendered area fits within frame, center it
scale = 1.0f;
}
else {
float wScale = fSize.width / rSize.width;
float hScale = fSize.height / rSize.height;
scale = (hScale < wScale) ? hScale : wScale;
}
NSRect dRect;
// center scaled image rectangle
dRect.size.width = rSize.width * scale;
dRect.size.height = rSize.height * scale;
dRect.origin.x = floorf((fSize.width - dRect.size.width) / 2.0f);
dRect.origin.y = floorf((fSize.height - dRect.size.height) / 2.0f);
[[NSColor colorWithDeviceRed: backgroundColor.r
green: backgroundColor.g
blue: backgroundColor.b
alpha: backgroundColor.a ] set];
[NSBezierPath fillRect: rect];
[mImage drawInRect: dRect fromRect: NSZeroRect
operation: NSCompositeSourceAtop
fraction: 1.0];
}
Prior to Snow Leopard this all worked fine. The images drawn onto the
bitmap in the rendering thread would be drawn into the view. If the
view was resized smaller then the NSImage would be shrunk and
centered. If the view was resized larger the NSImage would be centered.
But with Snow Leopard [mImage drawInRect ...] draws from the bitmap
only the first time it is called. All subsequent calls to the
drawInRect method draw whatever was in the bitmap on the first call,
not the current contents of the bitmap. It's as if the
NSBitmapImageRep was being cached on the first draw and the cached
copy was used from then on.
However, if I resize the window so that the drawing NSRect is smaller
than the NSBitmapImageRep (i.e., the bitmap has to be scaled) then the
current bitmap is drawn. And then if I expand the window to be larger
then it goes back drawing the cached bitmap. Shrinking the window
really small seems to reset something in the NSBitmapImageRep, then it
draws from the current bitmap whether there is scaling or not.
It seems that under Snow Leopard the NSBitmapImageRep is being cached
somewhere (video memory?) and as long as there is no scaling the
cached version of the NSBitmapImageRep is being used by drawInRect
instead of of the bitmap in system memory. For some reason making the
view really small causes drawInRect to use the data in system memory.
Here are some things that I tried:
1) Calling the NSImage recache method: [mImage recache];
2) Getting rid of the NSImage and using the NSImageRep drawInRect
method to draw in the view.
3) Call the NSBitmapImageRep bitmapData method before writing to the
bitmap data plane in the rendering thread
#1 and #2 has no effect. NSImage-level caching does not appear to be
the problem. There appears to be caching by NSBitmapImageRep. #3 was
an attempt to get Snow Leopard to invalidate the NSBitmapImageRep
cache. It didn't work.
So this is what worked:
4) Keep the bitmap data plane as a separate data structure, owned by
the view. The redisplayImage method creates a new NSBitmapImageRep and
NSImage every time it is called, using the bitmap data structure that
is owned by the view.
While creating new NSImages and NSBitmapImageReps all the time works I
hate doing all this object churning. Is there some way to use a single
NSImage and NSBitmapImageRep throughout the life of the view?
-- john
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden