// // NSBezierPath+MCAdditions.m // // Created by Sean Patrick O'Brien on 4/1/08. // Copyright 2008 MolokoCacao. All rights reserved. // #import "NSBezierPath+MCAdditions.h" #import "third_party/GTM/AppKit/GTMNSBezierPath+CGPath.h" // remove/comment out this line of you don't want to use undocumented functions #define MCBEZIER_USE_PRIVATE_FUNCTION #ifdef MCBEZIER_USE_PRIVATE_FUNCTION extern CGPathRef CGContextCopyPath(CGContextRef context); #endif static void CGPathCallback(void *info, const CGPathElement *element) { NSBezierPath *path = info; CGPoint *points = element->points; switch (element->type) { case kCGPathElementMoveToPoint: { [path moveToPoint:NSMakePoint(points[0].x, points[0].y)]; break; } case kCGPathElementAddLineToPoint: { [path lineToPoint:NSMakePoint(points[0].x, points[0].y)]; break; } case kCGPathElementAddQuadCurveToPoint: { // NOTE: This is untested. NSPoint currentPoint = [path currentPoint]; NSPoint interpolatedPoint = NSMakePoint((currentPoint.x + 2*points[0].x) / 3, (currentPoint.y + 2*points[0].y) / 3); [path curveToPoint:NSMakePoint(points[1].x, points[1].y) controlPoint1:interpolatedPoint controlPoint2:interpolatedPoint]; break; } case kCGPathElementAddCurveToPoint: { [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)]; break; } case kCGPathElementCloseSubpath: { [path closePath]; break; } } } @implementation NSBezierPath (MCAdditions) + (NSBezierPath *)bezierPathWithCGPath:(CGPathRef)pathRef { NSBezierPath *path = [NSBezierPath bezierPath]; CGPathApply(pathRef, path, CGPathCallback); return path; } - (NSBezierPath *)pathWithStrokeWidth:(CGFloat)strokeWidth { #ifdef MCBEZIER_USE_PRIVATE_FUNCTION NSBezierPath *path = [self copy]; CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; CGPathRef pathRef = [path gtm_CGPath]; [path release]; CGContextSaveGState(context); CGContextBeginPath(context); CGContextAddPath(context, pathRef); CGContextSetLineWidth(context, strokeWidth); CGContextReplacePathWithStrokedPath(context); CGPathRef strokedPathRef = CGContextCopyPath(context); CGContextBeginPath(context); NSBezierPath *strokedPath = [NSBezierPath bezierPathWithCGPath:strokedPathRef]; CGContextRestoreGState(context); CFRelease(pathRef); CFRelease(strokedPathRef); return strokedPath; #else return nil; #endif // MCBEZIER_USE_PRIVATE_FUNCTION } - (void)fillWithInnerShadow:(NSShadow *)shadow { [NSGraphicsContext saveGraphicsState]; NSSize offset = shadow.shadowOffset; NSSize originalOffset = offset; CGFloat radius = shadow.shadowBlurRadius; NSRect bounds = NSInsetRect(self.bounds, -(ABS(offset.width) + radius), -(ABS(offset.height) + radius)); // The context's user transform isn't automatically applied to shadow offsets. offset.height += bounds.size.height; shadow.shadowOffset = offset; NSAffineTransform *transform = [NSAffineTransform transform]; if ([[NSGraphicsContext currentContext] isFlipped]) [transform translateXBy:0 yBy:bounds.size.height]; else [transform translateXBy:0 yBy:-bounds.size.height]; NSBezierPath *drawingPath = [NSBezierPath bezierPathWithRect:bounds]; [drawingPath setWindingRule:NSEvenOddWindingRule]; [drawingPath appendBezierPath:self]; [drawingPath transformUsingAffineTransform:transform]; [self addClip]; [shadow set]; [[NSColor blackColor] set]; [drawingPath fill]; shadow.shadowOffset = originalOffset; [NSGraphicsContext restoreGraphicsState]; } - (void)drawBlurWithColor:(NSColor *)color radius:(CGFloat)radius { NSRect bounds = NSInsetRect(self.bounds, -radius, -radius); NSShadow *shadow = [[NSShadow alloc] init]; shadow.shadowOffset = NSMakeSize(0, bounds.size.height); shadow.shadowBlurRadius = radius; shadow.shadowColor = color; NSBezierPath *path = [self copy]; NSAffineTransform *transform = [NSAffineTransform transform]; if ([[NSGraphicsContext currentContext] isFlipped]) [transform translateXBy:0 yBy:bounds.size.height]; else [transform translateXBy:0 yBy:-bounds.size.height]; [path transformUsingAffineTransform:transform]; [NSGraphicsContext saveGraphicsState]; [shadow set]; [[NSColor blackColor] set]; NSRectClip(bounds); [path fill]; [NSGraphicsContext restoreGraphicsState]; [path release]; [shadow release]; } // Credit for the next two methods goes to Matt Gemmell - (void)strokeInside { /* Stroke within path using no additional clipping rectangle. */ [self strokeInsideWithinRect:NSZeroRect]; } - (void)strokeInsideWithinRect:(NSRect)clipRect { NSGraphicsContext *thisContext = [NSGraphicsContext currentContext]; float lineWidth = [self lineWidth]; /* Save the current graphics context. */ [thisContext saveGraphicsState]; /* Double the stroke width, since -stroke centers strokes on paths. */ [self setLineWidth:(lineWidth * 2.0)]; /* Clip drawing to this path; draw nothing outwith the path. */ [self setClip]; /* Further clip drawing to clipRect, usually the view's frame. */ if (clipRect.size.width > 0.0 && clipRect.size.height > 0.0) { [NSBezierPath clipRect:clipRect]; } /* Stroke the path. */ [self stroke]; /* Restore the previous graphics context. */ [thisContext restoreGraphicsState]; [self setLineWidth:lineWidth]; } @end