How to Mask an Image

December 9, 2008

Masking an image enables a developer to create images with irregular shapes dynamically. Masking is often used to create a user interface that is more compelling and less boring.

Take for example the following example …

maskingstoryboard.png

Creating the mask above is really simple using CoreGraphics on the iPhone. The following is a function that takes two images and uses one to mask the other.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {
 
	CGImageRef maskRef = maskImage.CGImage; 
 
	CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
		CGImageGetHeight(maskRef),
		CGImageGetBitsPerComponent(maskRef),
		CGImageGetBitsPerPixel(maskRef),
		CGImageGetBytesPerRow(maskRef),
		CGImageGetDataProvider(maskRef), NULL, false);
 
	CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
	return [UIImage imageWithCGImage:masked];
 
}

Yep … its just that simple!

NOTE: The mask image cannot have ANY transparency. Instead, transparent areas must be white or some value between black and white. The more towards black a pixel is the less transparent it becomes.

74 comments

Great post, I had no idea it was that easy to create such an effect. Any more CoreGraphics tips up your sleeve?

by John on Dec 10, 2008. Reply #

Nice tip, thanks for sharing.
I was doing this but had not seen that the family of functions CGImageGet* existed.

Gotta read more the manual.

by Vitor Almeida da Silva on Dec 11, 2008. Reply #

@Vitor Thanks! Glad it was helpful.

by Rodney on Dec 15, 2008. Reply #

I had tried the above code but for some images mask has been work but for some images mask will be displayed as a black background.. Is there any rule that I need to take some specific image?

Thanks in advance!

by Rajendra on Jan 21, 2009. Reply #

Hi Rajendra.

This code works only with images that have an alpha channel (no jpgs). I was having the same problem too.

by Vitor Almeida on Jan 21, 2009. Reply #

Hi Vitor,

Thanks a lot for your response. What do you mean by alpha channel image. The same problem occur for png or tiff images. Can you please give me your suggession in detail.

by Rajendra on Jan 22, 2009. Reply #

To be clear, the mask image should have NO alpha channel. It is a Black and White image. If you need to you can use a gradient ( a shade of gray). White translates to fully transparent (as in above example) and black is fully opaque. Any value in between will result is a semi-transparent effect.

If you are seeing a black background that is likely because you have some level of transparency in your mask image. Simply fill in the transparent areas of your mask with the color white and you should be fine.

by Rodney Aiglstorfer on Jan 22, 2009. Reply #

Yes, Rodney is right (I forget this detail).

When I say an “alpha channel” I mean an additional layer of the image that have transparency information. An image without this information cannot have transparent pixels when masked using the function (it will have “black pixels” when masked).

To see that, using Gimp (or Photoshop), open the image and see if it has a alpha channel (in gimp: Layer->Transparency->Add Alpha Channel).

You can see more details here: http://en.wikipedia.org/wiki/Alpha_channel

I tested only using pngs and jpgs (which doesnt have transparency).

Sorry for my english (not american).

by Vitor Almeida on Jan 22, 2009. Reply #

Thanks a lot for your response!!! It solves my issue!!!

by Rajendra on Jan 23, 2009. Reply #

One last observation: I have detected a small memory leak in this function. I used the following correction:

After the line:

CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);

I added:

CGImageRelease(mask);
CGImageRelease(maskRef);

And the leak is gone…

So here is the entire function:

- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {

CGImageRef maskRef = maskImage.CGImage;

CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);

CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);

// ops, lets not forget this…
CGImageRelease(mask);
CGImageRelease(maskRef);

return [UIImage imageWithCGImage:masked];

}

by Vitor Almeida on Jan 23, 2009. Reply #

@Vitor

Nice catch! Thanks.

by Rodney Aiglstorfer on Jan 23, 2009. Reply #

Thanks!

by Rajendra on Jan 28, 2009. Reply #

Thanks guys for this hint…

But I still have a problem with black background.

In fact, If I use both PNG for image and mask, everything works great.
but if the image is a JPG, I get a black background.

unfortunatly, I can’t convert the JPG image to a PNG, they are on a webserver.

although if the mask is in JPG, I get the whole result in black (not only borders).

anyone have a solution ?

thank you all

by TheSquad on Feb 4, 2009. Reply #

Hi,

I am painfully new to iphone development. I was wondering if you can provide a quick example of how or where you call this function? I don’t seem to be able to drop it into drawRect but I’m not sure how to reference it. Sorry if it is a n00b question. Thanks!

by shrug on Feb 24, 2009. Reply #

Ha! I think I figured it out. Anyway, this is what I coded and it’s working:

UIImage *masked = [self maskImage:glob withMask:masker];

That works, which is great! Any specific (or general Obj-C) guidance?

by shrug on Feb 24, 2009. Reply #

@shrug The easiest way to get this on screen would be to use a UIImageView. There are a ton of example on the Apple site that show how to use a UIImageView. You could create a subclass of UIImageView and in its constructor make a call to the method described above.

by Rodney on Feb 24, 2009. Reply #

@TheSquad I’ve not tried using a JPeg are the image or the mask … I’ll have to take your word for it not working. All I can offer is the creation of a proxy service that you would run on your own server to transcode the JPegs into PNGs. I do this often when I develop for “lesser” phones (e.g. J2ME devices which only support PNG). The Java 2D libraries that come with Java out of the box are excellent for this purpose … so creation a simple servlet that handles the conversion is pretty simple to create.

by Rodney on Feb 24, 2009. Reply #

I do have another question. I would really like to animate a mask, so if you move the iphone a certain way, it ‘slides’ off. Is this possible? From what I understand, we take image1 (original) and image2 (mask) and end up with, really, an image3 (masked original).

Can we still interact with image2 (or 1 for that matter) to animate the mask? Or would there be some other way of doing that?

I have to say, delving into iPhone dev (and Obj C) is very humbling. I’m a Flash coder by day, and this is a whole new realm for me, moreso than I expected. I know exactly how I would do all this in Flash, but unfortunately I have to throw all that out the window. :)

Thanks for the posting!

by shrug on Feb 25, 2009. Reply #

@Vitor: You must not release maskRef. Since you’ve got this from the CGImage property, which will return an autoreleased object. If you would release maskRef your app will most likely crash. Beside that it would be cool to release the masked image that you created via CGImageCreateWithMask. A non-crashing solution with no memory leaks could therefore look like this:

CGImageRef maskRef = maskImage.CGImage;

CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);

CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);

CGImageRelease(mask);

UIImage* retImage= [UIImage imageWithCGImage:masked];

CGImageRelease(masked);

return retImage;

by Carsten Schulz on Feb 27, 2009. Reply #

@Carsten: thanks for the correction. The way I was using it I wasn’t experiencing any crashes, but your info is correct anyway.

by Vitor Almeida on Feb 27, 2009. Reply #

I’ve improved the code so that it can correctly mask images that don’t have an alpha channel.

See the improved code here: http://pastie.org/418627

Hope it will be useful to someone else.

Thanks for the initial code.

by Jean Regisser on Mar 17, 2009. Reply #

For Jean:

I still get some incorrect results for images that don’t have an alpha channel.

I’ve change one of your lines to:

if ((CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNone) || (CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNoneSkipFirst)) {

This way I cover the 2 subcases of images without an alpha channel.

Hope it helps :)

by Xavier Boix on Mar 22, 2009. Reply #

hi there, I’m trying to get this method to work for masking an image and have come up against a brick wall. I can get the mask to work but the bit of the image that is being masked off always seems to be black instead of being transparent. I have made sure that both the mask and the maskee are png’s etc but still no luck. Does anyone know why this is the case?

by James Hay on Apr 1, 2009. Reply #

Jean, YOU ROCK!
Thank you so much!

by Eldad on Apr 14, 2009. Reply #

Perfect code!!!!
Thanks Rodney, Keep it going ….

by Sanniv on Apr 29, 2009. Reply #

Well, this should not be this hard.
First I had to remind myself that I should save my images as 24-bit pngs (not 8-bit).
My mask file is black-and-white, NO transparency.

The mask is applied, but there is a black place where the ‘knock-out’ should be.
I have tried saving the source testImage with and without transparency thinking that alpha bits were needed (or not) for the effect to happen.

I am displaying this image in a UIImageView set to opaque=NO and backgroundColor of clearColor, but underneath images are not showing through.

Here is my code:
UIImage *testMaskImage = [UIImage imageNamed: @"testMask.png"];
CGImageRef maskRef = testMaskImage.CGImage;

UIImage *anotherTestImage = [UIImage imageNamed: @"testImage.png"];
CGImageRef theImage = anotherTestImage.CGImage;

CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);

CGImageRef theMaskedImage = CGImageCreateWithMask(theImage, mask);
CGImageRelease(mask);
self.image = [UIImage imageWithCGImage:theMaskedImage]; // where I hand the image off
CGImageRelease(theMaskedImage);

by Mark on Jun 5, 2009. Reply #

Jean Regisser: Your code rocks!!!!
@everyone: This masking stuff is NOT, under no circumstances “simple stuff”. Indeed it is very hard to get it right. I spent 30 hours in this “simple” thing, as an programmer with 18 years of experience.

It highly depends on how the images are created, i.e. what kind of algorithms are used. Alpha is not alpha. There are a bunch of different alpha masking algorithms out there, and a PNG-24 with alpha can be very different than another PNG-24 with alpha. The key is in decoding all this things correctly, but that is nearly impossible with what Apple gives us. The API is very buggy here and is not able to automatically detect all this variancies.

So, even though I do create an nice image with alpha channel in GIMP, Jean Regisser’s code will still apply an alpha channel to my image.

Take care guys.

by Horst on Jul 16, 2009. Reply #

hello Jean,

thankyou for your update, its nicely worked for me.

by dazz on Aug 26, 2009. Reply #

Any idea on how to use CGImageCreateWithMaskingColors with a jpg? I used Jean’s great code for adding an alpha channel, but when no matter what values I use for the mask the entire image becomes invisible. I’m trying just to remove just a black background from my jpgs while leaving the rest of the image intact.

Here’s what my code currently looks like:

static CGImageRef CreateMaskedImage(CGImageRef image)
{
image = CopyImageAndAddAlphaChannel(image);
const float mask[] = {0, 0, 0, 0, 0, 0};
return CGImageCreateWithMaskingColors(image, mask);
}

by Greg on Aug 30, 2009. Reply #

HI
I have a question.How can I change a mask’size? Just like Flash’masked.

by Saint on Oct 7, 2009. Reply #

@Saint you can use crop your masked image or quickly you can change mask image ref. Something like :

Change
CGImageRef maskRef = maskImage.CGImage;
To
CGRect rect = CGRectMake(0.0f, 0.0f, 100.0f, 50.0f);
CGImageRef maskRef = CGImageCreateWithImageInRect ([maskImage CGImage], rect);

By this method you can get a new maskRef defined by rect.

by Alkim on Oct 15, 2009. Reply #

For Alkim:

I try your method. I can’t change my mask size.

And I have a other question.

If mask is circle,it can mask an image?

Thank you.

by Saint on Oct 15, 2009. Reply #

I am not sure what you wanna do :)
If you want to change your mask size, you can use that method, if you want to change image size also you can do the same method but you have to apply to [image CGImage] variable.

You can get masked image in any shape depends on your prepared mask image (Circle, bird shape, little mountains bla bla :) ). If you want this circle programmatically (not a mask image) you can do something like :

const float kBlackColor[] = { 0.0, 0.0, 0.0, 1.0};
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetFillColor(cgContext, kBlackColor);
CGContextStrokeEllipseInRect(currentContext, CGRectMake(0.0, 0.0, 150.0, 150.0));
CGImageRef maskRef = CGBitmapContextCreateImage(currentContext);

you have 150 by 150 started at 0 to 0 point circle mask. (Also you should design for your needs.)

then you apply this mask to your image with the code shown at this page…

by Alkim on Oct 19, 2009. Reply #

Thank you!

http://www.entheosweb.com/Flash/masking.asp
This web is masking in flash.
I want to do like this mask.
xcode can do like this?

by Saint on Oct 25, 2009. Reply #

many thanks for this post especially the later stages from Alkim. I was struggling because I was using the wrong routine to pull the image out of a custom context. Be warned, in this circumstance you should not use

UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();

But (obviously, in hindsight) use where you specify which context
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
UIImage* newImage = [UIImage imageWithCGImage:newImageRef];

Thanks again and I hope this helps others …

by Nick Forster on Dec 26, 2009. Reply #

Hi ,

I am new to iphone development and dont know how to implement this concept in xcode for iphone ,can anybody please provide me the whole source code or sample project for image masking with image.

Thanks in Advance…

by Amit on Dec 28, 2009. Reply #

Great tips! I’m having a bit of trouble though. I have a mask of a set size (100×100). I want to use this small mask on larger images of various sizes (320×480, 600×900, etc). However, my mask is stretching to the size of the image. Any ideas on how to maintain a smaller mask?

Thanks!

by Jacob on Jan 15, 2010. Reply #

So how do you can this method? Say your imageview is populated by the ‘didFinishPickingMediaWithInfo” method, how would you mask the results? Do you just add this code to the “didFinishPickingMediaWithInfo” method?

by ryemac3 on Feb 21, 2010. Reply #

The code looks great, and I have made my mask.png file

However, where should I insert the code? My gues is system/library/coreservices/springboard.app/info.plist but I’m not sure.

Also, would it automatically apply the mask to all apps? Thanks.

by John on Feb 28, 2010. Reply #

SOLUTION!!

Ok, this is stupidly easy to do after figuring out. The main problem is the mask image but after hours of banging my head on my desk I figured it out. Use whatever software you wish to create the mask.png with whatever colors you want and save it. The trick is to open it in Preview, yes Preview and click Save As, DESELECT the Alpha button and save. Your mask is now finished.

-(UIImage*)maskImage:(UIImage*)image withMask:(UIImage*)mask {

CGImageRef imgRef = [image CGImage];
CGImageRef maskRef = [mask CGImage];
CGImageRef actualMask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);

CGImageRef masked = CGImageCreateWithMask(imgRef, actualMask);

return [UIImage imageWithCGImage:masked];

}

by Casey on May 18, 2010. Reply #

Can we use CGImageCreateCopyWithColorSpace(maskref,CGColorSpaceCreateDeviceGray()) instead of CGImageMaskCreate(All the parameters and separate method calls)?

by MN on May 27, 2010. Reply #

Thank’s Rodney, this really helps perfect, which i am looking for! Thanks again!

by nishant on Jun 24, 2010. Reply #

question about how to creating mask image with programming way :

as we know, we can create such mask image via loading or reading one mask image edited already.

but what I want now is to create those data via some codes, just like GDI+ does, any clues ?

thanks

by Forrest on Jul 16, 2010. Reply #

In addition to Jean and Xavier’s code, I have added one more condition for the alpha channel check:

if ((CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNone)
|| (CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNoneSkipFirst)
|| (CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNoneSkipLast)) {
imageWithAlpha = CopyImageAndAddAlphaChannel(sourceImage);
}

by Brandon on Aug 10, 2010. Reply #

A shame, I thought I got rid of the black border, then I built it to the device and back there it was. :(

Any ideas?

Cheers,
Christoph

by Christoph B on Aug 20, 2010. Reply #

Thanks, this is a great code !!

It works perfectly on my iPhone simulator but when running on iPhone 4 simulator (retina high-res display) I am facing some problems. Can anyone confirm this code runs correctly when images are high-res ?

by Joshua on Sep 4, 2010. Reply #

What Brandon added on August 10 works on the device as well…
if ((CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNone)
|| (CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNoneSkipFirst)
|| (CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNoneSkipLast)) {

by Alexis on Oct 24, 2010. Reply #

For what it’s worth, I was having a problem in which the mask was working in the simulator and not on the device. I tried using a jpeg instead of a png, (A black shape with a white background), which worked like a charm. Yet, since all of my mask assets are png’s, instead of p-shopping all of them to jpegs, I did the following which also works like a charm, and lets me make the change at runtime.


UIImage *aFooImg = [UIImage imageNamed:@"aFoo.png"];

UIImageView *aFooImgView = [[UIImageView alloc] initWithImage:aFooImg];
CGRect imgRect = CGRectMake(0, 0, aFooImgView.frame.size.width, aFooImgView.frame.size.height);

UIView *maskMaster = [[UIView alloc] initWithFrame:imgRect];
[maskMaster setBackgroundColor:[UIColor whiteColor]];
[maskMaster addSubview:aFooImgView];
[aFooImgView release];

UIGraphicsBeginImageContext(maskMaster.bounds.size);
[maskMaster.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[maskMaster release];

NSLog(@"%@ is the view image", viewImage);

UIImage *bFooImg = [UIImage imageNamed:@"bFoo.png"];
UIImage *maskedBFooImg = [self maskImage:bFooImg withMask:viewImage];

Please forgive any memory leaks, I haven’t finalized the code just yet. I hope this helps those who are struggling as much as I did. Oh yeah, don’t forget to import the QuartzCore framework in your header.

by Ben on Dec 2, 2010. Reply #

Quick question.

Has anybody tried to use this to make a watermark? and if so how would you go about doing it?

Thanks!
Aaron

by Aaron G on Feb 8, 2011. Reply #

Hi,

I am trying to create a jigsaw puzzle and I need to mask the UIImages.

I did not understand how can I mask a JPG picture. Can anyone help me with this?
The JPGs are on an online server and there is no way to download them as PNG.

And one other thing, I can’t find this function anywhere on the Apple documentation:
“CopyImageAndAddAlphaChannel”

Thanks a lot,
Andrei

by Andrei on Feb 23, 2011. Reply #

I found the response. Here is the code:

CGImageRef imageRef = self.CGImage;
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);

// The bitsPerComponent and bitmapInfo values are hard-coded to prevent an “unsupported parameter combination” error
CGContextRef offscreenContext = CGBitmapContextCreate(NULL,
width,
height,
8,
0,
CGImageGetColorSpace(imageRef),
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);

// Draw the image into the context and retrieve the new image, which will now have an alpha layer
CGContextDrawImage(offscreenContext, CGRectMake(0, 0, width, height), imageRef);
CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(offscreenContext);
UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha];

// Clean up
CGContextRelease(offscreenContext);
CGImageRelease(imageRefWithAlpha);

return imageWithAlpha;

by Andrei on Feb 25, 2011. Reply #

thanks, I’ve been searching this on web, image cropping based on irregular shape.
cool!

by guyven69 on Mar 9, 2011. Reply #

You are leaking the “mask” and “masked” variables; use CFRelease to release them.

by Jason Boyle on Mar 29, 2011. Reply #

How can i do if i want apply a mask to a part of an image???

Thank, i think that your tutorial is very useful

by Giuseppe Frattura on Apr 3, 2011. Reply #

Note that the maskImage util method leaks memory. The corrected version of the util function is:

- (UIImage*) maskImage:(UIImage *)image
withMask:(UIImage *)maskImage
{
CGImageRef imageRef = image.CGImage;
CGImageRef maskRef = maskImage.CGImage;

CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef),
NULL, // decode should be NULL
FALSE // shouldInterpolate
);

CGImageRef masked = CGImageCreateWithMask(imageRef, mask);
CGImageRelease(mask);
UIImage *maskedImage = [UIImage imageWithCGImage:masked];
CGImageRelease(masked);
return maskedImage;
}

by Mo DeJong on Jul 6, 2011. Reply #

Hi everyone

I tried to mask an image using a masked image(320*480— : entire image is having black background except there is an elliptical shape in the middel which is white in color).I want to mask this image on another image having a face and get the face as cropped result.I am stuck with 2 issues:

1)The result shows the face in elliptical form but it appears grayish like a photo negative
2)Also i am getting Black background outside that face ellipse.

Any help will be appreciated

Thanks in Advance

by Vikas on Jul 13, 2011. Reply #

Hi Rodney Aiglstorfer ,

Can you provide me both of u reference images original and the mask image to test this functionality.

Thanks

by Vikas on Jul 13, 2011. Reply #

very simple solution to get rid of that black out area around

- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {

CGImageRef maskRef = maskImage.CGImage;

CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);

CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
UIImage *tempImage= [UIImage imageWithCGImage:masked];

NSData *imageData=UIImagePNGRepresentation(tempImage);

return ([UIImage imageWithData:imageData]);

by Ankit on Aug 25, 2011. Reply #

Thanks a lot for providing this important code.It’s really needed..

by tasnia on Dec 11, 2011. Reply #

Thanks for this valuable code. Can U suggest any ways to extract only masked portion from the image which is obtained from masking. I dont want transparent part to be present in the new image.

by Nirav on Jan 12, 2012. Reply #

Hi,

I implemented this, but the mask image (logo) is always taking up the whole original image size. I just want to add a logo at the right bottom corner, but the logo takes up the whole image. How do we fix this? Is there a way to position the mask?

by Van Du Tran on Jan 16, 2012. Reply #

Hi ,

I have an image with an white background. I want to extract that image portion of it. Can anybody help how to do this. I tried the above code it returning me an transparent image part of it.

by Harry on Jan 18, 2012. Reply #

the script works and give an image with transparency out of the mask..it’s correct and useful..thanks

by chicio on Jan 19, 2012. Reply #

This is the good one: But please note that the Mask must be WHITE background and the mask area is BLACK.

-(CGImageRef) CopyImageAndAddAlphaChannel :(CGImageRef) sourceImage
{
CGImageRef retVal = NULL;

size_t width = CGImageGetWidth(sourceImage);
size_t height = CGImageGetHeight(sourceImage);

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

CGContextRef offscreenContext = CGBitmapContextCreate(NULL, width, height,
8, 0, colorSpace, kCGImageAlphaPremultipliedFirst);

if (offscreenContext != NULL) {
CGContextDrawImage(offscreenContext, CGRectMake(0, 0, width, height), sourceImage);

retVal = CGBitmapContextCreateImage(offscreenContext);
CGContextRelease(offscreenContext);
}

CGColorSpaceRelease(colorSpace);

return retVal;
}

- (UIImage*)maskImage:(UIImage *)image withMask:(UIImage *)maskImage {
CGImageRef maskRef = maskImage.CGImage;
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);

CGImageRef sourceImage = [image CGImage];
CGImageRef imageWithAlpha = sourceImage;
//add alpha channel for images that don’t have one (ie GIF, JPEG, etc…)
//this however has a computational cost
if (CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNone) {
imageWithAlpha = [self CopyImageAndAddAlphaChannel :sourceImage];
}

CGImageRef masked = CGImageCreateWithMask(imageWithAlpha, mask);
CGImageRelease(mask);

//release imageWithAlpha if it was created by CopyImageAndAddAlphaChannel
if (sourceImage != imageWithAlpha) {
CGImageRelease(imageWithAlpha);
}

UIImage* retImage = [UIImage imageWithCGImage:masked];
CGImageRelease(masked);

return retImage;
}

- (void)viewDidLoad
{
[super viewDidLoad];

imgView.image = [self maskImage :[UIImage imageNamed:@"input.png"] withMask:[UIImage imageNamed:@"mask.png"]];

// Do any additional setup after loading the view, typically from a nib.
}

by Besterme on Jan 20, 2012. Reply #

Hi Besterme,

Thanks for the detailed code. However i tried the same snippet with in my project. It didn’t work for me Let me explain you the situation in more detailed way. I have one image of a fish which is the main image having a fish image in front but the background is white. I created another image which is a mask which is of the same size of the original image but is of color black. When i execute the following piece of code it gives me a transparent black.

[self maskImage:[UIImage imageNamed:@"MaskedBlack.png"] withMask:[UIImage imageNamed:@"Angel-fish-300x233.png"]];

With the following code i am getting the original image as it is. It does not give me the extracted image

[self maskImage:[UIImage imageNamed:@"Angel-fish-300x233.png"] withMask:[UIImage imageNamed:@"MaskedBlack.png"]]

Pl. let me know if there is any resolution to this.

Note: I want to extract the fish out of the image as it is without any white background.

Thanks
Harry

by Harry on Jan 22, 2012. Reply #

I tried this code, but for some reason the masked image shows up black n white. My image is in color…what could be the problem?

Here is my code:

- (void)viewDidLoad
{
[super viewDidLoad];
UIImage *imageToMask = [UIImage imageNamed:@"koko.jpg"];
UIImageView *imageToMaskImgView = [[UIImageView alloc] initWithImage:imageToMask];
CGRect imgRect = CGRectMake(0, 0, imageToMaskImgView.frame.size.width, imageToMaskImgView.frame.size.height);
UIView *maskMaster = [[UIView alloc] initWithFrame:imgRect];
[maskMaster setBackgroundColor:[UIColor whiteColor]];
[maskMaster addSubview:imageToMaskImgView];
UIGraphicsBeginImageContext(maskMaster.bounds.size);
[maskMaster.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSLog(@”%@ is the view image”, viewImage);
UIImage *bFooImg = [UIImage imageNamed:@"blackapple.png"];
self.myPic.image = [self maskImage:bFooImg withMask:viewImage];
}

- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {

CGImageRef maskRef = maskImage.CGImage;

CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);

CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
//memman
CGImageRelease(mask);
CGImageRelease(maskRef);
return [UIImage imageWithCGImage:masked];

}

by Marcio Valenzuela on Feb 2, 2012. Reply #

Thanks for the great post. I turned the code into a category on UIImage for easier use:


@interface UIImage (Mask)
- (UIImage *)imageWithMask:(UIImage *)maskImage;
@end


@implementation UIImage (Mask)

- (UIImage *)imageWithMask:(UIImage *)maskImage {
	CGImageRef maskRef = maskImage.CGImage;

	CGImageRef mask = CGImageMaskCreate(
        CGImageGetWidth(maskRef),
		CGImageGetHeight(maskRef),
		CGImageGetBitsPerComponent(maskRef),
		CGImageGetBitsPerPixel(maskRef),
		CGImageGetBytesPerRow(maskRef),
		CGImageGetDataProvider(maskRef), NULL, false
    );

    CGImageRef masked = CGImageCreateWithMask(self.CGImage, mask);
    CGImageRelease(mask);
	return [UIImage imageWithCGImage:masked];
}

@end

Enjoy!

David

by David E. Wheeler on Feb 5, 2012. Reply #

Thanks ., That is very Helpful

by Bala on Mar 10, 2012. Reply #

Hi,
Is there a way to do a color mask?

by Van on Mar 11, 2012. Reply #

Thank you all guys.It works successfully with Rodney’s code in my project.But I have another problem when I save the UIImage into disk by using UIImage’s method and then reload them from disk and show them with UIImageView.Sadly,I can print the image’s width and height by using NSLog(@”width:%f,height:%f”,img.size.width,img.size.height) but It does not disappear on the screen.Can you tell me why?Thanks very much.
best regards,dimmy

by dimmy on Apr 21, 2012. Reply #

Hi Everyone,
I used the code above. It all works fine except that the image I am masking rotates 90 degrees. So it appears sideways. The mask is the correct orientation but not the image. I tried it without the mask and the image appears the correct way. Does anyone know what could be causing this?

Code I have for the mask

- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {

CGImageRef maskRef = maskImage.CGImage;

CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef), NULL, false);

CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
return [UIImage imageWithCGImage:masked];
}

by Aaron on Aug 3, 2012. Reply #

I needed to mask a color and I had the same experience as most of you: why does it not work?
After many hours of trying and frustration I managed to get it to work !

1. First you have to remove the Alfa channel by changing it to a jpg representation for example.
2. Then you can mask the color as described in the book
3. Then you have to add the Alfa channel again as described on this forum.
4. Then you can save the image for further use.

This is my code :
+ (UIImage*)maskImage:(UIImage *)image withColors:(NSMutableArray*)colors {
    UIImage * im = [self RemoveAlphaChannel:image];
    CGImageRef imageWithNoAlpha = im.CGImage;

    float colorMasking[6];
    for (int i = 0; i<6; i++) {
        NSNumber * n = [colors objectAtIndex:i];
        colorMasking[i] = [n intValue]; // range is between 0..255
    }
    
    CGImageRef imageRef = CGImageCreateWithMaskingColors(imageWithNoAlpha, colorMasking);
    imageRef = [self AddAlphaChannel:imageRef];
    return [UIImage imageWithCGImage:imageRef];
}

+ (UIImage *) RemoveAlphaChannel:(UIImage *) sourceImage {
    return [UIImage imageWithData:UIImageJPEGRepresentation(sourceImage, 1.0)];
}

+ (CGImageRef) AddAlphaChannel:(CGImageRef) sourceImage {
    if ( CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNone || CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNoneSkipFirst || CGImageGetAlphaInfo(sourceImage) == kCGImageAlphaNoneSkipLast ) {
        
        CGImageRef retVal = NULL;
        size_t width = CGImageGetWidth(sourceImage);
        size_t height = CGImageGetHeight(sourceImage);
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGContextRef offScreenContext = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace, kCGImageAlphaPremultipliedFirst);
        if (offScreenContext != NULL) {
            CGContextDrawImage(offScreenContext, CGRectMake(0, 0, width, height), sourceImage);
            retVal = CGBitmapContextCreateImage(offScreenContext);
        }
    return retVal;
    }
    
    return sourceImage;
}

by Pieter Horchner on Sep 19, 2012. Reply #

Thanks :)

by Oritm on Oct 8, 2012. Reply #

Hi great code!! does anyone know why the image is black n white after the mask is applied to it?
thank you again!

by snowflakes on Dec 26, 2012. Reply #

Leave a Comment