Неровные пути при привязке закадрового CGLayer к текущему контексту

#performance #core-graphics #antialiasing #cglayer #blit

#Производительность #ядро-графика #сглаживание #cglayer #блиц

Вопрос:

В моем текущем проекте мне нужно нарисовать сложный фон в качестве фона для нескольких ячеек UITableView. Поскольку код для рисования этого фона довольно длинный и загружает процессор при выполнении в методе drawRect: ячейки, я решил отобразить его только один раз на CGLayer, а затем наложить его на ячейку, чтобы повысить общую производительность.

Код, который я использую для рисования фона для 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;
}
  

Код для привязки слоя к текущему контексту:

 - (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 .

Извините за пробуждение старого сообщения, но я наткнулся на это, так что кто-то другой тоже может. Более подробную информацию можно найти в UIGraphicsBeginImageContextWithOptions документации:

Если вы укажете значение 0.0 , масштабный коэффициент будет установлен на масштабный коэффициент главного экрана устройства.

В основном это означает, что если это дисплей retina, он создаст контекст retina, таким образом, вы можете указать 0.0 и обрабатывать координаты как точки.

Комментарии:

1. Отличный ответ! Не пробовал, но я приму это как более актуальный ответ, чем мой;)

Ответ №2:

Я собираюсь ответить на свой собственный вопрос, поскольку я понял это некоторое время назад. Вы должны создать контекст, учитывающий сетчатку. Неровности появлялись только на устройстве retina.

Чтобы противостоять этому поведению, вы должны создать контекст retina с помощью этого вспомогательного метода:

 // 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);

        ...
  

Комментарии:

1. Эй, @bitshiftcop, я попробовал ваш ответ, но он не работает, я не вижу никакого отрисованного рисунка. Можете ли вы помочь мне с этим.