Skip to content

Commit

Permalink
fill-opacity & gradients support
Browse files Browse the repository at this point in the history
  • Loading branch information
djphoenix committed May 13, 2015
1 parent 7783943 commit 7b8e401
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 39 deletions.
2 changes: 2 additions & 0 deletions pxSVG/pxSVGObject.h
Expand Up @@ -16,8 +16,10 @@
@property NSString *id;
@property NSArray *animations;
@property UIColor *fillColor;
@property NSString *fillDef;
@property UIColor *strokeColor;
@property CGFloat strokeWidth;
@property CGFloat opacity;
@property CGFloat fillOpacity;
@property CATransform3D transform;
@end
9 changes: 8 additions & 1 deletion pxSVG/pxSVGObject.m
Expand Up @@ -103,11 +103,18 @@ - (void)loadAttributes:(NSDictionary *)attributes
}
}
self.id = [ma objectForKey:@"id"];
self.fillColor = [self.class colorWithSVGColor:[ma objectForKey:@"fill"]];
if ([[ma objectForKey:@"fill"] hasPrefix:@"url("]) {
NSString *u = [[ma objectForKey:@"fill"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
u = [[u substringWithRange:NSMakeRange(3, u.length-4)] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\n\r\t ()#"]];
self.fillDef = u;
} else self.fillColor = [self.class colorWithSVGColor:[ma objectForKey:@"fill"]];
self.strokeColor = [self.class colorWithSVGColor:[ma objectForKey:@"stroke"]];
self.strokeWidth = [ma objectForKey:@"stroke-width"]?[[ma objectForKey:@"stroke-width"] doubleValue]:NAN;
self.transform = [self.class transformFromString:[ma objectForKey:@"transform"]];
self.opacity = [ma objectForKey:@"opacity"]?[[ma objectForKey:@"opacity"] doubleValue]:1;
CGFloat a = NAN;
if (self.fillColor) [self.fillColor getWhite:nil alpha:&a];
self.fillOpacity = [ma objectForKey:@"fill-opacity"]?[[ma objectForKey:@"fill-opacity"] doubleValue]:a;
}
- (void)setSubnodes:(NSArray *)subnodes { }
@end
180 changes: 142 additions & 38 deletions pxSVG/pxSVGRenderPath.m
Expand Up @@ -10,6 +10,16 @@
#import "pxSVGGroup.h"
#import "pxSVGPath.h"

@interface pxSVGGradient : NSObject
@property CGGradientRef gradient;
@property NSArray *colors;
@property NSArray *locations;
@property CGPoint startPoint, endPoint;
@end

@implementation pxSVGGradient
@end

@interface pxSVGRenderPath ()
@property NSMutableDictionary *defs;
@property pxSVGObject *root;
Expand Down Expand Up @@ -73,44 +83,117 @@ - (CGRect) objBounds:(pxSVGObject*)obj
}
return CGRectNull;
}
- (pxSVGObject*)reuseObjectWithAttributes:(NSDictionary*)attributes
{
NSString *href = [attributes objectForKey:@"xlink:href"];
if (!href) href = [attributes objectForKey:@"href"];
if (!href) return nil;
href = [href substringFromIndex:1];
pxSVGObject *oobj = [self.defs objectForKey:href], *obj;
if (!oobj) return nil;
obj = [oobj.class new];
obj.fillColor = oobj.fillColor;
obj.fillDef = oobj.fillDef;
obj.strokeColor = oobj.strokeColor;
obj.strokeWidth = oobj.strokeWidth;
obj.opacity = oobj.opacity;
obj.fillOpacity = oobj.fillOpacity;
if ([oobj respondsToSelector:@selector(d)])
[(id)obj setD:[(id)oobj d]];
if ([oobj respondsToSelector:@selector(subnodes)])
[(id)obj setSubnodes:[(id)oobj subnodes]];
CATransform3D tr = oobj.transform;
if ([attributes objectForKey:@"x"]) tr = CATransform3DTranslate(tr, [[attributes objectForKey:@"x"] doubleValue], 0, 0);
if ([attributes objectForKey:@"y"]) tr = CATransform3DTranslate(tr, 0, [[attributes objectForKey:@"y"] doubleValue], 0);
if ([attributes objectForKey:@"width"]) {
CGFloat ow = [self objBounds:oobj].size.width, w = [[attributes objectForKey:@"width"] doubleValue];
tr = CATransform3DScale(tr, ow/w, 1, 1);
}
if ([attributes objectForKey:@"height"]) {
CGFloat oh = [self objBounds:oobj].size.width, h = [[attributes objectForKey:@"height"] doubleValue];
tr = CATransform3DScale(tr, 1, oh/h, 1);
}
if ([attributes objectForKey:@"transform"]) tr = CATransform3DConcat(tr, [pxSVGObject transformFromString:[attributes objectForKey:@"transform"]]);
obj.transform = tr;
obj.animations = oobj.animations;
return obj;
}
- (void)parseLinearGradient:(pxXMLNode*)node
{
NSString *gid = [node.attributes objectForKey:@"id"];
if (!gid) return;
NSString *href = [node.attributes objectForKey:@"xlink:href"];
if (!href) href = [node.attributes objectForKey:@"href"];
CGGradientRef gr;
CGPoint sp = CGPointZero, ep = (CGPoint){INFINITY,INFINITY};
NSMutableArray *cls, *locs;
if (href) {
href = [href substringFromIndex:1];
pxSVGGradient *g = [self.defs objectForKey:href];
if (![g isKindOfClass:[pxSVGGradient class]]) return;
gr = g.gradient;
sp = g.startPoint;
ep = g.endPoint;
cls = (id)g.colors;
locs = (id)g.locations;
} else {
cls = [NSMutableArray new], locs = [NSMutableArray new];
CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
for (pxXMLNode *s in node.childNodes) {
if (![s.tagName isEqualToString:@"stop"]) {
NSLog(@"Unknown gradient node: %@",s);
}
NSMutableDictionary *ma = [s.attributes mutableCopy];
if ([s.attributes objectForKey:@"style"]) {
for (NSString *ss in [[s.attributes objectForKey:@"style"] componentsSeparatedByString:@";"]) {
NSUInteger sep = [ss rangeOfString:@":"].location;
if (sep == NSNotFound) continue;
NSString *k = [[ss substringToIndex:sep] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSString *v = [[ss substringFromIndex:sep+1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[ma setObject:v forKey:k];
}
}
if ([ma objectForKey:@"stop-color"] && [ma objectForKey:@"offset"]) {
UIColor *cl = [pxSVGObject colorWithSVGColor:[ma objectForKey:@"stop-color"]];
if ([ma objectForKey:@"stop-opacity"])
cl = [cl colorWithAlphaComponent:[[ma objectForKey:@"stop-opacity"] doubleValue]];
[cls addObject:(__bridge id)cl.CGColor];
[locs addObject:@([[ma objectForKey:@"offset"] doubleValue])];
}
}
CGFloat *locs_a = CFAllocatorAllocate(CFAllocatorGetDefault(), (sizeof(CGFloat)*locs.count), 0);
for (NSUInteger i=0; i<locs.count; i++) {
locs_a[i] = [locs[i] doubleValue];
}
gr = CGGradientCreateWithColors(cs, (__bridge CFArrayRef)cls, locs_a);
CFAllocatorDeallocate(CFAllocatorGetDefault(), locs_a);
CGColorSpaceRelease(cs);
}
if ([node.attributes objectForKey:@"x1"]) sp.x = [[node.attributes objectForKey:@"x1"] doubleValue];
if ([node.attributes objectForKey:@"y1"]) sp.y = [[node.attributes objectForKey:@"y1"] doubleValue];
if ([node.attributes objectForKey:@"x2"]) ep.x = [[node.attributes objectForKey:@"x2"] doubleValue];
if ([node.attributes objectForKey:@"y2"]) ep.y = [[node.attributes objectForKey:@"y2"] doubleValue];
if (gr) {
pxSVGGradient *g = [pxSVGGradient new];
g.gradient = gr;
g.colors = cls;
g.locations = locs;
g.startPoint = sp;
g.endPoint = ep;
[self.defs setObject:g forKey:gid];
}
}
- (pxSVGObject*)parseObject:(pxXMLNode*)node inheritAttributes:(pxSVGObject*)inherit
{
if ([node.tagName rangeOfString:@":"].location != NSNotFound) return nil;
if ([node.tagName isEqualToString:@"metadata"]) return nil;
if ([node.tagName isEqualToString:@"marker"]) return nil;
if ([node.tagName isEqualToString:@"filter"]) return nil;
if ([node.tagName isEqualToString:@"a"]) return nil;
if ([node.tagName isEqualToString:@"use"]) {
NSString *href = [node.attributes objectForKey:@"xlink:href"];
if (!href) href = [node.attributes objectForKey:@"href"];
if (!href) return nil;
href = [href substringFromIndex:1];
pxSVGObject *oobj = [self.defs objectForKey:href], *obj;
if (!oobj) return nil;
obj = [oobj.class new];
obj.fillColor = oobj.fillColor;
obj.strokeColor = oobj.strokeColor;
obj.strokeWidth = oobj.strokeWidth;
obj.opacity = oobj.opacity;
if ([oobj respondsToSelector:@selector(d)])
[(id)obj setD:[(id)oobj d]];
if ([oobj respondsToSelector:@selector(subnodes)])
[(id)obj setSubnodes:[(id)oobj subnodes]];
CATransform3D tr = oobj.transform;
if ([node.attributes objectForKey:@"x"]) tr = CATransform3DTranslate(tr, [[node.attributes objectForKey:@"x"] doubleValue], 0, 0);
if ([node.attributes objectForKey:@"y"]) tr = CATransform3DTranslate(tr, 0, [[node.attributes objectForKey:@"y"] doubleValue], 0);
if ([node.attributes objectForKey:@"width"]) {
CGFloat ow = [self objBounds:oobj].size.width, w = [[node.attributes objectForKey:@"width"] doubleValue];
tr = CATransform3DScale(tr, ow/w, 1, 1);
}
if ([node.attributes objectForKey:@"height"]) {
CGFloat oh = [self objBounds:oobj].size.width, h = [[node.attributes objectForKey:@"height"] doubleValue];
tr = CATransform3DScale(tr, 1, oh/h, 1);
}
if ([node.attributes objectForKey:@"transform"]) tr = CATransform3DConcat(tr, [pxSVGObject transformFromString:[node.attributes objectForKey:@"transform"]]);
obj.transform = tr;
obj.animations = oobj.animations;
return obj;
if ([node.tagName isEqualToString:@"use"]) return [self reuseObjectWithAttributes:node.attributes];
if ([node.tagName isEqualToString:@"linearGradient"]) {
[self parseLinearGradient:node];
return nil;
}
Class objClass = pxSVGObject.class;
if ([node.tagName isEqualToString:@"g"])
Expand All @@ -133,11 +216,13 @@ - (pxSVGObject*)parseObject:(pxXMLNode*)node inheritAttributes:(pxSVGObject*)inh
pxSVGObject *obj = [objClass new];
[obj loadAttributes:node.attributes];
if (!obj.fillColor)
obj.fillColor = inherit.fillColor;
if (obj.strokeWidth == NAN)
obj.strokeWidth = inherit.strokeWidth;
obj.fillColor = inherit?inherit.fillColor:[UIColor blackColor];
if (isnan(obj.strokeWidth))
obj.strokeWidth = inherit?inherit.strokeWidth:0;
if (isnan(obj.fillOpacity))
obj.fillOpacity = inherit?inherit.fillOpacity:1;
if (!obj.strokeColor)
obj.strokeColor = inherit.strokeColor;
obj.strokeColor = inherit?inherit.strokeColor:nil;
if (obj.id) [self.defs setObject:obj forKey:obj.id];
if (node.childNodes.count) {
NSMutableArray *subnodes = [NSMutableArray new];
Expand All @@ -153,13 +238,32 @@ - (pxSVGObject*)parseObject:(pxXMLNode*)node inheritAttributes:(pxSVGObject*)inh
}
- (CALayer *)makeLayerWithNode:(pxSVGObject*)node
{
CALayer *l;
CALayer *l, *fl;
if ([node respondsToSelector:@selector(d)]) {
CAShapeLayer *sl = [CAShapeLayer new];
sl.path = [(id)node d].CGPath;
sl.fillColor = (node.fillColor?:[UIColor blackColor]).CGColor;
if (node.fillDef) {
sl.fillColor = [UIColor clearColor].CGColor;
id def = [self.defs objectForKey:node.fillDef];
if ([def isKindOfClass:[pxSVGGradient class]]) {
CAGradientLayer *gl = [CAGradientLayer new];
gl.frame = [(id)node d].bounds;
gl.startPoint = (CGPoint){([def startPoint].x-gl.frame.origin.x)/gl.frame.size.width,([def startPoint].y-gl.frame.origin.y)/gl.frame.size.height};
gl.endPoint = (CGPoint){([def endPoint].x-gl.frame.origin.x)/gl.frame.size.width,([def endPoint].y-gl.frame.origin.y)/gl.frame.size.height};
gl.locations = [def locations];
gl.colors = [def colors];
gl.opacity = isnan(node.fillOpacity)?1:node.fillOpacity;
CAShapeLayer *ml = [CAShapeLayer new];
ml.frame = (CGRect){{0,0},{gl.frame.size.width+gl.frame.origin.x,gl.frame.size.height+gl.frame.origin.y}};
ml.path = sl.path;
gl.mask = ml;
[sl addSublayer:gl];
fl = gl;
}
} else
sl.fillColor = [(node.fillColor?:[UIColor blackColor]) colorWithAlphaComponent:isnan(node.fillOpacity)?1:node.fillOpacity].CGColor;
sl.strokeColor = node.strokeColor.CGColor;
sl.lineWidth = node.strokeWidth==NAN?0:node.strokeWidth;
sl.lineWidth = isnan(node.strokeWidth)?0:node.strokeWidth;
l = sl;
} else {
l = [CALayer new];
Expand Down

0 comments on commit 7b8e401

Please sign in to comment.