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.
Related
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;
}
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.
I am using this code to create the rectangle in my app. But i want to make it rounded. How can i do that.
- (void)setCropRect:(CGRect)cropRect
{
if(!CGRectEqualToRect(_cropRect,cropRect)){
_cropRect = CGRectOffset(cropRect, self.frame.origin.x, self.frame.origin.y);
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.f);
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor blackColor] setFill];
UIRectFill(self.bounds);
CGContextSetStrokeColorWithColor(context, [[UIColor whiteColor] colorWithAlphaComponent:0.5].CGColor);
CGContextStrokeRect(context, cropRect);
[[UIColor clearColor] setFill];
UIRectFill(CGRectInset(cropRect, 1, 1));
self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
}
I am using this code to crop the image in rectangular shape:
- (CGImageRef)newTransformedImage:(CGAffineTransform)transform
sourceImage:(CGImageRef)sourceImage
sourceSize:(CGSize)sourceSize
sourceOrientation:(UIImageOrientation)sourceOrientation
outputWidth:(CGFloat)outputWidth
cropRect:(CGRect)cropRect
imageViewSize:(CGSize)imageViewSize
{
CGImageRef source = sourceImage;
CGAffineTransform orientationTransform;
[self transform:&orientationTransform andSize:&imageViewSize forOrientation:sourceOrientation];
CGFloat aspect = cropRect.size.height/cropRect.size.width;
CGSize outputSize = CGSizeMake(outputWidth, outputWidth*aspect);
CGContextRef context = CGBitmapContextCreate(NULL,
outputSize.width,
outputSize.height,
CGImageGetBitsPerComponent(source),
0,
CGImageGetColorSpace(source),
CGImageGetBitmapInfo(source));
CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
CGContextFillRect(context, CGRectMake(0, 0, outputSize.width, outputSize.height));
CGAffineTransform uiCoords = CGAffineTransformMakeScale(outputSize.width/cropRect.size.width,
outputSize.height/cropRect.size.height);
uiCoords = CGAffineTransformTranslate(uiCoords, cropRect.size.width/2.0, cropRect.size.height/2.0);
uiCoords = CGAffineTransformScale(uiCoords, 1.0, -1.0);
CGContextConcatCTM(context, uiCoords);
CGContextConcatCTM(context, transform);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextConcatCTM(context, orientationTransform);
CGContextDrawImage(context, CGRectMake(-imageViewSize.width/2.0,
-imageViewSize.height/2.0,
imageViewSize.width,
imageViewSize.height)
,source);
CGImageRef resultRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
return resultRef;
}
Is i can use the brazier curve to crop the image also.
You cannot create a “circular CGRect”, or a rounded CGRect (which is what you appear to actually want). A CGRect has an origin and a width and a height. It doesn't store any corner radiuses (radii).
Instead of using UIRectFill, you need to create a UIBezierPath of a rounded rect and then fill the path. Example:
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:10];
[[UIColor blackColor] setFill];
[path fill];
You can also stroke a path if you need to. Example:
path = [UIBezierPath bezierPathWithRoundedRect:cropRect cornerRadius:10];
path.lineWidth = 10;
[[[UIColor whiteColor] colorWithAlphaComponent:0.5] setStroke];
[path stroke];
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);
I have a camera app to load photo into a device with mask. Everything is OK. When I try to use renderInContext to save the view to an image, I only see the image without any mask.
UIGraphicsBeginImageContext(contentView.bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[contentView.layer renderInContext:context];
UIImage *outImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(outImage, self, #selector(image: didFinishSavingWithError: contextInfo:), context);
I have read some paper from Apple to say that renderInContext don't support mask and composition. I've made some search on the internet to get the information that UIView needed to draw as a context first and then use renderInContext to save the image.
Now my question is what method to do the job? What about drawRect, drawInRect, drawLayer, drawInContent, or other method. Can anyone give me a hint. Thanks a lots.
I started from here: http://chinkisingh.com/2013/03/03/draw-on-iphoneipad-screen-using-bezier-paths-core-graphics-ios-app-development/
I have a UIBezierPath and I wanted to apply a gradient to it and apply to an existing image, see if the following code helps
CGRect r = CGRectMake(0, 0, HEART_SIZE, HEART_SIZE);
UIBezierPath *heart = [Bezier heartShape:r]; //this is only in my case
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
UIColor *darkPink = [UIColor colorWithHue:0.915 saturation:1.000 brightness:0.941 alpha:1.000];
UIColor *lightPink = [UIColor colorWithHue:0.917 saturation:0.647 brightness:1.000 alpha:1.000];
NSArray *gradientColors = [NSArray arrayWithObjects:
(id)darkPink.CGColor,
(id)lightPink.CGColor,
(id)darkPink.CGColor, nil];
CGFloat gradientLocations[] = {0, 0.5, 1};
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradientColors, gradientLocations);
CGColorSpaceRelease(colorSpace);
UIGraphicsBeginImageContext(r.size);
heart.lineCapStyle = kCGLineCapRound;
heart.lineWidth = 10.0f;
[[UIColor blackColor] setStroke];
[[UIColor redColor] setFill];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
[heart stroke]; //removed the black stroke around
[heart fill];
CGContextAddPath(context, heart.CGPath);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, CGPointMake(10, 10), CGPointMake(210, 210), kCGGradientDrawsAfterEndLocation); //check that gradient is drawn in the right place
CGGradientRelease(gradient);
UIImage *theRightImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();