Hi!
While updating my game graphics for retina iPad, I was deeply stressed with the HUGE file sizes for RGBA PNG's needed for new screen. So I wrote an extension which helps you shrink down the needed space by using two JPG - one with spritesheet image and one with alpha channel - to create transparent sprite frames on the fly.
It's an category extending CCSpriteFrameCache.
Usage:
-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName maskFile:(NSString*)maskFileName;
-plist is the name of a usial plist file for spritesheet - say, from TexturePacker.
-textureFileName is the name of jpg (or any other format supported by UIImage) file with spritesheet RGB picture (no alpha)
-maskFileName is thename of jpg (or any other format supported by UIImage) file with spritesheet alpha.
Mask uses black for fully transperent areas and white for fully opaque - not the usial CGImageMask behaviour, but I use this as TexturePacker produces alpha images in this format.
So how to prepare sprite sheet (using TexturePacker):
1) Combine all your sprites as usial. Make sure to turn off "Crop" or "Trim", or TP may create different layouts for RGB and alpha images.
2) Select "Texture format - JPG", "Image format - RGBA8888", enter the names for plist file and texture file, set JPEG compression options in "Advanced" panel and publish the spritesheet.
3) Now select "Image format - Alpha", alter the name for texture mask file (I suppose just adding "Mask") and publish again.
So you'll have three file: spriteSheet.plist, texture.jpg and textureMask.jpg. Add them to xCode and use the following in your init:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"spriteSheet.plist" textureFile:@"texture.jpg" maskFile:@"textureMask.jpg"];
And then just create your sprites from frames as usial. Profit! :)
The new spritesheet texture is given the "<textureFileName>_maskedBy_<maskFileName>" key in sharedTextureCache.
For me, it saved a few megabytes of space.
I didn't do any speed test, so if somebody have a bit of time - try to measure time for loading spritesheet into cache from usial RGBA8888 PNG and from identical JPG pack.
Here is the code:
CCSpriteFrameCacheExtentions.h :
/*
* Created by SergScout.
* Copyright (c) 2012 Kidstamatic. All rights reserved.
*
* It uses code from http://iphonedevelopertips.com/cocoa/how-to-mask-an-image.html
* and http://pastie.org/418627
*
* 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 "CCSpriteFrameCache.h"
@interface CCSpriteFrameCache (SSKExtentions)
-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName maskFile:(NSString*)maskFileName;
@end
CCSpriteFrameCacheExtentions.m
/*
* Created by SergScout on 11.03.12.
* Copyright (c) 2012 Kidstamatic. All rights reserved.
*
* It uses code from http://iphonedevelopertips.com/cocoa/how-to-mask-an-image.html
* and http://pastie.org/418627
*
* 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 "CCSpriteFrameCacheExtentions.h"
@implementation CCSpriteFrameCache (SSKExtentions)
CGImageRef CopyImageAndAddAlphaChannel(CGImageRef sourceImage) {
CGImageRef retVal = NULL;
size_t width = CGImageGetWidth(sourceImage);
size_t height = CGImageGetHeight(sourceImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef offscreenContext = CGBitmapContextCreate(NULL, width, height,
8, 0, colorSpace, kCGImageAlphaPremultipliedFirst);
if (offscreenContext != NULL) {
CGContextDrawImage(offscreenContext, CGRectMake(0, 0, width, height), sourceImage);
retVal = CGBitmapContextCreateImage(offscreenContext);
CGContextRelease(offscreenContext);
}
CGColorSpaceRelease(colorSpace);
return retVal;
}
-(void) addSpriteFramesWithFile:(NSString*)plist textureFile:(NSString*)textureFileName maskFile:(NSString*)maskFileName
{
NSAssert( textureFileName, @"Invalid texture file name");
NSAssert( maskFileName, @"Invalid mask file name");
NSString *texturePath = [CCFileUtils fullPathFromRelativePath:textureFileName];
NSString *maskPath = [CCFileUtils fullPathFromRelativePath:maskFileName];
UIImage *textureImage = [UIImage imageWithContentsOfFile:texturePath];
UIImage *maskImage = [UIImage imageWithContentsOfFile:maskPath];
CGImageRef maskRef = maskImage.CGImage;
CGFloat decode[2] = {1 , 0}; //Use it to invert alpha mask got from texturePacker.
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), decode, false); //if you don'n need to invert mask, pass NULL instead of decode
CGImageRef sourceImage = textureImage.CGImage;
CGImageRef imageWithAlpha = sourceImage;
//add alpha channel for images that don't have one (ie GIF, JPEG, etc...)
//this however has a computational cost
if ((CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNone)
|| (CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNoneSkipFirst)
|| (CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNoneSkipLast))
{
imageWithAlpha = CopyImageAndAddAlphaChannel(sourceImage);
}
CGImageRef masked = CGImageCreateWithMask(imageWithAlpha, mask);
CGImageRelease(mask);
//release imageWithAlpha if it was created by CopyImageAndAddAlphaChannel
if (sourceImage != imageWithAlpha) {
CGImageRelease(imageWithAlpha);
}
CCTexture2D *maskedTexture = [[CCTextureCache sharedTextureCache] addCGImage:masked forKey:[NSString stringWithFormat:@"%@_maskedBy_%@", textureFileName, maskFileName]];
CGImageRelease(masked);
[self addSpriteFramesWithFile:plist texture:maskedTexture];
}
@end
P.S. BTW, how can I change my userpic here?