diff options
Diffstat (limited to 'libs/cocos2d/CCTexturePVR.m')
-rwxr-xr-x | libs/cocos2d/CCTexturePVR.m | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/libs/cocos2d/CCTexturePVR.m b/libs/cocos2d/CCTexturePVR.m new file mode 100755 index 0000000..692d5f9 --- /dev/null +++ b/libs/cocos2d/CCTexturePVR.m | |||
@@ -0,0 +1,428 @@ | |||
1 | /* | ||
2 | |||
3 | File: PVRTexture.m | ||
4 | Abstract: The PVRTexture class is responsible for loading .pvr files. | ||
5 | |||
6 | Version: 1.0 | ||
7 | |||
8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. | ||
9 | ("Apple") in consideration of your agreement to the following terms, and your | ||
10 | use, installation, modification or redistribution of this Apple software | ||
11 | constitutes acceptance of these terms. If you do not agree with these terms, | ||
12 | please do not use, install, modify or redistribute this Apple software. | ||
13 | |||
14 | In consideration of your agreement to abide by the following terms, and subject | ||
15 | to these terms, Apple grants you a personal, non-exclusive license, under | ||
16 | Apple's copyrights in this original Apple software (the "Apple Software"), to | ||
17 | use, reproduce, modify and redistribute the Apple Software, with or without | ||
18 | modifications, in source and/or binary forms; provided that if you redistribute | ||
19 | the Apple Software in its entirety and without modifications, you must retain | ||
20 | this notice and the following text and disclaimers in all such redistributions | ||
21 | of the Apple Software. | ||
22 | Neither the name, trademarks, service marks or logos of Apple Inc. may be used | ||
23 | to endorse or promote products derived from the Apple Software without specific | ||
24 | prior written permission from Apple. Except as expressly stated in this notice, | ||
25 | no other rights or licenses, express or implied, are granted by Apple herein, | ||
26 | including but not limited to any patent rights that may be infringed by your | ||
27 | derivative works or by other works in which the Apple Software may be | ||
28 | incorporated. | ||
29 | |||
30 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO | ||
31 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED | ||
32 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
33 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN | ||
34 | COMBINATION WITH YOUR PRODUCTS. | ||
35 | |||
36 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR | ||
37 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | ||
38 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
39 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR | ||
40 | DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF | ||
41 | CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF | ||
42 | APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
43 | |||
44 | Copyright (C) 2008 Apple Inc. All Rights Reserved. | ||
45 | |||
46 | */ | ||
47 | |||
48 | /* | ||
49 | * Extended PVR formats for cocos2d project ( http://www.cocos2d-iphone.org ) | ||
50 | * - RGBA8888 | ||
51 | * - BGRA8888 | ||
52 | * - RGBA4444 | ||
53 | * - RGBA5551 | ||
54 | * - RGB565 | ||
55 | * - A8 | ||
56 | * - I8 | ||
57 | * - AI88 | ||
58 | */ | ||
59 | |||
60 | #import <Availability.h> | ||
61 | |||
62 | #import <zlib.h> | ||
63 | |||
64 | #import "CCTexturePVR.h" | ||
65 | #import "ccMacros.h" | ||
66 | #import "CCConfiguration.h" | ||
67 | #import "Support/ccUtils.h" | ||
68 | #import "Support/CCFileUtils.h" | ||
69 | #import "Support/ZipUtils.h" | ||
70 | #import "Support/OpenGL_Internal.h" | ||
71 | |||
72 | #pragma mark - | ||
73 | #pragma mark CCTexturePVR | ||
74 | |||
75 | #define PVR_TEXTURE_FLAG_TYPE_MASK 0xff | ||
76 | |||
77 | // Values taken from PVRTexture.h from http://www.imgtec.com | ||
78 | enum { | ||
79 | kPVRTextureFlagMipmap = (1<<8), // has mip map levels | ||
80 | kPVRTextureFlagTwiddle = (1<<9), // is twiddled | ||
81 | kPVRTextureFlagBumpmap = (1<<10), // has normals encoded for a bump map | ||
82 | kPVRTextureFlagTiling = (1<<11), // is bordered for tiled pvr | ||
83 | kPVRTextureFlagCubemap = (1<<12), // is a cubemap/skybox | ||
84 | kPVRTextureFlagFalseMipCol = (1<<13), // are there false coloured MIP levels | ||
85 | kPVRTextureFlagVolume = (1<<14), // is this a volume texture | ||
86 | kPVRTextureFlagAlpha = (1<<15), // v2.1 is there transparency info in the texture | ||
87 | kPVRTextureFlagVerticalFlip = (1<<16), // v2.1 is the texture vertically flipped | ||
88 | }; | ||
89 | |||
90 | |||
91 | static char gPVRTexIdentifier[4] = "PVR!"; | ||
92 | |||
93 | enum | ||
94 | { | ||
95 | kPVRTexturePixelTypeRGBA_4444= 0x10, | ||
96 | kPVRTexturePixelTypeRGBA_5551, | ||
97 | kPVRTexturePixelTypeRGBA_8888, | ||
98 | kPVRTexturePixelTypeRGB_565, | ||
99 | kPVRTexturePixelTypeRGB_555, // unsupported | ||
100 | kPVRTexturePixelTypeRGB_888, // unsupported | ||
101 | kPVRTexturePixelTypeI_8, | ||
102 | kPVRTexturePixelTypeAI_88, | ||
103 | kPVRTexturePixelTypePVRTC_2, | ||
104 | kPVRTexturePixelTypePVRTC_4, | ||
105 | kPVRTexturePixelTypeBGRA_8888, | ||
106 | kPVRTexturePixelTypeA_8, | ||
107 | }; | ||
108 | |||
109 | static const uint32_t tableFormats[][7] = { | ||
110 | |||
111 | // - PVR texture format | ||
112 | // - OpenGL internal format | ||
113 | // - OpenGL format | ||
114 | // - OpenGL type | ||
115 | // - bpp | ||
116 | // - compressed | ||
117 | // - Cocos2d texture format constant | ||
118 | { kPVRTexturePixelTypeRGBA_4444, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 16, NO, kCCTexture2DPixelFormat_RGBA4444 }, | ||
119 | { kPVRTexturePixelTypeRGBA_5551, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 16, NO, kCCTexture2DPixelFormat_RGB5A1 }, | ||
120 | { kPVRTexturePixelTypeRGBA_8888, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 32, NO, kCCTexture2DPixelFormat_RGBA8888 }, | ||
121 | { kPVRTexturePixelTypeRGB_565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16, NO, kCCTexture2DPixelFormat_RGB565 }, | ||
122 | { kPVRTexturePixelTypeA_8, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 8, NO, kCCTexture2DPixelFormat_A8 }, | ||
123 | { kPVRTexturePixelTypeI_8, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 8, NO, kCCTexture2DPixelFormat_I8 }, | ||
124 | { kPVRTexturePixelTypeAI_88, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 16, NO, kCCTexture2DPixelFormat_AI88 }, | ||
125 | #ifdef __IPHONE_OS_VERSION_MAX_ALLOWED | ||
126 | { kPVRTexturePixelTypePVRTC_2, GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, -1, -1, 2, YES, kCCTexture2DPixelFormat_PVRTC2 }, | ||
127 | { kPVRTexturePixelTypePVRTC_4, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, -1, -1, 4, YES, kCCTexture2DPixelFormat_PVRTC4 }, | ||
128 | #endif // iphone only | ||
129 | { kPVRTexturePixelTypeBGRA_8888, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 32, NO, kCCTexture2DPixelFormat_RGBA8888 }, | ||
130 | }; | ||
131 | #define MAX_TABLE_ELEMENTS (sizeof(tableFormats) / sizeof(tableFormats[0])) | ||
132 | |||
133 | enum { | ||
134 | kCCInternalPVRTextureFormat, | ||
135 | kCCInternalOpenGLInternalFormat, | ||
136 | kCCInternalOpenGLFormat, | ||
137 | kCCInternalOpenGLType, | ||
138 | kCCInternalBPP, | ||
139 | kCCInternalCompressedImage, | ||
140 | kCCInternalCCTexture2DPixelFormat, | ||
141 | }; | ||
142 | |||
143 | typedef struct _PVRTexHeader | ||
144 | { | ||
145 | uint32_t headerLength; | ||
146 | uint32_t height; | ||
147 | uint32_t width; | ||
148 | uint32_t numMipmaps; | ||
149 | uint32_t flags; | ||
150 | uint32_t dataLength; | ||
151 | uint32_t bpp; | ||
152 | uint32_t bitmaskRed; | ||
153 | uint32_t bitmaskGreen; | ||
154 | uint32_t bitmaskBlue; | ||
155 | uint32_t bitmaskAlpha; | ||
156 | uint32_t pvrTag; | ||
157 | uint32_t numSurfs; | ||
158 | } PVRTexHeader; | ||
159 | |||
160 | |||
161 | @implementation CCTexturePVR | ||
162 | |||
163 | @synthesize name = name_; | ||
164 | @synthesize width = width_; | ||
165 | @synthesize height = height_; | ||
166 | @synthesize hasAlpha = hasAlpha_; | ||
167 | |||
168 | // cocos2d integration | ||
169 | @synthesize retainName = retainName_; | ||
170 | @synthesize format = format_; | ||
171 | |||
172 | |||
173 | - (BOOL)unpackPVRData:(unsigned char*)data PVRLen:(NSUInteger)len | ||
174 | { | ||
175 | BOOL success = FALSE; | ||
176 | PVRTexHeader *header = NULL; | ||
177 | uint32_t flags, pvrTag; | ||
178 | uint32_t dataLength = 0, dataOffset = 0, dataSize = 0; | ||
179 | uint32_t blockSize = 0, widthBlocks = 0, heightBlocks = 0; | ||
180 | uint32_t width = 0, height = 0, bpp = 4; | ||
181 | uint8_t *bytes = NULL; | ||
182 | uint32_t formatFlags; | ||
183 | |||
184 | header = (PVRTexHeader *)data; | ||
185 | |||
186 | pvrTag = CFSwapInt32LittleToHost(header->pvrTag); | ||
187 | |||
188 | if ((uint32_t)gPVRTexIdentifier[0] != ((pvrTag >> 0) & 0xff) || | ||
189 | (uint32_t)gPVRTexIdentifier[1] != ((pvrTag >> 8) & 0xff) || | ||
190 | (uint32_t)gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) || | ||
191 | (uint32_t)gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff)) | ||
192 | { | ||
193 | return FALSE; | ||
194 | } | ||
195 | |||
196 | CCConfiguration *configuration = [CCConfiguration sharedConfiguration]; | ||
197 | |||
198 | flags = CFSwapInt32LittleToHost(header->flags); | ||
199 | formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK; | ||
200 | BOOL flipped = flags & kPVRTextureFlagVerticalFlip; | ||
201 | if( flipped ) | ||
202 | CCLOG(@"cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool"); | ||
203 | |||
204 | if( ! [configuration supportsNPOT] && | ||
205 | ( header->width != ccNextPOT(header->width) || header->height != ccNextPOT(header->height ) ) ) { | ||
206 | CCLOG(@"cocos2d: ERROR: Loding an NPOT texture (%dx%d) but is not supported on this device", header->width, header->height); | ||
207 | return FALSE; | ||
208 | } | ||
209 | |||
210 | for( tableFormatIndex_=0; tableFormatIndex_ < (unsigned int)MAX_TABLE_ELEMENTS ; tableFormatIndex_++) { | ||
211 | if( tableFormats[tableFormatIndex_][kCCInternalPVRTextureFormat] == formatFlags ) { | ||
212 | |||
213 | numberOfMipmaps_ = 0; | ||
214 | |||
215 | width_ = width = CFSwapInt32LittleToHost(header->width); | ||
216 | height_ = height = CFSwapInt32LittleToHost(header->height); | ||
217 | |||
218 | if (CFSwapInt32LittleToHost(header->bitmaskAlpha)) | ||
219 | hasAlpha_ = TRUE; | ||
220 | else | ||
221 | hasAlpha_ = FALSE; | ||
222 | |||
223 | dataLength = CFSwapInt32LittleToHost(header->dataLength); | ||
224 | bytes = ((uint8_t *)data) + sizeof(PVRTexHeader); | ||
225 | format_ = tableFormats[tableFormatIndex_][kCCInternalCCTexture2DPixelFormat]; | ||
226 | bpp = tableFormats[tableFormatIndex_][kCCInternalBPP]; | ||
227 | |||
228 | // Calculate the data size for each texture level and respect the minimum number of blocks | ||
229 | while (dataOffset < dataLength) | ||
230 | { | ||
231 | switch (formatFlags) { | ||
232 | case kPVRTexturePixelTypePVRTC_2: | ||
233 | blockSize = 8 * 4; // Pixel by pixel block size for 2bpp | ||
234 | widthBlocks = width / 8; | ||
235 | heightBlocks = height / 4; | ||
236 | break; | ||
237 | case kPVRTexturePixelTypePVRTC_4: | ||
238 | blockSize = 4 * 4; // Pixel by pixel block size for 4bpp | ||
239 | widthBlocks = width / 4; | ||
240 | heightBlocks = height / 4; | ||
241 | break; | ||
242 | case kPVRTexturePixelTypeBGRA_8888: | ||
243 | if( ! [[CCConfiguration sharedConfiguration] supportsBGRA8888] ) { | ||
244 | CCLOG(@"cocos2d: TexturePVR. BGRA8888 not supported on this device"); | ||
245 | return FALSE; | ||
246 | } | ||
247 | default: | ||
248 | blockSize = 1; | ||
249 | widthBlocks = width; | ||
250 | heightBlocks = height; | ||
251 | break; | ||
252 | } | ||
253 | |||
254 | // Clamp to minimum number of blocks | ||
255 | if (widthBlocks < 2) | ||
256 | widthBlocks = 2; | ||
257 | if (heightBlocks < 2) | ||
258 | heightBlocks = 2; | ||
259 | |||
260 | dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8); | ||
261 | float packetLength = (dataLength-dataOffset); | ||
262 | packetLength = packetLength > dataSize ? dataSize : packetLength; | ||
263 | |||
264 | mipmaps_[numberOfMipmaps_].address = bytes+dataOffset; | ||
265 | mipmaps_[numberOfMipmaps_].len = packetLength; | ||
266 | numberOfMipmaps_++; | ||
267 | |||
268 | NSAssert( numberOfMipmaps_ < CC_PVRMIPMAP_MAX, @"TexturePVR: Maximum number of mimpaps reached. Increate the CC_PVRMIPMAP_MAX value"); | ||
269 | |||
270 | dataOffset += packetLength; | ||
271 | |||
272 | width = MAX(width >> 1, 1); | ||
273 | height = MAX(height >> 1, 1); | ||
274 | } | ||
275 | |||
276 | success = TRUE; | ||
277 | break; | ||
278 | } | ||
279 | } | ||
280 | |||
281 | if( ! success ) | ||
282 | CCLOG(@"cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%2x. Re-encode it with a OpenGL pixel format variant", formatFlags); | ||
283 | |||
284 | return success; | ||
285 | } | ||
286 | |||
287 | |||
288 | - (BOOL)createGLTexture | ||
289 | { | ||
290 | GLsizei width = width_; | ||
291 | GLsizei height = height_; | ||
292 | GLenum err; | ||
293 | |||
294 | if (numberOfMipmaps_ > 0) | ||
295 | { | ||
296 | if (name_ != 0) | ||
297 | glDeleteTextures(1, &name_); | ||
298 | |||
299 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); | ||
300 | glGenTextures(1, &name_); | ||
301 | glBindTexture(GL_TEXTURE_2D, name_); | ||
302 | } | ||
303 | |||
304 | CHECK_GL_ERROR(); // clean possible GL error | ||
305 | |||
306 | // Generate textures with mipmaps | ||
307 | for (GLint i=0; i < numberOfMipmaps_; i++) | ||
308 | { | ||
309 | GLenum internalFormat = tableFormats[tableFormatIndex_][kCCInternalOpenGLInternalFormat]; | ||
310 | GLenum format = tableFormats[tableFormatIndex_][kCCInternalOpenGLFormat]; | ||
311 | GLenum type = tableFormats[tableFormatIndex_][kCCInternalOpenGLType]; | ||
312 | BOOL compressed = tableFormats[tableFormatIndex_][kCCInternalCompressedImage]; | ||
313 | |||
314 | if( compressed && ! [[CCConfiguration sharedConfiguration] supportsPVRTC] ) { | ||
315 | CCLOG(@"cocos2d: WARNING: PVRTC images are not supported"); | ||
316 | return FALSE; | ||
317 | } | ||
318 | |||
319 | unsigned char *data = mipmaps_[i].address; | ||
320 | unsigned int datalen = mipmaps_[i].len; | ||
321 | |||
322 | if( compressed) | ||
323 | glCompressedTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, datalen, data); | ||
324 | else | ||
325 | glTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, format, type, data); | ||
326 | |||
327 | if( i > 0 && (width != height || ccNextPOT(width) != width ) ) | ||
328 | CCLOG(@"cocos2d: TexturePVR. WARNING. Mipmap level %u is not squared. Texture won't render correctly. width=%u != height=%u", i, width, height); | ||
329 | |||
330 | err = glGetError(); | ||
331 | if (err != GL_NO_ERROR) | ||
332 | { | ||
333 | CCLOG(@"cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X", i, err); | ||
334 | return FALSE; | ||
335 | } | ||
336 | |||
337 | width = MAX(width >> 1, 1); | ||
338 | height = MAX(height >> 1, 1); | ||
339 | } | ||
340 | |||
341 | return TRUE; | ||
342 | } | ||
343 | |||
344 | |||
345 | - (id)initWithContentsOfFile:(NSString *)path | ||
346 | { | ||
347 | if((self = [super init])) | ||
348 | { | ||
349 | unsigned char *pvrdata = NULL; | ||
350 | NSInteger pvrlen = 0; | ||
351 | NSString *lowerCase = [path lowercaseString]; | ||
352 | |||
353 | if ( [lowerCase hasSuffix:@".ccz"]) | ||
354 | pvrlen = ccInflateCCZFile( [path UTF8String], &pvrdata ); | ||
355 | |||
356 | else if( [lowerCase hasSuffix:@".gz"] ) | ||
357 | pvrlen = ccInflateGZipFile( [path UTF8String], &pvrdata ); | ||
358 | |||
359 | else | ||
360 | pvrlen = ccLoadFileIntoMemory( [path UTF8String], &pvrdata ); | ||
361 | |||
362 | if( pvrlen < 0 ) { | ||
363 | [self release]; | ||
364 | return nil; | ||
365 | } | ||
366 | |||
367 | |||
368 | numberOfMipmaps_ = 0; | ||
369 | |||
370 | name_ = 0; | ||
371 | width_ = height_ = 0; | ||
372 | tableFormatIndex_ = -1; | ||
373 | hasAlpha_ = FALSE; | ||
374 | |||
375 | retainName_ = NO; // cocos2d integration | ||
376 | |||
377 | if( ! [self unpackPVRData:pvrdata PVRLen:pvrlen] || ![self createGLTexture] ) { | ||
378 | free(pvrdata); | ||
379 | [self release]; | ||
380 | return nil; | ||
381 | } | ||
382 | |||
383 | free(pvrdata); | ||
384 | } | ||
385 | |||
386 | return self; | ||
387 | } | ||
388 | |||
389 | - (id)initWithContentsOfURL:(NSURL *)url | ||
390 | { | ||
391 | if (![url isFileURL]) | ||
392 | { | ||
393 | CCLOG(@"cocos2d: CCPVRTexture: Only files are supported"); | ||
394 | [self release]; | ||
395 | return nil; | ||
396 | } | ||
397 | |||
398 | return [self initWithContentsOfFile:[url path]]; | ||
399 | } | ||
400 | |||
401 | |||
402 | + (id)pvrTextureWithContentsOfFile:(NSString *)path | ||
403 | { | ||
404 | return [[[self alloc] initWithContentsOfFile:path] autorelease]; | ||
405 | } | ||
406 | |||
407 | |||
408 | + (id)pvrTextureWithContentsOfURL:(NSURL *)url | ||
409 | { | ||
410 | if (![url isFileURL]) | ||
411 | return nil; | ||
412 | |||
413 | return [CCTexturePVR pvrTextureWithContentsOfFile:[url path]]; | ||
414 | } | ||
415 | |||
416 | |||
417 | - (void)dealloc | ||
418 | { | ||
419 | CCLOGINFO( @"cocos2d: deallocing %@", self); | ||
420 | |||
421 | if (name_ != 0 && ! retainName_ ) | ||
422 | glDeleteTextures(1, &name_); | ||
423 | |||
424 | [super dealloc]; | ||
425 | } | ||
426 | |||
427 | @end | ||
428 | |||