Ben Lachman
Ben Lachman

Reputation: 3100

Image Context produces artifacts when compositing UIImages

I'm trying to overlay a custom semi-transparent image over a base image. The overlay image is stretchable and created like this:

[[UIImage imageNamed:@"overlay.png"] stretchableImageWithLeftCapWidth:5.0 topCapHeight:5.0]

Then I pass that off to a method that overlays it onto the background image for a button:

- (void)overlayImage:(UIImage *)overlay forState:(UIControlState)state {
    UIImage *baseImage = [self backgroundImageForState:state];      

    CGRect frame = CGRectZero;
    frame.size = baseImage.size;

    // create a new image context
    UIGraphicsBeginImageContext(baseImage.size);        

    // get context
    CGContextRef context = UIGraphicsGetCurrentContext();   

    // clear context
    CGContextClearRect(context, frame);

    // draw images
    [baseImage drawInRect:frame];   
    [overlay drawInRect:frame];// blendMode:kCGBlendModeNormal alpha:1.0];

    // get UIImage
    UIImage *overlaidImage = UIGraphicsGetImageFromCurrentImageContext();

    // clean up context
    UIGraphicsEndImageContext();

    [self setBackgroundImage:overlaidImage forState:state];
}

The resulting overlaidImage looks mostly correct, it is the correct size, the alpha is blended correctly, etc. however it has vertical artifacts/noise.

UIImage artifacts example http://acaciatreesoftware.com/img/UIImage-artifacts.png

(example at http://acaciatreesoftware.com/img/UIImage-artifacts.png)

I tried clearing the context first and then turning off PNG compression--which reduces the artifacting some (completely on non stretched images I think).

Does anyone know a method for drawing stretchable UIImages with out this sort of artifacting happening?

Upvotes: 1

Views: 1959

Answers (2)

pms1969
pms1969

Reputation: 3704

Are you being too miserly with your original image, and forcing it to stretch rather than shrink? I've found best results out of images that fit the same aspect ratio and were reduced in size. Might not solve your problem tho.

Upvotes: 0

Ben Lachman
Ben Lachman

Reputation: 3100

So the answer is: Don't do this. Instead you can paint your overlay procedurally. Like so:

- (void)overlayWithColor:(UIColor *)overlayColor forState:(UIControlState)state {
    UIImage *baseImage = [self backgroundImageForState:state];      

    CGRect frame = CGRectZero;
    frame.size = baseImage.size;

    // create a new image context
    UIGraphicsBeginImageContext(baseImage.size);        

    // get context
    CGContextRef context = UIGraphicsGetCurrentContext();   

    // draw background image
    [baseImage drawInRect:frame];   

    // overlay color
    CGContextSetFillColorWithColor(context, [overlayColor CGColor]);
    CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
    CGContextFillRect(context, frame);

    // get UIImage
    UIImage *overlaidImage = UIGraphicsGetImageFromCurrentImageContext();

    // clean up context
    UIGraphicsEndImageContext();

    [self setBackgroundImage:overlaidImage forState:state];
}

Upvotes: 0

Related Questions