How to Mask a Square Image into an Image with Round Corners in iOS

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

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);

How get round layer mask?

To do it entirely with CoreImage you can follow this recipe:

let image = CIImage(contentsOf: URL(string: "https://s3.amazonaws.com/uploads.hipchat.com/27364/1957261/bJERqq8O2V3NVok/upload.jpg")!)

// Construct a circle
let circle = CIFilter(name: "CIRadialGradient", withInputParameters:[
"inputRadius0":100,
"inputRadius1":100,
"inputColor0":CIColor(red: 1, green: 1, blue: 1, alpha:1),
"inputColor1":CIColor(red: 0, green: 0, blue: 0, alpha:0)
])?.outputImage

// Turn the circle into an alpha mask
let mask = CIFilter(name: "CIMaskToAlpha", withInputParameters: [kCIInputImageKey:circle!])?.outputImage

// Apply the mask to the input image
let combine = CIFilter(name: "CIBlendWithAlphaMask", withInputParameters:[
kCIInputMaskImageKey:mask!,
kCIInputImageKey:image!
])

let output = combine?.outputImage

Although this only works for circles

Turn square image into round rect button with shine [Objective C]

You need to apply a mask. This blog post has a PNG or PSD that you could adapt

http://surgeworksmobile.com/iphone/iphone-icons-how-to-preview-the-glossy-effect

You could apply the mask with ImageMagick.

http://www.imagemagick.org/Usage/channels/#masking

If you need to do it on the phone, you'd have to link to a static lib version of ImageMagick or try to figure out how to mask it with CoreImage.

How to round corners of UIImage with Hexagon mask

You can define a path that is a hexagon with rounded corners (defining that path manually) and then apply that as the mask and border sublayer:

CGFloat lineWidth    = 5.0;
UIBezierPath *path = [UIBezierPath polygonInRect:self.imageView.bounds
sides:6
lineWidth:lineWidth
cornerRadius:30];

// mask for the image view

CAShapeLayer *mask = [CAShapeLayer layer];
mask.path = path.CGPath;
mask.lineWidth = lineWidth;
mask.strokeColor = [UIColor clearColor].CGColor;
mask.fillColor = [UIColor whiteColor].CGColor;
self.imageView.layer.mask = mask;

// if you also want a border, add that as a separate layer

CAShapeLayer *border = [CAShapeLayer layer];
border.path = path.CGPath;
border.lineWidth = lineWidth;
border.strokeColor = [UIColor blackColor].CGColor;
border.fillColor = [UIColor clearColor].CGColor;
[self.imageView.layer addSublayer:border];

Where the path of a regular polygon with rounded corners might be implemented in a category like so:

@interface UIBezierPath (Polygon)

/** Create UIBezierPath for regular polygon with rounded corners
*
* @param rect The CGRect of the square in which the path should be created.
* @param sides How many sides to the polygon (e.g. 6=hexagon; 8=octagon, etc.).
* @param lineWidth The width of the stroke around the polygon. The polygon will be inset such that the stroke stays within the above square.
* @param cornerRadius The radius to be applied when rounding the corners.
*
* @return UIBezierPath of the resulting rounded polygon path.
*/

+ (instancetype)polygonInRect:(CGRect)rect sides:(NSInteger)sides lineWidth:(CGFloat)lineWidth cornerRadius:(CGFloat)cornerRadius;

@end

And

@implementation UIBezierPath (Polygon)

+ (instancetype)polygonInRect:(CGRect)rect sides:(NSInteger)sides lineWidth:(CGFloat)lineWidth cornerRadius:(CGFloat)cornerRadius {
UIBezierPath *path = [UIBezierPath bezierPath];

CGFloat theta = 2.0 * M_PI / sides; // how much to turn at every corner
CGFloat offset = cornerRadius * tanf(theta / 2.0); // offset from which to start rounding corners
CGFloat squareWidth = MIN(rect.size.width, rect.size.height); // width of the square

// calculate the length of the sides of the polygon

CGFloat length = squareWidth - lineWidth;
if (sides % 4 != 0) { // if not dealing with polygon which will be square with all sides ...
length = length * cosf(theta / 2.0) + offset/2.0; // ... offset it inside a circle inside the square
}
CGFloat sideLength = length * tanf(theta / 2.0);

// start drawing at `point` in lower right corner

CGPoint point = CGPointMake(rect.origin.x + rect.size.width / 2.0 + sideLength / 2.0 - offset, rect.origin.y + rect.size.height / 2.0 + length / 2.0);
CGFloat angle = M_PI;
[path moveToPoint:point];

// draw the sides and rounded corners of the polygon

for (NSInteger side = 0; side < sides; side++) {
point = CGPointMake(point.x + (sideLength - offset * 2.0) * cosf(angle), point.y + (sideLength - offset * 2.0) * sinf(angle));
[path addLineToPoint:point];

CGPoint center = CGPointMake(point.x + cornerRadius * cosf(angle + M_PI_2), point.y + cornerRadius * sinf(angle + M_PI_2));
[path addArcWithCenter:center radius:cornerRadius startAngle:angle - M_PI_2 endAngle:angle + theta - M_PI_2 clockwise:YES];

point = path.currentPoint; // we don't have to calculate where the arc ended ... UIBezierPath did that for us
angle += theta;
}

[path closePath];

path.lineWidth = lineWidth; // in case we're going to use CoreGraphics to stroke path, rather than CAShapeLayer
path.lineJoinStyle = kCGLineJoinRound;

return path;
}

That yields something like so:

landscape with rounded hexagon border

Or in Swift 3 you might do:

let lineWidth: CGFloat = 5
let path = UIBezierPath(polygonIn: imageView.bounds, sides: 6, lineWidth: lineWidth, cornerRadius: 30)

let mask = CAShapeLayer()
mask.path = path.cgPath
mask.lineWidth = lineWidth
mask.strokeColor = UIColor.clear.cgColor
mask.fillColor = UIColor.white.cgColor
imageView.layer.mask = mask

let border = CAShapeLayer()
border.path = path.cgPath
border.lineWidth = lineWidth
border.strokeColor = UIColor.black.cgColor
border.fillColor = UIColor.clear.cgColor
imageView.layer.addSublayer(border)

With

extension UIBezierPath {

/// Create UIBezierPath for regular polygon with rounded corners
///
/// - parameter rect: The CGRect of the square in which the path should be created.
/// - parameter sides: How many sides to the polygon (e.g. 6=hexagon; 8=octagon, etc.).
/// - parameter lineWidth: The width of the stroke around the polygon. The polygon will be inset such that the stroke stays within the above square. Default value 1.
/// - parameter cornerRadius: The radius to be applied when rounding the corners. Default value 0.

convenience init(polygonIn rect: CGRect, sides: Int, lineWidth: CGFloat = 1, cornerRadius: CGFloat = 0) {
self.init()

let theta = 2 * .pi / CGFloat(sides) // how much to turn at every corner
let offset = cornerRadius * tan(theta / 2) // offset from which to start rounding corners
let squareWidth = min(rect.width, rect.height) // width of the square

// calculate the length of the sides of the polygon

var length = squareWidth - lineWidth
if sides % 4 != 0 { // if not dealing with polygon which will be square with all sides ...
length = length * cos(theta / 2) + offset / 2 // ... offset it inside a circle inside the square
}
let sideLength = length * tan(theta / 2)

// if you'd like to start rotated 90 degrees, use these lines instead of the following two:
//
// var point = CGPoint(x: rect.midX - length / 2, y: rect.midY + sideLength / 2 - offset)
// var angle = -CGFloat.pi / 2.0

// if you'd like to start rotated 180 degrees, use these lines instead of the following two:
//
// var point = CGPoint(x: rect.midX - sideLength / 2 + offset, y: rect.midY - length / 2)
// var angle = CGFloat(0)

var point = CGPoint(x: rect.midX + sideLength / 2 - offset, y: rect.midY + length / 2)
var angle = CGFloat.pi

move(to: point)

// draw the sides and rounded corners of the polygon

for _ in 0 ..< sides {
point = CGPoint(x: point.x + (sideLength - offset * 2) * cos(angle), y: point.y + (sideLength - offset * 2) * sin(angle))
addLine(to: point)

let center = CGPoint(x: point.x + cornerRadius * cos(angle + .pi / 2), y: point.y + cornerRadius * sin(angle + .pi / 2))
addArc(withCenter: center, radius: cornerRadius, startAngle: angle - .pi / 2, endAngle: angle + theta - .pi / 2, clockwise: true)

point = currentPoint
angle += theta
}

close()

self.lineWidth = lineWidth // in case we're going to use CoreGraphics to stroke path, rather than CAShapeLayer
lineJoinStyle = .round
}

}

For Swift 2 rendition, see previous revision of this answer.

UIImage rounded corners

The problem was the use of CGImageCreateWithMask which returned an all black image. The solution I found was to use CGContextClipToMask instead:

CGContextRef mainViewContentContext;
CGColorSpaceRef colorSpace;

colorSpace = CGColorSpaceCreateDeviceRGB();

// create a bitmap graphics context the size of the image
mainViewContentContext = CGBitmapContextCreate (NULL, targetSize.width, targetSize.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);

// free the rgb colorspace
CGColorSpaceRelease(colorSpace);

if (mainViewContentContext==NULL)
return NULL;

CGImageRef maskImage = [[UIImage imageNamed:@"mask.png"] CGImage];
CGContextClipToMask(mainViewContentContext, CGRectMake(0, 0, targetSize.width, targetSize.height), maskImage);
CGContextDrawImage(mainViewContentContext, CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledWidth, scaledHeight), self.CGImage);


// Create CGImageRef of the main view bitmap content, and then
// release that bitmap context
CGImageRef mainViewContentBitmapContext = CGBitmapContextCreateImage(mainViewContentContext);
CGContextRelease(mainViewContentContext);

// convert the finished resized image to a UIImage
UIImage *theImage = [UIImage imageWithCGImage:mainViewContentBitmapContext];
// image is retained by the property setting above, so we can
// release the original
CGImageRelease(mainViewContentBitmapContext);

// return the image
return theImage;

Use Storyboard to mask UIView and give rounded corners?

Yes, I use that a lot but question like this was already answered many times.

But anyway in Interface Builder.
You need to add User Defined Runtime Attributes like this:

layer.masksToBounds Boolean YES
layer.cornerRadius Number {View's Width/2}

Sample Image

and enable

Clips subviews

Sample Image

Results:

Sample Image

Make an Image have rounded corners with Objective-C / pure C

If you mean you want to draw a square image cropped to a rounded rectangle, take a look at NSBezierPath's clipping functions. Just clip to the frame you want and draw.

Creating a border with rounded corners using SVG masks results in lighter corners

You don't need to mask both the image and the border. Just mask the image, then draw a 1px black border on top of it.