Revision: 15158
http://bibdesk.svn.sourceforge.net/bibdesk/?rev=15158&view=rev
Author: amaxwell
Date: 2009-04-22 18:35:33 +0000 (Wed, 22 Apr 2009)
Log Message:
-----------
Since tile sizes can now be extended somewhat arbitrarily beyond the maximum
size, remove usage of max size from memory allocation decisions.
Require FVImageBuffer objects to be instantiated with a specific size.
Remove caching and API from FVImageBuffer, since it was essentially superseded
by fv_zone. Standard retain/release semantics can now be used.
Never scale tile buffers down. Compute the maximum size required based on tile
regions, and don't assume that raster origin has the largest region.
When allocation fails for the interleaved image buffer, return NULL instead
of dereferencing it. Only happens with huge images or scaling up.
Modified Paths:
--------------
trunk/bibdesk_vendorsrc/amaxwell/FileView/FVCGImageUtilities.h
trunk/bibdesk_vendorsrc/amaxwell/FileView/FVCGImageUtilities.mm
trunk/bibdesk_vendorsrc/amaxwell/FileView/FVImageBuffer.h
trunk/bibdesk_vendorsrc/amaxwell/FileView/FVImageBuffer.m
Modified: trunk/bibdesk_vendorsrc/amaxwell/FileView/FVCGImageUtilities.h
===================================================================
--- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVCGImageUtilities.h
2009-04-22 17:53:01 UTC (rev 15157)
+++ trunk/bibdesk_vendorsrc/amaxwell/FileView/FVCGImageUtilities.h
2009-04-22 18:35:33 UTC (rev 15158)
@@ -49,20 +49,6 @@
@return CGImage size in pixels. */
FV_PRIVATE_EXTERN NSSize FVCGImageSize(CGImageRef image);
-/** @internal @brief Maximum width of tiles.
-
- Returns the maximum width of tiles that will be created in scaling using
vImage.
- @warning Only exported for FVImageBuffer; do not use.
- @return Tile width in pixels. */
-FV_PRIVATE_EXTERN size_t __FVMaximumTileWidth(void);
-
-/** @internal @brief Maximum height of tiles.
-
- Returns the maximum height of tiles that will be created in scaling using
vImage.
- @warning Only exported for FVImageBuffer; do not use.
- @return Tile height in pixels. */
-FV_PRIVATE_EXTERN size_t __FVMaximumTileHeight(void);
-
/** @internal @brief Resample an image.
This function is used for resampling CGImages or converting an image to be
compatible with cache limitations (if it uses the wrong colorspace, for
instance). Tiling and scaling are performed using vImage, which may give
unacceptable results at very small scale levels due to limitations in the
tiling scheme. However, it should be more memory-efficient than using
FVCGCreateResampledImageOfSize. Images returned are always host-order 8-bit
with alpha channel.
Modified: trunk/bibdesk_vendorsrc/amaxwell/FileView/FVCGImageUtilities.mm
===================================================================
--- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVCGImageUtilities.mm
2009-04-22 17:53:01 UTC (rev 15157)
+++ trunk/bibdesk_vendorsrc/amaxwell/FileView/FVCGImageUtilities.mm
2009-04-22 18:35:33 UTC (rev 15158)
@@ -47,6 +47,10 @@
// http://lists.apple.com/archives/perfoptimization-dev/2005/Mar/msg00041.html
// this is a good size for the MBP, and presumably other vector units
+/*
+ NOTE: MIN/MAX are advisory limits only, and may be exceeded. Do not make
+ allocation decisions based on these limits anymore.
+ */
#define DEFAULT_TILE_WIDTH 1024
#define MAX_TILE_WIDTH 2048
#define MIN_TILE_WIDTH 512
@@ -91,9 +95,6 @@
return s;
}
-size_t __FVMaximumTileWidth(void) { return MAX_TILE_WIDTH; }
-size_t __FVMaximumTileHeight(void) { return MAX_TILE_HEIGHT; }
-
static CGImageRef __FVCopyImageUsingCacheColorspace(CGImageRef image, NSSize
size)
{
[_copyLock lock];
@@ -320,6 +321,10 @@
{
const size_t bytesPerSample = 3;
vImage_Buffer *dstBuffer = destBuffer->buffer;
+ if (region.h * FVPaddedRowBytesForWidth(bytesPerSample, region.w) >
[destBuffer bufferSize]) {
+ FVLog(@"invalid reshape: %lu height and %lu rowBytes from buffer with
size %lu", region.h, FVPaddedRowBytesForWidth(bytesPerSample, region.w),
[destBuffer bufferSize]);
+ HALT;
+ }
dstBuffer->width = region.w;
dstBuffer->height = region.h;
dstBuffer->rowBytes = FVPaddedRowBytesForWidth(bytesPerSample, region.w);
@@ -374,6 +379,10 @@
{
const size_t bytesPerSample = 4;
vImage_Buffer *dstBuffer = destBuffer->buffer;
+ if (region.h * FVPaddedRowBytesForWidth(bytesPerSample, region.w) >
[destBuffer bufferSize]) {
+ FVLog(@"invalid reshape: %lu height and %lu rowBytes from buffer with
size %lu", region.h, FVPaddedRowBytesForWidth(bytesPerSample, region.w),
[destBuffer bufferSize]);
+ HALT;
+ }
dstBuffer->width = region.w;
dstBuffer->height = region.h;
dstBuffer->rowBytes = FVPaddedRowBytesForWidth(bytesPerSample, region.w);
@@ -661,7 +670,7 @@
{
// make this call early so we avoid other allocations on this thread in
case we have to wait on _copyLock
CFDataRef originalImageData = NULL;
- const uint8_t *srcBytes = __FVCGImageGetBytePtr(image, NULL);
+ const uint8_t *srcBytes = __FVCGImageGetBytePtr(image, NULL);
if (NULL == srcBytes) {
// block other threads from copying at the same time; this tends to be
a large memory hit
[_copyLock lock];
@@ -681,6 +690,17 @@
size_t destRowBytes = FVPaddedRowBytesForWidth(4, desiredSize.width);
FVImageBuffer *interleavedImageBuffer = [[FVImageBuffer alloc]
initWithWidth:desiredSize.width height:desiredSize.height
rowBytes:destRowBytes];
+
+ // this is the most likely to fail, since it's the largest contiguous
allocation
+ if (nil == interleavedImageBuffer) {
+ if (originalImageData) {
+ CFRelease(originalImageData);
+ [_copyLock unlock];
+ }
+ FVLog(@"Unable to allocate memory for image of size %.0f x %.0f",
desiredSize.width, desiredSize.height);
+ return NULL;
+ }
+
vImage_Buffer *interleavedBuffer = interleavedImageBuffer->buffer;
/*
@@ -694,19 +714,34 @@
std::vector <FVRegion> regions = __FVTileRegionsForImage(image, scale);
- // we don't need to retain the tiles, since we don't release them after
adding to the arrays
- NSMutableArray *planarTilesA = (NSMutableArray
*)CFArrayCreateMutable(NULL, 4, NULL);
- NSMutableArray *planarTilesB = (NSMutableArray
*)CFArrayCreateMutable(NULL, 4, NULL);
+ // fixed size mutable arrays
+ NSMutableArray *planarTilesA = (NSMutableArray
*)CFArrayCreateMutable(NULL, 4, &kCFTypeArrayCallBacks);
+ NSMutableArray *planarTilesB = (NSMutableArray
*)CFArrayCreateMutable(NULL, 4, &kCFTypeArrayCallBacks);
+ // first region is not necessarily the largest region anymore; figure out
the maximum height and width for tiles
+ size_t maxWidth = 0, maxHeight = 0;
+ std::vector<FVRegion>::iterator iter;
+ for (iter = regions.begin(); iter < regions.end(); iter++) {
+ maxWidth = std::max(maxWidth, iter->w);
+ maxHeight = std::max(maxHeight, iter->h);
+ }
+
+ // if scaling up, allocate extra memory for usage as a scaling destination
+ if (scale > 1) {
+ maxWidth = ceil(scale * maxWidth);
+ maxHeight = ceil(scale * maxHeight);
+ }
+
FVImageBuffer *imageBuffer;
NSUInteger i;
for (i = 0; i < 4; i++) {
- // if scaling up, allocate extra memory, since we reuse these buffers
- imageBuffer = [FVImageBuffer newPlanarBufferWithScale:ceil(scale)];
+ imageBuffer = [[FVImageBuffer alloc] initWithWidth:maxWidth
height:maxHeight bytesPerSample:1];
[planarTilesA addObject:imageBuffer];
+ [imageBuffer release];
- imageBuffer = [FVImageBuffer newPlanarBufferWithScale:ceil(scale)];
+ imageBuffer = [[FVImageBuffer alloc] initWithWidth:maxWidth
height:maxHeight bytesPerSample:1];
[planarTilesB addObject:imageBuffer];
+ [imageBuffer release];
}
imageBuffer = nil;
@@ -725,16 +760,16 @@
NSUInteger regionRowIndex = 0;
NSMutableArray *currentRegionRow = [NSMutableArray new];
- // first region should be the largest region; this is a temporary buffer
passed to the planar conversion function
// NB: not required for the indexed images, since we copy those directly
to the planar buffers
- FVImageBuffer *regionBuffer = isIndexedImage ? nil : [[FVImageBuffer
alloc] initWithWidth:regions[0].w height:regions[0].h bytesPerSample:4];
+ // this is a temporary buffer passed to the planar conversion function
+ FVImageBuffer *regionBuffer = isIndexedImage ? nil : [[FVImageBuffer
alloc] initWithWidth:maxWidth height:maxHeight bytesPerSample:4];
// maintain these instead of creating new buffers on each pass through the
loop
NSUInteger regionColumnIndex =
__FVGetNumberOfColumnsInRegionVector(regions);
for (NSUInteger j = 0; j < regionColumnIndex; j++) {
imageBuffer = [[FVImageBuffer alloc] initWithWidth:(regions[j].w *
ceil(scale)) height:(regions[j].h * ceil(scale)) bytesPerSample:4];
[currentRegionRow addObject:imageBuffer];
- [imageBuffer dispose];
+ [imageBuffer release];
}
// reset to zero so we can use this as index into currentRegionRow
@@ -841,15 +876,13 @@
// cleanup is safe now
- // call -dispose on the tiles so they're returned to the cache
- [planarTilesA makeObjectsPerformSelector:@selector(dispose)];
+ // dispose of the tiles
[planarTilesA release];
- [planarTilesB makeObjectsPerformSelector:@selector(dispose)];
[planarTilesB release];
[currentRegionRow release];
// could probably cache a few of these
- [regionBuffer dispose];
+ [regionBuffer release];
#if FV_LIMIT_TILEMEMORY_USAGE
pthread_mutex_lock(&_memoryMutex);
@@ -873,8 +906,8 @@
CGDataProviderRelease(provider);
CGColorSpaceRelease(cspace);
- // we need to release this one, since its memory is now transferred to
NSData (and it's too big to cache)
- [interleavedImageBuffer dispose];
+ // memory is now transferred to NSData
+ [interleavedImageBuffer release];
return image;
}
Modified: trunk/bibdesk_vendorsrc/amaxwell/FileView/FVImageBuffer.h
===================================================================
--- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVImageBuffer.h 2009-04-22
17:53:01 UTC (rev 15157)
+++ trunk/bibdesk_vendorsrc/amaxwell/FileView/FVImageBuffer.h 2009-04-22
18:35:33 UTC (rev 15158)
@@ -41,20 +41,10 @@
/** @internal @brief Wrapper around vImage_Buffer.
- FVImageBuffer provides a Cocoa object wrapper around a vImage_Buffer
structure. It also provides thread-safe caching of tile-sized buffers in order
to avoid repeated malloc/free overhead, since each thread needs its own
buffers. Additionally, it uses a custom allocator to track memory allocations,
and can report the number of bytes allocated for all threads.
+ FVImageBuffer provides a Cocoa object wrapper around a vImage_Buffer
structure. It uses a custom allocator to track memory allocations, and can
report the number of bytes allocated for all threads.
FVImageBuffer can also be set to not free its underlying vImage_Buffer data
pointer in dealloc, which allows transfer of ownership of the raw byte pointer
to another object without copying. This data pointer will have been allocated
using the CFAllocator returned by FVImageBuffer::allocator, so
CFDataCreateWithBytesNoCopy() should be passed this allocator in order to
ensure proper cleanup.
- Cache management is slightly confusing here, but the intended lifecycle goes
like this:
-
- @li Create an FVImageBuffer via alloc/init... or new... method
- @li Add to an array, retain/release
- @li Mess with bytes, manipulate vImage_Buffer, etc.
- @li Do more things with the buffer, retain/autorelease
- @li When done with the buffer, call FVImageBuffer::dispose
-
- <b>Only the first allocation message is balanced by a call to
FVImageBuffer::dispose</b>. FVImage::dispose is not a general-purpose
replacement for release; it is only sent when you are done with the object.
For now, deal with this weirdness; this one of the reasons it's a private
class.
-
@warning FVImageBuffer is designed for usage with FVCGImageUtilities.h
functions and should not be used elsewhere as-is. FVImageBuffer instances must
not be shared between threads unless protected by a mutex.
*/
@@ -73,32 +63,12 @@
@return The number of bytes in use by FVImageBuffer instances. */
+ (uint64_t)allocatedBytes;
-/** @internal @brief Initialize a new tile buffer.
+/** @internal @brief Raises an exception.
- Returns a planar buffer of appropriate size for tiling. Dimensions are
(FVCGImageUtilities.h::__FVMaximumTileWidth() x
FVCGImageUtilities.h::__FVMaximumTileHeight()).
+ Callers must pass an explicit size.
@return An initialized buffer instance. */
- (id)init;
-/** @internal @brief Get a cached tile buffer.
-
- Returns a previously cached buffer of the same size as that returned by
FVImageBuffer::init.
- @warning You must call FVImageBuffer::dispose to transfer ownership back to
the cache, or else you will leak the instance.
- @return An initialized buffer instance. */
-+ (id)new;
-
-/** @internal @brief Scaled tile buffer.
-
- Creates a buffer of the default size with each side multiplied by scale.
- @warning You must call FVImageBuffer::dispose to transfer ownership back to
the cache, since this may return a cached instance.
- @return An initialized buffer instance. */
-+ (id)newPlanarBufferWithScale:(double)scale;
-
-/** @internal @brief Transfer ownership.
-
- Transfers ownership of the receiver back to the cache if it was previously
cached, or calls release.
- @warning Must only be called once, and you must not use the object after
calling this method. */
-- (oneway void)dispose;
-
/** @internal @brief Designated initializer.
Initializes a new buffer with the specified width, height, and bytes per row.
@@ -127,4 +97,9 @@
@return A CFAllocator instance. */
- (CFAllocatorRef)allocator;
+/** @internal @brief Internal buffer size.
+
+ Use for debugging and asserting. In case of reshaping the underlying buffer,
this preserves the original size. If you change the data pointer in the buffer
and then call this, you'll get what you deserve. */
+- (size_t)bufferSize;
+
@end
Modified: trunk/bibdesk_vendorsrc/amaxwell/FileView/FVImageBuffer.m
===================================================================
--- trunk/bibdesk_vendorsrc/amaxwell/FileView/FVImageBuffer.m 2009-04-22
17:53:01 UTC (rev 15157)
+++ trunk/bibdesk_vendorsrc/amaxwell/FileView/FVImageBuffer.m 2009-04-22
18:35:33 UTC (rev 15158)
@@ -43,9 +43,7 @@
#import "FVBitmapContext.h"
#import "FVAllocator.h"
-static NSMutableSet *_availableBuffers = nil;
-static NSMutableSet *_workingBuffers = nil;
-static OSSpinLock _bufferCacheLock = OS_SPINLOCK_INIT;
+
static CFAllocatorRef _allocator = NULL;
static OSSpinLock _monitorLock = OS_SPINLOCK_INIT;
@@ -105,8 +103,6 @@
+ (void)initialize
{
FVINITIALIZE(FVImageBuffer);
- _availableBuffers = [NSMutableSet new];
- _workingBuffers = [NSMutableSet new];
// create before _allocator
_monitoredPointers = CFDictionaryCreateMutable(NULL, 0, NULL,
&FVIntegerValueDictionaryCallBacks);
@@ -130,36 +126,10 @@
return _allocatedBytes;
}
-+ (id)new
-{
- OSSpinLockLock(&_bufferCacheLock);
- id obj = nil;
- if ([_availableBuffers count]) {
- obj = [_availableBuffers anyObject];
- [_workingBuffers addObject:obj];
- [_availableBuffers removeObject:obj];
- }
- if (nil == obj) {
- obj = [super new];
- [_workingBuffers addObject:obj];
- }
- OSSpinLockUnlock(&_bufferCacheLock);
- return obj;
-}
-
-+ (id)newPlanarBufferWithScale:(double)scale
-{
- NSParameterAssert(scale > 0);
- if (scale < 1) return [self new];
-
- size_t w = __FVMaximumTileWidth() * ceil(scale);
- size_t h = __FVMaximumTileHeight() * ceil(scale);
- return [[self allocWithZone:[self zone]] initWithWidth:w height:h
bytesPerSample:1];
-}
-
- (id)init
{
- return [self initWithWidth:__FVMaximumTileWidth()
height:__FVMaximumTileHeight() bytesPerSample:1];
+ [self doesNotRecognizeSelector:_cmd];
+ return nil;
}
// safe initializer for copy, in case there's a mismatch between
width/height/rowBytes
@@ -223,20 +193,6 @@
[super dealloc];
}
-- (oneway void)dispose;
-{
- OSSpinLockLock(&_bufferCacheLock);
- // if previously cached, return to the cache; otherwise, release
- if ([_workingBuffers containsObject:self]) {
- NSAssert(_freeBufferOnDealloc, @"returning buffer to cache after
transferring contents ownership");
- [_availableBuffers addObject:self];
- [_workingBuffers removeObject:self];
- } else {
- [self release];
- }
- OSSpinLockUnlock(&_bufferCacheLock);
-}
-
- (void)setFreeBufferOnDealloc:(BOOL)flag;
{
if (NO == flag) {
@@ -253,4 +209,6 @@
- (CFAllocatorRef)allocator { return _allocator; }
+- (size_t)bufferSize { return _bufferSize; }
+
@end
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
------------------------------------------------------------------------------
Stay on top of everything new and different, both inside and
around Java (TM) technology - register by April 22, and save
$200 on the JavaOne (SM) conference, June 2-5, 2009, San Francisco.
300 plus technical and hands-on sessions. Register today.
Use priority code J9JMT32. http://p.sf.net/sfu/p
_______________________________________________
Bibdesk-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bibdesk-commit