4

I've developed an app on iOS5 and iOS6. After I upgraded to XCode 5 and iOS7, I have some new bugs to play with.

The main one is the colorMasking no longer works. The exact same code still compiles and works on a phone with iOS6. On iOS7, the masked color is still there. I tried to find the answer on Google, but haven't found an answer. Is it a bug of iOS7, or does anybody know of a better way of doing colormasking?

Here is the code:

- (UIImage*) processImage :(UIImage*) image
{
    UIImage *inputImage = [UIImage imageWithData:UIImageJPEGRepresentation(image, 1.0)];
    const float colorMasking[6]={100.0, 255.0, 0.0, 100.0, 100.0, 255.0};
    CGImageRef imageRef = CGImageCreateWithMaskingColors(inputImage.CGImage, colorMasking);
    UIImage* finalImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    return finalImage;
}

Here are a couple StackOverflow posts I found that helped me get it working in iOS6 the first place: Transparency iOS iOS color to transparent in UIImage

Community
  • 1
  • 1
surreal
  • 43
  • 1
  • 5
  • have you sanity-checked that the bits/component matches your assumed constants for `inputImage`? you might want to check the color models as well. if you have exact control over the inputs, it may not matter. – justin Oct 01 '13 at 05:59

2 Answers2

8

I have stumbled across some strange behavior of CGImageCreateWithMaskingColors in conjunction with UIImagePNGRepresentation. This may or may not be related to your problem. I have found that if:

  1. If use CGImageCreateWithMaskingColors and immediately add that image to an image view, I can see that the transparency appears to have been applied correctly;

  2. But in iOS 7, if I then:

    • take this image from CGImageCreateWithMaskingColors and create a NSData using UIImagePNGRepresentation; and

    • if reload the image from that NSData using imageWithData, then the resulting image will no longer have its transparency.

    To confirm this, if I writeToFile for this NSData and examine the saved image in a tool like Photoshop, I can confirm that the file does not have any transparency applied.

    This only manifests itself in iOS 7. In iOS 6 it's fine.

  3. But if I take the image in step 1 and roundtrip it through drawInRect, the same process of saving the image and subsequently loading it works fine.

This following code illustrates the issue:

- (UIImage*) processImage :(UIImage*) inputImage
{
    const float colorMasking[6] = {255.0, 255.0, 255.0, 255.0, 255.0, 255.0};
    CGImageRef imageRef = CGImageCreateWithMaskingColors(inputImage.CGImage, colorMasking);
    UIImage* finalImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);

    // If I put this image in an image view, I see the transparency fine.

    self.imageView.image = finalImage;                           // this works

    // But if I save it to disk and the file does _not_ have any transparency

    NSString *documentsPath           = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *pathWithoutTransparency = [documentsPath stringByAppendingPathComponent:@"image-but-no-transparency.png"];
    NSData   *data                    = UIImagePNGRepresentation(finalImage);
    [data writeToFile:pathWithoutTransparency atomically:YES];   // save it so I can check out the file in Photoshop

    // In iOS 7, the following imageview does not honor the transparency

    self.imageView2.image = [UIImage imageWithData:data];        // this does not work in iOS 7

    // but, if I round-trip the original image through `drawInRect` one final time,
    // the transparency works

    UIGraphicsBeginImageContextWithOptions(finalImage.size, NO, 1.0);
    [finalImage drawInRect:CGRectMake(0, 0, finalImage.size.width, finalImage.size.height)];
    UIImage *anotherRendition = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    data = UIImagePNGRepresentation(anotherRendition);
    NSString *pathWithTransparency = [documentsPath stringByAppendingPathComponent:@"image-with-transparancy.png"];
    [data writeToFile:pathWithTransparency atomically:YES];

    // But this image is fine

    self.imageView3.image = [UIImage imageWithContentsOfFile:pathWithTransparency];   // this does work

    return anotherRendition;
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • From reading over your comments and code, it sounds like this could be the answer!! I'll have time tonight to try it out and will let you know how it goes. Thank you so much!!!!! – surreal Oct 01 '13 at 15:34
  • 1
    Amazing! Your idea and code work perfectly! I would NEVER have figured this out on my own...Thank you very much for your quick and correct answer. Kinda cool to help find a bug for Apple, too. ;) Thanks for reporting it. – surreal Oct 02 '13 at 03:55
  • 1
    This is awesome - I was tearing my hair our for two days on a similar issue. Thank you! – BooTooMany Apr 02 '14 at 19:51
0

I was loading a JPEG which for some reason loads with an alpha channel, which won't work when masking, so here I recreate the CGImage ignoring the alpha channel. There may be a better way of doing this but this works!

- (UIImage *)imageWithChromaKeyMasking {
    const CGFloat colorMasking[6]={255.0,255.0,255.0,255.0,255.0,255.0};
    CGImageRef oldImage = self.CGImage;
    CGBitmapInfo oldInfo = CGImageGetBitmapInfo(oldImage);
    CGBitmapInfo newInfo = (oldInfo & (UINT32_MAX ^ kCGBitmapAlphaInfoMask)) | kCGImageAlphaNoneSkipLast;
    CGDataProviderRef provider = CGImageGetDataProvider(oldImage);
    CGImageRef newImage = CGImageCreate(self.size.width, self.size.height, CGImageGetBitsPerComponent(oldImage), CGImageGetBitsPerPixel(oldImage), CGImageGetBytesPerRow(oldImage), CGImageGetColorSpace(oldImage), newInfo, provider, NULL, false, kCGRenderingIntentDefault);
    CGDataProviderRelease(provider); provider = NULL;
    CGImageRef im = CGImageCreateWithMaskingColors(newImage, colorMasking);
    UIImage *ret = [UIImage imageWithCGImage:im];
    CGImageRelease(im);
    return ret;
}
Nick
  • 3,958
  • 4
  • 32
  • 47
  • 1
    FYI, you shouldn't release `provider` (ownership wasn't transferred to you with a function with `Create` or `Copy` in the name, per the [Create Rule](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html#//apple_ref/doc/uid/20001148-103029)), but you should release `newImage`. – Rob Apr 24 '16 at 18:43