/* * cocos2d for iPhone: http://www.cocos2d-iphone.org * * Copyright (c) 2009 Jason Booth * * Copyright (c) 2009 Robert J Payne * * 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. * */ /* * To create sprite frames and texture atlas, use this tool: * http://zwoptex.zwopple.com/ */ #import "Platforms/CCNS.h" #import "ccMacros.h" #import "CCTextureCache.h" #import "CCSpriteFrameCache.h" #import "CCSpriteFrame.h" #import "CCSprite.h" #import "Support/CCFileUtils.h" @implementation CCSpriteFrameCache #pragma mark CCSpriteFrameCache - Alloc, Init & Dealloc static CCSpriteFrameCache *sharedSpriteFrameCache_=nil; + (CCSpriteFrameCache *)sharedSpriteFrameCache { if (!sharedSpriteFrameCache_) sharedSpriteFrameCache_ = [[CCSpriteFrameCache alloc] init]; return sharedSpriteFrameCache_; } +(id)alloc { NSAssert(sharedSpriteFrameCache_ == nil, @"Attempted to allocate a second instance of a singleton."); return [super alloc]; } +(void)purgeSharedSpriteFrameCache { [sharedSpriteFrameCache_ release]; sharedSpriteFrameCache_ = nil; } -(id) init { if( (self=[super init]) ) { spriteFrames_ = [[NSMutableDictionary alloc] initWithCapacity: 100]; spriteFramesAliases_ = [[NSMutableDictionary alloc] initWithCapacity:10]; } return self; } - (NSString*) description { return [NSString stringWithFormat:@"<%@ = %08X | num of sprite frames = %i>", [self class], self, [spriteFrames_ count]]; } -(void) dealloc { CCLOGINFO(@"cocos2d: deallocing %@", self); [spriteFrames_ release]; [spriteFramesAliases_ release]; [super dealloc]; } #pragma mark CCSpriteFrameCache - loading sprite frames -(void) addSpriteFramesWithDictionary:(NSDictionary*)dictionary texture:(CCTexture2D*)texture { /* Supported Zwoptex Formats: ZWTCoordinatesFormatOptionXMLLegacy = 0, // Flash Version ZWTCoordinatesFormatOptionXML1_0 = 1, // Desktop Version 0.0 - 0.4b ZWTCoordinatesFormatOptionXML1_1 = 2, // Desktop Version 1.0.0 - 1.0.1 ZWTCoordinatesFormatOptionXML1_2 = 3, // Desktop Version 1.0.2+ */ NSDictionary *metadataDict = [dictionary objectForKey:@"metadata"]; NSDictionary *framesDict = [dictionary objectForKey:@"frames"]; int format = 0; // get the format if(metadataDict != nil) format = [[metadataDict objectForKey:@"format"] intValue]; // check the format NSAssert( format >= 0 && format <= 3, @"cocos2d: WARNING: format is not supported for CCSpriteFrameCache addSpriteFramesWithDictionary:texture:"); // add real frames for(NSString *frameDictKey in framesDict) { NSDictionary *frameDict = [framesDict objectForKey:frameDictKey]; CCSpriteFrame *spriteFrame; if(format == 0) { float x = [[frameDict objectForKey:@"x"] floatValue]; float y = [[frameDict objectForKey:@"y"] floatValue]; float w = [[frameDict objectForKey:@"width"] floatValue]; float h = [[frameDict objectForKey:@"height"] floatValue]; float ox = [[frameDict objectForKey:@"offsetX"] floatValue]; float oy = [[frameDict objectForKey:@"offsetY"] floatValue]; int ow = [[frameDict objectForKey:@"originalWidth"] intValue]; int oh = [[frameDict objectForKey:@"originalHeight"] intValue]; // check ow/oh if(!ow || !oh) CCLOG(@"cocos2d: WARNING: originalWidth/Height not found on the CCSpriteFrame. AnchorPoint won't work as expected. Regenerate the .plist"); // abs ow/oh ow = abs(ow); oh = abs(oh); // create frame spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture rectInPixels:CGRectMake(x, y, w, h) rotated:NO offset:CGPointMake(ox, oy) originalSize:CGSizeMake(ow, oh)]; } else if(format == 1 || format == 2) { CGRect frame = CCRectFromString([frameDict objectForKey:@"frame"]); BOOL rotated = NO; // rotation if(format == 2) rotated = [[frameDict objectForKey:@"rotated"] boolValue]; CGPoint offset = CCPointFromString([frameDict objectForKey:@"offset"]); CGSize sourceSize = CCSizeFromString([frameDict objectForKey:@"sourceSize"]); // create frame spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture rectInPixels:frame rotated:rotated offset:offset originalSize:sourceSize]; } else if(format == 3) { // get values CGSize spriteSize = CCSizeFromString([frameDict objectForKey:@"spriteSize"]); CGPoint spriteOffset = CCPointFromString([frameDict objectForKey:@"spriteOffset"]); CGSize spriteSourceSize = CCSizeFromString([frameDict objectForKey:@"spriteSourceSize"]); CGRect textureRect = CCRectFromString([frameDict objectForKey:@"textureRect"]); BOOL textureRotated = [[frameDict objectForKey:@"textureRotated"] boolValue]; // get aliases NSArray *aliases = [frameDict objectForKey:@"aliases"]; for(NSString *alias in aliases) { if( [spriteFramesAliases_ objectForKey:alias] ) CCLOG(@"cocos2d: WARNING: an alias with name %@ already exists",alias); [spriteFramesAliases_ setObject:frameDictKey forKey:alias]; } // create frame spriteFrame = [[CCSpriteFrame alloc] initWithTexture:texture rectInPixels:CGRectMake(textureRect.origin.x, textureRect.origin.y, spriteSize.width, spriteSize.height) rotated:textureRotated offset:spriteOffset originalSize:spriteSourceSize]; } // add sprite frame [spriteFrames_ setObject:spriteFrame forKey:frameDictKey]; [spriteFrame release]; } } -(void) addSpriteFramesWithFile:(NSString*)plist texture:(CCTexture2D*)texture { NSString *path = [CCFileUtils fullPathFromRelativePath:plist]; NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; [self addSpriteFramesWithDictionary:dict texture:texture]; } -(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName { NSAssert( textureFileName, @"Invalid texture file name"); CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:textureFileName]; if( texture ) [self addSpriteFramesWithFile:plist texture:texture]; else CCLOG(@"cocos2d: CCSpriteFrameCache: couldn't load texture file. File not found: %@", textureFileName); } -(void) addSpriteFramesWithFile:(NSString*)plist { NSString *path = [CCFileUtils fullPathFromRelativePath:plist]; NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; NSString *texturePath = nil; NSDictionary *metadataDict = [dict objectForKey:@"metadata"]; if( metadataDict ) // try to read texture file name from meta data texturePath = [metadataDict objectForKey:@"textureFileName"]; if( texturePath ) { // build texture path relative to plist file NSString *textureBase = [plist stringByDeletingLastPathComponent]; texturePath = [textureBase stringByAppendingPathComponent:texturePath]; } else { // build texture path by replacing file extension texturePath = [plist stringByDeletingPathExtension]; texturePath = [texturePath stringByAppendingPathExtension:@"png"]; CCLOG(@"cocos2d: CCSpriteFrameCache: Trying to use file '%@' as texture", texturePath); } CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:texturePath]; if( texture ) [self addSpriteFramesWithDictionary:dict texture:texture]; else CCLOG(@"cocos2d: CCSpriteFrameCache: Couldn't load texture"); } -(void) addSpriteFrame:(CCSpriteFrame*)frame name:(NSString*)frameName { [spriteFrames_ setObject:frame forKey:frameName]; } #pragma mark CCSpriteFrameCache - removing -(void) removeSpriteFrames { [spriteFrames_ removeAllObjects]; [spriteFramesAliases_ removeAllObjects]; } -(void) removeUnusedSpriteFrames { NSArray *keys = [spriteFrames_ allKeys]; for( id key in keys ) { id value = [spriteFrames_ objectForKey:key]; if( [value retainCount] == 1 ) { CCLOG(@"cocos2d: CCSpriteFrameCache: removing unused frame: %@", key); [spriteFrames_ removeObjectForKey:key]; } } } -(void) removeSpriteFrameByName:(NSString*)name { // explicit nil handling if( ! name ) return; // Is this an alias ? NSString *key = [spriteFramesAliases_ objectForKey:name]; if( key ) { [spriteFrames_ removeObjectForKey:key]; [spriteFramesAliases_ removeObjectForKey:name]; } else [spriteFrames_ removeObjectForKey:name]; } - (void) removeSpriteFramesFromFile:(NSString*) plist { NSString *path = [CCFileUtils fullPathFromRelativePath:plist]; NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; [self removeSpriteFramesFromDictionary:dict]; } - (void) removeSpriteFramesFromDictionary:(NSDictionary*) dictionary { NSDictionary *framesDict = [dictionary objectForKey:@"frames"]; NSMutableArray *keysToRemove=[NSMutableArray array]; for(NSString *frameDictKey in framesDict) { if ([spriteFrames_ objectForKey:frameDictKey]!=nil) [keysToRemove addObject:frameDictKey]; } [spriteFrames_ removeObjectsForKeys:keysToRemove]; } - (void) removeSpriteFramesFromTexture:(CCTexture2D*) texture { NSMutableArray *keysToRemove=[NSMutableArray array]; for (NSString *spriteFrameKey in spriteFrames_) { if ([[spriteFrames_ valueForKey:spriteFrameKey] texture] == texture) [keysToRemove addObject:spriteFrameKey]; } [spriteFrames_ removeObjectsForKeys:keysToRemove]; } #pragma mark CCSpriteFrameCache - getting -(CCSpriteFrame*) spriteFrameByName:(NSString*)name { CCSpriteFrame *frame = [spriteFrames_ objectForKey:name]; if( ! frame ) { // try alias dictionary NSString *key = [spriteFramesAliases_ objectForKey:name]; frame = [spriteFrames_ objectForKey:key]; if( ! frame ) CCLOG(@"cocos2d: CCSpriteFrameCache: Frame '%@' not found", name); } return frame; } #pragma mark CCSpriteFrameCache - sprite creation -(CCSprite*) createSpriteWithFrameName:(NSString*)name { CCSpriteFrame *frame = [spriteFrames_ objectForKey:name]; return [CCSprite spriteWithSpriteFrame:frame]; } @end