| OLD | NEW |
| (Empty) | |
| 1 // |
| 2 // NSBezierPath+MCAdditions.m |
| 3 // |
| 4 // Created by Sean Patrick O'Brien on 4/1/08. |
| 5 // Copyright 2008 MolokoCacao. All rights reserved. |
| 6 // |
| 7 |
| 8 #import "NSBezierPath+MCAdditions.h" |
| 9 |
| 10 // remove/comment out this line of you don't want to use undocumented functions |
| 11 #define MCBEZIER_USE_PRIVATE_FUNCTION |
| 12 |
| 13 #ifdef MCBEZIER_USE_PRIVATE_FUNCTION |
| 14 extern CGPathRef CGContextCopyPath(CGContextRef context); |
| 15 #endif |
| 16 |
| 17 static void CGPathCallback(void *info, const CGPathElement *element) |
| 18 { |
| 19 NSBezierPath *path = info; |
| 20 CGPoint *points = element->points; |
| 21 |
| 22 switch (element->type) { |
| 23 case kCGPathElementMoveToPoint: |
| 24 { |
| 25 [path moveToPoint:NSMakePoint(points[0].x, points[0].y)]
; |
| 26 break; |
| 27 } |
| 28 case kCGPathElementAddLineToPoint: |
| 29 { |
| 30 [path lineToPoint:NSMakePoint(points[0].x, points[0].y)]
; |
| 31 break; |
| 32 } |
| 33 case kCGPathElementAddQuadCurveToPoint: |
| 34 { |
| 35 // NOTE: This is untested. |
| 36 NSPoint currentPoint = [path currentPoint]; |
| 37 NSPoint interpolatedPoint = NSMakePoint((currentPoint.x
+ 2*points[0].x) / 3, (currentPoint.y + 2*points[0].y) / 3); |
| 38 [path curveToPoint:NSMakePoint(points[1].x, points[1].y)
controlPoint1:interpolatedPoint controlPoint2:interpolatedPoint]; |
| 39 break; |
| 40 } |
| 41 case kCGPathElementAddCurveToPoint: |
| 42 { |
| 43 [path curveToPoint:NSMakePoint(points[2].x, points[2].y)
controlPoint1:NSMakePoint(points[0].x, points[0].y) controlPoint2:NSMakePoint(p
oints[1].x, points[1].y)]; |
| 44 break; |
| 45 } |
| 46 case kCGPathElementCloseSubpath: |
| 47 { |
| 48 [path closePath]; |
| 49 break; |
| 50 } |
| 51 } |
| 52 } |
| 53 |
| 54 @implementation NSBezierPath (MCAdditions) |
| 55 |
| 56 + (NSBezierPath *)bezierPathWithCGPath:(CGPathRef)pathRef |
| 57 { |
| 58 NSBezierPath *path = [NSBezierPath bezierPath]; |
| 59 CGPathApply(pathRef, path, CGPathCallback); |
| 60 |
| 61 return path; |
| 62 } |
| 63 |
| 64 // Method borrowed from Google's Cocoa additions |
| 65 - (CGPathRef)cgPath |
| 66 { |
| 67 CGMutablePathRef thePath = CGPathCreateMutable(); |
| 68 if (!thePath) return nil; |
| 69 |
| 70 unsigned int elementCount = [self elementCount]; |
| 71 |
| 72 // The maximum number of points is 3 for a NSCurveToBezierPathElement. |
| 73 // (controlPoint1, controlPoint2, and endPoint) |
| 74 NSPoint controlPoints[3]; |
| 75 |
| 76 for (unsigned int i = 0; i < elementCount; i++) { |
| 77 switch ([self elementAtIndex:i associatedPoints:controlPoints])
{ |
| 78 case NSMoveToBezierPathElement: |
| 79 CGPathMoveToPoint(thePath, &CGAffineTransformIde
ntity, |
| 80 controlPoints[
0].x, controlPoints[0].y); |
| 81 break; |
| 82 case NSLineToBezierPathElement: |
| 83 CGPathAddLineToPoint(thePath, &CGAffineTransform
Identity, |
| 84 control
Points[0].x, controlPoints[0].y); |
| 85 break; |
| 86 case NSCurveToBezierPathElement: |
| 87 CGPathAddCurveToPoint(thePath, &CGAffineTransfor
mIdentity, |
| 88 contro
lPoints[0].x, controlPoints[0].y, |
| 89 contro
lPoints[1].x, controlPoints[1].y, |
| 90 contro
lPoints[2].x, controlPoints[2].y); |
| 91 break; |
| 92 case NSClosePathBezierPathElement: |
| 93 CGPathCloseSubpath(thePath); |
| 94 break; |
| 95 default: |
| 96 NSLog(@"Unknown element at [NSBezierPath (GTMBez
ierPathCGPathAdditions) cgPath]"); |
| 97 break; |
| 98 }; |
| 99 } |
| 100 return thePath; |
| 101 } |
| 102 |
| 103 - (NSBezierPath *)pathWithStrokeWidth:(CGFloat)strokeWidth |
| 104 { |
| 105 #ifdef MCBEZIER_USE_PRIVATE_FUNCTION |
| 106 NSBezierPath *path = [self copy]; |
| 107 CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]
; |
| 108 CGPathRef pathRef = [path cgPath]; |
| 109 [path release]; |
| 110 |
| 111 CGContextSaveGState(context); |
| 112 |
| 113 CGContextBeginPath(context); |
| 114 CGContextAddPath(context, pathRef); |
| 115 CGContextSetLineWidth(context, strokeWidth); |
| 116 CGContextReplacePathWithStrokedPath(context); |
| 117 CGPathRef strokedPathRef = CGContextCopyPath(context); |
| 118 CGContextBeginPath(context); |
| 119 NSBezierPath *strokedPath = [NSBezierPath bezierPathWithCGPath:strokedPa
thRef]; |
| 120 |
| 121 CGContextRestoreGState(context); |
| 122 |
| 123 CFRelease(pathRef); |
| 124 CFRelease(strokedPathRef); |
| 125 |
| 126 return strokedPath; |
| 127 #else |
| 128 return nil; |
| 129 #endif//MCBEZIER_USE_PRIVATE_FUNCTION |
| 130 } |
| 131 |
| 132 - (void)fillWithInnerShadow:(NSShadow *)shadow |
| 133 { |
| 134 [NSGraphicsContext saveGraphicsState]; |
| 135 |
| 136 NSSize offset = shadow.shadowOffset; |
| 137 NSSize originalOffset = offset; |
| 138 CGFloat radius = shadow.shadowBlurRadius; |
| 139 NSRect bounds = NSInsetRect(self.bounds, -(ABS(offset.width) + radius),
-(ABS(offset.height) + radius)); |
| 140 offset.height += bounds.size.height; |
| 141 shadow.shadowOffset = offset; |
| 142 NSAffineTransform *transform = [NSAffineTransform transform]; |
| 143 if ([[NSGraphicsContext currentContext] isFlipped]) |
| 144 [transform translateXBy:0 yBy:bounds.size.height]; |
| 145 else |
| 146 [transform translateXBy:0 yBy:-bounds.size.height]; |
| 147 |
| 148 NSBezierPath *drawingPath = [NSBezierPath bezierPathWithRect:bounds]; |
| 149 [drawingPath setWindingRule:NSEvenOddWindingRule]; |
| 150 [drawingPath appendBezierPath:self]; |
| 151 [drawingPath transformUsingAffineTransform:transform]; |
| 152 |
| 153 [self addClip]; |
| 154 [shadow set]; |
| 155 [[NSColor blackColor] set]; |
| 156 [drawingPath fill]; |
| 157 |
| 158 shadow.shadowOffset = originalOffset; |
| 159 |
| 160 [NSGraphicsContext restoreGraphicsState]; |
| 161 } |
| 162 |
| 163 - (void)drawBlurWithColor:(NSColor *)color radius:(CGFloat)radius |
| 164 { |
| 165 NSRect bounds = NSInsetRect(self.bounds, -radius, -radius); |
| 166 NSShadow *shadow = [[NSShadow alloc] init]; |
| 167 shadow.shadowOffset = NSMakeSize(0, bounds.size.height); |
| 168 shadow.shadowBlurRadius = radius; |
| 169 shadow.shadowColor = color; |
| 170 NSBezierPath *path = [self copy]; |
| 171 NSAffineTransform *transform = [NSAffineTransform transform]; |
| 172 if ([[NSGraphicsContext currentContext] isFlipped]) |
| 173 [transform translateXBy:0 yBy:bounds.size.height]; |
| 174 else |
| 175 [transform translateXBy:0 yBy:-bounds.size.height]; |
| 176 [path transformUsingAffineTransform:transform]; |
| 177 |
| 178 [NSGraphicsContext saveGraphicsState]; |
| 179 |
| 180 [shadow set]; |
| 181 [[NSColor blackColor] set]; |
| 182 NSRectClip(bounds); |
| 183 [path fill]; |
| 184 |
| 185 [NSGraphicsContext restoreGraphicsState]; |
| 186 |
| 187 [path release]; |
| 188 [shadow release]; |
| 189 } |
| 190 |
| 191 // Credit for the next two methods goes to Matt Gemmell |
| 192 - (void)strokeInside |
| 193 { |
| 194 /* Stroke within path using no additional clipping rectangle. */ |
| 195 [self strokeInsideWithinRect:NSZeroRect]; |
| 196 } |
| 197 |
| 198 - (void)strokeInsideWithinRect:(NSRect)clipRect |
| 199 { |
| 200 NSGraphicsContext *thisContext = [NSGraphicsContext currentContext]; |
| 201 float lineWidth = [self lineWidth]; |
| 202 |
| 203 /* Save the current graphics context. */ |
| 204 [thisContext saveGraphicsState]; |
| 205 |
| 206 /* Double the stroke width, since -stroke centers strokes on paths. */ |
| 207 [self setLineWidth:(lineWidth * 2.0)]; |
| 208 |
| 209 /* Clip drawing to this path; draw nothing outwith the path. */ |
| 210 [self setClip]; |
| 211 |
| 212 /* Further clip drawing to clipRect, usually the view's frame. */ |
| 213 if (clipRect.size.width > 0.0 && clipRect.size.height > 0.0) { |
| 214 [NSBezierPath clipRect:clipRect]; |
| 215 } |
| 216 |
| 217 /* Stroke the path. */ |
| 218 [self stroke]; |
| 219 |
| 220 /* Restore the previous graphics context. */ |
| 221 [thisContext restoreGraphicsState]; |
| 222 [self setLineWidth:lineWidth]; |
| 223 } |
| 224 |
| 225 @end |
| OLD | NEW |