Skip to content

Instantly share code, notes, and snippets.

@martinwinter
Created March 18, 2014 18:49
Show Gist options
  • Save martinwinter/9626774 to your computer and use it in GitHub Desktop.
Save martinwinter/9626774 to your computer and use it in GitHub Desktop.
A thin object wrapper around CGPath, mainly for the purpose of serializing paths. (Not yet converted to ARC, sorry.)
//
// MWCGPath.h
//
// Created by Martin Winter on 14.01.11.
// Copyright 2011 Martin Winter. All rights reserved.
//
// Provides an NSObject wrapper around CGPathRef.
//
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#import <CoreGraphics/CoreGraphics.h>
#else
#import <Cocoa/Cocoa.h>
#import <ApplicationServices/ApplicationServices.h>
#endif
@interface MWCGPath : NSObject
@property (nonatomic, retain) __attribute__((NSObject)) CGPathRef CGPath;
- (id)initWithCGPath:(CGPathRef)inCGPath;
@end
//
// MWCGPath.m
//
// Created by Martin Winter on 14.01.11.
// Copyright 2011 Martin Winter. All rights reserved.
//
#import "MWCGPath.h"
void MWCGPathApplierFunction(void *info, const CGPathElement *element)
{
NSMutableArray *array = (NSMutableArray *)info;
CGPathElementType type = element->type;
CGPoint *points = element->points;
// Determine number of points for element type.
NSUInteger pointCount = 0;
switch (type)
{
case kCGPathElementMoveToPoint:
pointCount = 1;
break;
case kCGPathElementAddLineToPoint:
pointCount = 1;
break;
case kCGPathElementAddQuadCurveToPoint:
pointCount = 2;
break;
case kCGPathElementAddCurveToPoint:
pointCount = 3;
break;
case kCGPathElementCloseSubpath:
pointCount = 0;
break;
default:
[array replaceObjectAtIndex:0 withObject:[NSNumber numberWithBool:NO]]; // Set status flag ...
return; // ... and return.
}
// Serialize points.
NSData *pointsData = [NSData dataWithBytes:points
length:(pointCount * sizeof(CGPoint))];
// Package type and points.
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:type], @"type",
pointsData, @"points",
nil];
// Add dictionary to array.
[array addObject:dictionary];
}
// Return value is autoreleased.
NSData *MWNSDataFromCGPath(CGPathRef path)
{
if (path == NULL) return nil;
// Create array that will hold path elements. Initialize with status flag.
NSMutableArray *array = [NSMutableArray arrayWithObject:[NSNumber numberWithBool:YES]];
// Examine path elements.
CGPathApply(path, array, MWCGPathApplierFunction);
// Serialize array.
return [NSKeyedArchiver archivedDataWithRootObject:array];
}
// Caller is responsible for releasing return value.
CGPathRef MWCGPathCreateFromNSData(NSData *data)
{
if (data == nil) return NULL;
// Unserialize array.
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// Recreate path.
CGMutablePathRef path = CGPathCreateMutable();
for (NSUInteger elementIndex = 0; elementIndex < [array count]; elementIndex++)
{
NSDictionary *dictionary = [array objectAtIndex:elementIndex];
CGPathElementType type = [(NSNumber *)[dictionary objectForKey:@"type"] intValue];
CGPoint *points = (CGPoint *)[(NSData *)[dictionary objectForKey:@"points"] bytes];
switch (type)
{
case kCGPathElementMoveToPoint:
CGPathMoveToPoint(path, NULL, points[0].x, points[0].y);
break;
case kCGPathElementAddLineToPoint:
CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y);
break;
case kCGPathElementAddQuadCurveToPoint:
CGPathAddQuadCurveToPoint(path, NULL, points[0].x, points[0].y, points[1].x, points[1].y);
break;
case kCGPathElementAddCurveToPoint:
CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
break;
case kCGPathElementCloseSubpath:
CGPathCloseSubpath(path);
break;
default:
CGPathRelease(path);
return NULL;
}
}
return path;
}
@implementation MWCGPath
@synthesize CGPath;
- (id)initWithCGPath:(CGPathRef)inCGPath
{
self = [super init];
if (self)
{
if (inCGPath != NULL)
{
CGPath = CGPathCreateCopy(inCGPath);
}
}
return self;
}
- (id)init
{
return [self initWithCGPath:NULL];
}
- (void)dealloc
{
if (CGPath != NULL) CGPathRelease(CGPath);
[super dealloc];
}
#pragma mark - NSCoding Protocol.
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if (self)
{
NSData *CGPathData = [decoder decodeObjectForKey:@"CGPathData"];
CGPath = MWCGPathCreateFromNSData(CGPathData); // Retain count 1.
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
NSData *CGPathData = MWNSDataFromCGPath(CGPath);
[encoder encodeObject:CGPathData forKey:@"CGPathData"];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment