如何创建适用于Retina显示屏和定期显示不浪费空间CGBitmapContext?(How to

2019-06-25 03:38发布

这是真的,如果是在UIKit中,包括drawRect ,Retina显示屏的高清方面是自动处理的? 这是否意味着中drawRect ,为1024×768针对当前图形上下文实际上是2048×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处理缓冲器对我们来说,大小大概是一样的,如果我们在一个数据缓冲区传递,这仅仅是标准分辨率,而不是视网膜的分辨率)。

我们总是可以创造2048×1536的iPad 1和iPad 2以及全新iPad,但它会浪费内存和处理器和GPU供电,因为它只需要新的iPad。

那么我们不得不使用if () { } else { }创建这样一个位图背景和我们究竟要怎样做呢? 我们所有的代码CGContextMoveToPoint已被调整Retina显示屏使用x * 2y * 2 VS只是使用的非Retina显示屏x, y呢? 这可以为代码相当混乱。 (或者我们可以定义一个局部变量scaleFactor ,并将其设置为[[UIScreen mainScreen] scale]所以它是1标准分辨率和2如果是视网膜,所以我们的xy永远是x * scaleFactory * scaleFactor而不只是xy当我们绘制使用CGContextMoveToPoint等)

看来, UIGraphicsBeginImageContextWithOptions可以创建一个自动的Retina如果0.0规模传入,但我不认为它可以用,如果我需要创建上下文,并保持它(并使用伊瓦尔或UIViewController中的属性来保存它)。 如果我不使用它释放UIGraphicsEndImageContext ,那么它停留在图形上下文堆栈,因此它似乎我必须使用CGBitmapContextCreate代替。 (或者我们就让它留在堆栈的底部,不担心吗?)

Answer 1:

做更多的研究,我发现了以下解决方案:

如果你必须使用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 * 768 区域,并在新的iPad,这将是1536 * 1536 像素 。 在iPad 2,它是768 x 768 像素

一个关键因素是, CGContextScaleCTM(context, scaleFactor, scaleFactor); 用于调整坐标系,所以通过核芯显卡,如任何绘图CGContextMoveToPoint等,会自动工作,无论是标准分辨率或视网膜分辨率。


一个更值得注意的是, UIGraphicsBeginImageContext(CGSizeMake(300, 300)); 将创建的Retina显示器上的300×300 像素 ,而UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 300), NO, 0.0); 将创建Retina显示屏上的600 x 600 像素 。 的0.0为方法调用来自动得到适当的大小以便显示标准或视网膜显示。



Answer 2:

也试试这个:

- (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;
}


Answer 3:

开始新的图像内容后,您可以使用检索UIGraphicsGetCurrentContext 。 然后,如果你想挂到它,然后重新使用它,只是保留它像你将任何物体CF(记住释放它,当你用它做,按照规则 )。 你仍然需要调用UIGraphicsEndImageContext弹出它关闭UIKit中的上下文堆栈的,但如果你已经保留的情况下,则上下文将后来住在和你应该能够继续使用它,直到你释放它。

以后,如果要再次使用上下文(并没有它尚未公布),一个办法是打电话UIGraphicsPushContext ,这将推动背景下返回到上下文堆栈。

使用上下文的另一个方法是先假定它是一个CGBitmapContext(在UIKit的文档称之为“基于位图的情况下”,但不要被名字说CGBitmapContext),并使用CGBitmapContextCreateImage绘图后拍照从上下文的新形象。

主要的区别是,如果你已经创建了上下文UIGraphicsCreateImageContextWithOptionsUIGraphicsGetImageFromCurrentImageContext返回一个UIImage,其scale应该与你创建的上下文的价值。 (我不知道这是否标值获取如果你弹出上下文并保存,然后将其推回后。) CGBitmapContextCreateImage返回一个CGImage和CGImage只知道像素。

另一个区别是,UIKit的绘图API,如UIBezierPath,在UIKit中的上下文堆栈的当前环境中工作。 因此,如果你不推的背景下,你只能使用Quartz API来吸引到的上下文。

我没有测试任何上述的,所以你应该在代码中,你将提供给用户这样做之前,对其进行全面测试自己。



Answer 4:

刚刚创建上下文与缩放0.0为让主屏幕的搭配:

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

没有第三步。



文章来源: How to create a CGBitmapContext which works for Retina display and not wasting space for regular display?