Files
IJSVG/source/IJSVGRadialGradient.m

168 lines
5.6 KiB
Objective-C

//
// IJSVGRadialGradient.m
// IJSVGExample
//
// Created by Curtis Hard on 03/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import "IJSVGRadialGradient.h"
@implementation IJSVGRadialGradient
@synthesize cx;
@synthesize cy;
@synthesize fx;
@synthesize fy;
@synthesize radius;
- (void)dealloc
{
[cx release], cx = nil;
[cy release], cy = nil;
[fx release], fx = nil;
[fy release], fy = nil;
[radius release], radius = nil;
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
IJSVGRadialGradient * grad = [super copyWithZone:zone];
grad.fx = self.fx;
grad.fy = self.fy;
grad.cx = self.cx;
grad.cy = self.cy;
grad.radius = self.radius;
return grad;
}
+ (NSGradient *)parseGradient:(NSXMLElement *)element
gradient:(IJSVGRadialGradient *)gradient
{
// cx defaults to 50% if not specified
NSDictionary * kv = @{@"cx":@"cx",
@"cy":@"cy",
@"r":@"radius"};
for(NSString * key in kv.allKeys) {
NSString * str = [element attributeForName:key].stringValue;
IJSVGUnitLength * unit = nil;
if(str != nil) {
unit = [IJSVGUnitLength unitWithString:str];
} else {
unit = [IJSVGUnitLength unitWithPercentageFloat:.5f];
}
[gradient setValue:unit
forKey:kv[key]];
}
gradient.fx = gradient.cx;
gradient.fy = gradient.cy;
// needs fixing
NSString * fx = [element attributeForName:@"fx"].stringValue;
if(fx != nil) {
if(fx.floatValue < 1.f) {
gradient.fx = [IJSVGUnitLength unitWithPercentageString:fx];
} else {
gradient.fx = [IJSVGUnitLength unitWithString:fx];
}
}
NSString * fy = [element attributeForName:@"fy"].stringValue;
if(fx != nil) {
if(fx.floatValue < 1.f) {
gradient.fy = [IJSVGUnitLength unitWithPercentageString:fy];
} else {
gradient.fy = [IJSVGUnitLength unitWithString:fy];
}
}
if( gradient.gradient != nil ) {
return nil;
}
NSArray * colors = nil;
CGFloat * colorStops = [[self class] computeColorStopsFromString:element colors:&colors];
NSGradient * ret = [[[NSGradient alloc] initWithColors:colors
atLocations:colorStops
colorSpace:IJSVGColor.defaultColorSpace] autorelease];
free(colorStops);
return ret;
}
- (void)drawInContextRef:(CGContextRef)ctx
objectRect:(NSRect)objectRect
absoluteTransform:(CGAffineTransform)absoluteTransform
viewPort:(CGRect)viewBox
{
BOOL inUserSpace = self.units == IJSVGUnitUserSpaceOnUse;
CGFloat radius = self.radius.value;
CGPoint startPoint = CGPointZero;
CGPoint gradientStartPoint = CGPointZero;
CGPoint gradientEndPoint = CGPointZero;
// transforms
CGAffineTransform selfTransform = IJSVGConcatTransforms(self.transforms);
#pragma mark User Space On Use
if(inUserSpace == YES) {
CGFloat rad = radius*2.f;
startPoint = CGPointMake(self.cx.value, self.cy.value);
// work out the new radius
CGRect rect = CGRectMake(startPoint.x, startPoint.y, rad, rad);
rect = CGRectApplyAffineTransform(rect, selfTransform);
rect = CGRectApplyAffineTransform(rect, absoluteTransform);
radius = CGRectGetHeight(rect)/2.f;
gradientStartPoint = startPoint;
gradientEndPoint = CGPointMake(self.fx.value, self.fy.value);
// apply the absolute position
CGContextConcatCTM(ctx, absoluteTransform);
} else {
#pragma mark Object Bounding Box
// compute size based on percentages
CGFloat x = [self.cx computeValue:CGRectGetWidth(objectRect)];
CGFloat y = [self.cy computeValue:CGRectGetHeight(objectRect)];
startPoint = CGPointMake(x, y);
CGFloat val = MIN(CGRectGetWidth(objectRect), CGRectGetHeight(objectRect));
radius = [self.radius computeValue:val];
CGFloat ex = [self.fx computeValue:CGRectGetWidth(objectRect)];
CGFloat ey = [self.fy computeValue:CGRectGetHeight(objectRect)];
gradientEndPoint = CGPointMake(ex, ey);
gradientStartPoint = startPoint;
// transform if width or height is not equal
if(CGRectGetWidth(objectRect) != CGRectGetHeight(objectRect)) {
CGAffineTransform tr = CGAffineTransformMakeTranslation(gradientStartPoint.x,
gradientStartPoint.y);
if(CGRectGetWidth(objectRect) > CGRectGetHeight(objectRect)) {
tr = CGAffineTransformScale(tr, CGRectGetWidth(objectRect)/CGRectGetHeight(objectRect), 1);
} else {
tr = CGAffineTransformScale(tr, 1.f, CGRectGetHeight(objectRect)/CGRectGetWidth(objectRect));
}
tr = CGAffineTransformTranslate(tr, -gradientStartPoint.x, -gradientStartPoint.y);
selfTransform = CGAffineTransformConcat(tr, selfTransform);
}
}
#pragma mark Default drawing
// transform the context
CGContextConcatCTM(ctx, selfTransform);
// draw the gradient
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation|
kCGGradientDrawsAfterEndLocation;
CGContextDrawRadialGradient(ctx, self.CGGradient,
gradientEndPoint, 0, gradientStartPoint,
radius, options);
}
@end