如何创build一个适用于视网膜显示的CGBitmapContext,而不是为正常显示浪费空间?

如果是在UIKit,包括drawRect ,Retina显示的高清方面是自动处理的吗? 那么这是否意味着在drawRect ,1024 x 768视图的当前graphics上下文实际上是2048 x 1536像素的位图上下文?

更新:如果我使用drawRect的当前上下文创build图像并打印其大小:

 CGContextRef context = UIGraphicsGetCurrentContext(); CGImageRef image = CGBitmapContextCreateImage(context); NSLog(@"width of context %i", (int) CGImageGetWidth(image)); NSLog(@"height of context %i", (int) CGImageGetHeight(image)); 

然后在新的iPad上,禁用状态栏,打印2048和1536,iPad 2将显示1024和768)

实际上,我们享受了为我们自动处理的1点= 4像素的奢华。

但是,如果我们使用CGBitmapContextCreate ,那么这些将是像素,而不是点? (至less如果我们为该位图提供数据缓冲区,则缓冲区的大小(字节数)显然不是为了更高的分辨率,而是用于标准分辨率,即使我们将NULL作为缓冲区,以便CGBitmapContextCreate处理缓冲区对于我们来说,大小可能与传入数据缓冲区的大小相同,只是标准分辨率,而不是Retina的分辨率)。

我们总是可以为iPad 1和iPad 2以及新iPad创build2048 x 1536,但是这会浪费内存,处理器和GPU功耗,因为只有新iPad需要。

所以我们必须使用if () { } else { }来创build这样一个位图上下文,我们该怎么做呢? 我们所有的代码CGContextMoveToPoint必须调整Retina显示使用x * 2y * 2与非视网膜显示只使用x, y ? 这对代码来说可能相当麻烦。 (或者我们可以定义一个局部variablesscaleFactor并将其设置为[[UIScreen mainScreen] scale]因此标准分辨率为1,视网膜为2,所以我们的xy总是x * scaleFactory * scaleFactor而不是当我们使用CGContextMoveToPoint绘制时,只有xy等)

似乎UIGraphicsBeginImageContextWithOptions可以自动创build一个视网膜,如果0.0的规模被传入,但我不认为它可以使用,如果我需要创build上下文,并保持它(和使用IVAR或UIViewController属性来保存它)。 如果我不使用UIGraphicsEndImageContext释放它,那么它将停留在graphics上下文栈中,所以看起来像我必须使用CGBitmapContextCreate 。 (或者我们只是让它留在堆栈的底部而不用担心呢?)

经过更多的研究,我发现了以下解决scheme:

如果您必须使用CGBitmapContextCreate ,那么有两个步骤可以使尺寸和坐标系统的上下文适应标准显示或Retina显示:

 float scaleFactor = [[UIScreen mainScreen] scale]; CGSize size = CGSizeMake(768, 768); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(NULL, size.width * scaleFactor, size.height * scaleFactor, 8, size.width * scaleFactor * 4, colorSpace, kCGImageAlphaPremultipliedFirst); CGContextScaleCTM(context, scaleFactor, scaleFactor); 

该示例将创build一个768 x 768 点的区域,在新iPad上,它将是1536 x 1536 像素 。 在iPad 2上,它是768 x 768 像素

一个关键因素是, CGContextScaleCTM(context, scaleFactor, scaleFactor); 是用来调整坐标系的,所以Core Graphics的任何绘图,比如CGContextMoveToPoint等都会自动工作,不pipe是标准分辨率还是Retina分辨率。


还有一点需要注意的是UIGraphicsBeginImageContext(CGSizeMake(300, 300)); 将在Retina显示器上创build一个300 x 300 像素 ,而UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 300), NO, 0.0); 将在Retina显示屏上创build600 x 600 像素0.0是方法调用自动给标准显示或视网膜显示适当的大小。

也试试这个:

 - (UIImage *)maskImageWithColor:(UIColor *)color { CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height); UIGraphicsBeginImageContextWithOptions(rect.size, NO, self.scale); CGContextRef c = UIGraphicsGetCurrentContext(); [self drawInRect:rect]; CGContextSetFillColorWithColor(c, [color CGColor]); CGContextSetBlendMode(c, kCGBlendModeSourceAtop); CGContextFillRect(c, rect); UIImage *result = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return result; } 

开始新的图像上下文后,您可以使用UIGraphicsGetCurrentContext检索它。 然后,如果你想坚持下去并重新使用它,就像保留任何CF对象一样保留它(并且记住按照规则完成后释放它)。 您仍然需要调用UIGraphicsEndImageContext来从UIKit的上下文堆栈中popup它,但是如果您保留了上下文,那么上下文将继续存在,您应该可以继续使用它,直到您释放它为止。

后来,如果你想再次使用上下文(还没有发布它),一种方法是调用UIGraphicsPushContext ,这会将上下文推回上下文栈。

另一种使用上下文的方法是假定它是一个CGBitmapContext(UIKit文档称之为“基于位图的上下文”,但不要按名称说CGBitmapContext),并使用CGBitmapContextCreateImage在绘制后从上下文中捕获新的图像。

主要区别在于,如果使用UIGraphicsCreateImageContextWithOptions创build了上下文,则UIGraphicsGetImageFromCurrentImageContext将返回一个UIImage,其scale应与您创build上下文的值相匹配。 (我不知道如果popup上下文,那么缩放值是否保留,然后再推回去。) CGBitmapContextCreateImage返回一个CGImage,而CGImage只知道像素。

另一个区别是,UIKit绘图API(例如UIBezierPath)在UIKit上下文堆栈中的当前上下文中工作。 因此,如果不推送上下文,则只能使用Quartz API来绘制上下文。

我没有testing任何上述,所以你应该在你将交付给用户的代码之前,自己彻底地testing它。

只需创build缩放比例为0.0的上下文,以获得主屏幕的:

 UIGraphicsBeginImageContextWithOptions(size,NO,0.0); CGContextRef context = UIGraphicsGetCurrentContext(); 

没有第三步。