将屏幕外 CGLayer 传送到当前上下文时出现锯齿状路径

Posted

技术标签:

【中文标题】将屏幕外 CGLayer 传送到当前上下文时出现锯齿状路径【英文标题】:Jaggy paths when blitting an offscreen CGLayer to the current context 【发布时间】:2011-10-11 15:31:53 【问题描述】:

在我当前的项目中,我需要绘制一个复杂的背景作为几个 UITableView 单元格的背景。由于在单元格的 drawRect: 方法中执行时用于绘制此背景的代码很长且占用大量 CPU 资源,因此我决定仅将其渲染到 CGLayer 一次,然后将其 blit 到单元格以提高整体性能。

我用来为 CGLayer 绘制背景的代码:

+ (CGLayerRef)standardCellBackgroundLayer

    static CGLayerRef standardCellBackgroundLayer;

    if(standardCellBackgroundLayer == NULL)
    
        CGContextRef viewContext = UIGraphicsGetCurrentContext();
        CGRect rect = CGRectMake(0, 0, [UIScreen mainScreen].applicationFrame.size.width, PLACES_DEFAULT_CELL_HEIGHT);

        standardCellBackgroundLayer = CGLayerCreateWithContext(viewContext, rect.size, NULL);
        CGContextRef context = CGLayerGetContext(standardCellBackgroundLayer);

        // Setup the paths
        CGRect rectForShadowPadding = CGRectInset(rect, (PLACES_DEFAULT_CELL_SHADOW_SIZE / 2) + PLACES_DEFAULT_CELL_SIDE_PADDING, (PLACES_DEFAULT_CELL_SHADOW_SIZE / 2));
        CGMutablePathRef path = createPathForRoundedRect(rectForShadowPadding, LIST_ITEM_CORNER_RADIUS);

        // Save the graphics context state
        CGContextSaveGState(context);

        // Draw shadow
        CGContextSetShadowWithColor(context, CGSizeMake(0, 0), PLACES_DEFAULT_CELL_SHADOW_SIZE, [Skin shadowColor]);
        CGContextAddPath(context, path);
        CGContextSetFillColorWithColor(context, [Skin whiteColor]);
        CGContextFillPath(context);

        // Clip for gradient
        CGContextAddPath(context, path);
        CGContextClip(context);

        // Draw gradient on clipped path
        CGPoint startPoint = rectForShadowPadding.origin;
        CGPoint endPoint = CGPointMake(rectForShadowPadding.origin.x, CGRectGetMaxY(rectForShadowPadding));
        CGContextDrawLinearGradient(context, [Skin listGradient], startPoint, endPoint, 0);

        // Restore the graphics state and release everything
        CGContextRestoreGState(context);
        CGPathRelease(path);
    

    return standardCellBackgroundLayer;

将图层blit到当前上下文的代码:

- (void)drawRect:(CGRect)rect

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawLayerAtPoint(context, CGPointMake(0.0, 0.0), [Skin standardCellBackgroundLayer]);

这实际上做得很好,但我遇到的唯一问题是圆角(检查静态方法)。闪到屏幕上时非常锯齿。当绘图代码位于其原始位置时,情况并非如此:在 drawRect 方法中。

如何恢复这种抗锯齿功能?

由于某种原因,以下方法对抗锯齿没有任何影响:

CGContextSetShouldAntialias(context, YES);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextSetAllowsAntialiasing(context, YES);

提前致谢!

【问题讨论】:

【参考方案1】:

您可以通过使用UIGraphicsBeginImageContextWithOptions 并将比例设置为0.0 来简化此操作。

很抱歉唤醒了一个旧帖子,但我偶然发现了它,所以其他人也可以。更多详情可以查看UIGraphicsBeginImageContextWithOptionsdocumentation:

如果指定值0.0,则比例因子设置为比例 设备主屏幕的因素。

基本上意味着,如果它是一个视网膜显示器,它将创建一个视网膜上下文,这样您就可以指定0.0 并将坐标视为点。

【讨论】:

很好的答案!没有尝试过,但我会接受这个比我的答案更新的答案;)【参考方案2】:

我要回答我自己的问题,因为我前段时间想通了。 您应该创建一个视网膜感知上下文。锯齿状只出现在视网膜设备上。

为了应对这种行为,您应该使用这个辅助方法创建一个视网膜上下文:

// Begin a graphics context for retina or SD
void RetinaAwareUIGraphicsBeginImageContext(CGSize size) 

    static CGFloat scale = -1.0;

    if(scale < 0.0) 
       
        UIScreen *screen = [UIScreen mainScreen];

        if([[[UIDevice currentDevice] systemVersion] floatValue] >= 4.0)
            
            scale = [screen scale]; // Retina
        
        else 
        
            scale = 0.0; // SD
        
    

    if(scale > 0.0)
        
        UIGraphicsBeginImageContextWithOptions(size, NO, scale);   
    
    else 
        
        UIGraphicsBeginImageContext(size);   
    

然后,在您的绘图方法中调用上面列出的方法,如下所示:

+ (CGLayerRef)standardCellBackgroundLayer

    static CGLayerRef standardCellBackgroundLayer;

    if(standardCellBackgroundLayer == NULL)
    
        RetinaAwareUIGraphicsBeginImageContext(CGSizeMake(320.0, 480.0));
        CGRect rect = CGRectMake(0, 0, [UIScreen mainScreen].applicationFrame.size.width, PLACES_DEFAULT_CELL_HEIGHT);

        ...

【讨论】:

嘿@bitshiftcop,我试过你的答案,但它不起作用,我没有看到任何绘图。你能帮我解决这个问题吗?

以上是关于将屏幕外 CGLayer 传送到当前上下文时出现锯齿状路径的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 CgLayer 进行最佳绘图

iOS离屏渲染

离屏渲染(图层性能 15.2)

将 SwiftUI 视图渲染到屏幕外并将视图另存为 UIImage 以共享

在方法drawRect之外绘制CGLayer:更快?

将电子邮件传送到解析器脚本时出现“参数 1 中的错误,字符 3:找不到选项”