I have a background UIImage, and I would like to crop the background UIImage with a custom shape so this background image only "appears" through the custom shape. For example, I have a moon-shaped custom shape, and I would like the background image to only come through on the moon-shaped part.
Based on another answer, I am trying to impose a mask on the image like so:
- (UIImage *)createImageFromImage:(UIImage *)image
withMaskImage:(UIImage *)mask {
CGImageRef imageRef = image.CGImage;
CGImageRef maskRef = mask.CGImage;
CGImageRef imageMask = CGImageMaskCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGImageGetDataProvider(maskRef),
NULL,
YES);
CGImageRef maskedReference = CGImageCreateWithMask(imageRef, imageMask);
CGImageRelease(imageMask);
UIImage *maskedImage = [UIImage imageWithCGImage:maskedReference];
CGImageRelease(maskedReference);
return maskedImage;
}
However, the result of this looks like , and there are several issues with it:
the moon I have is 100*100, this has stretched do a strange proportion
My goal is only for the image to come through the moon - however, at the moment the moon is a white shape that is coming over the image
Any ideas about how I could fix the crop issue would be much appreciated. Thanks!
On your background image you'll have to add a custom mask through CALayer.
Keep in mind, everything you color in the mask.png (moon) will be visible, everything transparent will not display.
UIImage *moonImage = [UIImage imageNamed:#"mask.png"];
CALayer *maskLayer = [CALayer layer];
[maskLayer setContents:(id)moonImage.CGImage];
[maskLayer setFrame:CGRectMake(0.0f, 0.0f, moonImage.size.width, moonImage.size.height)];
UIImageView *yourBackgroundImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
// Keep your image view in tact here, just add:
[yourBackgroundImageView.layer setMask:maskLayer];
Related
I'm trying to develop an easy game. But then, I came across this problem with my UIImage.
When I import an image, I get this annoying background on this ball, not just the ball itself. So when I play with this ball, this background has to match the UIView color, or else it will look weird. How do I solve this problem?
You can use following code to get a mask shape image with a mask:
//beCroppedImage is the image be cropped, maskImage is a image with a white background color and a black sharp that ever you want.
theShapeImage = [beCroppedImage maskImageWithMask:maskImage];
- (UIImage *)maskImageWithMask:(UIImage*)mask {
CGImageRef imgRef = [self 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);
UIImage *image = [UIImage imageWithCGImage:masked];
CGImageRelease(masked);
CGImageRelease(actualMask);
return image;
}
I'am trying to create profile picture image with custom shape pattern with using masking in ios. Here is what i've used to create masked image :
- (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 maskedImageRef = CGImageCreateWithMask([image CGImage], mask);
UIImage *maskedImage = [UIImage imageWithCGImage:maskedImageRef];
CGImageRelease(mask);
CGImageRelease(maskedImageRef);
// returns new image with mask applied
return maskedImage;
}
After implementing instance , i am calling : *(_profileImage is global UIImage)
UIImage *maskImage = [UIImage imageNamed:#"maskProfilePicture.png"];
UIImage *maskedImage = [self maskImage:_profileImage withMask:maskImage];
and result (Image should be added in subview of mask. But it seems , mask image overlap on Image that need to be masked ):
The mask image : (Maybe mask image properties are wrong that i created in photoshop :)
The Core Graphics calls look correct.
Change the mask image to be have a black where you want the image to show through, and white elsewhere.
You can use CALayer's mask property to achieve the result.
CALayer Apple Documentation
Your masking layer should be transparent in places where you want to cut out the image.
I want to mask an image by passing another image as mask. I am able to mask the image but the resulting image doesn't look good. It is jagged at borders.
I guess the problem is related to retina graphics. The scale property for the two images are different as:
The image from which I want to mask has a scale value 1. This image generally has a resolution greater than 1000x1000 pixels.
The image according to which I want the resulting image(image having black and white colors only) has scale value 2. This image is generally of resolution 300x300 pixels.
The resulting image has a scale value of 1.
The code I am using is:
+ (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);
CGImageRelease(mask);
UIImage *maskedImage = [UIImage imageWithCGImage:masked ];
CGImageRelease(masked);
return maskedImage;
}
How can I get a masked image which follows retina scale?
I had the same issue. It appears that this line ignore scale factor.
UIImage *maskedImage = [UIImage imageWithCGImage:masked];
So you should draw the image by yourself. Replace it by the following :
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextDrawImage(context, rect, masked);
UIImage * maskedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Works fine by me.
EDIT
OR
UIImage * maskedImage = [UIImage imageWithCGImage:masked
scale:[[UIScreen mainScreen] scale]
orientation:UIImageOrientationUp];
You can do
UIImage * maskedImage = [UIImage imageWithCGImage:masked scale:[[UIScreen mainScreen] scale] orientation:UIImageOrientationUp];
I recently found out that the renderinContext: method ignores any layer masks. I am trying to capture a video of my app's screen, in which I animate the path of a masked CALayer.
To record the screen, I am using the ScreenCaptureView class, found online. The view takes a "screenshot" of it's subviews every x seconds and compiles them into a video.
In the drawRect method, I call
[[self.layer presentationLayer] renderInContext:context];
After looking online, I found someone's solution; however, it applies for masks that are images. (My mask is a CGPath).
-(UIImage*) imageFromView:(UIView*)originalView
{
//Creating a fakeView which is initialized as our original view.
//Actually i render images on a fakeView and take screenshot of this fakeView.
UIView *fakeView = [[UIView alloc] initWithFrame:originalView.frame];
[fakeView.layer setMasksToBounds:originalView.layer.masksToBounds];
//Getting subviews of originalView.
for (UIView *view in originalView.subviews)
{
//Getting screenshot of layer of view.
UIGraphicsBeginImageContext(view.bounds.size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *imageLayer = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//If it is masked view, then get masked image.
if (view.layer.mask)
{
//getting screenshot of masked layer.
UIGraphicsBeginImageContext(view.bounds.size);
[view.layer.mask renderInContext:UIGraphicsGetCurrentContext()];
//PNG Representation is most important. otherwise this masked image will not work.
UIImage *maskedLayerImage=[UIImage imageWithData:UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext())];
UIGraphicsEndImageContext();
//getting image by masking original image.
imageLayer = [self maskImage:imageLayer withMask:maskedLayerImage];
}
//If imageLayer is pointing to a valid object, then setting this image to UIImageView, our UIImageView frame having exactly as view.frame.
if (imageLayer)
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:view.frame];
[imageView setImage:imageLayer];
[fakeView addSubview:imageView];
}
}
//At the end, taking screenshot of fakeView. This will get your Original Image.
UIGraphicsBeginImageContext(fakeView.bounds.size);
[fakeView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *previewCapturedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return previewCapturedImage;
}
}
//Method is used to get masked image.
- (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];
}
Can I do something similar for masks from cgpaths?
You can use CGContextClip() in renderInContext: to clip to a path:
- (void)renderInContext:(CGContextRef)context {
// Assign maskPath to whatever path you want to mask to (Here, it's assumed that your layer's mask is a CAShapeLayer)
CGPathPathRef maskPath = [(CAShapeLayer *)self.mask path];
CGContextAddPath(context, maskPath);
CGContextClip(context);
// Do drawing code here
}
I'll make this as simple as possible.
How do I create reverse masks on CALayers in iOS?
I have a red view and an image that is used to mask the red view.
I use the view's CALayer's mask property to apply the mask, the result is the following.
However what I desire would be the opposite result, such as
(imagine that the white part here is actually the wood in the background, because I am not very good with image editing software)
To put in other words: I want the masking image to punch a hole through the view, and not act as an actual masking layer.
Answers in both C# (MonoTouch) or Obj-C are fine either way
Hope it is helpful
Step 1: Create a mask without any alpha channel.
Step 2: Create a invert mask by following method
- (UIImage*)createInvertMask:(UIImage *)maskImage withTargetImage:(UIImage *) image {
CGImageRef maskRef = maskImage.CGImage;
CGBitmapInfo bitmapInfo = kCGImageAlphaNone;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGImageRef mask = CGImageCreate(CGImageGetWidth(maskRef),
CGImageGetHeight(maskRef),
CGImageGetBitsPerComponent(maskRef),
CGImageGetBitsPerPixel(maskRef),
CGImageGetBytesPerRow(maskRef),
CGColorSpaceCreateDeviceGray(),
bitmapInfo,
CGImageGetDataProvider(maskRef),
nil,
NO,
renderingIntent);
CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
CGImageRelease(mask);
CGImageRelease(maskRef);
return [UIImage imageWithCGImage:masked];
}
Step 3: Set the invert mask to a UIView by following method
- (void)maskWithImage:(UIImage*) maskImage TargetView:(UIView*) targetView {
CALayer *_maskingLayer = [CALayer layer];
_maskingLayer.frame = targetView.bounds;
[_maskingLayer setContents:(id)[maskImage CGImage]];
[targetView.layer setMask:_maskingLayer];
}
Finished.
How to call?
UIImage* targetImage = [UIImage imageNamed:#"sky_bg.png"];
UIImage* mask = [UIImage imageNamed:#"mask01.png"];
UIImage* invertMask = [self createInvertMask:mask withTargetImage:targetImage];
[self maskWithImage:invertMask TargetView:targetview];
You need to reverse the alpha of your mask, so the "holes" are the transparent pixels. You'll also want to stretch your image to cover the whole red view.