2 // NSBezierPath+MCAdditions.m
4 // Created by Sean Patrick O'Brien on 4/1/08.
5 // Copyright 2008 MolokoCacao. All rights reserved.
8 #import "NSBezierPath+MCAdditions.h"
10 #import "third_party/GTM/AppKit/GTMNSBezierPath+CGPath.h"
12 // remove/comment out this line of you don't want to use undocumented functions
13 #define MCBEZIER_USE_PRIVATE_FUNCTION
15 #ifdef MCBEZIER_USE_PRIVATE_FUNCTION
16 extern CGPathRef CGContextCopyPath(CGContextRef context);
19 static void CGPathCallback(void *info, const CGPathElement *element)
21 NSBezierPath *path = info;
22 CGPoint *points = element->points;
24 switch (element->type) {
25 case kCGPathElementMoveToPoint:
27 [path moveToPoint:NSMakePoint(points[0].x, points[0].y)];
30 case kCGPathElementAddLineToPoint:
32 [path lineToPoint:NSMakePoint(points[0].x, points[0].y)];
35 case kCGPathElementAddQuadCurveToPoint:
37 // NOTE: This is untested.
38 NSPoint currentPoint = [path currentPoint];
39 NSPoint interpolatedPoint = NSMakePoint((currentPoint.x + 2*points[0].x) / 3, (currentPoint.y + 2*points[0].y) / 3);
40 [path curveToPoint:NSMakePoint(points[1].x, points[1].y) controlPoint1:interpolatedPoint controlPoint2:interpolatedPoint];
43 case kCGPathElementAddCurveToPoint:
45 [path curveToPoint:NSMakePoint(points[2].x, points[2].y) controlPoint1:NSMakePoint(points[0].x, points[0].y) controlPoint2:NSMakePoint(points[1].x, points[1].y)];
48 case kCGPathElementCloseSubpath:
56 @implementation NSBezierPath (MCAdditions)
58 + (NSBezierPath *)bezierPathWithCGPath:(CGPathRef)pathRef
60 NSBezierPath *path = [NSBezierPath bezierPath];
61 CGPathApply(pathRef, path, CGPathCallback);
66 - (NSBezierPath *)pathWithStrokeWidth:(CGFloat)strokeWidth
68 #ifdef MCBEZIER_USE_PRIVATE_FUNCTION
69 NSBezierPath *path = [self copy];
70 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
71 CGPathRef pathRef = [path gtm_CGPath];
74 CGContextSaveGState(context);
76 CGContextBeginPath(context);
77 CGContextAddPath(context, pathRef);
78 CGContextSetLineWidth(context, strokeWidth);
79 CGContextReplacePathWithStrokedPath(context);
80 CGPathRef strokedPathRef = CGContextCopyPath(context);
81 CGContextBeginPath(context);
82 NSBezierPath *strokedPath = [NSBezierPath bezierPathWithCGPath:strokedPathRef];
84 CGContextRestoreGState(context);
87 CFRelease(strokedPathRef);
92 #endif // MCBEZIER_USE_PRIVATE_FUNCTION
95 - (void)fillWithInnerShadow:(NSShadow *)shadow
97 [NSGraphicsContext saveGraphicsState];
99 NSSize offset = shadow.shadowOffset;
100 NSSize originalOffset = offset;
101 CGFloat radius = shadow.shadowBlurRadius;
102 NSRect bounds = NSInsetRect(self.bounds, -(ABS(offset.width) + radius), -(ABS(offset.height) + radius));
104 // The context's user transform isn't automatically applied to shadow offsets.
105 offset.height += bounds.size.height;
106 shadow.shadowOffset = offset;
107 NSAffineTransform *transform = [NSAffineTransform transform];
108 if ([[NSGraphicsContext currentContext] isFlipped])
109 [transform translateXBy:0 yBy:bounds.size.height];
111 [transform translateXBy:0 yBy:-bounds.size.height];
113 NSBezierPath *drawingPath = [NSBezierPath bezierPathWithRect:bounds];
114 [drawingPath setWindingRule:NSEvenOddWindingRule];
115 [drawingPath appendBezierPath:self];
116 [drawingPath transformUsingAffineTransform:transform];
120 [[NSColor blackColor] set];
123 shadow.shadowOffset = originalOffset;
125 [NSGraphicsContext restoreGraphicsState];
128 - (void)drawBlurWithColor:(NSColor *)color radius:(CGFloat)radius
130 NSRect bounds = NSInsetRect(self.bounds, -radius, -radius);
131 NSShadow *shadow = [[NSShadow alloc] init];
132 shadow.shadowOffset = NSMakeSize(0, bounds.size.height);
133 shadow.shadowBlurRadius = radius;
134 shadow.shadowColor = color;
135 NSBezierPath *path = [self copy];
136 NSAffineTransform *transform = [NSAffineTransform transform];
137 if ([[NSGraphicsContext currentContext] isFlipped])
138 [transform translateXBy:0 yBy:bounds.size.height];
140 [transform translateXBy:0 yBy:-bounds.size.height];
141 [path transformUsingAffineTransform:transform];
143 [NSGraphicsContext saveGraphicsState];
146 [[NSColor blackColor] set];
150 [NSGraphicsContext restoreGraphicsState];
156 // Credit for the next two methods goes to Matt Gemmell
159 /* Stroke within path using no additional clipping rectangle. */
160 [self strokeInsideWithinRect:NSZeroRect];
163 - (void)strokeInsideWithinRect:(NSRect)clipRect
165 NSGraphicsContext *thisContext = [NSGraphicsContext currentContext];
166 float lineWidth = [self lineWidth];
168 /* Save the current graphics context. */
169 [thisContext saveGraphicsState];
171 /* Double the stroke width, since -stroke centers strokes on paths. */
172 [self setLineWidth:(lineWidth * 2.0)];
174 /* Clip drawing to this path; draw nothing outwith the path. */
177 /* Further clip drawing to clipRect, usually the view's frame. */
178 if (clipRect.size.width > 0.0 && clipRect.size.height > 0.0) {
179 [NSBezierPath clipRect:clipRect];
182 /* Stroke the path. */
185 /* Restore the previous graphics context. */
186 [thisContext restoreGraphicsState];
187 [self setLineWidth:lineWidth];