Re: NSBitmapImageRep caching behavior in Snow Leopard
Re: NSBitmapImageRep caching behavior in Snow Leopard
- Subject: Re: NSBitmapImageRep caching behavior in Snow Leopard
- From: Scott Anguish <email@hidden>
- Date: Mon, 28 Sep 2009 01:07:22 -0400
I strongly suggest reading the release notes on NSImage in the
Application Kit release notes for Snow Leopard.
it has many, many, changes. These won't translate to the Cocoa Drawing
Guide for a bit yet. Sadly.
On Sep 25, 2009, at 7:22 PM, John Horigan wrote:
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
_______________________________________________
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