Create a radial gradient mask to create a hole in the picture
Asked Answered
B

1

0

I want to create a radial gradient mask to create a hole in the picture, so that you can see through the picture. I created an overlaying image with a hole and the image beneath is visible, like below:

enter image description here

I am able to create this image using CGImageMaskCreate() successfully. But due to the unusual behaviour of CGImageMaskCreate() in iOS 10.0, I am looking for another solutions.

One such solution is to create image mask using the UIView's maskView property. I am able to write the code for creating the above mentioned image effect using maskView. But its not giving the correct results.

I am not very good with Core Graphics, maybe that's why I am not able to figure out where I am going wrong. My code is:

CGRect rect = CGRectMake(0.0, 0.0, self.view.bounds.size.width, self.view.bounds.size.height);
CGSize size = self.view.bounds.size;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat radius = 160.0;
CGPoint center = CGPointMake(150.0, 150.0);
CGFloat components[] = {1.0,1.0,1.0,1.0,   1.0,1.0,1.0,1.0,    1.0,1.0,1.0,1.0,     1.0,1.0,1.0,1.0,    1.0,1.0,1.0,0.5,    1.0,1.0,1.0,0.0};
CGFloat locations[] = {0.0, 0.1, 0.2, 0.8, 0.9, 1.0};

//creating bitmap context
CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, size.width*4, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrder32Big);
//creating gradient
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 6);
CGContextDrawRadialGradient(context, gradient, center, 0.1, center, radius, 0);
//get the gradient image
CGImageRef imageHole = CGBitmapContextCreateImage(context);
UIImage *img = [UIImage imageWithCGImage:imageHole];
CGGradientRelease(gradient);

//creating mask view whose alpha channels will be used for masking
UIImageView *maskImgView = [[UIImageView alloc] initWithFrame:CGRectMake(50.0, 50.0, 100.0, 100.0)];
[maskImgView setImage:img];

//This is the view on which I want to perform masking
UIImageView *mainView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[mainView setImage:[UIImage imageNamed:@"insta-logo.png"]];
mainView.maskView = maskImgView;
[self.view addSubview:mainView];

This the result I am getting:

enter image description here

The image used is entirely filled with red color. I want the resulting image to be transparent in the gradient region so that you can through it and I want the rest of the area to be red preserving the opacity of the rest of image, like the very first picture I posted.

I want to create this effect using maskView property, because of the unusual behaviour of CAShapeLayer and CGImageMaskCreate() encountered in iOS 10 and later.

Any suggestions will be very helpful. Thank you.

Ballon answered 2/2, 2017 at 20:29 Comment(9)
"the unusual behaviour of CAShapeLayer and CGImageMaskCreate() encountered in iOS 10 and later" What exactly are you referring to?Bickart
Using CGImageMaskCreate() creates the mask properly but the final masked image is almost transparent. For my application, this problem occurs only in iPhone 7 and above. I suppose thats because of some issue with iOS 10.Ballon
By the way, I got some help from this thread forums.developer.apple.com/thread/50854Ballon
No, I really don't see the relevance of that; it's about some dumb stuff people have been trying to with UIVisualEffectView, which has nothing to do with your question. As for CGImageMaskCreate(), I think I can pretty much guarantee that it hasn't changed its behavior since iOS 3; Apple wouldn't mess with something at such a deep level so basic to the drawing system. Nor do I believe that hardware would make a difference. Apologies for the skepticism, but there it is. However, I never use CGImageMaskCreate() so I can't pretend to real knowledge.Bickart
The information which I got from this thread is to use maskView property. I really couldn't figure out why the same piece of code working fine on iPhone 6 is giving this problem. Just now one of friend told that its working fine on iPhone 6 running iOS 10. So now I just don't have any clue why its not working on only iPhone 7. Initially I thought maybe it could be because of the wide color gamut introduced in iPhone 7.Ballon
I am having this exact same issue #41152057Ballon
I always use either CALayer mask or UIView mask (as I did in my answer below) so, as I said, I don't really know about CGImageMaskCreate. Your guess about wide color is devilishly clever. You're certainly punching radial holes in my skepticism.Bickart
Well that's a wild guess since the problem is occurring in a specific device and not in a specific OS.Ballon
In iOS 10, the way to cope with wide color is to use the new UIGraphicsImageRenderer class, with a UIGraphicsImageRendererFormat whose prefersExtendedRange is appropriately set, to get your image format. Perhaps you need to steer clear of CGBitmapContextCreate, which has not been updated for this purpose.Bickart
B
0

I think what you're describing is something like this:

enter image description here

As you can see, I've punched a radial-gradient hole in an image view, allowing the yellow background to show through.

How was that done? It's a CIFilter ("CIRadialGradient") with clear and black color values, used as the mask of the image view.

Bickart answered 2/2, 2017 at 21:12 Comment(1)
The problem with your code is that you have no clear center in your gradient; instead of fading from a clear center to a black outside, you fade from a black center to a clear outside. In effect, your color array components is backwards — that's all. Fix that, and your code will work fine. Mine is the way I would probably do it, but your way provides more precise control, so go right ahead and do it your way.Bickart

© 2022 - 2024 — McMap. All rights reserved.