【发布时间】:2013-06-20 14:59:53
【问题描述】:
第一次运行游戏时,我需要生成 320 张图片并将其保存为 PNG。然后将加载这些图像,而不是再次生成这些图像。流程如下:
- 加载图像模板(带 alpha 的黑白)
- 用指定颜色覆盖非透明像素
- 将不透明度为 0.3 的模板放在模板顶部,将其合并为一张最终图像
- 返回 UIImage
- 将 UIImage 转换为 NSData 为 PNG 存储在 Cache 目录中
这是使用 UIGraphicsBeginImageContextWithOptions 完成的。此过程需要在后台线程上为 10 种颜色的 32 个图像模板完成。目的是这些将用作该游戏中的头像/个人资料图像,并在某些屏幕上酌情缩小。但它们不能每次都生成,因为这会导致太多延迟。
每张图片的尺寸为 400x400。存储时,它们的大小约为 20/25 kB。当我尝试使用当前的生成和存储方式时,我收到了内存警告,并且我看到(使用 Instruments)活动的 CGImage 和 UIImage 对象的数量不断快速增加。这似乎它们被保留了,但我没有对它们的任何引用。
这是我的另一个问题,详细说明了我正在使用的代码:UIGraphicsBeginImageContext created image
创建这么多图像并将其存储到辅助存储的最佳方法是什么?提前致谢。
编辑:
这是我目前用于创建和保存图像的全部代码:
//==========================================================
// Definitions and Macros
//==========================================================
//HEX color macro
#define UIColorFromRGB(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
//Colours
#define RED_COLOUR UIColorFromRGB(0xF65D58)
#define ORANGE_COLOUR UIColorFromRGB(0xFF8D16)
#define YELLOW_COLOUR UIColorFromRGB(0xFFD100)
#define LIGHT_GREEN_COLOUR UIColorFromRGB(0x82DE13)
#define DARK_GREEN_COLOUR UIColorFromRGB(0x67B74F)
#define TURQUOISE_COLOUR UIColorFromRGB(0x32ADA6)
#define LIGHT_BLUE_COLOUR UIColorFromRGB(0x11C9FF)
#define DARK_BLUE_COLOUR UIColorFromRGB(0x2E97F5)
#define PURPLE_COLOUR UIColorFromRGB(0x8F73FD)
#define PINK_COLOUR UIColorFromRGB(0xF35991)
#import "ViewController.h"
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//Generate the graphics
[self generateAndSaveGraphics];
}
//==========================================================
// Generating and Saving Graphics
//==========================================================
-(void)generateAndSaveGraphics {
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self createAvatarImages];
//Here create all other images that need to be saved to Cache directory
dispatch_async( dispatch_get_main_queue(), ^{ //Finished
NSLog(@"DONE"); //always runs out of memory before getting here
});
});
}
-(void)createAvatarImages {
//Create avatar images
NSArray *colours = [NSArray arrayWithObjects:RED_COLOUR, ORANGE_COLOUR, YELLOW_COLOUR, LIGHT_GREEN_COLOUR, DARK_GREEN_COLOUR, TURQUOISE_COLOUR, LIGHT_BLUE_COLOUR, DARK_BLUE_COLOUR, PURPLE_COLOUR, PINK_COLOUR, nil];
NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
for(int i = 0; i < 32; i++) { //Avatar image templates are named m1 - m16 and f1 - f16
NSString *avatarImageName;
if(i < 16) { //female avatars
avatarImageName = [NSString stringWithFormat:@"f%i", i+1];
}
else { //male avatars
avatarImageName = [NSString stringWithFormat:@"m%i", i-15];
}
for(int j = 0; j < colours.count; j++) { //make avatar image for each colour
@autoreleasepool { //only helps very slightly
UIColor *colour = [colours objectAtIndex:j];
UIImage *avatarImage = [self tintedImageFromImage:[UIImage imageNamed:avatarImageName] colour:colour intensity:0.3];
NSString *fileName = [NSString stringWithFormat:@"%@_%i.png", avatarImageName, j];
NSString *filePath = [cacheDir stringByAppendingPathComponent:fileName];
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(avatarImage)];
[imageData writeToFile:filePath atomically:YES];
NSLog(@"AVATAR IMAGE CREATED");
}
}
}
}
//==========================================================
// Universal Image Tinting Code
//==========================================================
//Creates a tinted image based on the source greyscale image and tinting intensity
-(UIImage *)tintedImageFromImage:(UIImage *)sourceImage colour:(UIColor *)color intensity:(float)intensity {
if (UIGraphicsBeginImageContextWithOptions != NULL) {
UIGraphicsBeginImageContextWithOptions(sourceImage.size, NO, 0.0);
} else {
UIGraphicsBeginImageContext(sourceImage.size);
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, sourceImage.size.width, sourceImage.size.height);
// draw alpha-mask
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, sourceImage.CGImage);
// draw tint color, preserving alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[color setFill];
CGContextFillRect(context, rect);
//Set the original greyscale template as the overlay of the new image
sourceImage = [self verticallyFlipImage:sourceImage];
[sourceImage drawInRect:CGRectMake(0,0, sourceImage.size.width,sourceImage.size.height) blendMode:kCGBlendModeMultiply alpha:intensity];
UIImage *colouredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
colouredImage = [self verticallyFlipImage:colouredImage];
return colouredImage;
}
//Vertically flips an image
-(UIImage *)verticallyFlipImage:(UIImage *)originalImage {
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:originalImage];
UIGraphicsBeginImageContext(tempImageView.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, tempImageView.frame.size.height);
CGContextConcatCTM(context, flipVertical);
[tempImageView.layer renderInContext:context];
UIImage *flippedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return flippedImage;
}
@end
我创建了一个测试项目(在 zip 中)来说明问题:
为了以后参考,解决方法是这行代码:
tempImageView.image = nil;
感谢 Matic。
【问题讨论】:
-
您没有发布任何代码,但考虑到您编写的内容,这可能是自动释放池的问题:如果您使用“for”循环,您应该在循环内创建内部自动释放池并将其排空循环以确保释放里面的那些对象。
-
我尝试将 for 循环的内部包裹在 @autorelease {} 中,但没有帮助。有什么我需要添加以排出它吗?我以为是自动的。谢谢
-
我通常分配自动释放池,然后将其排空,但我认为这应该做同样的事情。它是自动的,但它不会简单地阻止你的代码并释放推送到自动释放池的对象,除非你指定所以。无论如何,似乎有些东西正在保留图像。如果可能(并且没有其他工作),请尝试将 UIImage 子类化并覆盖保留和释放方法以查看哪些对象保留了它们。你也在用ARC吗?能不能看到一些相关的代码?
-
..不是那些着色和翻转图像的方法(那些看起来不错),带有“for”循环和自动释放池的代码可能会有所帮助。
-
我会在几分钟后用循环代码更新问题。
标签: ios objective-c multithreading image colors