15

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

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

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,那么那些真的是像素,而不是点?(至少如果我们为那个位图提供一个数据缓冲区,那么缓冲区的大小(字节数)显然不是为了更高分辨率,而是为了标准分辨率,即使我们NULL作为缓冲区传递,以便CGBitmapContextCreate为我们处理缓冲区,大小可能和我们传入一个数据缓冲区一样,只是标准分辨率,而不是 Retina 的分辨率)。

我们总是可以为 iPad 1 和 iPad 2 以及新 iPad 创建 2048 x 1536,但它会浪费内存、处理器和 GPU 能力,因为只有新 iPad 才需要它。

那么我们是否必须使用 aif () { } else { }来创建这样的位图上下文,我们实际上是如何做到的呢?并且我们所有的代码CGContextMoveToPoint都必须针对 Retina 显示器进行调整以使用x * 2y * 2与非 Retina 显示器一样使用x, y?这对代码来说可能非常混乱。(或者也许我们可以定义一个局部变量scaleFactor并将其设置[[UIScreen mainScreen] scale]为 1 表示标准分辨率,2 表示视网膜,所以我们的xandy将永远是x * scaleFactor,而y * scaleFactor不是当我们使用 绘制时,等等)xyCGContextMoveToPoint

如果传入 0.0 的比例,似乎UIGraphicsBeginImageContextWithOptions可以自动为 Retina 创建一个,但如果我需要创建上下文并保留它(并使用 ivar 或 UIViewController 的属性来保存它),我认为它不能使用. 如果我不使用 释放它UIGraphicsEndImageContext,那么它会保留在图形上下文堆栈中,所以看来我必须CGBitmapContextCreate改用它。(或者我们只是让它留在堆栈的底部而不担心它?)

4

4 回答 4

41

经过更多研究,我找到了以下解决方案:

如果您必须使用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);

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

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


还有一点需要注意的是,这UIGraphicsBeginImageContext(CGSizeMake(300, 300));将在 Retina 显示屏上创建 300 x 300像素,而在 Retina 显示屏上UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 300), NO, 0.0);创建 600 x 600像素。这0.0是为了让方法调用自动为标准显示器或 Retina 显示器提供适当的尺寸。

于 2012-06-03T12:07:48.197 回答
4

也试试这个:

- (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;
}
于 2013-02-10T21:26:09.457 回答
1

开始新的图像上下文后,您可以使用UIGraphicsGetCurrentContext. 然后,如果您想保留它并在之后重用它,只需像保留任何 CF 对象一样保留它(并记住在完成后根据规则释放它)。您仍然需要调用UIGraphicsEndImageContext以将其从 UIKit 的上下文堆栈中弹出,但如果您保留了上下文,那么上下文将在之后继续存在,您应该能够继续使用它,直到您释放它。

稍后,如果您想再次使用上下文(并且还没有释放它),一种方法是调用UIGraphicsPushContext,这会将上下文推回上下文堆栈。

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

主要区别在于,如果您使用 创建了上下文UIGraphicsCreateImageContextWithOptions,则UIGraphicsGetImageFromCurrentImageContext返回一个 UIImage ,其scale应该与您创建上下文所使用的值匹配。(我不知道如果您弹出上下文然后将其推回,则该比例值是否会保留。)CGBitmapContextCreateImage返回一个CGImage,而CGImage只知道像素。

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

我还没有测试过上述任何内容,因此您应该先自己彻底测试,然后再用您将交付给用户的代码进行测试。

于 2012-06-03T05:44:21.427 回答
-1

只需创建缩放 0.0 的上下文即可获得主屏幕:

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

没有第三步。

于 2013-02-11T14:50:33.500 回答