circle image created by objective-c code has always been clipped - ios

I was researching how to make a circle image by code in objective-c these two days. I found several way to do this, but no matter which way, the image created is not an exact circle, which is cut. Please see following code and image:
CGRect rect = CGRectMake(0.0f, 0.0f, radius*2.0f, radius*2.0f);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillEllipseInRect(context, rect);
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
If you look at the image carefully, you will find that the edge has been cut.
Finally I found if I change the code as following:
CGRect rect = CGRectMake(0.0f, 0.0f, radius*2.0f+4, radius*2.0f+4);
CGRect rectmin = CGRectMake(2.0f, 2.0f, radius*2, radius*2);
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillEllipseInRect(context, rectmin);
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
The result is much better, but I don't think it's a nice solution. Does anybody know exactly what's the problem of my first code snippet? Thanks in advance.
p.s. all the screenshots are captured from the simulator.

Related

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 draw a large image in small size with new context?

![enter image description here][1]By using the following code, I am able to draw a large image in small form, But new one image have their superview context or in other other word new image show in centre of new image but have a large white background to cover up large imageSize.
So, Actual image size is same as large image but I want to remove the extra white space.
UIGraphicsBeginImageContextWithOptions(largeImage.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor clearColor] setFill];
CGContextSetLineWidth(context, 4.0);
CGContextSetStrokeColorWithColor(context,
[UIColor clearColor].CGColor);
CGRect rectangle = CGRectMake((largeImage.size.width-80)/2, (largeImage.size.height-60)/2, 60, 80);
CGContextAddEllipseInRect(context, rectangle);
CGContextStrokePath(context);
[largeImage drawInRect:rectangle];
UIImage *smallSizeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Finally , I am getting smallSizeimage with a lot of white background. I want to change their white background without loosing the image quality.
Please guide me..
You are over-complicating the process mate.
I will post below an explanation to your problem,
Since I'm not a native english speaker, if some of my explanations aren't really clear, tell me, and I'll try to rephrase them.
If you are just interested in a working code, without any of my explanations, just scroll to the bottom.
First of all, your main issues are the following two:
UIGraphicsBeginImageContextWithOptions(largeImage.size, NO, 0); and [largeImage drawInRect:rectangle];
In UIGraphicsBeginImageContextWithOptions you suppose to set the new graphics context you are interested in, meaning, the new image size, and not the large one, as you are doing in your example.
Second, the UIImage instance method [yourImage drawInRect:yourRect is in your coordinate system of the graphics context, and not the coordinate system of the view.
Meaning, you suppose to treat it as a rectangle which begins at (0, 0) and is the size of your image.
After explaining to you the mistakes you've had, I'll post 2 variations of code.
One is to make a small image out of your original one, which you'll see in a second how much of a simple process that is.
And the second one is a modified version of your code above, since I don't know what you are using it to, could be that you do need the use of some of the other things from you original code.
To resize an image:
CGSize newSize = CGSizeMake(60,80); // Our new image size
CGRect newRect = CGRectMake(0, 0, 60, 80); // Our 'work' rect in out image context
// Begin a new image context with the new size we want
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
// Draw our image to our new rect
[largeImage drawInRect:newRect];
// Create a new UIImage from our new context
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
// End our image context
UIGraphicsEndImageContext();
And that's it!
If you still need to use some of the stuff from your original code, Here is a modified version of it:
CGSize newSize = CGSizeMake(60, 80);
CGRect newRect = CGRectMake(0, 0, 60, 80);
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor clearColor] setFill];
CGContextSetLineWidth(context, 4.0);
CGContextSetStrokeColorWithColor(context,
[UIColor clearColor].CGColor);
CGContextAddEllipseInRect(context, newRect);
CGContextClip(context); // EDIT- this is what we were missing
CGContextStrokePath(context);
[largeImage drawInRect:newRect];
UIImage *smallSizeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Good luck mate!
EDIT- Check out the code above for the edit.
Basically, what you and I have both missed in your code, is that, even tough you create a rounded context by using CGContextAddEllipseInRect(), we don't have a call to CGContextClip() to actually 'clipping' the context in our ellipse shape.
Now your image should be 'rounded'.
Replace this
UIGraphicsBeginImageContextWithOptions(largeImage.size, NO, 0);
with this:
UIGraphicsBeginImageContextWithOptions(small.size, NO, 0);
Furhermore here it is a method that i call to resize images:
- (UIImage*)imageWithImage:(UIImage*)image
{
CGSize newSize=CGSizeMake(100, 100);
UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

Core Graphics Circular Image Blurry

I'm drawing a circle image using core graphics with a modified implementation of this SO answer
Here's my source:
+ (UIImage*)circularImageWithRadius:(CGFloat)radius color:(UIColor*)color{
CGRect rect = CGRectMake(0.0f, 0.0f, radius*2, radius*2);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillEllipseInRect(context, rect);
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
The edges are blurry and I'm not sure why (I thought it didn't matter what resolution the device was, it would work right out of the box).
I tried replacing CGContextFillEllipseInRect(context, rect); with CGContextFillRect(context, rect); and that was blurry too. Then I tried CGContextFillRect(context, CGRectMake(0, 0, radius*4, radius*4) and it works perfectly, sharp image and everything (albeit being a a square, not a circle). So I changed back to CGContextDrawEllipseInRect(context, CGRectMake(0, 0, radius*4, radius*4) but this was my result:
whereas with the rectangle, it was the same size as when using radius*2 but with a much sharper image.
How can I fix my blurry issue and why does CGContextFillEllipseInRect not fill the pre-defined image rect?
I feel dumb for having found this immediately after I posted but this answer pointed the way.
I just added
if(UIGraphicsBeginImageContextWithOptions != NULL) {
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
} else {
UIGraphicsBeginImageContext(rect.size);
}
instead of just UIGraphicsBeginImageContext(rect.size); into my original source and it's crystal clear and sharp.
For Swift 3.0
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
defer { UIGraphicsEndImageContext() }
if let context = UIGraphicsGetCurrentContext() {
/// Do Stuff
}

CALayer renderInContext draws a blank image

This code works:
UIGraphicsBeginImageContextWithOptions(aRect.size, NO, 0.0);
[self.view drawViewHierarchyInRect:aRect afterScreenUpdates:YES];
anImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
but I need to support iOS 5 and 6. My Googling says this code ought to work:
UIGraphicsBeginImageContextWithOptions(aRect.size, NO, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
anImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
but the image is blank. How do I take a snapshot of a view in iOS 5 and 6?
The solution was to scale the view to fit it the bounds of the graphics context. Most examples I found of this assume that the source view and the destination context are the same size. The graphics context I was using was much smaller than the view being snapshotted, and it was actually just clipping a corner of the view that was transparent.
UIGraphicsBeginImageContextWithOptions(aRect.size, NO, 0.0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGFloat scale = CGRectGetWidth(aRect) / CGRectGetWidth(self.view.bounds);
CGContextScaleCTM(ctx, scale, scale);
[self.view.layer renderInContext:ctx];
anImageView.image = 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);

Resources