diff options
Diffstat (limited to 'libs/cocos2d/CCTextureCache.m')
-rwxr-xr-x | libs/cocos2d/CCTextureCache.m | 498 |
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 | ||
44 | static EAGLContext *auxGLcontext = nil; | ||
45 | #elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) | ||
46 | static 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 | ||
78 | static 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 | ||