Files
IJSVG/source/IJSVGTransform.m
T

337 lines
11 KiB
Objective-C

//
// IJSVGTransform.m
// IconJar
//
// Created by Curtis Hard on 01/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGTransform.h"
@implementation IJSVGTransform
@synthesize command;
@synthesize parameters;
@synthesize parameterCount;
@synthesize sort;
- (void)dealloc
{
free(parameters);
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
IJSVGTransform * trans = [[[self class] alloc] init];
trans.command = self.command;
trans.parameters = (CGFloat*)malloc(sizeof(CGFloat)*self.parameterCount);
trans.sort = sort;
trans.parameterCount = self.parameterCount;
memcpy( trans.parameters, self.parameters, sizeof(CGFloat)*self.parameterCount);
return trans;
}
- (void)recalculateWithBounds:(CGRect)bounds
{
CGFloat max = bounds.size.width>bounds.size.height?bounds.size.width:bounds.size.height;
switch (self.command) {
case IJSVGTransformCommandRotate: {
if( self.parameterCount == 1 )
return;
self.parameters[1] = self.parameters[1]*max;
self.parameters[2] = self.parameters[2]*max;
}
default:
return;
}
}
+ (IJSVGTransformCommand)commandForCommandString:(NSString *)str
{
if( [str isEqualToString:@"matrix"] )
return IJSVGTransformCommandMatrix;
if( [str isEqualToString:@"translate"] )
return IJSVGTransformCommandTranslate;
if( [str isEqualToString:@"scale"] )
return IJSVGTransformCommandScale;
if( [str isEqualToString:@"rotate"] )
return IJSVGTransformCommandRotate;
return IJSVGTransformCommandNotImplemented;
}
+ (NSInteger)sortForTransformCommand:(IJSVGTransformCommand)command
{
switch (command) {
case IJSVGTransformCommandScale:
return 0;
case IJSVGTransformCommandRotate:
return 1;
case IJSVGTransformCommandMatrix:
return 2;
case IJSVGTransformCommandTranslate:
return -1;
default:
return 10;
}
return 10;
}
+ (NSArray *)transformsForString:(NSString *)string
{
static NSRegularExpression * _reg = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_reg = [[NSRegularExpression alloc] initWithPattern:@"([a-zA-Z]+)\\((.*?)\\)"
options:0
error:nil];
});
NSMutableArray * transforms = [[[NSMutableArray alloc] init] autorelease];
@autoreleasepool {
[_reg enumerateMatchesInString:string
options:0
range:NSMakeRange( 0, string.length )
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
{
NSString * command = [string substringWithRange:[result rangeAtIndex:1]];
IJSVGTransformCommand commandType = [[self class] commandForCommandString:command];
if( commandType == IJSVGTransformCommandNotImplemented )
return;
// create the transform
NSString * params = [string substringWithRange:[result rangeAtIndex:2]];
IJSVGTransform * transform = [[[[self class] alloc] init] autorelease];
NSInteger count = 0;
transform.command = commandType;
transform.parameters = [IJSVGUtils commandParameters:params
count:&count];
transform.parameterCount = count;
transform.sort = [[self class] sortForTransformCommand:commandType];
[transforms addObject:transform];
}];
}
return transforms;
}
+ (NSBezierPath *)transformedPath:(IJSVGPath *)path
{
if( path.transforms.count == 0 )
return path.path;
NSBezierPath * cop = [[path.path copy] autorelease];
for( IJSVGTransform * transform in path.transforms )
{
NSAffineTransform * at = [NSAffineTransform transform];
switch( transform.command )
{
// matrix
case IJSVGTransformCommandMatrix: {
at.transformStruct = (NSAffineTransformStruct) {
.m11 = transform.parameters[0],
.m12 = transform.parameters[1],
.m21 = transform.parameters[2],
.m22 = transform.parameters[3],
.tX = transform.parameters[4],
.tY = transform.parameters[5],
};
break;
}
// translate
case IJSVGTransformCommandTranslate: {
if( transform.parameterCount == 1 )
[at translateXBy:transform.parameters[0]
yBy:0];
else
[at translateXBy:transform.parameters[0]
yBy:transform.parameters[1]];
break;
}
// scale
case IJSVGTransformCommandScale: {
if( transform.parameterCount == 1 )
[at scaleBy:transform.parameters[0]];
else
[at scaleXBy:transform.parameters[0]
yBy:transform.parameters[1]];
break;
}
// rotate
case IJSVGTransformCommandRotate: {
if( transform.parameterCount == 1 )
[at rotateByDegrees:transform.parameters[0]];
else {
CGFloat centerX = transform.parameters[1];
CGFloat centerY = transform.parameters[2];
CGFloat angle = transform.parameters[0]*(M_PI/180.f);
[at translateXBy:centerX
yBy:centerY];
[at rotateByRadians:angle];
[at translateXBy:-1.f*centerX
yBy:-1.f*centerY];
}
break;
}
// do nothing
case IJSVGTransformCommandNotImplemented: {
}
}
[cop transformUsingAffineTransform:at];
}
return cop;
}
+ (void)performTransform:(IJSVGTransform *)transform
inContext:(CGContextRef)context
{
switch( transform.command )
{
// matrix
case IJSVGTransformCommandMatrix: {
CGContextConcatCTM( context, CGAffineTransformMake( transform.parameters[0],
transform.parameters[1],
transform.parameters[2],
transform.parameters[3],
transform.parameters[4],
transform.parameters[5]));
break;
}
// translate
case IJSVGTransformCommandTranslate: {
if( transform.parameterCount == 1 )
CGContextTranslateCTM( context, transform.parameters[0], 0 );
else
CGContextTranslateCTM( context, transform.parameters[0], transform.parameters[1]);
break;
}
// scale
case IJSVGTransformCommandScale: {
if( transform.parameterCount == 1 )
CGContextScaleCTM( context, transform.parameters[0], transform.parameters[0] );
else
CGContextScaleCTM( context, transform.parameters[0], transform.parameters[1] );
break;
}
// rotate
case IJSVGTransformCommandRotate: {
// these are in radians, not degrees
if( transform.parameterCount == 1 )
{
CGContextRotateCTM( context, (transform.parameters[0] / 180) * M_PI);
} else {
// need support for rotate around a point
CGFloat centerX = transform.parameters[1];
CGFloat centerY = transform.parameters[2];
CGFloat angle = transform.parameters[0]*(M_PI/180.f);
CGContextTranslateCTM(context, centerX, centerY);
CGContextRotateCTM(context, angle);
CGContextTranslateCTM(context, -1.f*centerX, -1.f*centerY);
}
}
// do nothing
case IJSVGTransformCommandNotImplemented: {
}
}
}
- (CGAffineTransform)CGAffineTransform
{
return [self CGAffineTransformWithModifier:nil];
}
- (CGAffineTransform)CGAffineTransformWithModifier:(IJSVGTransformParameterModifier)modifier
{
switch(self.command)
{
// matrix
case IJSVGTransformCommandMatrix: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
CGFloat p3 = self.parameters[3];
CGFloat p4 = self.parameters[4];
CGFloat p5 = self.parameters[5];
if(modifier != nil)
{
p0 = modifier(0,p0);
p1 = modifier(1,p1);
p2 = modifier(2,p2);
p3 = modifier(2,p3);
p4 = modifier(2,p4);
p5 = modifier(2,p5);
}
return CGAffineTransformMake(p0, p1, p2, p3, p4, p5);
}
// translate
case IJSVGTransformCommandTranslate: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
if(modifier != nil)
{
p0 = modifier(0,p0);
p1 = modifier(1,p1);
}
if(self.parameterCount == 1)
return CGAffineTransformMakeTranslation( p0, 0 );
return CGAffineTransformMakeTranslation(p0, p1);
}
// scale
case IJSVGTransformCommandScale: {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
if(modifier != nil)
{
p0 = modifier(0,p0);
p1 = modifier(1,p1);
}
if(self.parameterCount == 1)
return CGAffineTransformMakeScale( p0, p0);
return CGAffineTransformMakeScale( p0, p1);
}
// rotate
case IJSVGTransformCommandRotate: {
if(self.parameterCount == 1)
return CGAffineTransformMakeRotation((self.parameters[0]/180) * M_PI);
else {
CGFloat p0 = self.parameters[0];
CGFloat p1 = self.parameters[1];
CGFloat p2 = self.parameters[2];
if(modifier != nil)
{
p0 = modifier(0,p0);
p1 = modifier(1,p1);
p2 = modifier(2,p2);
}
CGFloat angle = p0*(M_PI/180.f);
CGAffineTransform def = CGAffineTransformIdentity;
def = CGAffineTransformTranslate(def, p1, p2);
def = CGAffineTransformRotate(def, angle);
def = CGAffineTransformTranslate(def, -1.f*p1, -1.f*p2);
return def;
}
break;
}
// do nothing
case IJSVGTransformCommandNotImplemented: {
return CGAffineTransformIdentity;
}
}
return CGAffineTransformIdentity;
}
@end