I am working on "drawing" project.My project works well on screen. But when i test it on retina screen so i got an image like this retina screen!
It works on ipad2 very well.
some part of my code:
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
[curImage drawAtPoint:CGPointMake(0, 0)];
CGContextRef context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
CGContextMoveToPoint(context, mid1.x, mid1.y);
CGContextAddQuadCurveToPoint(context, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
if(previousPoint1.x == previousPoint2.x && previousPoint1.y == previousPoint2.y && previousPoint1.x == currentPoint.x && previousPoint1.y == currentPoint.y)
{
CGContextSetLineCap(context, kCGLineCapRound);
}
else
{
CGContextSetLineCap(context, kCGLineCapRound);
}
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, self.lineColor.CGColor);
CGContextSetShouldAntialias(context, YES);
CGContextSetAllowsAntialiasing(context, YES);
CGContextSetFlatness(context, 0.1f);
CGContextSetAlpha(context, self.lineAlpha);
CGContextStrokePath(context);
- (void) calculateMinImageArea:(CGPoint)pp1 :(CGPoint)pp2 :(CGPoint)cp{
CGPoint mid1 = midPoint(pp1, pp2);
CGPoint mid2 = midPoint(cp, pp1);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, mid1.x, mid1.y);
CGPathAddQuadCurveToPoint(path, NULL, pp1.x, pp1.y, mid2.x, mid2.y);
CGRect bounds = CGPathGetBoundingBox(path);
CGPathRelease(path);
CGRect drawBox = bounds;
//Pad our values so the bounding box respects our line width
drawBox.origin.x -= self.lineWidth * 1;
drawBox.origin.y -= self.lineWidth * 1;
drawBox.size.width += self.lineWidth * 2;
drawBox.size.height += self.lineWidth * 2;
UIGraphicsBeginImageContext(drawBox.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
curImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setNeedsDisplayInRect:drawBox];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
CGPoint midPoint(CGPoint p1, CGPoint p2)
{
return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);}
Related
I have implemented custom implementation of MKCircleRenderer.I need to have a circle with attached color. I can fill it with specific color. But how can add blur effect?
Here is my code:
- (void)fillPath:(CGPathRef)path inContext:(CGContextRef)context_{
if(!distance)
return;
#synchronized (self) {
if(!image){
rect = CGPathGetBoundingBox(path);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddPath(context, path);
CGContextSetBlendMode (context, kCGBlendModePlusDarker);
CGContextClip(context);
CGFloat gradientLocations[2] = {0, 1.0f};
UIColor* color = [UIColor colorWithHTMLString:#"#27B255"];
const CGFloat* components = CGColorGetComponents(color.CGColor);
CGFloat gradientColors[8] = {components[0], components[1], components[2], 0.3, components[0], components[1], components[2], 0.3};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradientColors, gradientLocations, 2);
CGColorSpaceRelease(colorSpace);
CGPoint gradientCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
CGFloat gradientRadius = MIN(rect.size.width, rect.size.height) / 2;
CGContextDrawRadialGradient(context, gradient, gradientCenter, 0, gradientCenter, gradientRadius, kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradient);
if(distance.accuracy > 0){
CGFloat k = (CGFloat)(rect.size.width/(2*(distance.distance + distance.accuracy)));
CGFloat radius = (distance.distance - distance.accuracy) * k;
if(radius > 0){
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextAddArc(context, CGRectGetMidX(rect), CGRectGetMidY(rect), radius, 0, 2 * M_PI, 0);
CGContextDrawPath(context, kCGPathFill);
CGContextSetBlendMode(context, kCGBlendModeNormal);
}
}
image = UIGraphicsGetImageFromCurrentImageContext();
image = [UIImageEffects imageByApplyingBlurToImage:image withRadius:1 tintColor:nil saturationDeltaFactor:1.0 maskImage:nil];
UIGraphicsEndImageContext();
}
}
CGContextDrawImage(context_, rect, image.CGImage);
}
I use UIImageEffects classes from Apple, but no result that I expect. Could you provide any other varian how to do this?
I'd like to draw circle with hole (like donut) on mapview.
my code is here.
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CG ContextRef)context {
WPCircleOverlay * circleOverlay = self.overlay;
CGPoint centerPoint = [self pointForMapPoint:MKMapPointForCoordinate(circleOverlay.coordinate)];
CGFloat innerRadius = MKMapPointsPerMeterAtLatitude(circleOverlay.coordinate.latitude) * circleOverlay.innerRadius;
CGFloat outerRadius = MKMapPointsPerMeterAtLatitude(circleOverlay.coordinate.latitude) * circleOverlay.outerRadius;
CGMutablePathRef path = CGPathCreateMutable();
//CGPathMoveToPoint(path, ...);
CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, outerRadius, 0, 2 * M_PI, true);
CGPathCloseSubpath(path);
// Add the inner arc to the path (later used to substract the inner area)
CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, innerRadius, 0, 2 * M_PI, true);
CGPathCloseSubpath(path);
// Add the path to the context
CGContextAddPath(context, path);
CGContextSetFillColorWithColor(context, self.fillColor.CGColor);
CGContextEOFillPath(context);
CGPathRelease(path);
It works well in simulator, but on device it doesn't.
On device, outer circle is filled with color, and inner circle wasn't clipped.
How can I modify my code to work well on device?
I fixed it by using CGContextAddEllipseInRect method instead of CGContextAddArc.
WPCircleOverlay * circleOverlay = self.overlay;
CGRect rectForMapRect = [self rectForMapRect:mapRect];
CGPoint centerPoint = [self pointForMapPoint:MKMapPointForCoordinate(circleOverlay.coordinate)];
CGFloat innerRadius = MKMapPointsPerMeterAtLatitude(circleOverlay.coordinate.latitude) * circleOverlay.innerRadius;
CGFloat outerRadius = MKMapPointsPerMeterAtLatitude(circleOverlay.coordinate.latitude) * circleOverlay.outerRadius;
CGRect innerRect = CGRectMake(centerPoint.x - innerRadius, centerPoint.y - innerRadius, innerRadius * 2.0, innerRadius * 2.0);
CGRect outerRect = CGRectMake(centerPoint.x - outerRadius, centerPoint.y - outerRadius, outerRadius * 2.0, outerRadius * 2.0);
if (CGRectIntersectsRect(rectForMapRect, outerRect)) {
CGContextAddRect(context, rectForMapRect);
CGContextSaveGState(context);
CGContextClip(context);
CGContextAddEllipseInRect(context, outerRect);
CGContextAddEllipseInRect(context, innerRect);
CGContextSaveGState(context);
CGContextEOClip(context);
UIColor * color = [self.fillColor copy];
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, outerRect);
CGContextRestoreGState(context);
CGContextRestoreGState(context);
UIGraphicsPopContext();
}
I draw a table "by hand", by using primitives of Core Graphics.
The view is re-draw when I click a button.
The problem is that when I profile the code in Instruments, the VM Regions keep increase, while Heap and Anonymous VM oscillate (and I would expect it).
And the details about CoreAnimation:
An excerpt of my drawRect:
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect paperRect = CGRectMake(self.bounds.origin.x+hourLabelSize+self.marginX,
10 +self.cellBorder ,
self.bounds.size.width,
self.cellHeight * 48
);
// custom shadow
drawLinearGradient(context, paperRect, whiteColor.CGColor, lightGrayColor.CGColor);
CGContextSaveGState(context);
CGFloat outerMargin = 5.0f;
CGFloat eventSize;
// Draw table
CGRect hourRect;
CGRect outerRect;
CGMutablePathRef outerPath;
CGRect strokeRect;
CGRect rowRect;
for (int j=0; j < 7;j++)
{
for (int i=0; i < numberOfRows; i++)
{
// Odd index means we are in the half of an hour
if ( (i%2) == 0)
{
[hour setString:[NSString stringWithFormat:#"%d:00",(int)floor(i/2)]];
// Draw box around hours //
if (j == 0 && i >0)
{
CGContextSaveGState(context);
hourRect = CGRectMake(5, i*cellHeight-5, hourLabelSize, 28);
outerRect = CGRectInset(hourRect, outerMargin, outerMargin);
outerPath = newRoundedRectForRect(outerRect, 6.0);
CGContextAddPath(context, outerPath);
CFRelease(outerPath); // <--- This solve the leak!!
// Draw gradient
strokeRect = CGRectInset(hourRect, 5.0, 5.0);
CGContextSetStrokeColorWithColor(context, boxColor.CGColor);
CGContextSetLineWidth(context, 1.0);
CGContextStrokeRect(context, strokeRect);
drawLinearGradient(context, strokeRect, whiteColor.CGColor, lightGrayColor.CGColor);
CGContextRestoreGState(context);
}
}
else
{
[hour setString:[NSString stringWithFormat:#"%d:30",(int)floor(i/2)]];
}
// Draw hours
if (j == 0 && i > 0)
[hour drawInRect:CGRectMake(0, i*cellHeight, hourLabelSize+10, 26) withFont:font lineBreakMode:NSLineBreakByClipping alignment:NSTextAlignmentCenter];
// Draw row
CGContextBeginPath(context);
CGContextSetStrokeColorWithColor(context, boxColor.CGColor);
CGContextSetLineWidth(context, self.cellBorder);
rowRect = CGRectMake(j*cellWidth + hourLabelSize +self. marginX - self.cellBorder/2, i*cellHeight+10 + self.cellBorder / 2, cellWidth , cellHeight);
CGContextStrokeRect(context, rowRect);
} //
CGContextFlush(context);
CGContextRestoreGState(context);
The first thing I do not understand is why I see VM:CoreAnimation. CoreGraphics is part of Core Animation?
Secondly, what I shall watch as syntoms of bad allocation: VM Regions, XCode measurements or Head and Anonymous?
Regards!
[EDIT]
The createRoundedRect is as follows
CGMutablePathRef newRoundedRectForRect(CGRect rect, CGFloat radius)
{
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPathAddArcToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMaxY(rect), radius);
CGPathAddArcToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMaxY(rect), radius);
CGPathAddArcToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMinY(rect), radius);
CGPathAddArcToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMinY(rect), radius);
CGPathCloseSubpath(path);
return path;
}:
and the drawLineGradient.m :
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = #[(__bridge id) startColor, (__bridge id) endColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
colors = nil;
}
The reason you see core animation is because all iOS views are layer-backed views.
The reason for your leak is that you release the path.
ARC does not manage Core Foundation objects.
Most of the time the code below works correctly and draws paths based upon the user's gestures. However, when the app initially launches the drawRect method isn't able to keep pace with the pan gesture, meaning that the pan gesture is sometimes called multiple times before drawrect is able to draw. This results in a space where the path should be drawn. How could I improve this please?
- (void)pan:(UIPanGestureRecognizer *)pan
{
if (pan.state == UIGestureRecognizerStateChanged)
{
inputPathCounter++;
CGPoint velocity = [pan velocityInView:self];
previousPoint2 = previousPoint1;
previousPoint1 = currentPoint;
currentPoint = [pan locationInView:self];
float velocityMagnitude = sqrtf(velocity.x * velocity.x + velocity.y * velocity.y);
float clampedVelocityMagnitude = clamp(VELOCITY_CLAMP_MIN, VELOCITY_CLAMP_MAX, velocityMagnitude);
float normalizedVelocity = (clampedVelocityMagnitude - VELOCITY_CLAMP_MIN) / (VELOCITY_CLAMP_MAX - VELOCITY_CLAMP_MIN);
float lowPassFilterAlpha = STROKE_WIDTH_SMOOTHING;
float newThickness = (STROKE_WIDTH_MAX - STROKE_WIDTH_MIN) * normalizedVelocity + STROKE_WIDTH_MIN;
self.lineWidth = self.lineWidth * lowPassFilterAlpha + newThickness * (1 - lowPassFilterAlpha);
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);
CGMutablePathRef subpath = CGPathCreateMutable();
CGPathMoveToPoint(subpath, NULL, mid1.x, mid1.y);
CGPathAddQuadCurveToPoint(subpath, NULL, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);
CGRect drawBounds = CGPathGetBoundingBox(subpath);
path = [[Path alloc] init];
path.width = self.lineWidth;
[self selectedColor];
path.bezierPath = [UIBezierPath bezierPathWithCGPath:subpath];
path.num = inputPathCounter;
CGPathRelease(subpath);
CGRect drawBox = drawBounds;
drawBox.origin.x -= self.lineWidth * 2.0;
drawBox.origin.y -= self.lineWidth * 2.0;
drawBox.size.width += self.lineWidth * 4.0;
drawBox.size.height += self.lineWidth * 4.0;
[self setNeedsDisplayInRect:drawBox];
}
else if (pan.state == UIGestureRecognizerStateEnded | pan.state == UIGestureRecognizerStateCancelled)
{
[self saveImage];
self.lineWidth = STROKE_WIDTH_MIN;
}
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
if(layer == nil)
{
//NSLog(#"reload layer");
layer = CGLayerCreateWithContext(context, bounds.size, NULL);
layerContext = CGLayerGetContext(layer);
CGContextScaleCTM(layerContext, scale, scale);
viewRect = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
//NSLog(#"viewrect %f %f",viewRect.size.width, viewRect.size.height);
CGContextSaveGState(layerContext);
UIGraphicsBeginImageContext (viewRect.size);
CGContextTranslateCTM(layerContext, 0, self.image.size.height/scale);
CGContextScaleCTM(layerContext, 1.0, -1.0);
CGContextDrawImage(layerContext, viewRect, self.image.CGImage);
UIGraphicsEndImageContext();
CGContextRestoreGState(layerContext);
}
UIBezierPath *bezierPath = path.bezierPath;
CGContextAddPath(layerContext, bezierPath.CGPath);
CGContextSetLineWidth(layerContext, path.width);
CGContextSetStrokeColorWithColor(layerContext, path.color.CGColor);
CGContextSetLineCap(layerContext, kCGLineCapRound);
CGContextStrokePath(layerContext);
CGContextDrawLayerInRect(context, viewRect, layer);
}
I am programming first time using Core Graphics, so don't have much idea that how exactly can I solve the problem.
I am drawing rounded rect bezier path along with gradient and stroke as background view for UITableviewCells. Everything has gone fine, except the extra black corners as shown in figure.
I haven't got any idea why they are showing and exactly what they are. Please can anyone help me? Thanks..
Code for creating cell
#import "CustomCellBackground.h"
.
.
.
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
cell.backgroundView = [[CustomCellBackground alloc] init];
cell.selectedBackgroundView = [[CustomCellBackground alloc]init];
}
// Configure the cell.
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.textColor = [UIColor whiteColor];
cell.textLabel.backgroundColor = [UIColor clearColor];
return cell;
}
In CustomCellBackground.m
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGPathRef path = [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:10.0] CGPath];
CGContextAddPath(context, path);
CGContextClip(context);
//CGContextSetLineJoin(context, kCGLineJoinRound);
drawLinearGradientWithFourColors(context, self.bounds);
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context,[UIColor whiteColor].CGColor);
CGContextSetLineWidth(context, 1.0);
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
void drawLinearGradientWithFourColors(CGContextRef context, CGRect rect)
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 0.2, 0.5, 1.0};
CGFloat colors[16] = {
85/255.0, 85/255.0, 85/255.0, 1.0,
45/255.0, 45/255.0, 45/255.0, 1.0,
22/255.0, 22/255.0, 22/255.0, 1.0,
0, 0, 0, 1.0
};
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, 4);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
When you init your view , setOpaque parameter for layer of the view and view.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setOpaque:NO];
[self.layer setOpaque:NO];
}
return self;
}
I googled it and pasted the code below. For more info refer link:
Use below code:
typedef enum {
CustomCellBackgroundViewPositionTop,
CustomCellBackgroundViewPositionMiddle,
CustomCellBackgroundViewPositionBottom,
CustomCellBackgroundViewPositionSingle
} CustomCellBackgroundViewPosition;
#interface CustomCellBackgroundView : UIView {
UIColor *borderColor;
UIColor *fillColor;
CustomCellBackgroundViewPosition position;
}
#property(nonatomic, retain) UIColor *borderColor, *fillColor;
#property(nonatomic) CustomCellBackgroundViewPosition position;
#end
- (void)drawRect:(CGRect)rect {
// Drawing code
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(c, [fillColor CGColor]);
CGContextSetStrokeColorWithColor(c, [borderColor CGColor]);
if (position == CustomCellBackgroundViewPositionTop) {
CGContextFillRect(c, CGRectMake(0.0f, rect.size.height - 10.0f, rect.size.width, 10.0f));
CGContextBeginPath(c);
CGContextMoveToPoint(c, 0.0f, rect.size.height - 10.0f);
CGContextAddLineToPoint(c, 0.0f, rect.size.height);
CGContextAddLineToPoint(c, rect.size.width, rect.size.height);
CGContextAddLineToPoint(c, rect.size.width, rect.size.height - 10.0f);
CGContextStrokePath(c);
CGContextClipToRect(c, CGRectMake(0.0f, 0.0f, rect.size.width, rect.size.height - 10.0f));
} else if (position == CustomCellBackgroundViewPositionBottom) {
CGContextFillRect(c, CGRectMake(0.0f, 0.0f, rect.size.width, 10.0f));
CGContextBeginPath(c);
CGContextMoveToPoint(c, 0.0f, 10.0f);
CGContextAddLineToPoint(c, 0.0f, 0.0f);
CGContextStrokePath(c);
CGContextBeginPath(c);
CGContextMoveToPoint(c, rect.size.width, 0.0f);
CGContextAddLineToPoint(c, rect.size.width, 10.0f);
CGContextStrokePath(c);
CGContextClipToRect(c, CGRectMake(0.0f, 10.0f, rect.size.width, rect.size.height));
} else if (position == CustomCellBackgroundViewPositionMiddle) {
CGContextFillRect(c, rect);
CGContextBeginPath(c);
CGContextMoveToPoint(c, 0.0f, 0.0f);
CGContextAddLineToPoint(c, 0.0f, rect.size.height);
CGContextAddLineToPoint(c, rect.size.width, rect.size.height);
CGContextAddLineToPoint(c, rect.size.width, 0.0f);
CGContextStrokePath(c);
return; // no need to bother drawing rounded corners, so we return
}
// At this point the clip rect is set to only draw the appropriate
// corners, so we fill and stroke a rounded rect taking the entire rect
CGContextBeginPath(c);
addRoundedRectToPath(c, rect, 10.0f, 10.0f);
CGContextFillPath(c);
CGContextSetLineWidth(c, 1);
CGContextBeginPath(c);
addRoundedRectToPath(c, rect, 10.0f, 10.0f);
CGContextStrokePath(c);
}
static void addRoundedRectToPath(CGContextRef context, CGRect rect,
float ovalWidth,float ovalHeight)
{
float fw, fh;
if (ovalWidth == 0 || ovalHeight == 0) {// 1
CGContextAddRect(context, rect);
return;
}
CGContextSaveGState(context);// 2
CGContextTranslateCTM (context, CGRectGetMinX(rect),// 3
CGRectGetMinY(rect));
CGContextScaleCTM (context, ovalWidth, ovalHeight);// 4
fw = CGRectGetWidth (rect) / ovalWidth;// 5
fh = CGRectGetHeight (rect) / ovalHeight;// 6
CGContextMoveToPoint(context, fw, fh/2); // 7
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);// 8
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);// 9
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);// 10
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); // 11
CGContextClosePath(context);// 12
CGContextRestoreGState(context);// 13
}