3

I'm trying to make an object that can draw a portion of a large (say 2048 x 1537) image using CGContextDrawImage(…). It works just fine, except that it is extremely blurry. I'm using a "drawingController" which overrides drawLayer:inContext: like this

-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
    CGImageRef drawImage = CGImageRetain(imageToDraw);
    if(drawImage) {
        CGRect drawRect = CGRectMake(0, 0, 1024, 748);//Just hard coded to my window size for now.
        CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
        CGContextDrawImage(ctx, drawRect, drawImage);
    }
    CGImageRelease(drawImage);
}

And this is how I set up my custom image hosting view:

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"large" ofType:@"jpg"];
        CGImageRef imageRef = [self createCGImageFromFilePath:filePath];


        drawingController = [[DrawingLayerController alloc] init];
        drawingController.imageToDraw = imageRef;

        [self setWantsLayer:YES];
        imageLayer = [[CALayer alloc] init];
        imageLayer.frame = self.bounds;
        imageLayer.drawsAsynchronously = YES;
        imageLayer.delegate = drawingController;
        [self.layer addSublayer:imageLayer];

        imageLayer.contentsRect = CGRectMake(0.5, 0.265452179, 0.5, 0.486662329); //Just an example contents rect.
        imageLayer.drawsAsynchronously = YES;

        [imageLayer setNeedsDisplay];
        [imageLayer displayIfNeeded];
    }
    return self;
}

Here is a portion of the image produced along side the source image at a similar zoom scale opened in Preview, so you can see just how blurry the image is as compared to the source image.

Hopefully the image doesn't become blurry when I upload it haha

As you can see, the image produced by my code looks HORRIBLE. What is up with that?

Also, just for completeness, I have tried using a CATiledLayer for my imageLayer. The resulting image looks just as awful.

One final note: The code needs to be compatible with iOS AND OSX, but I am doing all my testing on OSX.

EDIT: Thanks to @Andrea, I've added the following above CGContextDrawImage(…)

CGFloat scale = MAX(1,MAX(CGImageGetWidth(drawImage)/drawRect.size.width,CGImageGetHeight(drawImage)/drawRect.size.height));

layer.contentsScale = scale;
Kelly Bennett
  • 725
  • 5
  • 27

1 Answers1

0

I don't know exactly for osx but I can give you some advices.

  • Check the contentScale property as stated in commets, I wrote also a question about it, but I never had a real answer
  • Most of time blurriness is due to unmatching pixel, this asks to the system to apply antialiasing on the image, I see that you set a contentsRect this, with the contentsGravity, probably could change proportion and scaling of the image turning it into a blur one. Here a good answer about it.
  • Which is your contentGravity?
  • Keep it simple in first try, do you really need to draw async?

Hope this helps.

Community
  • 1
  • 1
Andrea
  • 26,120
  • 10
  • 85
  • 131
  • 2
    A contentScale of 2.0 does indeed cause the image to become much clearer. I'm at a total loss as to how to explain that. Why do I need to use a content scale of 2.0? I'm loading the image directly from a file into a CGImageRef, so there is no NSImage @2x magic happening, and I'm drawing to a non-retina screen. – Kelly Bennett Dec 11 '13 at 20:41
  • Hi @RagnarDanneskjöld Which is the resolution of the original image? – Andrea Dec 12 '13 at 06:41
  • Hi @RagnarDanneskjöld The resolution of the image is bigger(twice), thus the layer need to redraw it in a smaller space, so probably you need to set some options while drawing it, have you tried with CGContextSetShouldAntialias(ctx, true); CGContextSetAllowsAntialiasing(ctx, true);. I had a similar problem and solved by setting the minificationFilter to kCAFilterTrilinear directly on the layer, but I was setting directly the image as a contents of the layer – Andrea Dec 12 '13 at 06:46
  • Image resolution is 2048 x 1537. "The resolution of the image is bigger" -- bigger than what, my layer? I don't follow. The image is much better than 2x my layer, but setting the content scale to 2 makes the image just as sharp as setting it to 5 (as far as I can tell). Also, the antialiasing stuff had no effect. – Kelly Bennett Dec 12 '13 at 18:55
  • CGRect drawRect = CGRectMake(0, 0, 1024, 748); – Andrea Dec 12 '13 at 18:57
  • Oh, I see what you mean. 2048 x 1537 is ~2x my drawRect. – Kelly Bennett Dec 12 '13 at 19:00