commitffb55b0f31Author: Curtis Hard <curtishard@me.com> Date: Fri Mar 2 13:28:40 2018 +0000 Fixes viewBox origin translate commita4c032afa2Author: Curtis Hard <curtishard@me.com> Date: Wed Feb 28 21:29:11 2018 +0000 Added clip to viewport commitabe8f4cba5Author: Curtis Hard <curtishard@me.com> Date: Wed Feb 28 21:00:20 2018 +0000 Fixes drawInRect not obeying origin commita16842271bAuthor: Curtis Hard <curtishard@me.com> Date: Mon Feb 26 13:46:41 2018 +0000 Resolves namespaces correctly + added common HTML list to be parsed as groups commit58126e06e4Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 25 21:58:49 2018 +0000 Fixes! commit5af5e054abAuthor: Curtis Hard <curtishard@me.com> Date: Fri Feb 23 22:37:53 2018 +0000 Fixed defNode being removed commitedd3aa1f33Author: Curtis Hard <curtishard@me.com> Date: Fri Feb 23 16:29:54 2018 +0000 excluded various elements from diff commit3366bc4fa5Author: Curtis Hard <curtishard@me.com> Date: Fri Feb 23 14:05:03 2018 +0000 Better optimaztion commit104002183bAuthor: Curtis Hard <curtishard@me.com> Date: Thu Feb 22 22:17:50 2018 +0000 Added rudimenatry inline styles -> stylesheet commit7010b7ea50Author: Curtis Hard <curtishard@me.com> Date: Thu Feb 22 13:33:32 2018 +0000 Correct order of cleanup commit5266a8c07aAuthor: Curtis Hard <curtishard@me.com> Date: Thu Feb 22 08:22:17 2018 +0000 corrent length of string commitcaf55e8bdfAuthor: Curtis Hard <curtishard@me.com> Date: Wed Feb 21 20:45:17 2018 +0000 removes useless def if required commit8160d05ebaAuthor: Curtis Hard <curtishard@me.com> Date: Tue Feb 20 14:12:02 2018 +0000 Refactor of a few methods commita6d6a06521Author: Curtis Hard <curtishard@me.com> Date: Tue Feb 20 09:40:21 2018 +0000 Added collpasing of gradients commit103a4d71f6Author: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 19:07:30 2018 +0000 Reduced floats even more commit98874b1d2cAuthor: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 18:53:02 2018 +0000 More compression goodness commite742db31e0Author: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 14:02:15 2018 +0000 Added intermediateParent commit198fd09f07Author: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 11:51:37 2018 +0000 Fixes! and performance increases commit3493194b1bAuthor: Curtis Hard <curtishard@me.com> Date: Mon Feb 19 08:20:02 2018 +0000 Scale computation commit0016775eafAuthor: Curtis Hard <curtishard@me.com> Date: Sun Feb 18 22:33:26 2018 +0000 More goodness commit304a04cc22Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 18 22:32:04 2018 +0000 Vastly improved the exporter commite4fd0af582Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 18 15:20:28 2018 +0000 This is insanely important! commit69a2a0c97eAuthor: Curtis Hard <curtishard@me.com> Date: Sun Feb 4 22:02:19 2018 +0000 Refactor commit5299bb0479Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 4 21:58:21 2018 +0000 will continue to use CoreAnimation for the time being commit4f1943cad1Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 4 21:57:46 2018 +0000 Fixes gradient strokes commitf02d186293Author: Curtis Hard <curtishard@me.com> Date: Sun Feb 4 11:15:04 2018 +0000 trying to get masks to work commit3291718cfbAuthor: Curtis Hard <curtishard@me.com> Date: Fri Feb 2 22:35:22 2018 +0000 Beginning of quartz renderer commit6fbaaf5884Author: Curtis Hard <curtishard@me.com> Date: Mon Jan 29 22:32:08 2018 +0000 Removed useless log commitabc65797eaAuthor: Curtis Hard <curtishard@me.com> Date: Mon Jan 29 21:37:45 2018 +0000 I think gradients work :D commitaf5a1c2718Author: Curtis Hard <curtishard@me.com> Date: Sun Jan 28 22:18:02 2018 +0000 Possible fx and fy things… commitfb9a5282b9Author: Curtis Hard <curtishard@me.com> Date: Sun Jan 28 19:22:40 2018 +0000 Even more gradient fixes commitd83933a103Author: Curtis Hard <curtishard@me.com> Date: Sun Jan 28 14:21:50 2018 +0000 Various improvements commitbd7a0d5021Author: Curtis Hard <curtishard@me.com> Date: Sat Jan 27 22:26:33 2018 +0000 Start to linear commita9a038568cAuthor: Curtis Hard <curtishard@me.com> Date: Sat Jan 27 21:14:25 2018 +0000 This kind of actually works... commit77fbb38b6fAuthor: Curtis Hard <curtishard@me.com> Date: Sat Jan 27 20:31:36 2018 +0000 I guess this could be a good start? Posssible start of fixes? commit12c3191569Author: Curtis Hard <curtishard@me.com> Date: Sat Jan 27 15:32:15 2018 +0000 Added method for findind absolute position commite3e9626ef7Author: Curtis Hard <curtishard@me.com> Date: Sat Jan 27 14:25:03 2018 +0000 Fixes crash due to parentNode on temp groups being released commit1160d89f16Author: Curtis Hard <curtishard@me.com> Date: Fri Jan 26 22:29:22 2018 +0000 Fixes use statements with transforms commit1575cbfde8Author: Curtis Hard <curtishard@me.com> Date: Fri Jan 26 18:16:03 2018 +0000 Moved color tree over to modern syntax commit5c4c2eee91Author: Curtis Hard <curtishard@me.com> Date: Thu Jan 25 18:23:53 2018 +0000 Added HSL/HSLA support commit087b13e58fAuthor: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 22:24:43 2018 +0000 Various image loading issues resolved from base64 images commit1183e167aaAuthor: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 21:21:41 2018 +0000 Rect issue fix commit4dbfc59437Author: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 21:19:24 2018 +0000 Moved transforms over from being concatinated to seperate calls commit409bd509faAuthor: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 20:30:38 2018 +0000 Fixes color issue commit8ae1d1b4e0Author: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 18:41:08 2018 +0000 Removed check as its not needed here commit7243fbe5ffAuthor: Curtis Hard <curtishard@me.com> Date: Wed Jan 24 18:35:43 2018 +0000 Added excludeAttributes list to parseCommonAttributes added x and y to that list for rect commit51b9a5e85fAuthor: Curtis Hard <curtishard@me.com> Date: Tue Jan 23 19:53:04 2018 +0000 Added isSubcommand to IJSVGCommand - Fixes move command going awol when preceding move commands are not subcommands (woah) commit40098589deAuthor: Curtis Hard <curtishard@me.com> Date: Mon Jan 22 22:04:48 2018 +0000 removed reverseObjectEnumerator IJSVGTransform already deals with this at parse stage (was a test from earlier), spec states transforms are applied in reverse order (which parser already dealth with :-)) commit7cb96b21f2Author: Curtis Hard <curtishard@me.com> Date: Mon Jan 22 22:01:56 2018 +0000 Removed use of origin here as its computed in apply defaults commit1bff7c6970Author: Curtis Hard <curtishard@me.com> Date: Mon Jan 22 21:48:07 2018 +0000 Various fixes… still going…
248 lines
8.3 KiB
Objective-C
248 lines
8.3 KiB
Objective-C
//
|
|
// IJSVGRenderer.m
|
|
// IJSVGExample
|
|
//
|
|
// Created by Curtis Hard on 31/01/2018.
|
|
// Copyright © 2018 Curtis Hard. All rights reserved.
|
|
//
|
|
|
|
#import "IJSVGQuartzRenderer.h"
|
|
#import "IJSVG.h"
|
|
#import "IJSVGLayerTree.h"
|
|
#import "IJSVGGradientLayer.h"
|
|
#import "IJSVGStrokeLayer.h"
|
|
|
|
@implementation IJSVGQuartzRenderer
|
|
|
|
@synthesize scale, viewPort, backingScale;
|
|
|
|
|
|
- (void)renderLayer:(IJSVGLayer *)layer
|
|
inContext:(CGContextRef)ctx
|
|
{
|
|
// if layer is hidden, no need to continue
|
|
// trying to render it, its useless!
|
|
if(layer.hidden == YES) {
|
|
return;
|
|
}
|
|
|
|
// check which layers can actually be rendered
|
|
if(layer.class == IJSVGStrokeLayer.class ||
|
|
layer.class == IJSVGGradientLayer.class) {
|
|
return;
|
|
}
|
|
|
|
CGContextSaveGState(ctx);
|
|
|
|
// apply any transfroms so children also transform too
|
|
CGContextConcatCTM(ctx, layer.affineTransform);
|
|
CGContextSetAlpha(ctx, layer.opacity);
|
|
|
|
// is there a mask?!
|
|
if(layer.mask != nil) {
|
|
IJSVGLayer * maskLayer = (IJSVGLayer *)layer.mask;
|
|
CGRect rect;
|
|
CGImageRef maskImage = [self newMaskedImageForLayer:maskLayer
|
|
proposedRect:&rect];
|
|
CGRect maskRect = CGRectMake(0, 0, rect.size.width, rect.size.height);
|
|
CGContextClipToMask(ctx, maskRect, maskImage);
|
|
CGContextTranslateCTM(ctx, rect.origin.x, rect.origin.y);
|
|
CGImageRelease(maskImage);
|
|
}
|
|
|
|
// render itself
|
|
[self _renderLayer:layer
|
|
inContext:ctx];
|
|
|
|
// render the children recursively
|
|
for(IJSVGLayer * sublayer in layer.sublayers) {
|
|
[self renderLayer:sublayer
|
|
inContext:ctx];
|
|
}
|
|
CGContextRestoreGState(ctx);
|
|
}
|
|
|
|
- (void)_renderLayer:(IJSVGLayer *)layer
|
|
inContext:(CGContextRef)ctx
|
|
{
|
|
// is a shape layer
|
|
if(layer.class == IJSVGShapeLayer.class) {
|
|
// move the path
|
|
CGContextTranslateCTM(ctx, layer.frame.origin.x,
|
|
layer.frame.origin.y);
|
|
|
|
// find the shape
|
|
IJSVGShapeLayer * shape = (IJSVGShapeLayer *)layer;
|
|
CGPathRef path = shape.path;
|
|
|
|
// has just a plain fill color
|
|
CGContextSaveGState(ctx); {
|
|
|
|
CGContextAddPath(ctx, path);
|
|
if([shape.fillRule isEqualToString:kCAFillRuleEvenOdd]) {
|
|
CGContextEOClip(ctx);
|
|
} else {
|
|
CGContextClip(ctx);
|
|
}
|
|
|
|
CGContextAddPath(ctx, path);
|
|
if(shape.gradientFillLayer == nil) {
|
|
if(shape.fillColor != nil) {
|
|
CGContextSetFillColorWithColor(ctx, shape.fillColor);
|
|
CGContextFillPath(ctx);
|
|
}
|
|
// has a gradient fill
|
|
} else if(shape.gradientFillLayer != nil) {
|
|
CGContextSaveGState(ctx); {
|
|
CGContextSetAlpha(ctx, shape.gradientFillLayer.opacity);
|
|
[shape.gradientFillLayer drawInContext:ctx];
|
|
} CGContextRestoreGState(ctx);
|
|
}
|
|
} CGContextRestoreGState(ctx);
|
|
|
|
// stroke must be done outside of the fill due to
|
|
// clipping path will cause it to render incorrectly
|
|
CGContextSaveGState(ctx); {
|
|
|
|
// any stroke?
|
|
if(shape.strokeLayer != nil) {
|
|
IJSVGStrokeLayer * strokeLayer = (IJSVGStrokeLayer *)shape.strokeLayer;
|
|
if(strokeLayer.strokeColor != nil) {
|
|
CGContextSaveGState(ctx); {
|
|
|
|
// set opacity
|
|
CGContextSetAlpha(ctx, strokeLayer.opacity);
|
|
|
|
CGContextSetLineCap(ctx, [self.class lineCapFromLayer:strokeLayer]);
|
|
CGContextSetLineJoin(ctx, [self.class lineJoinFromLayer:strokeLayer]);
|
|
|
|
// are there any line dashes?
|
|
NSArray * dash = strokeLayer.lineDashPattern;
|
|
CGFloat * lengths = (CGFloat *)malloc(sizeof(CGFloat)*dash.count);
|
|
NSInteger i = 0;
|
|
for(NSNumber * number in dash) {
|
|
lengths[i++] = number.floatValue;
|
|
}
|
|
CGContextSetLineDash(ctx, strokeLayer.lineDashPhase,
|
|
lengths, dash.count);
|
|
free(lengths);
|
|
|
|
// get bounding box of the current path
|
|
CGContextAddPath(ctx, strokeLayer.path);
|
|
CGContextSetLineWidth(ctx, strokeLayer.lineWidth);
|
|
CGContextSetStrokeColorWithColor(ctx, strokeLayer.strokeColor);
|
|
CGContextStrokePath(ctx);
|
|
|
|
} CGContextRestoreGState(ctx);
|
|
}
|
|
} else {
|
|
CGContextSetLineWidth(ctx, 0.f);
|
|
CGContextStrokePath(ctx);
|
|
}
|
|
|
|
} CGContextRestoreGState(ctx);
|
|
|
|
}
|
|
}
|
|
|
|
+ (CGLineJoin)lineJoinFromLayer:(IJSVGShapeLayer *)layer
|
|
{
|
|
if([layer.lineJoin isEqualToString:kCALineJoinBevel]) {
|
|
return kCGLineJoinBevel;
|
|
} else if([layer.lineJoin isEqualToString:kCALineJoinMiter]) {
|
|
return kCGLineJoinMiter;
|
|
}
|
|
return kCGLineJoinRound;
|
|
}
|
|
|
|
+ (CGLineCap)lineCapFromLayer:(IJSVGShapeLayer *)layer
|
|
{
|
|
if([layer.lineCap isEqualToString:kCALineCapButt]) {
|
|
return kCGLineCapButt;
|
|
} else if([layer.lineCap isEqualToString:kCALineCapRound]) {
|
|
return kCGLineCapRound;
|
|
}
|
|
return kCGLineCapSquare;
|
|
}
|
|
|
|
+ (CGRect)findFrameForLayer:(IJSVGLayer *)layer
|
|
{
|
|
CGRect rect = layer.frame;
|
|
return [self _recursivelyFindFrameForLayer:layer
|
|
rect:rect];
|
|
}
|
|
|
|
+ (CGRect)_recursivelyFindFrameForLayer:(IJSVGLayer *)layer
|
|
rect:(CGRect)rect
|
|
{
|
|
CGRect frame = layer.frame;
|
|
if(CGRectGetMinX(frame) < CGRectGetMinX(rect)) {
|
|
rect.origin.x = CGRectGetMinX(frame);
|
|
}
|
|
if(CGRectGetMinY(frame) < CGRectGetMinY(rect)) {
|
|
rect.origin.y = CGRectGetMinY(frame);
|
|
}
|
|
if(CGRectGetMaxX(frame) > CGRectGetMaxX(rect)) {
|
|
rect.size.width = CGRectGetMaxX(frame);
|
|
}
|
|
if(CGRectGetMaxY(frame) > CGRectGetMaxY(rect)) {
|
|
rect.size.height = CGRectGetMaxY(frame);
|
|
}
|
|
for(IJSVGLayer * sublayer in layer.sublayers) {
|
|
rect = [self _recursivelyFindFrameForLayer:sublayer
|
|
rect:rect];
|
|
}
|
|
return rect;
|
|
}
|
|
|
|
- (CGRect)convertLayerFrame:(CGRect)rect
|
|
{
|
|
rect.size.width = fabs(rect.origin.x - rect.size.width);
|
|
rect.size.height = fabs(rect.origin.y - rect.size.height);
|
|
rect.origin.x = rect.origin.y = 0.f;
|
|
return rect;
|
|
}
|
|
|
|
- (CGImageRef)newImageForLayer:(IJSVGLayer *)layer
|
|
colorSpace:(CGColorSpaceRef)colorSpace
|
|
proposedRect:(CGRect *)proposedRect
|
|
{
|
|
// create color space and new context
|
|
NSRect cRect = [self.class findFrameForLayer:layer];
|
|
NSRect convertedSize = [self convertLayerFrame:cRect];
|
|
CGSize size = cRect.size;
|
|
*proposedRect = cRect;
|
|
CGFloat actualScale = self.scale * self.backingScale;
|
|
CGContextRef ctx = CGBitmapContextCreate(NULL, size.width * actualScale,
|
|
size.height * actualScale,
|
|
8, 0, colorSpace,
|
|
kCGImageAlphaPremultipliedLast);
|
|
|
|
CGContextScaleCTM(ctx, actualScale, actualScale);
|
|
|
|
// render the layer tree into this context
|
|
[self renderLayer:layer
|
|
inContext:ctx];
|
|
|
|
// grab image from it
|
|
CGImageRef image = CGBitmapContextCreateImage(ctx);
|
|
|
|
// clean memory
|
|
CGContextRelease(ctx);
|
|
return image;
|
|
}
|
|
|
|
- (CGImageRef)newMaskedImageForLayer:(IJSVGLayer *)layer
|
|
proposedRect:(CGRect *)proposedRect
|
|
{
|
|
// create color space and new context
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
|
|
CGImageRef image = [self newImageForLayer:layer
|
|
colorSpace:colorSpace
|
|
proposedRect:proposedRect];
|
|
CGColorSpaceRelease(colorSpace);
|
|
return image;
|
|
}
|
|
|
|
@end
|