19

I'm drawing a graph on a CALayer in its delegate method drawLayer:inContext:.

Now I want to support Retina Display, as the graph looks blurry on the latest devices.

For the parts that I draw directly on the graphics context passed by the CALayer, I could nicely draw in high resolution by setting the CALayer's contentScale property as follows.

if ([myLayer respondsToSelector:@selector(setContentsScale:)]) {
    myLayer.contentsScale = [[UIScreen mainScreen] scale];
}

But for the parts that I use CGLayer are still drawn blurry.

How do I draw on a CGLayer in high resolution to support Retina Display?

I want to use CGLayer to draw the same plot shapes of the graph repeatedly, as well as to cut off the graph lines exceeding the edge of the layer.

I get CGLayer by CGLayerCreateWithContex with the graphics context passed from the CALayer, and draw on its context using CG functions such as CGContextFillPath or CGContextAddLineToPoint.

I need to support both iOS 4.x and iOS 3.1.3, both Retina and legacy display.

Thanks,

Kura

Taka
  • 1,334
  • 17
  • 24
  • 1
    Looks like this post may be relevant: http://stackoverflow.com/questions/3896968/how-do-i-adjust-a-quartz-2d-context-to-account-for-a-retina-display – Duncan Babbage Jul 18 '11 at 07:49
  • Duncan, thanks for your comment, but no... I've read this post before but this is talking about drawing directly on the context of UIView. CALayer has the same property `scale` as UIView and it worked as I have stated above. My problem is about drawing on CGLayer. CGLayer does not have the `scale` property. :( – Taka Jul 18 '11 at 10:08

2 Answers2

28

This is how to draw a CGLayer correctly for all resolutions.

  1. When first creating the layer, you need to calculate the correct bounds by multiplying the dimensions with the scale:

    int width = 25; 
    int height = 25;
    float scale = [self contentScaleFactor];
    CGRect bounds = CGRectMake(0, 0, width * scale, height * scale);
    CGLayer layer = CGLayerCreateWithContext(context, bounds.size, NULL);
    CGContextRef layerContext = CGLayerGetContext(layer);
    
  2. You then need to set the correct scale for your layer context:

    CGContextScaleCTM(layerContext, scale, scale);
    
  3. If the current device has a retina display, all drawing made to the layer will now be drawn twice as large.

  4. When you finally draw the contents of your layer, make sure you use CGContextDrawLayerInRect and supply the unscaled CGRect:

    CGRect bounds = CGRectMake(0, 0, width, height);
    CGContextDrawLayerInRect(context, bounds, layerContext);
    

That's it!

Johannes
  • 1,370
  • 16
  • 15
  • Thanks! I'd try that next time. ;) – Taka Jan 08 '12 at 14:15
  • Thanks you, this is exactly how it should go. – neevek May 30 '12 at 16:59
  • This improves the sharpness significantly but it still looks slightly less sharp that drawing directly with the context (not using `CGlayer`). Is this normal? I also tried setting scale to 4 or 8. – User Jul 23 '16 at 10:09
1

I decided not to use CGLayer and directly draw on the graphics context of the CALayer, and now it's drawn nicely in high resolution on retina display.

I found the similar question here, and found that there is no point of using CGLayer in my case.

I used CGLayer because of the Apple's sample program "Using Multiple CGLayer Objects to Draw a Flag" found in the Quartz 2D Programming guide. In this example, it creates one CGLayer for a star and uses it multiple times to draw 50 stars. I thought this was for a performance reason, but I didn't see any performance difference.

For the purpose of cutting off the graph lines exceeding the edge of the layer, I decided to use multiple CALayers.

Taka
  • 1,334
  • 17
  • 24
  • This isn't really a solution to the problem. It's an interesting question and I wish there was an answer. The link also doesn't come up with a solution other than advice which contradicts the apple docs. – Hari Honor Dec 29 '11 at 18:05
  • 1
    Regarding performance: In my app I am using the `UIImage` method `drawInRect:`. In one of the scenarios that I have been measuring, using `CGLayer` is roughly 20x faster than directly drawing with the `CALayer` context (~50 ms vs. ~1 sec). So my guess is that it probably depends on the type of drawing operations stored in the `CGLayer` whether or not there is a performance difference. – herzbube Jun 04 '14 at 23:39