summary refs log tree commit diff stats
path: root/libs/cocos2d/CCTextureCache.m
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cocos2d/CCTextureCache.m')
-rwxr-xr-xlibs/cocos2d/CCTextureCache.m498
1 files changed, 498 insertions, 0 deletions
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 @@
1/*
2 * cocos2d for iPhone: http://www.cocos2d-iphone.org
3 *
4 * Copyright (c) 2008-2010 Ricardo Quesada
5 * Copyright (c) 2011 Zynga Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 */
26
27#import <Availability.h>
28
29#import "Platforms/CCGL.h"
30#import "CCTextureCache.h"
31#import "CCTexture2D.h"
32#import "CCTexturePVR.h"
33#import "ccMacros.h"
34#import "CCConfiguration.h"
35#import "Support/CCFileUtils.h"
36#import "CCDirector.h"
37#import "ccConfig.h"
38
39// needed for CCCallFuncO in Mac-display_link version
40#import "CCActionManager.h"
41#import "CCActionInstant.h"
42
43#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
44static EAGLContext *auxGLcontext = nil;
45#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
46static NSOpenGLContext *auxGLcontext = nil;
47#endif
48
49
50@interface CCAsyncObject : NSObject
51{
52 SEL selector_;
53 id target_;
54 id data_;
55}
56@property (readwrite,assign) SEL selector;
57@property (readwrite,retain) id target;
58@property (readwrite,retain) id data;
59@end
60
61@implementation CCAsyncObject
62@synthesize selector = selector_;
63@synthesize target = target_;
64@synthesize data = data_;
65- (void) dealloc
66{
67 CCLOGINFO(@"cocos2d: deallocing %@", self);
68 [target_ release];
69 [data_ release];
70 [super dealloc];
71}
72@end
73
74
75@implementation CCTextureCache
76
77#pragma mark TextureCache - Alloc, Init & Dealloc
78static CCTextureCache *sharedTextureCache;
79
80+ (CCTextureCache *)sharedTextureCache
81{
82 if (!sharedTextureCache)
83 sharedTextureCache = [[CCTextureCache alloc] init];
84
85 return sharedTextureCache;
86}
87
88+(id)alloc
89{
90 NSAssert(sharedTextureCache == nil, @"Attempted to allocate a second instance of a singleton.");
91 return [super alloc];
92}
93
94+(void)purgeSharedTextureCache
95{
96 [sharedTextureCache release];
97 sharedTextureCache = nil;
98}
99
100-(id) init
101{
102 if( (self=[super init]) ) {
103 textures_ = [[NSMutableDictionary dictionaryWithCapacity: 10] retain];
104 dictLock_ = [[NSLock alloc] init];
105 contextLock_ = [[NSLock alloc] init];
106 }
107
108 return self;
109}
110
111- (NSString*) description
112{
113 return [NSString stringWithFormat:@"<%@ = %08X | num of textures = %i | keys: %@>",
114 [self class],
115 self,
116 [textures_ count],
117 [textures_ allKeys]
118 ];
119
120}
121
122-(void) dealloc
123{
124 CCLOGINFO(@"cocos2d: deallocing %@", self);
125
126 [textures_ release];
127 [dictLock_ release];
128 [contextLock_ release];
129 [auxGLcontext release];
130 auxGLcontext = nil;
131 sharedTextureCache = nil;
132 [super dealloc];
133}
134
135#pragma mark TextureCache - Add Images
136
137-(void) addImageWithAsyncObject:(CCAsyncObject*)async
138{
139 NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
140
141#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
142 // textures will be created on the main OpenGL context
143 // it seems that in SDK 2.2.x there can't be 2 threads creating textures at the same time
144 // the lock is used for this purpose: issue #472
145 [contextLock_ lock];
146 if( auxGLcontext == nil ) {
147 auxGLcontext = [[EAGLContext alloc]
148 initWithAPI:kEAGLRenderingAPIOpenGLES1
149 sharegroup:[[[[CCDirector sharedDirector] openGLView] context] sharegroup]];
150
151 if( ! auxGLcontext )
152 CCLOG(@"cocos2d: TextureCache: Could not create EAGL context");
153 }
154
155 if( [EAGLContext setCurrentContext:auxGLcontext] ) {
156
157 // load / create the texture
158 CCTexture2D *tex = [self addImage:async.data];
159
160 // The callback will be executed on the main thread
161 [async.target performSelectorOnMainThread:async.selector withObject:tex waitUntilDone:NO];
162
163 [EAGLContext setCurrentContext:nil];
164 } else {
165 CCLOG(@"cocos2d: TetureCache: EAGLContext error");
166 }
167 [contextLock_ unlock];
168
169 [autoreleasepool release];
170
171#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
172
173 [contextLock_ lock];
174 if( auxGLcontext == nil ) {
175
176 MacGLView *view = [[CCDirector sharedDirector] openGLView];
177
178 NSOpenGLPixelFormat *pf = [view pixelFormat];
179 NSOpenGLContext *share = [view openGLContext];
180
181 auxGLcontext = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:share];
182
183 if( ! auxGLcontext )
184 CCLOG(@"cocos2d: TextureCache: Could not create NSOpenGLContext");
185 }
186
187 [auxGLcontext makeCurrentContext];
188
189 // load / create the texture
190 CCTexture2D *tex = [self addImage:async.data];
191
192#if CC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREAD
193 id action = [CCCallFuncO actionWithTarget:async.target selector:async.selector object:tex];
194 [[CCActionManager sharedManager] addAction:action target:async.target paused:NO];
195#else
196 // The callback will be executed on the main thread
197 [async.target performSelector:async.selector
198 onThread:[[CCDirector sharedDirector] runningThread]
199 withObject:tex
200 waitUntilDone:NO];
201#endif
202
203
204 [NSOpenGLContext clearCurrentContext];
205
206 [contextLock_ unlock];
207
208 [autoreleasepool release];
209
210#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
211}
212
213-(void) addImageAsync: (NSString*)path target:(id)target selector:(SEL)selector
214{
215 NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
216
217 // optimization
218
219 CCTexture2D * tex;
220
221 path = ccRemoveHDSuffixFromFile(path);
222
223 if( (tex=[textures_ objectForKey: path] ) ) {
224 [target performSelector:selector withObject:tex];
225 return;
226 }
227
228 // schedule the load
229
230 CCAsyncObject *asyncObject = [[CCAsyncObject alloc] init];
231 asyncObject.selector = selector;
232 asyncObject.target = target;
233 asyncObject.data = path;
234
235 [NSThread detachNewThreadSelector:@selector(addImageWithAsyncObject:) toTarget:self withObject:asyncObject];
236 [asyncObject release];
237}
238
239-(CCTexture2D*) addImage: (NSString*) path
240{
241 NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
242
243 CCTexture2D * tex = nil;
244
245 // MUTEX:
246 // Needed since addImageAsync calls this method from a different thread
247 [dictLock_ lock];
248
249 // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
250 path = ccRemoveHDSuffixFromFile( path );
251
252 tex=[textures_ objectForKey: path];
253
254 if( ! tex ) {
255
256 NSString *lowerCase = [path lowercaseString];
257 // all images are handled by UIImage except PVR extension that is handled by our own handler
258
259 if ( [lowerCase hasSuffix:@".pvr"] || [lowerCase hasSuffix:@".pvr.gz"] || [lowerCase hasSuffix:@".pvr.ccz"] )
260 tex = [self addPVRImage:path];
261
262 // Only iPhone
263#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
264
265 // Issue #886: TEMPORARY FIX FOR TRANSPARENT JPEGS IN IOS4
266 else if ( ( [[CCConfiguration sharedConfiguration] OSVersion] >= kCCiOSVersion_4_0) &&
267 ( [lowerCase hasSuffix:@".jpg"] || [lowerCase hasSuffix:@".jpeg"] )
268 ) {
269 // convert jpg to png before loading the texture
270
271 NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ];
272
273 UIImage *jpg = [[UIImage alloc] initWithContentsOfFile:fullpath];
274 UIImage *png = [[UIImage alloc] initWithData:UIImagePNGRepresentation(jpg)];
275 tex = [ [CCTexture2D alloc] initWithImage: png ];
276 [png release];
277 [jpg release];
278
279 if( tex )
280 [textures_ setObject: tex forKey:path];
281 else
282 CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
283
284 // autorelease prevents possible crash in multithreaded environments
285 [tex autorelease];
286 }
287
288 else {
289
290 // prevents overloading the autorelease pool
291 NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ];
292
293 UIImage *image = [ [UIImage alloc] initWithContentsOfFile: fullpath ];
294 tex = [ [CCTexture2D alloc] initWithImage: image ];
295 [image release];
296
297 if( tex )
298 [textures_ setObject: tex forKey:path];
299 else
300 CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
301
302 // autorelease prevents possible crash in multithreaded environments
303 [tex autorelease];
304 }
305
306 // Only in Mac
307#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
308 else {
309 NSString *fullpath = [CCFileUtils fullPathFromRelativePath: path ];
310
311 NSData *data = [[NSData alloc] initWithContentsOfFile:fullpath];
312 NSBitmapImageRep *image = [[NSBitmapImageRep alloc] initWithData:data];
313 tex = [ [CCTexture2D alloc] initWithImage:[image CGImage]];
314
315 [data release];
316 [image release];
317
318 if( tex )
319 [textures_ setObject: tex forKey:path];
320 else
321 CCLOG(@"cocos2d: Couldn't add image:%@ in CCTextureCache", path);
322
323 // autorelease prevents possible crash in multithreaded environments
324 [tex autorelease];
325 }
326#endif // __MAC_OS_X_VERSION_MAX_ALLOWED
327
328 }
329
330 [dictLock_ unlock];
331
332 return tex;
333}
334
335
336-(CCTexture2D*) addCGImage: (CGImageRef) imageref forKey: (NSString *)key
337{
338 NSAssert(imageref != nil, @"TextureCache: image MUST not be nill");
339
340 CCTexture2D * tex = nil;
341
342 // If key is nil, then create a new texture each time
343 if( key && (tex=[textures_ objectForKey: key] ) ) {
344 return tex;
345 }
346
347#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
348 // prevents overloading the autorelease pool
349 UIImage *image = [[UIImage alloc] initWithCGImage:imageref];
350 tex = [[CCTexture2D alloc] initWithImage: image];
351 [image release];
352
353#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
354 tex = [[CCTexture2D alloc] initWithImage: imageref];
355#endif
356
357 if(tex && key)
358 [textures_ setObject: tex forKey:key];
359 else
360 CCLOG(@"cocos2d: Couldn't add CGImage in CCTextureCache");
361
362 return [tex autorelease];
363}
364
365#pragma mark TextureCache - Remove
366
367-(void) removeAllTextures
368{
369 [textures_ removeAllObjects];
370}
371
372-(void) removeUnusedTextures
373{
374 NSArray *keys = [textures_ allKeys];
375 for( id key in keys ) {
376 id value = [textures_ objectForKey:key];
377 if( [value retainCount] == 1 ) {
378 CCLOG(@"cocos2d: CCTextureCache: removing unused texture: %@", key);
379 [textures_ removeObjectForKey:key];
380 }
381 }
382}
383
384-(void) removeTexture: (CCTexture2D*) tex
385{
386 if( ! tex )
387 return;
388
389 NSArray *keys = [textures_ allKeysForObject:tex];
390
391 for( NSUInteger i = 0; i < [keys count]; i++ )
392 [textures_ removeObjectForKey:[keys objectAtIndex:i]];
393}
394
395-(void) removeTextureForKey:(NSString*)name
396{
397 if( ! name )
398 return;
399
400 [textures_ removeObjectForKey:name];
401}
402
403#pragma mark TextureCache - Get
404- (CCTexture2D *)textureForKey:(NSString *)key
405{
406 return [textures_ objectForKey:key];
407}
408
409@end
410
411
412@implementation CCTextureCache (PVRSupport)
413
414#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
415-(CCTexture2D*) addPVRTCImage:(NSString*)path bpp:(int)bpp hasAlpha:(BOOL)alpha width:(int)w
416{
417 NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
418 NSAssert( bpp==2 || bpp==4, @"TextureCache: bpp must be either 2 or 4");
419
420 CCTexture2D * tex;
421
422 // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
423 path = ccRemoveHDSuffixFromFile( path );
424
425 if( (tex=[textures_ objectForKey: path] ) ) {
426 return tex;
427 }
428
429 // Split up directory and filename
430 NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path];
431
432 NSData *nsdata = [[NSData alloc] initWithContentsOfFile:fullpath];
433 tex = [[CCTexture2D alloc] initWithPVRTCData:[nsdata bytes] level:0 bpp:bpp hasAlpha:alpha length:w pixelFormat:bpp==2?kCCTexture2DPixelFormat_PVRTC2:kCCTexture2DPixelFormat_PVRTC4];
434 if( tex )
435 [textures_ setObject: tex forKey:path];
436 else
437 CCLOG(@"cocos2d: Couldn't add PVRTCImage:%@ in CCTextureCache",path);
438
439 [nsdata release];
440
441 return [tex autorelease];
442}
443#endif // __IPHONE_OS_VERSION_MAX_ALLOWED
444
445-(CCTexture2D*) addPVRImage:(NSString*)path
446{
447 NSAssert(path != nil, @"TextureCache: fileimage MUST not be nill");
448
449 CCTexture2D * tex;
450
451 // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
452 path = ccRemoveHDSuffixFromFile( path );
453
454 if( (tex=[textures_ objectForKey: path] ) ) {
455 return tex;
456 }
457
458 // Split up directory and filename
459 NSString *fullpath = [CCFileUtils fullPathFromRelativePath:path];
460
461 tex = [[CCTexture2D alloc] initWithPVRFile: fullpath];
462 if( tex )
463 [textures_ setObject: tex forKey:path];
464 else
465 CCLOG(@"cocos2d: Couldn't add PVRImage:%@ in CCTextureCache",path);
466
467 return [tex autorelease];
468}
469
470@end
471
472
473@implementation CCTextureCache (Debug)
474
475-(void) dumpCachedTextureInfo
476{
477 NSUInteger count = 0;
478 NSUInteger totalBytes = 0;
479 for (NSString* texKey in textures_) {
480 CCTexture2D* tex = [textures_ objectForKey:texKey];
481 NSUInteger bpp = [tex bitsPerPixelForFormat];
482 // Each texture takes up width * height * bytesPerPixel bytes.
483 NSUInteger bytes = tex.pixelsWide * tex.pixelsWide * bpp / 8;
484 totalBytes += bytes;
485 count++;
486 CCLOG( @"cocos2d: \"%@\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB",
487 texKey,
488 (long)[tex retainCount],
489 (long)tex.name,
490 (long)tex.pixelsWide,
491 (long)tex.pixelsHigh,
492 (long)bpp,
493 (long)bytes / 1024 );
494 }
495 CCLOG( @"cocos2d: CCTextureCache dumpDebugInfo: %ld textures, for %lu KB (%.2f MB)", (long)count, (long)totalBytes / 1024, totalBytes / (1024.0f*1024.0f));
496}
497
498@end