UIImage fill transparent part with another image - ios

I would like to fill the transparent part of an image with another image in iOS.
I have tried some stuff with UIGraphicsContext, but I can't get it working because I've never used that before.
Here is the image:
Some code I tried:
UIImageView *v = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 70, 70)];
UIGraphicsBeginImageContextWithOptions(v.frame.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// beach image
UIImage *img = [UIImage imageNamed:#"zivogosce_camping_05.jpg"];
//[img drawInRect:CGRectMake(2, 0, 12, 12)];
UIImage *annotationImg = [UIImage imageNamed:#"custom_annotation"];
[annotationImg drawAtPoint:CGPointMake(0, 0)];
CGContextSetFillColorWithColor(context, [[UIColor colorWithPatternImage:img] CGColor]);
CGContextFillRect(context, CGRectMake(0, 0, 50, 50));
CGContextDrawImage(context, CGRectMake(0, 0, v.frame.size.width, v.frame.size.height), [annotationImg CGImage]);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
[v setImage:newImage];
UIGraphicsEndImageContext();
But images don't fit well...

Here's how I would do it:
You can make a clipping path in the shape of the circle by doing something like this:
CGContextSaveGState(context);
CGRect circleRect = CGRectMake(circleCenter.x - radius, circleCenter.y - radius, radius * 2.0, radius * 2.0);
CGContextAddEllipseInRect (context, circleRect);
CGContextClip(context);
// ... do whatever you need to in order to draw the inner image ...
CGContextRestoreGState(context);
// ... draw the transparent png ...
Setting a clipping path will cause drawing to only happen within the path. When you restore the state, it removes the clipping path (or rather sets it back to its previous value which is usually allowing it to draw to the entire canvas).

Related

iOS - Overlay UIImage with color on non-transparent area only?

I have a white image with transparent area in the middle like this:
I want to overlay it with colour (non-transparent area only) so it looks like this:
How can I do this?
I have looked into this answer but it overlay the whole image including the non-transparent area.
So your image is effectively a "mask". Play around with the "CGContextClipToMask" call and the above code that you referenced.
Try the following code and it gives the exact what you want.
UIGraphicsBeginImageContextWithOptions(imgView.image.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
CGContextFillRect(context, (CGRect){ {0,0}, imgView.image.size} );
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, imgView.image.size.height);
CGContextConcatCTM(context, flipVertical);
CGContextDrawImage(context, (CGRect){ {0,0}, imgView.image.size }, [imgView.image CGImage]);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[imgView setImage:image];

UIImage Add Grey tranparency

I want to do the following:
As you can see one image (selected) does not have a grey fade and the other (nonselected item) does not.
I have tried multiple solutions. Including coloring the images with a grey color
- (UIImage *)colorizeImage:(UIImage *)image withColor:(UIColor *)color {
UIGraphicsBeginImageContext(image.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -area.size.height);
CGContextSaveGState(context);
CGContextClipToMask(context, area, image.CGImage);
[color set];
CGContextFillRect(context, area);
CGContextRestoreGState(context);
CGContextSetBlendMode(context, kCGBlendModeMultiply);
CGContextDrawImage(context, area, image.CGImage);
UIImage *colorizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return colorizedImage;
}
However with this approach I run into an issue where if the bg of an image has a transparency the white below it shine through and it looks strange:
How can I detect the transparency and change the transparency to white? Or is there a better solution for doing this?
image = [self imageWithImage:image scaledToSize:CGSizeMake((self.collectionViewContainer.frame.size.width/3),90)];
UIImage *unSelected = [self colorizeImage:image withColor:[[UIColor grayColor] colorWithAlphaComponent:.9]];
UIImageView *imgView = [[UIImageView alloc] initWithImage:unSelected highlightedImage:image];
Since you simply want to apply a gray tint to an image, try it this way:
- (UIImage *)colorizeImage:(UIImage *)image withColor:(UIColor *)color {
UIGraphicsBeginImageContext(image.size, NO, image.scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, image.size.width, image.size.height);
[image drawInRect:area];
[color set];
CGContextFillRect(context, area);
UIImage *colorizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return colorizedImage;
}
This draws the image and then draws the color over the image. Assuming the color is partially transparent, the entire new image will be colored.
As you had it originally, you were only coloring the non-transparent parts due to the image clipping.
This update also applies the proper scale to the new image to match the original image.

Transparent Pixels on bottom corners of UIImage?

I am trying to make my image transparent on corners at the bottom so as to support a face curved look. I have tried with the following code but CGPathAddArc is a bit confusing as I am not much of a math bluff.
Red corners as shown in image defines the area I want to make transparent
UIGraphicsBeginImageContext(originalImage.size);
[originalImage drawAtPoint:CGPointZero];
CGMutablePathRef testRef = CGPathCreateMutable();
//CGAffineTransform temp = CGAffineTransformMakeRotation(M_PI / 2);
//CGPathAddEllipseInRect(testRef, &temp, CGRectMake(0,0,20,20));
//CGPathAddQuadCurveToPoint(testRef, nil, 20,20, 150, 150);
//CGPathAddCurveToPoint(testRef, NULL, 20, 20, 40, 40, 100, 100);
CGPathMoveToPoint(testRef, NULL, 20, 20);
CGPathAddArc(testRef, NULL, 0, 15, 15, M_PI_2, -M_PI_2, true);
// Clip to the path and clear that portion of the image.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddPath(context,testRef);
CGContextClip(context);
CGContextClearRect(context,CGRectMake(0,0,originalImage.size.width,originalImage.size.height));
// Build a new UIImage from the image context.
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if you are not that familiar with core-foundation and low level stuff, you can simple use [UIBezierPath bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:] to get a Rectangle with rounded corners.
Example:
UIGraphicsBeginImageContext(originalImage.size);
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, originalImage.size.width, originalImage.size.height)
byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight
cornerRadii:CGSizeMake(15, 15)];
[path addClip];
[originalImage drawAtPoint:CGPointZero];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Why is my image upside down after using CGContextSetFillColorWithColor

I am trying to apply a color fill to the MKAnnotation. I found some code that pretty much works but for some reason the filled image is upside down after applying the fill to it.
Here is the current code that I am running on a map pin.
CGRect rect = CGRectMake(0, 0, self.image.size.width, self.image.size.height);
UIGraphicsBeginImageContext(self.image.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClipToMask(context, rect, self.image.CGImage);
CGContextSetFillColorWithColor(context, [[UIColor grayColor] CGColor]);
CGContextFillRect(context, rect);
CGContextRotateCTM(context, 90);
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImage *flippedImage = [UIImage imageWithCGImage:img.CGImage
scale:1.0 orientation:self.image.imageOrientation];
self.image = flippedImage;
Here is what the pins look like after this code runs.
http://d.pr/i/UaPU
I was thinking that if I applied the current image orientation to the flippedImage that would do the trick but that did not work. I also tried setting self.image = img; and removing the flippedImage var completely but the result is still the same.
CGContext coordinate system is flipped vertically in regard to UIView coordinate system.
Just flip it like this:
CGContextTranslateCTM(ctx, 0, imageHeight);,
CGContextScaleCTM(ctx, 1, -1);

How to mask a square image into an image with round corners in iOS?

How can you mask a square image into an image with round corners?
You can use CoreGraphics to create a path for a round rectangle with this code snippet:
static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth, float ovalHeight)
{
float fw, fh;
if (ovalWidth == 0 || ovalHeight == 0) {
CGContextAddRect(context, rect);
return;
}
CGContextSaveGState(context);
CGContextTranslateCTM (context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextScaleCTM (context, ovalWidth, ovalHeight);
fw = CGRectGetWidth (rect) / ovalWidth;
fh = CGRectGetHeight (rect) / ovalHeight;
CGContextMoveToPoint(context, fw, fh/2);
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1);
CGContextClosePath(context);
CGContextRestoreGState(context);
}
And then call CGContextClip(context); to clip it to the rectangle path. Now any drawing done, including drawing an image, will be clipped to the round rectangle shape.
As an example, assuming "image" is a UIImage, and this is in a drawRect: method:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
addRoundedRectToPath(context, self.frame, 10, 10);
CGContextClip(context);
[image drawInRect:self.frame];
CGContextRestoreGState(context);
Here is an even easier method that is available in iPhone 3.0 and up. Every View-based object has an associated layer. Each layer can have a corner radius set, this will give you just what you want:
UIImageView * roundedView = [[UIImageView alloc] initWithImage: [UIImage imageNamed:#"wood.jpg"]];
// Get the Layer of any view
CALayer * layer = [roundedView layer];
[layer setMasksToBounds:YES];
[layer setCornerRadius:10.0];
// You can even add a border
[layer setBorderWidth:4.0];
[layer setBorderColor:[[UIColor blueColor] CGColor]];
To use these methods you might need to add:
#import <QuartzCore/QuartzCore.h>
I realize this is old news but just to boil it down a bit:
There are two possible questions here: (1) how do I apply rounded corners to a UIView (such as a UIImageView), which will be displayed on screen, and (2) how do I mask a square image (that is, a UIImage) to produce a new image with rounded corners.
For (1), the easiest course is to use CoreAnimation and set the view.layer.cornerRadius property
// Because we're using CoreAnimation, we must include QuartzCore.h
// and link QuartzCore.framework in a build phases
#import <QuartzCore/QuartzCore.h>
// start with an image
UIImage * fooImage = [UIImage imageNamed:#"foo.png"];
// put it in a UIImageView
UIView * view = [UIImageView alloc] initWithImage:fooImage];
// round its corners. This mask now applies to the view's layer's *background*
view.layer.cornerRadius = 10.f
// enable masksToBounds, so the mask applies to its foreground, the image
view.layer.masksToBounds = YES;
For (2), the best way is to use the UIKit graphics operations:
// start with an image
UIImage * fooImage = [UIImage imageNamed:#"foo.png"];
CGRect imageRect = CGRectMake(0, 0, fooImage.size.width, fooImage.size.height);
// set the implicit graphics context ("canvas") to a bitmap context for images
UIGraphicsBeginImageContextWithOptions(imageRect.size,NO,0.0);
// create a bezier path defining rounded corners
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:imageRect cornerRadius:10.f];
// use this path for clipping in the implicit context
[path addClip];
// draw the image into the implicit context
[fooImage drawInRect:imageRect];
// save the clipped image from the implicit context into an image
UIImage *maskedImage = UIGraphicsGetImageFromCurrentImageContext();
// cleanup
UIGraphicsEndImageContext();
What's tricky about problem (2) is that you might think you could do the whole operation using the view.layer.mask property in CoreAnimation. But you can't because the CALayer renderInContext: method, which you'd use to generate a UIImage from the masked layer, seems to ignore the mask. Worse, the documentation for renderInContext: doesn't mention this, and only alludes to the behavior for OSX 10.5.
Some further context: the above approach to (2) is using UIKit's wrappers around more basic CoreGraphics functionality. You can do the same thing using the CoreGraphics calls directly – that is what the chosen answer is doing -- but then you need build the rounded rect bezier path manually from curves and lines and you also need to compensate for the fact that CoreGraphics uses a drawing coordinate system which is flipped with respect to UIKit's.
See this Post - Very simple answer
How to set round corners in UI images in iphone
UIImageView * roundedView = [[UIImageView alloc] initWithImage: [UIImage imageNamed:#"wood.jpg"]];
// Get the Layer of any view
CALayer * l = [roundedView layer];
[l setMasksToBounds:YES];
[l setCornerRadius:10.0];
Very simple.
self.profileImageView.layer.cornerRadius = self.profileImageView.frame.size.width / 2;
self.profileImageView.clipsToBounds = YES;
For every view, there is a bundled layer property. So the first line of the above is to set the corner radius of the layer object (i.e. an instance of CALayer class). To make a circular image from a squared image, the radius is set to the half of the width of UIImageView. For instance, if the width of squared image is 100 pixels. The radius is set to 50 pixels. Secondly, you have to set the clipsToBounds property to YES in order to make the layer works.
Both the methods work but the differences shows up depending on where you use it.
For Ex: If you have a table view with the cells showing an image along with other labels, etc., and you use layer to set the cornerRadius, the scrolling will take a big hit. It gets jerky.
I faced this issue when I was using Layer for an image in a table view cell and was trying to figure out the cause of that jerkiness only to find that CALayer was the culprit.
Used the first solution of doing the stuff in drawRect explained by NilObject. That works like a charm with scrolling being smooth as silk.
On the other hand, if you want to use this in static views like popover view, etc., layer is the easiest way to do it.
As I said, both the methods work well just that you need to decide based on where you want to use it.
I use this method.
+ (UIImage *)imageWithColor:(UIColor *)color andSize:(CGSize)size;
{
UIImage *img = nil;
CGRect rect = CGRectMake(0, 0, size.width, size.height);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context,
color.CGColor);
CGContextFillRect(context, rect);
img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
Building off of algal, here are a couple methods that are nice to put in an UIImage category:
- (UIImage *) roundedCornerImageWithRadius:(CGFloat)radius
{
CGRect imageRect = CGRectMake(0, 0, self.size.width, self.size.height);
UIGraphicsBeginImageContextWithOptions(imageRect.size,NO,0.0); //scale 0 yields better results
//create a bezier path defining rounded corners and use it for clippping
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:imageRect cornerRadius:radius];
[path addClip];
// draw the image into the implicit context
[self drawInRect:imageRect];
// get image and cleanup
UIImage *roundedCornerImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return roundedCornerImage;
}
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size andCornerRadius:(CGFloat)radius
{
UIImage *image = nil;
if (size.width == 0 || size.height == 0) {
size = CGSizeMake(1.0, 1.0);
}
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
UIGraphicsBeginImageContextWithOptions(rect.size,NO,0.0); //yields sharper results than UIGraphicsBeginImageContext(rect.size)
CGContextRef context = UIGraphicsGetCurrentContext();
if (context)
{
CGContextSetFillColorWithColor(context, [color CGColor]);
if (radius > 0.0) {
//create a bezier path defining rounded corners and use it for clippping
UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius];
[path addClip];
CGContextAddPath(context, path.CGPath);
}
CGContextFillRect(context, rect);
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return image;
}

Resources