From 9cd57b731ab1c666d4a1cb725538fdc137763d12 Mon Sep 17 00:00:00 2001 From: Starla Insigna Date: Sat, 30 Jul 2011 11:19:14 -0400 Subject: Initial commit (version 0.2.1) --- libs/cocos2d/CCTextureCache.m | 498 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100755 libs/cocos2d/CCTextureCache.m (limited to 'libs/cocos2d/CCTextureCache.m') diff --git a/libs/cocos2d/CCTextureCache.m b/libs/cocos2d/CCTextureCache.m new file mode 100755 index 0000000..080770a --- /dev/null +++ b/libs/cocos2d/CCTextureCache.m @@ -0,0 +1,498 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011 Zynga Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#import + +#import "Platforms/CCGL.h" +#import "CCTextureCache.h" +#import "CCTexture2D.h" +#import "CCTexturePVR.h" +#import "ccMacros.h" +#import "CCConfiguration.h" +#import "Support/CCFileUtils.h" +#import "CCDirector.h" +#import "ccConfig.h" + +// needed for CCCallFuncO in Mac-display_link version +#import "CCActionManager.h" +#import "CCActionInstant.h" + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +static EAGLContext *auxGLcontext = nil; +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) +static NSOpenGLContext *auxGLcontext = nil; +#endif + + +@interface CCAsyncObject : NSObject +{ + SEL selector_; + id target_; + id data_; +} +@property (readwrite,assign) SEL selector; +@property (readwrite,retain) id target; +@property (readwrite,retain) id data; +@end + +@implementation CCAsyncObject +@synthesize selector = selector_; +@synthesize target = target_; +@synthesize data = data_; +- (void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + [target_ release]; + [data_ release]; + [super dealloc]; +} +@end + + +@implementation CCTextureCache + +#pragma mark TextureCache - Alloc, Init & Dealloc +static CCTextureCache *sharedTextureCache; + ++ (CCTextureCache *)sharedTextureCache +{ + if (!sharedTextureCache) + sharedTextureCache = [[CCTextureCache alloc] init]; + + return sharedTextureCache; +} + ++(id)alloc +{ + NSAssert(sharedTextureCache == nil, @"Attempted to allocate a second instance of a singleton."); + return [super alloc]; +} + ++(void)purgeSharedTextureCache +{ + [sharedTextureCache release]; + sharedTextureCache = nil; +} + +-(id) init +{ + if( (self=[super init]) ) { + textures_ = [[NSMutableDictionary dictionaryWithCapacity: 10] retain]; + dictLock_ = [[NSLock alloc] init]; + contextLock_ = [[NSLock alloc] init]; + } + + return self; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | num of textures = %i | keys: %@>", + [self class], + self, + [textures_ count], + [textures_ allKeys] + ]; + +} + +-(void) dealloc +{ + CCLOGINFO(@"cocos2d: deallocing %@", self); + + [textures_ release]; + [dictLock_ release]; + [contextLock_ release]; + [auxGLcontext release]; + auxGLcontext = nil; + sharedTextureCache = nil; + [super dealloc]; +} + +#pragma mark TextureCache - Add Images + +-(void) addImageWithAsyncObject:(CCAsyncObject*)async +{ + NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init]; + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + // textures will be created on the main OpenGL context + // it seems that in SDK 2.2.x there can't be 2 threads creating textures at the same time + // the lock is used for this purpose: issue #472 + [contextLock_ lock]; + if( auxGLcontext == nil ) { + auxGLcontext = [[EAGLContext alloc] + initWithAPI:kEAGLRenderingAPIOpenGLES1 + sharegroup:[[[[CCDirector sharedDirector] openGLView] context] sharegroup]]; + + if( ! auxGLcontext ) + CCLOG(@"cocos2d: TextureCache: Could not create EAGL context"); + } + + if( [EAGLContext setCurrentContext:auxGLcontext] ) { + + // load / create the texture + CCTexture2D *tex = [self addImage:async.data]; + + // The callback will be executed on the main thread + [async.target performSelectorOnMainThread:async.selector withObject:tex waitUntilDone:NO]; + + [EAGLContext setCurrentContext:nil]; + } else { + CCLOG(@"cocos2d: TetureCache: EAGLContext error"); + } + [contextLock_ unlock]; + + [autoreleasepool release]; + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + + [contextLock_ lock]; + if( auxGLcontext == nil ) { + + MacGLView *view = [[CCDirector sharedDirector] openGLView]; + + NSOpenGLPixelFormat *pf = [view pixelFormat]; + NSOpenGLContext *share = [view openGLContext]; + + auxGLcontext = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:share]; + + if( ! auxGLcontext ) + CCLOG(@"cocos2d: TextureCache: Could not create NSOpenGLContext"); + } + + [auxGLcontext makeCurrentContext]; + + // load / create the texture + CCTexture2D *tex = [self addImage:async.data]; + +#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD + id action = [CCCallFuncO actionWithTarget:async.target selector:async.selector object:tex]; + [[CCActionManager sharedManager] addAction:action target:async.target paused:NO]; +#else + // The callback will be executed on the main thread + [async.target performSelector:async.selector + onThread:[[CCDirector sharedDirector] runningThread] + withObject:tex + waitUntilDone:NO]; +#endif + + + [NSOpenGLContext clearCurrentContext]; + + [contextLock_ unlock]; + + [autoreleasepool release]; + +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED +} + +-(void) addImageAsync: (NSString*)path target:(id)target selector:(SEL)selector +{ + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + + // optimization + + CCTexture2D * tex; + + path = ccRemoveHDSuffixFromFile(path); + + if( (tex=[textures_ objectForKey: path] ) ) { + [target performSelector:selector withObject:tex]; + return; + } + + // schedule the load + + CCAsyncObject *asyncObject = [[CCAsyncObject alloc] init]; + asyncObject.selector = selector; + asyncObject.target = target; + asyncObject.data = path; + + [NSThread detachNewThreadSelector:@selector(addImageWithAsyncObject:) toTarget:self withObject:asyncObject]; + [asyncObject release]; +} + +-(CCTexture2D*) addImage: (NSString*) path +{ + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + + CCTexture2D * tex = nil; + + // MUTEX: + // Needed since addImageAsync calls this method from a different thread + [dictLock_ lock]; + + // remove possible -HD suffix to prevent caching the same image twice (issue #1040) + path = ccRemoveHDSuffixFromFile( path ); + + tex=[textures_ objectForKey: path]; + + if( ! tex ) { + + NSString *lowerCase = [path lowercaseString]; + // all images are handled by UIImage except PVR extension that is handled by our own handler + + if ( [lowerCase hasSuffix:@".pvr"] || [lowerCase hasSuffix:@".pvr.gz"] || [lowerCase hasSuffix:@".pvr.ccz"] ) + tex = [self addPVRImage:path]; + + // Only iPhone +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + + // Issue #886: TEMPORARY FIX FOR TRANSPARENT JPEGS IN IOS4 + else if ( ( [[CCConfiguration sharedConfiguration] OSVersion] >= kCCiOSVersion_4_0) && + ( [lowerCase hasSuffix:@".jpg"] || [lowerCase hasSuffix:@".jpeg"] ) + ) { + // convert jpg to png before loading the texture + + NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ]; + + UIImage *jpg = [[UIImage alloc] initWithContentsOfFile:fullpath]; + UIImage *png = [[UIImage alloc] initWithData:UIImagePNGRepresentation(jpg)]; + tex = [ [CCTexture2D alloc] initWithImage: png ]; + [png release]; + [jpg release]; + + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path); + + // autorelease prevents possible crash in multithreaded environments + [tex autorelease]; + } + + else { + + // prevents overloading the autorelease pool + NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ]; + + UIImage *image = [ [UIImage alloc] initWithContentsOfFile: fullpath ]; + tex = [ [CCTexture2D alloc] initWithImage: image ]; + [image release]; + + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path); + + // autorelease prevents possible crash in multithreaded environments + [tex autorelease]; + } + + // Only in Mac +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + else { + NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ]; + + NSData *data = [[NSData alloc] initWithContentsOfFile:fullpath]; + NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data]; + tex = [ [CCTexture2D alloc] initWithImage:[image CGImage]]; + + [data release]; + [image release]; + + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path); + + // autorelease prevents possible crash in multithreaded environments + [tex autorelease]; + } +#endif // __MAC_OS_X_VERSION_MAX_ALLOWED + + } + + [dictLock_ unlock]; + + return tex; +} + + +-(CCTexture2D*) addCGImage: (CGImageRef) imageref forKey: (NSString *)key +{ + NSAssert(imageref != nil, @"TextureCache: image MUST not be nill"); + + CCTexture2D * tex = nil; + + // If key is nil, then create a new texture each time + if( key && (tex=[textures_ objectForKey: key] ) ) { + return tex; + } + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + // prevents overloading the autorelease pool + UIImage *image = [[UIImage alloc] initWithCGImage:imageref]; + tex = [[CCTexture2D alloc] initWithImage: image]; + [image release]; + +#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + tex = [[CCTexture2D alloc] initWithImage: imageref]; +#endif + + if(tex && key) + [textures_ setObject: tex forKey:key]; + else + CCLOG(@"cocos2d: Couldn't add CGImage in CCTextureCache"); + + return [tex autorelease]; +} + +#pragma mark TextureCache - Remove + +-(void) removeAllTextures +{ + [textures_ removeAllObjects]; +} + +-(void) removeUnusedTextures +{ + NSArray *keys = [textures_ allKeys]; + for( id key in keys ) { + id value = [textures_ objectForKey:key]; + if( [value retainCount] == 1 ) { + CCLOG(@"cocos2d: CCTextureCache: removing unused texture: %@", key); + [textures_ removeObjectForKey:key]; + } + } +} + +-(void) removeTexture: (CCTexture2D*) tex +{ + if( ! tex ) + return; + + NSArray *keys = [textures_ allKeysForObject:tex]; + + for( NSUInteger i = 0; i < [keys count]; i++ ) + [textures_ removeObjectForKey:[keys objectAtIndex:i]]; +} + +-(void) removeTextureForKey:(NSString*)name +{ + if( ! name ) + return; + + [textures_ removeObjectForKey:name]; +} + +#pragma mark TextureCache - Get +- (CCTexture2D *)textureForKey:(NSString *)key +{ + return [textures_ objectForKey:key]; +} + +@end + + +@implementation CCTextureCache (PVRSupport) + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +-(CCTexture2D*) addPVRTCImage:(NSString*)path bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w +{ + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + NSAssert( bpp==2 || bpp==4, @"TextureCache: bpp must be either 2 or 4"); + + CCTexture2D * tex; + + // remove possible -HD suffix to prevent caching the same image twice (issue #1040) + path = ccRemoveHDSuffixFromFile( path ); + + if( (tex=[textures_ objectForKey: path] ) ) { + return tex; + } + + // Split up directory and filename + NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path]; + + NSData *nsdata = [[NSData alloc] initWithContentsOfFile:fullpath]; + tex = [[CCTexture2D alloc] initWithPVRTCData:[nsdata bytes] level:0 bpp:bpp hasAlpha:alpha length:w pixelFormat:bpp==2?kCCTexture2DPixelFormat_PVRTC2:kCCTexture2DPixelFormat_PVRTC4]; + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add PVRTCImage:%@ in CCTextureCache",path); + + [nsdata release]; + + return [tex autorelease]; +} +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED + +-(CCTexture2D*) addPVRImage:(NSString*)path +{ + NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill"); + + CCTexture2D * tex; + + // remove possible -HD suffix to prevent caching the same image twice (issue #1040) + path = ccRemoveHDSuffixFromFile( path ); + + if( (tex=[textures_ objectForKey: path] ) ) { + return tex; + } + + // Split up directory and filename + NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path]; + + tex = [[CCTexture2D alloc] initWithPVRFile: fullpath]; + if( tex ) + [textures_ setObject: tex forKey:path]; + else + CCLOG(@"cocos2d: Couldn't add PVRImage:%@ in CCTextureCache",path); + + return [tex autorelease]; +} + +@end + + +@implementation CCTextureCache (Debug) + +-(void) dumpCachedTextureInfo +{ + NSUInteger count = 0; + NSUInteger totalBytes = 0; + for (NSString* texKey in textures_) { + CCTexture2D* tex = [textures_ objectForKey:texKey]; + NSUInteger bpp = [tex bitsPerPixelForFormat]; + // Each texture takes up width * height * bytesPerPixel bytes. + NSUInteger bytes = tex.pixelsWide * tex.pixelsWide * bpp / 8; + totalBytes += bytes; + count++; + CCLOG( @"cocos2d: \"%@\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB", + texKey, + (long)[tex retainCount], + (long)tex.name, + (long)tex.pixelsWide, + (long)tex.pixelsHigh, + (long)bpp, + (long)bytes / 1024 ); + } + CCLOG( @"cocos2d: CCTextureCache dumpDebugInfo: %ld textures, for %lu KB (%.2f MB)", (long)count, (long)totalBytes / 1024, totalBytes / (1024.0f*1024.0f)); +} + +@end -- cgit 1.4.1