How to clear circle in CGContext in iOS - ios

I want create image using CGcontext. This is simple image with white or black background. also I want to add transperent part which is in circle ( check attached image). I know how to do this in rect. But i want to make it circle. Please anyone help me in this.

Use the below code to clear circle in your context
-(UIImage *) getImageWithcenterClear:(CGPoint) center{
CGRect frame = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContextWithOptions([[UIScreen mainScreen] bounds].size,
NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [[UIColor colorWithRed:0 green:0 blue:0 alpha:0.5 ] CGColor]);
CGContextFillRect(context, frame);
float radius = 50 * 2;
// Clear Circle
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextAddArc(context, center.x, center.y, radius - 0.54, 0, 2 * M_PI, 0);
CGContextDrawPath(context, kCGPathFill);
CGContextSetBlendMode(context, kCGBlendModeNormal);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

Related

Remove outside of bezier path

I have a function where I fill the image with a color and use a UIBezierPath to erase a point for corners.
CGRect rect = CGRectMake(0.0f, 0.0f, width, height);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(context, kCGBlendModeCopy);
// Fill image
CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
CGContextFillRect(context, rect);
// Round corners
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:15.0];
CGContextSetStrokeColorWithColor(context, [[UIColor clearColor] CGColor]);
[bezierPath stroke];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
With the above, I get an image that does have the Bézier path cut out, and the background filled.
However, how can I remove the corners outside of the path, or get at least some way to reference where they are so I can clear them?
A couple of options:
Use CoreGraphics, like you have, but clip it to a path:
CGRect rect = CGRectMake(0.0f, 0.0f, width, height);
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(context, kCGBlendModeCopy);
// Round corners
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:15.0];
CGContextAddPath(context, bezierPath.CGPath);
CGContextClip(context);
// Fill image
CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Alternatively, eliminate CoreGraphics and just fill the UIBezierPath:
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0);
[[UIColor redColor] setFill];
[[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:15.0] fill];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Note, in both of those examples, I used UIGraphicsBeginImageContextWithOptions, supplying a scale of 0 (a scale optimized for display on the device in question). If you really want, you can supply a scale of 1, which obviously will be a bit pixelated when rendered on a retina device, but that's up to you.

Why The CGContext draw nothing when I fill path color before stroke path color.

Code like this
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(context, 5, 5);
CGContextAddLineToPoint(context, 100, 100);
CGContextSetLineWidth(context, 5);
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextFillPath(context);
CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
CGContextStrokePath(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
when I remove the fill color the image is correct.
But I can't understand why I add fill color the image is nothing?
The problem is the how you draw on the context.
Please try the following:
CGRect rect = CGRectMake(0.0,0.0, 100, 100);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
//Set background color
CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
CGContextFillRect(context,rect);
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor grayColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextMoveToPoint(context, 5, 5);
CGContextAddLineToPoint(context, 100, 100);
CGContextStrokePath(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Now
_testImage.image = image;
You will get the result. Please let me know your feedback.

Core graphics images low quality

I'm the new one in Core Graphics and I try to draw circles. They can be transparent (only stroke) and filled with the stroke color. So the method looks like this:
+ (UIImage *)iconImageWithColor: (UIColor *)markColor strokeOnly:(BOOL)strokeOnly
{
CGRect rect = CGRectMake(0.0f, 0.0f, 20.0f, 20.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGPathRef clippingPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:7.5f].CGPath;
CGContextAddPath(context, clippingPath);
CGContextClip(context);
CGContextSetFillColorWithColor(context, strokeOnly == YES ? [[UIColor clearColor] CGColor] : [markColor CGColor]);
CGContextFillRect(context, rect);
CGPathRef path = CGPathCreateWithRoundedRect(rect, 10.f, 10.f, NULL);
[markColor setStroke];
CGContextAddPath(context, path);
CGContextDrawPath(context, kCGPathFillStroke);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
The result is strange - it looks like the resolution of the image is very low. Is it possible to make the result image looks good on retina display?
UIGraphicsBeginImageContext creates a context with a scale of 1, so non retina.
What you want to use is
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0f)
scale 0 means it will use the device screen scale
Use UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0);
Last parameter means the resulting pixel density (scale). When you pass 0 it picks it up automatically from device native screen scale.

how to clip a circle created using CGContextFillEllipseInRect with another circle

I am drawing a simple filled circle using following code in draw rect.
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(con, CGRectMake(0,0,self.bounds.size.width,self.bounds.size.height));
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
CGContextFillPath(con);
it draws a circle properly. Now I want to clip the circle with another small circle in the middle so that it becomes a hollow circle and you can see whatever is behind the main circle. How can i do that?
Use the even-odd rule with CGContextEOClip()
CGSize size = rect.size;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, 0);
CGContextSaveGState(context);
CGContextAddPath(context, ([UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, size.width, size.height)].CGPath));
CGContextAddPath(context, ([UIBezierPath bezierPathWithOvalInRect:CGRectMake(size.width/4, size.height/4, size.width/2, size.height/2)].CGPath));
CGContextEOClip(context); //clip
CGContextAddPath(context, [UIBezierPath bezierPathWithRect:rect].CGPath);
CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
CGContextFillPath(context);
CGContextRestoreGState(context);
I was able to do that using the EO rule.
CGContextRef con = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(con, rect);
CGContextAddEllipseInRect(con, CGRectInset(rect, 40, 40));
CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);
CGContextEOFillPath(con);

CGContextClipToMask does not clip

This code results in the image below. As far as I understand CGContextClipToMask, the red rectangle should not be visible, since it is outside of the clipped area. What am I missing here? Thanks for any help!
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor);
CGContextFillRect(context, rect);
CGContextSetLineWidth(context, 20);
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
// draw partial circle
UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:NO];
CGContextAddPath(context, [arc CGPath]);
CGContextStrokePath(context);
// create mask
CGImageRef mask = CGBitmapContextCreateImage(context);
self.maskCreated(mask);
// save state
CGContextSaveGState(context);
// clip with mask
CGContextClipToMask(context, rect, mask);
// draw test rect
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, 100, 100));
// restore state
CGContextRestoreGState(context);
The documentation for CGContextClipToMask says:
If mask is an image, then it must be in the DeviceGray color space,
may not have an alpha component, and may not be masked by an image
mask or masking color.
I'm assuming your code is in a the -drawRect: method of a subclass of UIView, so you are using the CGContext which was provided to you, which is in an RGB color space and probably has an alpha component. Your mask image is created from that context, so it gets the same attributes.
To fix this, use a separate bitmap context to generate the mask, using a gray colorspace with no alpha. Here's a self-contained example that does something similar to your code.
- (void)drawRect:(CGRect)rect
{
// Create a context for the mask
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef maskContext = CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, 8, 0, colorSpace, kCGImageAlphaNone | kCGBitmapByteOrderDefault);
CGColorSpaceRelease(colorSpace);
// Fill with black
CGContextSetFillColorWithColor(maskContext, [UIColor blackColor].CGColor);
CGContextFillRect(maskContext, rect);
// Draw an arc in white
CGContextSetLineWidth(maskContext, 20);
CGContextSetStrokeColorWithColor(maskContext, [UIColor whiteColor].CGColor);
CGContextAddArc(maskContext, CGRectGetMidX(rect), CGRectGetMidY(rect), 50, M_PI, 0, false);
CGContextStrokePath(maskContext);
// Create the mask image from the context, and discard the context
CGImageRef mask = CGBitmapContextCreateImage(maskContext);
CGContextRelease(maskContext);
// Now draw into the view itself
CGContextRef context = UIGraphicsGetCurrentContext();
// Apply the mask
CGContextClipToMask(context, rect, mask);
// Then draw something that overlaps the mask
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
// Make sure to clean up when we're done
CGImageRelease(mask);
}
Actually don't understand your concern, but you can hide rectangle in your method like this:
// draw a test rect
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGRect rect = CGRectZero;
CGContextFillRect(context, rect);

Resources