Compare commits

..

50 Commits

Author SHA1 Message Date
Curtis Hard ffb55b0f31 Fixes viewBox origin translate 2018-03-02 13:28:40 +00:00
Curtis Hard a4c032afa2 Added clip to viewport 2018-02-28 21:29:11 +00:00
Curtis Hard abe8f4cba5 Fixes drawInRect not obeying origin 2018-02-28 21:00:20 +00:00
Curtis Hard a16842271b Resolves namespaces correctly + added common HTML list to be parsed as groups 2018-02-26 13:46:41 +00:00
Curtis Hard 58126e06e4 Fixes! 2018-02-25 21:58:49 +00:00
Curtis Hard 5af5e054ab Fixed defNode being removed 2018-02-23 22:37:53 +00:00
Curtis Hard edd3aa1f33 excluded various elements from diff 2018-02-23 16:29:54 +00:00
Curtis Hard 3366bc4fa5 Better optimaztion 2018-02-23 14:05:03 +00:00
Curtis Hard 104002183b Added rudimenatry inline styles -> stylesheet 2018-02-22 22:17:50 +00:00
Curtis Hard 7010b7ea50 Correct order of cleanup 2018-02-22 13:33:32 +00:00
Curtis Hard 5266a8c07a corrent length of string 2018-02-22 08:22:17 +00:00
Curtis Hard caf55e8bdf removes useless def if required 2018-02-21 20:45:17 +00:00
Curtis Hard 8160d05eba Refactor of a few methods 2018-02-20 14:12:02 +00:00
Curtis Hard a6d6a06521 Added collpasing of gradients 2018-02-20 09:40:21 +00:00
Curtis Hard 103a4d71f6 Reduced floats even more 2018-02-19 19:07:30 +00:00
Curtis Hard 98874b1d2c More compression goodness 2018-02-19 18:53:02 +00:00
Curtis Hard e742db31e0 Added intermediateParent 2018-02-19 14:02:15 +00:00
Curtis Hard 198fd09f07 Fixes! and performance increases 2018-02-19 11:51:37 +00:00
Curtis Hard 3493194b1b Scale computation 2018-02-19 08:20:02 +00:00
Curtis Hard 0016775eaf More goodness 2018-02-18 22:33:26 +00:00
Curtis Hard 304a04cc22 Vastly improved the exporter 2018-02-18 22:32:04 +00:00
Curtis Hard e4fd0af582 This is insanely important! 2018-02-18 15:20:28 +00:00
Curtis Hard 69a2a0c97e Refactor 2018-02-04 22:02:19 +00:00
Curtis Hard 5299bb0479 will continue to use CoreAnimation for the time being 2018-02-04 21:58:21 +00:00
Curtis Hard 4f1943cad1 Fixes gradient strokes 2018-02-04 21:57:46 +00:00
Curtis Hard f02d186293 trying to get masks to work 2018-02-04 11:15:04 +00:00
Curtis Hard 3291718cfb Beginning of quartz renderer 2018-02-02 22:35:22 +00:00
Curtis Hard 6fbaaf5884 Removed useless log 2018-01-29 22:32:08 +00:00
Curtis Hard abc65797ea I think gradients work :D 2018-01-29 21:37:45 +00:00
Curtis Hard af5a1c2718 Possible fx and fy things… 2018-01-28 22:18:02 +00:00
Curtis Hard fb9a5282b9 Even more gradient fixes 2018-01-28 19:22:40 +00:00
Curtis Hard d83933a103 Various improvements 2018-01-28 14:21:50 +00:00
Curtis Hard bd7a0d5021 Start to linear 2018-01-27 22:26:33 +00:00
Curtis Hard a9a038568c This kind of actually works... 2018-01-27 21:14:25 +00:00
Curtis Hard 77fbb38b6f I guess this could be a good start?
Posssible start of fixes?
2018-01-27 20:32:13 +00:00
Curtis Hard 12c3191569 Added method for findind absolute position 2018-01-27 15:32:15 +00:00
Curtis Hard e3e9626ef7 Fixes crash due to parentNode on temp groups being released 2018-01-27 14:25:03 +00:00
Curtis Hard 1160d89f16 Fixes use statements with transforms 2018-01-26 22:29:22 +00:00
Curtis Hard 1575cbfde8 Moved color tree over to modern syntax 2018-01-26 18:16:03 +00:00
Curtis Hard 5c4c2eee91 Added HSL/HSLA support 2018-01-25 18:23:53 +00:00
Curtis Hard 087b13e58f Various image loading issues resolved from base64 images 2018-01-24 22:24:43 +00:00
Curtis Hard 1183e167aa Rect issue fix 2018-01-24 21:21:41 +00:00
Curtis Hard 4dbfc59437 Moved transforms over from being concatinated to seperate calls 2018-01-24 21:19:24 +00:00
Curtis Hard 409bd509fa Fixes color issue 2018-01-24 20:30:38 +00:00
Curtis Hard 8ae1d1b4e0 Removed check as its not needed here 2018-01-24 18:41:08 +00:00
Curtis Hard 7243fbe5ff Added excludeAttributes list to parseCommonAttributes
added x and y to that list for rect
2018-01-24 18:35:43 +00:00
Curtis Hard 51b9a5e85f Added isSubcommand to IJSVGCommand
- Fixes move command going awol when preceding move commands are not subcommands (woah)
2018-01-23 19:53:04 +00:00
Curtis Hard 40098589de 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 :-))
2018-01-22 22:04:48 +00:00
Curtis Hard 7cb96b21f2 Removed use of origin here as its computed in apply defaults 2018-01-22 22:01:56 +00:00
Curtis Hard 1bff7c6970 Various fixes… still going… 2018-01-22 21:48:59 +00:00
48 changed files with 820 additions and 756 deletions
@@ -53,9 +53,6 @@
59B93C6F19B7D32C0063E823 /* products.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59B93C6E19B7D32C0063E823 /* products.svg */; };
59D1E39E2022577500C54672 /* IJSVGQuartzRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59D1E39D2022577500C54672 /* IJSVGQuartzRenderer.m */; };
59D1E3A0202279CA00C54672 /* Group.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59D1E39F202279CA00C54672 /* Group.svg */; };
59D6C4142049939800F16F13 /* move.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59D6C4132049939800F16F13 /* move.svg */; };
59D6C4162049947E00F16F13 /* sprite-1.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59D6C4152049947E00F16F13 /* sprite-1.svg */; };
59D6C4182049977E00F16F13 /* fa-brands.svg in Resources */ = {isa = PBXBuildFile; fileRef = 59D6C4172049977E00F16F13 /* fa-brands.svg */; };
59E0F5ED1E29964700F757F7 /* IJSVGUnitLength.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E0F5EC1E29964700F757F7 /* IJSVGUnitLength.m */; };
59E2645119BA240D008A6FDB /* IJSVG.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E2641C19BA240D008A6FDB /* IJSVG.m */; };
59E2645219BA240D008A6FDB /* IJSVGBezierPathAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E2641E19BA240D008A6FDB /* IJSVGBezierPathAdditions.m */; };
@@ -181,9 +178,6 @@
59D1E39C2022577500C54672 /* IJSVGQuartzRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGQuartzRenderer.h; sourceTree = "<group>"; };
59D1E39D2022577500C54672 /* IJSVGQuartzRenderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGQuartzRenderer.m; sourceTree = "<group>"; };
59D1E39F202279CA00C54672 /* Group.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Group.svg; sourceTree = "<group>"; };
59D6C4132049939800F16F13 /* move.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = move.svg; sourceTree = "<group>"; };
59D6C4152049947E00F16F13 /* sprite-1.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "sprite-1.svg"; sourceTree = "<group>"; };
59D6C4172049977E00F16F13 /* fa-brands.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "fa-brands.svg"; sourceTree = "<group>"; };
59E0F5EB1E29964700F757F7 /* IJSVGUnitLength.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVGUnitLength.h; sourceTree = "<group>"; };
59E0F5EC1E29964700F757F7 /* IJSVGUnitLength.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGUnitLength.m; sourceTree = "<group>"; };
59E2641B19BA240D008A6FDB /* IJSVG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IJSVG.h; sourceTree = "<group>"; };
@@ -321,13 +315,11 @@
5956657A19B62F4600D805FF /* Supporting Files */ = {
isa = PBXGroup;
children = (
59D6C4172049977E00F16F13 /* fa-brands.svg */,
59D1E39F202279CA00C54672 /* Group.svg */,
590C87F5201FD0D4004A1554 /* car.svg */,
590C87F3201FC9E3004A1554 /* radialgradient2.svg */,
590C87F0201FBF27004A1554 /* AJ_Digital_Camera.svg */,
590C87EC201FA08C004A1554 /* intertwingly.svg */,
59D6C4152049947E00F16F13 /* sprite-1.svg */,
590C87EE201FA092004A1554 /* NewTux.svg */,
590C87EA201F9888004A1554 /* json.svg */,
5991A2AA201E310200913E3B /* heart.svg */,
@@ -340,7 +332,6 @@
595665DD19B6309C00D805FF /* test.svg */,
5986308619BA104800CF15EA /* linecap.svg */,
5986308B19BA180E00CF15EA /* dashed.svg */,
59D6C4132049939800F16F13 /* move.svg */,
5956657B19B62F4600D805FF /* Info.plist */,
5956657C19B62F4600D805FF /* main.m */,
5956659A19B62F9500D805FF /* IJSVGExample-Prefix.pch */,
@@ -568,10 +559,8 @@
59B93C6D19B7D1840063E823 /* paperplane.svg in Resources */,
5956658219B62F4600D805FF /* Images.xcassets in Resources */,
590C87ED201FA08C004A1554 /* intertwingly.svg in Resources */,
59D6C4182049977E00F16F13 /* fa-brands.svg in Resources */,
590C87F1201FBF27004A1554 /* AJ_Digital_Camera.svg in Resources */,
590C87EF201FA093004A1554 /* NewTux.svg in Resources */,
59D6C4162049947E00F16F13 /* sprite-1.svg in Resources */,
59265CE81C4F840400F333F0 /* css.svg in Resources */,
595665DE19B6309C00D805FF /* test.svg in Resources */,
590C87EB201F9888004A1554 /* json.svg in Resources */,
@@ -584,7 +573,6 @@
590C87F4201FC9E4004A1554 /* radialgradient2.svg in Resources */,
59459CEC19B906FE00CE493B /* clipped.svg in Resources */,
590C87F6201FD0D4004A1554 /* car.svg in Resources */,
59D6C4142049939800F16F13 /* move.svg in Resources */,
5991A2AB201E310200913E3B /* heart.svg in Resources */,
5986308C19BA180E00CF15EA /* dashed.svg in Resources */,
);
@@ -678,9 +678,31 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView misplaced="YES" id="hhC-1S-CCX" customClass="SVGView">
<rect key="frame" x="0.0" y="0.0" width="600" height="479"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<rect key="frame" x="0.0" y="0.0" width="600" height="385"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
</customView>
<button verticalHuggingPriority="750" misplaced="YES" id="gpW-KV-dZb">
<rect key="frame" x="14" y="431" width="205" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Render with CoreAnimation" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="b5N-FF-ykt">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="switchToCoreAnimation:" target="hhC-1S-CCX" id="318-qP-1z4"/>
</connections>
</button>
<button verticalHuggingPriority="750" misplaced="YES" id="68n-xB-I0F">
<rect key="frame" x="14" y="398" width="205" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Render with CoreGraphics" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Tcq-b5-8Y0">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="switchToCoreGraphics:" target="hhC-1S-CCX" id="gzP-YM-P2p"/>
</connections>
</button>
</subviews>
</view>
<point key="canvasLocation" x="98" y="496.5"/>
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="447px" height="336px" viewBox="0 0 447 336" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
<title>Rectangle</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#1E54F6" offset="0%"></stop>
<stop stop-color="#EE2323" offset="100%"></stop>
</linearGradient>
<rect id="path-2" x="0" y="0" width="447" height="336"></rect>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Rectangle">
<use fill="#D8D8D8" fill-rule="evenodd" xlink:href="#path-2"></use>
<rect stroke="url(#linearGradient-1)" stroke-width="10" x="5" y="5" width="437" height="326"></rect>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 992 B

+3 -1
View File
@@ -11,9 +11,11 @@
@interface SVGView : NSView {
@private
IJSVG * svg;
}
- (IBAction)switchToCoreGraphics:(id)sender;
- (IBAction)switchToCoreAnimation:(id)sender;
@end
+14 -3
View File
@@ -22,17 +22,28 @@
if( ( self = [super initWithFrame:frameRect] ) != nil )
{
svg = [self svg];
svg.renderQuality = IJSVGRenderQualityLow;
svg.renderingBackingScaleHelper = ^{
return self.window.backingScaleFactor;
return [svg computeBackingScale:self.window.backingScaleFactor];
};
}
return self;
}
- (void)switchToCoreGraphics:(id)sender
{
svg.renderingEngine = IJSVGRenderingEngineQuartz;
[self setNeedsDisplay:YES];
}
- (void)switchToCoreAnimation:(id)sender
{
svg.renderingEngine = IJSVGRenderingEngineLayered;
[self setNeedsDisplay:YES];
}
- (IJSVG *)svg
{
return [IJSVG svgNamed:@"car"];
return [IJSVG svgNamed:@"clipped"];
}
- (void)drawRect:(NSRect)dirtyRect
+9 -5
View File
@@ -13,10 +13,9 @@
#import "IJSVGGroupLayer.h"
#import "IJSVGImageLayer.h"
#import "IJSVGExporter.h"
#import "IJSVGRendering.h"
#import "IJSVGQuartzRenderer.h"
@class IJSVG;
@class IJSVGQuartzRenderer;
void IJSVGBeginTransactionLock();
void IJSVGEndTransactionLock();
@@ -36,6 +35,13 @@ withSVGString:(NSString *)subSVGString;
@end
typedef CGFloat (^IJSVGRenderingBackingScaleFactorHelper)();
typedef NS_ENUM(NSInteger, IJSVGRenderingEngine) {
IJSVGRenderingEngineCoreGraphics,
IJSVGRenderingEngineCoreAnimation
};
@interface IJSVG : NSObject <NSPasteboardWriting, IJSVGParserDelegate> {
@private
@@ -47,7 +53,6 @@ withSVGString:(NSString *)subSVGString;
CGRect _viewBox;
CGSize _proposedViewSize;
CGFloat _lastProposedBackingScale;
IJSVGRenderQuality _lastProposedRenderQuality;
CGFloat _backingScale;
NSMutableDictionary * _replacementColors;
IJSVGQuartzRenderer * _quartzRenderer;
@@ -73,12 +78,11 @@ withSVGString:(NSString *)subSVGString;
@property (nonatomic, assign) CGFloat strokeWidth;
@property (nonatomic, assign) IJSVGLineCapStyle lineCapStyle;
@property (nonatomic, assign) IJSVGLineJoinStyle lineJoinStyle;
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, assign) IJSVGRenderingEngine renderingEngine;
@property (nonatomic, assign) BOOL clipToViewport;
- (void)prepForDrawingInView:(NSView *)view;
- (BOOL)isFont;
- (IJSVGGroup *)rootNode;
- (NSRect)viewBox;
- (NSArray<IJSVGPath *> *)glyphs;
- (NSString *)identifier;
+62 -64
View File
@@ -19,12 +19,11 @@
@synthesize lineCapStyle;
@synthesize lineJoinStyle;
@synthesize renderingBackingScaleHelper;
@synthesize renderingEngine;
@synthesize clipToViewport;
@synthesize renderQuality;
- (void)dealloc
{
IJSVGBeginTransactionLock();
[renderingBackingScaleHelper release], renderingBackingScaleHelper = nil;
[fillColor release], fillColor = nil;
[strokeColor release], strokeColor = nil;
@@ -33,7 +32,6 @@
[_replacementColors release], _replacementColors = nil;
[_quartzRenderer release], _quartzRenderer = nil;
[super dealloc];
IJSVGEndTransactionLock();
}
+ (id)svgNamed:(NSString *)string
@@ -338,8 +336,8 @@
- (void)_setupBasicsFromAnyInitializer
{
renderingEngine = IJSVGRenderingEngineCoreAnimation;
self.clipToViewport = YES;
self.renderQuality = IJSVGRenderQualityOptimized;
// setup low level backing scale
_lastProposedBackingScale = 0.f;
@@ -365,11 +363,6 @@
return _viewBox;
}
- (IJSVGGroup *)rootNode
{
return _group;
}
- (BOOL)isFont
{
return [_group isFont];
@@ -422,7 +415,7 @@
{
NSImage * im = [[[NSImage alloc] initWithSize:aSize] autorelease];
[im lockFocus];
CGContextRef ref = [[NSGraphicsContext currentContext] CGContext];
CGContextRef ref = [[NSGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(ref);
if(flipped) {
CGContextTranslateCTM(ref, 0.f, aSize.height);
@@ -568,7 +561,7 @@
error:(NSError **)error
{
return [self _drawInRect:rect
context:[[NSGraphicsContext currentContext] CGContext]
context:[[NSGraphicsContext currentContext] graphicsPort]
error:error];
}
@@ -623,6 +616,7 @@
@synchronized (self) {
CGContextSaveGState(ref);
@try {
[self _beginDraw:rect];
// we also need to calculate the viewport so we can clip
@@ -630,64 +624,73 @@
BOOL canDraw = NO;
NSRect viewPort = [self computeRectDrawingInRect:rect isValid:&canDraw];
// check the viewport
if( canDraw == NO ) {
if( !canDraw ) {
if( error != NULL ) {
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
code:IJSVGErrorDrawing
userInfo:nil] autorelease];
}
} else {
// clip to mask
if(self.clipToViewport == YES) {
CGContextClipToRect( ref, viewPort);
}
// add the origin back onto the viewport
viewPort.origin.x -= round((_viewBox.origin.x)*_scale);
viewPort.origin.y -= round((_viewBox.origin.y)*_scale);
viewPort = CGRectIntegral(viewPort);
// transforms
CGContextTranslateCTM( ref, viewPort.origin.x, viewPort.origin.y);
CGContextScaleCTM( ref, _scale, _scale );
// render the layer, its really important we lock
// the transaction when drawing
IJSVGBeginTransactionLock();
// do we need to update the backing scales on the
// layers?
if(self.renderingBackingScaleHelper != nil) {
[self _askHelperForBackingScale];
}
CGInterpolationQuality quality;
switch(self.renderQuality) {
case IJSVGRenderQualityLow: {
quality = kCGInterpolationLow;
break;
}
case IJSVGRenderQualityOptimized: {
quality = kCGInterpolationMedium;
break;
}
default: {
quality = kCGInterpolationHigh;
}
}
CGContextSetInterpolationQuality(ref, quality);
[self.layer renderInContext:ref];
IJSVGEndTransactionLock();
CGContextRestoreGState(ref);
return NO;
}
// clip to mask
if(self.clipToViewport == YES) {
CGContextClipToRect( ref, viewPort);
}
// add the origin back onto the viewport
viewPort.origin.x -= round((_viewBox.origin.x)*_scale);
viewPort.origin.y -= round((_viewBox.origin.y)*_scale);
viewPort = CGRectIntegral(viewPort);
// transforms
CGContextTranslateCTM( ref, viewPort.origin.x, viewPort.origin.y);
CGContextScaleCTM( ref, _scale, _scale );
// render the layer, its really important we lock
// the transaction when drawing
IJSVGBeginTransactionLock();
// do we need to update the backing scales on the
// layers?
if(self.renderingBackingScaleHelper != nil) {
[self _askHelperForBackingScale];
}
// render the layers
switch(self.renderingEngine) {
// CoreGraphics / Quartz
case IJSVGRenderingEngineCoreGraphics: {
if(_quartzRenderer == nil) {
// init the renderer if its not already defined
_quartzRenderer = [[IJSVGQuartzRenderer alloc] init];
}
_quartzRenderer.scale = _scale;
_quartzRenderer.backingScale = _backingScale;
_quartzRenderer.viewPort = viewPort;
// render it
[_quartzRenderer renderLayer:self.layer
inContext:ref];
break;
}
// CALayer tree
case IJSVGRenderingEngineCoreAnimation: {
[self.layer renderInContext:ref];
}
}
IJSVGEndTransactionLock();
}
@catch (NSException *exception) {
// just catch and give back a drawing error to the caller
if( error != NULL ) {
if( error != NULL )
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
code:IJSVGErrorDrawing
userInfo:nil] autorelease];
}
}
CGContextRestoreGState(ref);
@finally {
CGContextRestoreGState(ref);
}
}
return (error == nil);
}
@@ -701,18 +704,14 @@
// dont do anything, nothing has changed, no point of iterating over
// every layer for no reason!
if(scale == _lastProposedBackingScale && renderQuality == _lastProposedRenderQuality) {
if(scale == _lastProposedBackingScale) {
return;
}
IJSVGRenderQuality quality = self.renderQuality;
_lastProposedBackingScale = scale;
_lastProposedRenderQuality = quality;
// walk the tree
void (^block)(CALayer * layer, BOOL isMask) = ^void (CALayer * layer, BOOL isMask) {
IJSVGLayer * propLayer = ((IJSVGLayer *)layer);
propLayer.renderQuality = quality;
if(propLayer.requiresBackingScaleHelp == YES) {
propLayer.backingScaleFactor = scale;
}
@@ -770,6 +769,7 @@
// force rebuild of the tree
IJSVGBeginTransactionLock();
_layerTree = [[tree layerForNode:_group] retain];
IJSVGEndTransactionLock();
return _layerTree;
}
@@ -802,19 +802,17 @@
// block to find colors in stroke and fill
void (^block)(CALayer * layer, BOOL isMask) = ^void (CALayer * layer, BOOL isMask) {
if([layer isKindOfClass:[IJSVGShapeLayer class]] && isMask == NO && layer.isHidden == NO) {
if([layer isKindOfClass:[IJSVGShapeLayer class]] && isMask == NO) {
IJSVGShapeLayer * sLayer = (IJSVGShapeLayer *)layer;
NSColor * color = nil;
if(sLayer.fillColor != nil) {
color = [NSColor colorWithCGColor:sLayer.fillColor];
color = [IJSVGColor computeColorSpace:color];
if(color.alphaComponent != 0.f) {
[colors addObject:color];
}
}
if(sLayer.strokeColor != nil) {
color = [NSColor colorWithCGColor:sLayer.strokeColor];
color = [IJSVGColor computeColorSpace:color];
if(color.alphaComponent != 0.f) {
[colors addObject:color];
}
-1
View File
@@ -169,7 +169,6 @@ CGFloat * IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightne
forceHex:(BOOL)forceHex
allowShorthand:(BOOL)allowShorthand;
+ (NSString *)colorStringFromColor:(NSColor *)color;
+ (NSColor *)colorFromHEXInteger:(NSInteger)hex;
+ (NSColor *)computeColor:(id)colour;
+ (NSColor *)colorFromString:(NSString *)string;
+ (NSColor *)colorFromHEXString:(NSString *)string
+175 -182
View File
@@ -35,7 +35,7 @@ CGFloat * IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightne
+ (void)load
{
[self.class _generateTree];
[[self class] _generateTree];
}
+ (NSColorSpace *)defaultColorSpace
@@ -57,151 +57,154 @@ CGFloat * IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightne
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_colorTree = [@{
@"aliceblue":@(0xf0f8ff),
@"antiquewhite":@(0xfaebd7),
@"aqua":@(0x00ffff),
@"aquamarine":@(0x7fffd4),
@"azure":@(0xf0ffff),
@"beige":@(0xf5f5dc),
@"bisque":@(0xffe4c4),
@"black":@(0x000000),
@"blanchedalmond":@(0xffebcd),
@"blue":@(0x0000ff),
@"blueviolet":@(0x8a2be2),
@"brown":@(0xa52a2a),
@"burlywood":@(0xdeb887),
@"cadetblue":@(0x5f9ea0),
@"chartreuse":@(0x7fff00),
@"chocolate":@(0xd2691e),
@"coral":@(0xff7f50),
@"cornflowerblue":@(0x6495ed),
@"cornsilk":@(0xfff8dc),
@"crimson":@(0xdc143c),
@"currentcolor":@(0x000000),
@"cyan":@(0x00ffff),
@"darkblue":@(0x00008b),
@"darkcyan":@(0x008b8b),
@"darkgoldenrod":@(0xb8860b),
@"darkgray":@(0xa9a9a9),
@"darkgreen":@(0x006400),
@"darkgrey":@(0xa9a9a9),
@"darkkhaki":@(0xbdb76b),
@"darkmagenta":@(0x8b008b),
@"darkolivegreen":@(0x556b2f),
@"darkorange":@(0xff8c00),
@"darkorchid":@(0x9932cc),
@"darkred":@(0x8b0000),
@"darksalmon":@(0xe9967a),
@"darkseagreen":@(0x8fbc8f),
@"darkslateblue":@(0x483d8b),
@"darkslategray":@(0x2f4f4f),
@"darkturquoise":@(0x00ced1),
@"darkviolet":@(0x9400d3),
@"deeppink":@(0xff1493),
@"deepskyblue":@(0x00bfff),
@"dimgray":@(0x696969),
@"dimgrey":@(0x696969),
@"dodgerblue":@(0x1e90ff),
@"firebrick":@(0xb22222),
@"floralwhite":@(0xfffaf0),
@"forestgreen":@(0x228b22),
@"fuchsia":@(0xff00ff),
@"gainsboro":@(0xdcdcdc),
@"ghostwhite":@(0xf8f8ff),
@"gold":@(0xffd700),
@"goldenrod":@(0xdaa520),
@"gray":@(0x808080),
@"green":@(0x008000),
@"greenyellow":@(0xadff2f),
@"grey":@(0x808080),
@"honeydew":@(0xf0fff0),
@"hotpink":@(0xff69b4),
@"indianred":@(0xcd5c5c),
@"indigo":@(0x4b0082),
@"ivory":@(0xfffff0),
@"khaki":@(0xf0e68c),
@"lavender":@(0xe6e6fa),
@"lavenderblush":@(0xfff0f5),
@"lawngreen":@(0x7cfc00),
@"lemonchiffon":@(0xfffacd),
@"lightblue":@(0xadd8e6),
@"lightcoral":@(0xf08080),
@"lightcyan":@(0xe0ffff),
@"lightgoldenrodyellow":@(0xfafad2),
@"lightgray":@(0xd3d3d3),
@"lightgreen":@(0x90ee90),
@"lightgrey":@(0xd3d3d3),
@"lightpink":@(0xffb6c1),
@"lightsalmon":@(0xffa07a),
@"lightseagreen":@(0x20b2aa),
@"lightskyblue":@(0x87cefa),
@"lightslategray":@(0x778899),
@"lightsteelblue":@(0xb0c4de),
@"lightyellow":@(0xffffe0),
@"lime":@(0x00ff00),
@"limegreen":@(0x32cd32),
@"linen":@(0xfaf0e6),
@"magenta":@(0xff00ff),
@"maroon":@(0x800000),
@"mediumaquamarine":@(0x66cdaa),
@"mediumblue":@(0x0000cd),
@"mediumorchid":@(0xba55d3),
@"mediumpurple":@(0x9370db),
@"mediumseagreen":@(0x3cb371),
@"mediumslateblue":@(0x7b68ee),
@"mediumspringgreen":@(0x00fa9a),
@"mediumturquoise":@(0x48d1cc),
@"mediumvioletred":@(0xc71585),
@"midnightblue":@(0x191970),
@"mintcream":@(0xf5fffa),
@"mistyrose":@(0xffe4e1),
@"moccasin":@(0xffe4b5),
@"navajowhite":@(0xffdead),
@"navy":@(0x000080),
@"oldlace":@(0xfdf5e6),
@"olive":@(0x808000),
@"olivedrab":@(0x6b8e23),
@"orange":@(0xffa500),
@"orangered":@(0xff4500),
@"orchid":@(0xda70d6),
@"palegoldenrod":@(0xeee8aa),
@"palegreen":@(0x98fb98),
@"paleturquoise":@(0xafeeee),
@"palevioletred":@(0xdb7093),
@"papayawhip":@(0xffefd5),
@"peachpuff":@(0xffdab9),
@"peru":@(0xcd853f),
@"pink":@(0xffc0cb),
@"plum":@(0xdda0dd),
@"powderblue":@(0xb0e0e6),
@"purple":@(0x800080),
@"red":@(0xff0000),
@"rosybrown":@(0xbc8f8f),
@"royalblue":@(0x4169e1),
@"saddlebrown":@(0x8b4513),
@"salmon":@(0xfa8072),
@"sandybrown":@(0xf4a460),
@"seagreen":@(0x2e8b57),
@"seashell":@(0xfff5ee),
@"sienna":@(0xa0522d),
@"silver":@(0xc0c0c0),
@"skyblue":@(0x87ceeb),
@"slateblue":@(0x6a5acd),
@"slategrey":@(0x708090),
@"snow":@(0xfffafa),
@"springgreen":@(0x00ff7f),
@"steelblue":@(0x4682b4),
@"tan":@(0xd2b48c),
@"teal":@(0x008080),
@"thistle":@(0xd8bfd8),
@"tomato":@(0xff6347),
@"turquoise":@(0x40e0d0),
@"violet":@(0xee82ee),
@"wheat":@(0xf5deb3),
@"white":@(0xffffff),
@"whitesmoke":@(0xf5f5f5),
@"yellow":@(0xffff00),
@"yellowgreen":@(0x9acd32)
@"aliceblue":@"f0f8ff",
@"antiquewhite":@"faebd7",
@"aqua":@"00ffff",
@"aquamarine":@"7fffd4",
@"azure":@"f0ffff",
@"beige":@"f5f5dc",
@"bisque":@"ffe4c4",
@"black":@"000000",
@"blanchedalmond":@"ffebcd",
@"blue":@"0000ff",
@"blueviolet":@"8a2be2",
@"brown":@"a52a2a",
@"burlywood":@"deb887",
@"cadetblue":@"5f9ea0",
@"chartreuse":@"7fff00",
@"chocolate":@"d2691e",
@"coral":@"ff7f50",
@"cornflowerblue":@"6495ed",
@"cornsilk":@"fff8dc",
@"crimson":@"dc143c",
@"currentcolor":@"000000",
@"cyan":@"00ffff",
@"darkblue":@"00008b",
@"darkcyan":@"008b8b",
@"darkgoldenrod":@"b8860b",
@"darkgray":@"a9a9a9",
@"darkgreen":@"006400",
@"darkgrey":@"a9a9a9",
@"darkkhaki":@"bdb76b",
@"darkmagenta":@"8b008b",
@"darkolivegreen":@"556b2f",
@"darkorange":@"ff8c00",
@"darkorchid":@"9932cc",
@"darkred":@"8b0000",
@"darksalmon":@"e9967a",
@"darkseagreen":@"8fbc8f",
@"darkslateblue":@"483d8b",
@"darkslategray":@"2f4f4f",
@"darkslategrey":@"2f4f4f",
@"darkturquoise":@"00ced1",
@"darkviolet":@"9400d3",
@"deeppink":@"ff1493",
@"deepskyblue":@"00bfff",
@"dimgray":@"696969",
@"dimgrey":@"696969",
@"dodgerblue":@"1e90ff",
@"firebrick":@"b22222",
@"floralwhite":@"fffaf0",
@"forestgreen":@"228b22",
@"fuchsia":@"ff00ff",
@"gainsboro":@"dcdcdc",
@"ghostwhite":@"f8f8ff",
@"gold":@"ffd700",
@"goldenrod":@"daa520",
@"gray":@"808080",
@"green":@"008000",
@"greenyellow":@"adff2f",
@"grey":@"808080",
@"honeydew":@"f0fff0",
@"hotpink":@"ff69b4",
@"indianred":@"cd5c5c",
@"indigo":@"4b0082",
@"ivory":@"fffff0",
@"khaki":@"f0e68c",
@"lavender":@"e6e6fa",
@"lavenderblush":@"fff0f5",
@"lawngreen":@"7cfc00",
@"lemonchiffon":@"fffacd",
@"lightblue":@"add8e6",
@"lightcoral":@"f08080",
@"lightcyan":@"e0ffff",
@"lightgoldenrodyellow":@"fafad2",
@"lightgray":@"d3d3d3",
@"lightgreen":@"90ee90",
@"lightgrey":@"d3d3d3",
@"lightpink":@"ffb6c1",
@"lightsalmon":@"ffa07a",
@"lightseagreen":@"20b2aa",
@"lightskyblue":@"87cefa",
@"lightslategray":@"778899",
@"lightslategrey":@"778899",
@"lightsteelblue":@"b0c4de",
@"lightyellow":@"ffffe0",
@"lime":@"00ff00",
@"limegreen":@"32cd32",
@"linen":@"faf0e6",
@"magenta":@"ff00ff",
@"maroon":@"800000",
@"mediumaquamarine":@"66cdaa",
@"mediumblue":@"0000cd",
@"mediumorchid":@"ba55d3",
@"mediumpurple":@"9370db",
@"mediumseagreen":@"3cb371",
@"mediumslateblue":@"7b68ee",
@"mediumspringgreen":@"00fa9a",
@"mediumturquoise":@"48d1cc",
@"mediumvioletred":@"c71585",
@"midnightblue":@"191970",
@"mintcream":@"f5fffa",
@"mistyrose":@"ffe4e1",
@"moccasin":@"ffe4b5",
@"navajowhite":@"ffdead",
@"navy":@"000080",
@"oldlace":@"fdf5e6",
@"olive":@"808000",
@"olivedrab":@"6b8e23",
@"orange":@"ffa500",
@"orangered":@"ff4500",
@"orchid":@"da70d6",
@"palegoldenrod":@"eee8aa",
@"palegreen":@"98fb98",
@"paleturquoise":@"afeeee",
@"palevioletred":@"db7093",
@"papayawhip":@"ffefd5",
@"peachpuff":@"ffdab9",
@"peru":@"cd853f",
@"pink":@"ffc0cb",
@"plum":@"dda0dd",
@"powderblue":@"b0e0e6",
@"purple":@"800080",
@"red":@"ff0000",
@"rosybrown":@"bc8f8f",
@"royalblue":@"4169e1",
@"saddlebrown":@"8b4513",
@"salmon":@"fa8072",
@"sandybrown":@"f4a460",
@"seagreen":@"2e8b57",
@"seashell":@"fff5ee",
@"sienna":@"a0522d",
@"silver":@"c0c0c0",
@"skyblue":@"87ceeb",
@"slateblue":@"6a5acd",
@"slategray":@"708090",
@"slategrey":@"708090",
@"snow":@"fffafa",
@"springgreen":@"00ff7f",
@"steelblue":@"4682b4",
@"tan":@"d2b48c",
@"teal":@"008080",
@"thistle":@"d8bfd8",
@"tomato":@"ff6347",
@"turquoise":@"40e0d0",
@"violet":@"ee82ee",
@"wheat":@"f5deb3",
@"white":@"ffffff",
@"whitesmoke":@"f5f5f5",
@"yellow":@"ffff00",
@"yellowgreen":@"9acd32"
} retain];
});
}
@@ -222,13 +225,10 @@ CGFloat * IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightne
return nil;
}
NSColor * color = nil;
string = [string lowercaseString];
if([self.class isHex:string] == NO) {
color = [self.class colorFromPredefinedColorName:string];
if( color != nil ) {
return color;
}
NSColor * color = [[self class] colorFromPredefinedColorName:string];
if( color != nil ) {
return color;
}
if( [[string lowercaseString] isEqualToString:@"none"] ) {
@@ -275,18 +275,19 @@ CGFloat * IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightne
return color;
}
color = [self.class colorFromHEXString:string alpha:1.f];
color = [[self class] colorFromHEXString:string
alpha:1.f];
return color;
}
+ (NSColor *)colorFromPredefinedColorName:(NSString *)name
{
NSNumber * hex = nil;
NSString * hex = nil;
name = [name.lowercaseString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if((hex = _colorTree[name]) == nil ) {
if( ( hex = [_colorTree objectForKey:name] ) == nil )
return nil;
}
return [self.class colorFromHEXInteger:hex.integerValue];
return [[self class] colorFromHEXString:hex
alpha:1.f];
}
+ (NSString *)colorStringFromColor:(NSColor *)color
@@ -650,35 +651,20 @@ CGFloat * IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightne
+ (BOOL)isColor:(NSString *)string
{
return [[string substringToIndex:1] isEqualToString:@"#"] ||
[[string substringToIndex:3] isEqualToString:@"rgb"];
return [[string substringToIndex:1] isEqualToString:@"#"] || [[string substringToIndex:3] isEqualToString:@"rgb"];
}
+ (BOOL)isHex:(NSString *)string
{
const char * validList = "0123456789ABCDEFabcdef#";
for(NSInteger i = 0; i < string.length; i++) {
char c = [string characterAtIndex:i];
if(strchr(validList, c) == NULL) {
return NO;
}
}
return YES;
}
+ (NSColor *)colorFromHEXInteger:(NSInteger)hex
{
return [NSColor colorWithDeviceRed:((hex >> 16) & 0xFF) / 255.f
green:((hex >> 6) & 0xFF) / 255.f
blue:(hex & 0xFF) / 255.f
alpha:1.f];
NSCharacterSet *chars = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef#"] invertedSet];
return [string rangeOfCharacterFromSet:chars].location == NSNotFound;
}
+ (NSColor *)colorFromHEXString:(NSString *)string
alpha:(CGFloat)alpha
{
// absolutely no string
if( string == nil || string.length == 0 || ![self.class isHex:string] )
if( string == nil || string.length == 0 || ![[self class] isHex:string] )
return nil;
if( [[string substringToIndex:1] isEqualToString:@"#"] )
@@ -696,12 +682,19 @@ CGFloat * IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightne
string = str;
}
const char * hexString = [string cStringUsingEncoding:NSUTF8StringEncoding];
unsigned long hex = strtoul(hexString, NULL, 16);
return [NSColor colorWithDeviceRed:((hex>>16) & 0xFF)/255.f
green:((hex>>8) & 0xFF)/255.f
blue:(hex & 0xFF)/255.f
alpha:alpha];
NSScanner * scanner = [NSScanner scannerWithString:string];
unsigned int hex;
if( [scanner scanHexInt:&hex] )
{
NSInteger r = (hex>>16) & 0xFF;
NSInteger g = (hex>>8) & 0xFF;
NSInteger b = (hex) & 0xFF;
return [NSColor colorWithDeviceRed:r/255.f
green:g/255.f
blue:b/255.f
alpha:alpha];
}
return nil;
}
@end
+5
View File
@@ -60,6 +60,11 @@ typedef NS_ENUM( NSInteger, IJSVGCommandType ) {
+ (NSPoint)readCoordinatePair:(CGFloat *)pairs
index:(NSInteger)index;
+ (void)registerClass:(Class)aClass
forCommand:(NSString *)command;
+ (NSDictionary *)registeredCommandClasses;
+ (Class<IJSVGCommandProtocol>)commandClassForCommandLetter:(NSString *)str;
- (CGFloat)readFloat;
- (NSPoint)readPoint;
- (BOOL)readBOOL;
+25 -29
View File
@@ -9,17 +9,6 @@
#import "IJSVGCommand.h"
#import "IJSVGUtils.h"
#import "IJSVGCommandArc.h"
#import "IJSVGCommandMove.h"
#import "IJSVGCommandClose.h"
#import "IJSVGCommandCurve.h"
#import "IJSVGCommandLineTo.h"
#import "IJSVGCommandVerticalLine.h"
#import "IJSVGCommandHorizontalLine.h"
#import "IJSVGCommandSmoothCurve.h"
#import "IJSVGCommandQuadraticCurve.h"
#import "IJSVGCommandCommandSmoothQuadraticCurve.h"
@implementation IJSVGCommand
@synthesize commandString;
@@ -33,6 +22,8 @@
@synthesize previousCommand;
@synthesize isSubCommand;
static NSMutableDictionary * _classes = nil;
- (void)dealloc
{
[commandString release], commandString = nil;
@@ -48,9 +39,10 @@
{
// work out the basics
_currentIndex = 0;
subCommands = [[NSMutableArray alloc] init];
command = [[str substringToIndex:1] copy];
type = [IJSVGUtils typeForCommandString:self.command];
commandClass = [[self class] commandClassForCommandChar:[self.command characterAtIndex:0]];
commandClass = [[self class] commandClassForCommandLetter:self.command];
parameters = [IJSVGUtils commandParameters:str count:&parameterCount];
requiredParameters = [self.commandClass requiredParameterCount];
@@ -63,8 +55,6 @@
sets = self.parameterCount/self.requiredParameters;
}
subCommands = [[NSMutableArray alloc] initWithCapacity:sets];
// interate over the sets
for( NSInteger i = 0; i < sets; i++ ) {
// memory for this will be handled by the created subcommand
@@ -99,27 +89,33 @@
return NSMakePoint( pairs[index*2], pairs[index*2+1]);
}
+ (void)registerClass:(Class)aClass
forCommand:(NSString *)command
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_classes = [[NSMutableDictionary alloc] init];
});
[_classes setObject:NSStringFromClass(aClass)
forKey:command];
}
+ (NSDictionary *)registeredCommandClasses
{
return _classes;
}
+ (void)load
{
// register here...
}
+ (Class<IJSVGCommandProtocol>)commandClassForCommandChar:(char)aChar
+ (Class<IJSVGCommandProtocol>)commandClassForCommandLetter:(NSString *)str
{
aChar = tolower(aChar);
switch(aChar) {
case 'a': return IJSVGCommandArc.class;
case 'c': return IJSVGCommandCurve.class;
case 'h': return IJSVGCommandHorizontalLine.class;
case 'l': return IJSVGCommandLineTo.class;
case 'm': return IJSVGCommandMove.class;
case 'q': return IJSVGCommandQuadraticCurve.class;
case 's': return IJSVGCommandSmoothCurve.class;
case 't': return IJSVGCommandCommandSmoothQuadraticCurve.class;
case 'v': return IJSVGCommandVerticalLine.class;
case 'z': return IJSVGCommandClose.class;
}
return nil;
NSString * command = nil;
if( ( command = [_classes objectForKey:[str lowercaseString]] ) == nil )
return nil;
return NSClassFromString(command);
}
- (CGFloat)readFloat
+5
View File
@@ -11,6 +11,11 @@
@implementation IJSVGCommandArc
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"a"];
}
+ (NSInteger)requiredParameterCount
{
+6
View File
@@ -10,6 +10,12 @@
@implementation IJSVGCommandClose
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"z"];
}
+ (NSInteger)requiredParameterCount
{
return 0;
@@ -12,6 +12,12 @@
@implementation IJSVGCommandCommandSmoothQuadraticCurve
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"t"];
}
+ (NSInteger)requiredParameterCount
{
return 2;
+6
View File
@@ -10,6 +10,12 @@
@implementation IJSVGCommandCurve
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"c"];
}
+ (NSInteger)requiredParameterCount
{
return 6;
+6
View File
@@ -10,6 +10,12 @@
@implementation IJSVGCommandHorizontalLine
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"h"];
}
+ (NSInteger)requiredParameterCount
{
return 1;
+6
View File
@@ -10,6 +10,12 @@
@implementation IJSVGCommandLineTo
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"l"];
}
+ (NSInteger)requiredParameterCount
{
return 2;
+6
View File
@@ -12,6 +12,12 @@
@implementation IJSVGCommandMove
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"m"];
}
+ (NSInteger)requiredParameterCount
{
return 2;
+6
View File
@@ -11,6 +11,12 @@
@implementation IJSVGCommandQuadraticCurve
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"q"];
}
+ (NSInteger)requiredParameterCount
{
return 4;
+6
View File
@@ -12,6 +12,12 @@
@implementation IJSVGCommandSmoothCurve
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"s"];
}
+ (NSInteger)requiredParameterCount
{
return 4;
+6
View File
@@ -10,6 +10,12 @@
@implementation IJSVGCommandVerticalLine
+ (void)load
{
[IJSVGCommand registerClass:[self class]
forCommand:@"v"];
}
+ (NSInteger)requiredParameterCount
{
return 1;
+1 -3
View File
@@ -28,7 +28,6 @@ typedef NS_OPTIONS( NSInteger, IJSVGExporterOptions) {
IJSVGExporterOptionCompressOutput = 1 << 10,
IJSVGExporterOptionCollapseGradients = 1 << 11,
IJSVGExporterOptionCreateClasses = 1 << 12,
IJSVGExporterOptionRemoveWidthHeightAttributes = 1 << 13,
IJSVGExporterOptionAll = IJSVGExporterOptionRemoveUselessDef|
IJSVGExporterOptionRemoveUselessGroups|
IJSVGExporterOptionCreateUseForPaths|
@@ -39,8 +38,7 @@ typedef NS_OPTIONS( NSInteger, IJSVGExporterOptions) {
IJSVGExporterOptionRemoveHiddenElements|
IJSVGExporterOptionScaleToSizeIfNecessary|
IJSVGExporterOptionCompressOutput|
IJSVGExporterOptionCollapseGradients|
IJSVGExporterOptionRemoveWidthHeightAttributes
IJSVGExporterOptionCollapseGradients
};
@interface IJSVGExporter : NSObject {
+1 -9
View File
@@ -26,7 +26,7 @@
#define XML_DOC_NSXLINK @"http://www.w3.org/1999/xlink"
#define XML_DOCTYPE_VERSION @"1.0"
#define XML_DOC_CHARSET @"UTF-8"
#define XML_DOC_GENERATOR @"Generated by IJSVG (https://github.com/iconjar/IJSVG)"
#define XML_DOC_GENERATOR @"Generated by IJSVG (https://github.com/curthard89/IJSVG)"
@synthesize title;
@synthesize description;
@@ -179,14 +179,6 @@ NSString * IJSVGHash(NSString * key) {
@"xmlns:xlink": XML_DOC_NSXLINK
};
// add on width and height unless specified otherwise
if((_options & IJSVGExporterOptionRemoveWidthHeightAttributes) == 0) {
NSMutableDictionary * attDict = [[attributes mutableCopy] autorelease];
attDict[@"width"] = IJSVGShortFloatString(_size.width);
attDict[@"height"] = IJSVGShortFloatString(_size.height);
attributes = [[attDict copy] autorelease];
}
// was there a size set?
if(CGSizeEqualToSize(CGSizeZero, _size) == NO &&
(_size.width != viewBox.size.width && _size.height != viewBox.size.height)) {
+1 -52
View File
@@ -25,60 +25,10 @@
{
if((self = [super init]) != nil) {
self.requiresBackingScaleHelp = YES;
self.shouldRasterize = YES;
}
return self;
}
- (void)setGradient:(IJSVGGradient *)newGradient
{
if(gradient != nil) {
[gradient release], gradient = nil;
}
gradient = [newGradient retain];
// lets check its alpha properties on the colors
BOOL hasAlphaChannel = NO;
NSInteger stops = gradient.gradient.numberOfColorStops;
for(NSInteger i = 0; i < stops; i++) {
NSColor * color = nil;
[gradient.gradient getColor:&color
location:NULL
atIndex:i];
if(color.alphaComponent != 1.f) {
hasAlphaChannel = YES;
break;
}
}
self.opaque = hasAlphaChannel == NO;
}
- (void)setOpacity:(float)opacity
{
if(opacity != 1.f) {
self.opaque = NO;
}
[super setOpacity:opacity];
}
- (void)setBackingScaleFactor:(CGFloat)backingScaleFactor
{
switch (self.renderQuality) {
case IJSVGRenderQualityOptimized: {
backingScaleFactor = .35f;
break;
}
case IJSVGRenderQualityLow: {
backingScaleFactor = .05f;
break;
}
default: {
break;
}
}
[super setBackingScaleFactor:backingScaleFactor];
}
- (void)drawInContext:(CGContextRef)ctx
{
[super drawInContext:ctx];
@@ -92,12 +42,11 @@
CGAffineTransform trans = CGAffineTransformMakeTranslation(-CGRectGetMinX(objectRect),
-CGRectGetMinY(objectRect));
CGAffineTransform transform = CGAffineTransformConcat(absoluteTransform,trans);
CGContextSaveGState(ctx);
[self.gradient drawInContextRef:ctx
objectRect:objectRect
absoluteTransform:transform
viewPort:self.viewBox];
CGContextRestoreGState(ctx);
}
@end
-1
View File
@@ -30,7 +30,6 @@
// make sure we say we need help
self.requiresBackingScaleHelp = YES;
self.shouldRasterize = YES;
// set the frame, simple stuff
self.frame = (CGRect){
-22
View File
@@ -1,22 +0,0 @@
//
// IJSVGImageRep.h
// IJSVGExample
//
// Created by Curtis Hard on 15/03/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "IJSVGParser.h"
@class IJSVG;
@interface IJSVGImageRep : NSImageRep {
@private
IJSVG * _svg;
}
@property (nonatomic, readonly) CGRect viewBox;
@end
-104
View File
@@ -1,104 +0,0 @@
//
// IJSVGImageRep.m
// IJSVGExample
//
// Created by Curtis Hard on 15/03/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import "IJSVGImageRep.h"
#import "IJSVG.h"
@implementation IJSVGImageRep
@synthesize viewBox = _viewBox;
+ (void)load
{
[NSBitmapImageRep registerImageRepClass:self];
}
+ (BOOL)canInitWithData:(NSData *)data
{
return [IJSVGParser isDataSVG:data];
}
+ (NSArray<NSString *> *)imageTypes
{
return @[(NSString *)kUTTypeScalableVectorGraphics, @"svg"];
}
+ (NSArray<NSString *> *)imageUnfilteredTypes
{
return @[(NSString *)kUTTypeScalableVectorGraphics, @"svg"];
}
+ (NSArray<NSImageRep *> *)imageRepsWithData:(NSData *)data
{
IJSVGImageRep * instance = [self imageRepWithData:data];
if(instance == nil) {
return @[];
}
return @[instance];
}
+ (instancetype)imageRepWithData:(NSData *)data
{
return [[self alloc] initWithData:data];
}
- (void)dealloc
{
[_svg release], _svg = nil;
[super dealloc];
}
- (instancetype)initWithData:(NSData *)data
{
if((self = [super init]) != nil) {
// grab the string from the data
// its more then likely UTF-8...
NSString * string = [[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding] autorelease];
_svg = [[IJSVG alloc] initWithSVGString:string];
// no valid SVG, just return nil;
if(_svg == nil) {
[self release];
return nil;
}
// set default properties
self.pixelsWide = _svg.viewBox.size.width;
self.pixelsHigh = _svg.viewBox.size.height;
self.size = _svg.viewBox.size;
}
return self;
}
- (BOOL)draw
{
[_svg drawInRect:self.viewBox];
return YES;
}
- (BOOL)drawAtPoint:(NSPoint)point
{
[_svg drawAtPoint:point
size:_svg.viewBox.size];
return YES;
}
- (BOOL)drawInRect:(NSRect)rect
{
[_svg drawInRect:rect];
return YES;
}
- (CGRect)viewBox
{
return _svg.viewBox;
}
@end
-2
View File
@@ -8,7 +8,6 @@
#import <QuartzCore/QuartzCore.h>
#import "IJSVGTransaction.h"
#import "IJSVGRendering.h"
@class IJSVGShapeLayer;
@class IJSVGGradientLayer;
@@ -29,7 +28,6 @@
@property (nonatomic, assign) IJSVGPatternLayer * patternStrokeLayer;
@property (nonatomic, assign) BOOL requiresBackingScaleHelp;
@property (nonatomic, assign) CGFloat backingScaleFactor;
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, assign) CGBlendMode blendingMode;
@property (nonatomic, assign) CGPoint absoluteOrigin;
@property (nonatomic, assign) BOOL convertMasksToPaths;
+3 -7
View File
@@ -26,8 +26,10 @@
- (void)dealloc
{
IJSVGBeginTransactionLock();
[_maskingLayer release], _maskingLayer = nil;
[super dealloc];
IJSVGEndTransactionLock();
}
+ (NSArray *)deepestSublayersOfLayer:(CALayer *)layer
@@ -62,8 +64,7 @@
}
}
- (void)addSublayer:(CALayer *)layer
{
- (void)addSublayer:(CALayer *)layer {
if([layer isKindOfClass:[IJSVGLayer class]] == NO &&
[layer isKindOfClass:[IJSVGShapeLayer class]] == NO) {
NSString * r = [NSString stringWithFormat:@"The layer must be an instance of IJSVGLayer, %@ given.",
@@ -176,9 +177,4 @@
[self _customRenderInContext:ctx];
}
- (id<CAAction>)actionForKey:(NSString *)event
{
return nil;
}
@end
+21 -32
View File
@@ -71,38 +71,21 @@
// create the new layer
layer = [self applyTransforms:node.transforms
toLayer:layer
fromNode:node];
toLayer:layer];
return layer;
}
- (IJSVGLayer *)applyTransforms:(NSArray<IJSVGTransform *> *)transforms
toLayer:(IJSVGLayer *)layer
fromNode:(IJSVGNode *)node
{
// any x and y?
CGFloat x = [node.x computeValue:layer.frame.size.width];
CGFloat y = [node.y computeValue:layer.frame.size.height];
// do some magic transform
if(transforms.count == 0 && x == 0.f && y == 0.f) {
if(transforms.count == 0) {
return layer;
}
if(x != 0.f || y != 0.f) {
// we must add translate to the stack
NSMutableArray * trans = nil;
if(transforms != nil) {
trans = [[transforms mutableCopy] autorelease];
} else {
trans = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
}
[trans addObject:[IJSVGTransform transformByTranslatingX:x y:y]];
transforms = trans;
}
// add any transforms
IJSVGLayer * topLayer = nil;
IJSVGLayer * parentLayer = nil;
@@ -135,6 +118,9 @@
fromNode:(IJSVGNode *)node
{
CGFloat opacity = node.opacity.value;
if(opacity == 0.f) {
opacity = 1.f;
}
layer.opacity = opacity;
// setup the blending mode
@@ -146,6 +132,11 @@
if(node.shouldRender == NO) {
layer.hidden = YES;
}
CGRect frame = layer.frame;
frame.origin.x += [node.x computeValue:frame.size.width];
frame.origin.y += [node.y computeValue:frame.size.height];
layer.frame = frame;
}
- (IJSVGLayer *)layerForImage:(IJSVGImage *)image
@@ -166,12 +157,6 @@
- (IJSVGLayer *)layerForGroup:(IJSVGGroup *)group
{
// grab the sub layer tree from the SVG
if(group.svg != nil) {
return [self layerForGroup:group.svg.rootNode];
}
IJSVGGroupLayer * groupLayer = [[[IJSVGGroupLayer alloc] init] autorelease];
for(IJSVGNode * node in group.children) {
[groupLayer addSublayer:[self layerForNode:node]];
@@ -271,7 +256,13 @@
- (IJSVGLayer *)layerForPath:(IJSVGPath *)path
{
// grab the basic shape layer
// is there a sub SVG?
if(path.svg != nil) {
// grab the sub layer tree from the SVG
return [path.svg layerWithTree:self];
}
// garb the basic shape layer
CGRect originalShapeBounds;
IJSVGShapeLayer * layer = [self basicLayerForPath:path
originalBoundingBox:&originalShapeBounds];
@@ -313,14 +304,14 @@
// only use the global if its set and the current colors
// alpha channel is not 0.f, otherwise its a blank clear color,
// aka, not filled in
NSColor * fColor = path.fillColor;
BOOL hasColor = (fColor.alphaComponent == 0.f || fColor == nil) == NO;
BOOL hasFill = path.fillPattern != nil || path.fillGradient != nil;
if(self.fillColor && (hasFill || hasColor || fColor == nil)) {
fColor = self.fillColor;
} else if(fColor != nil && path.fillOpacity.value != 1.f) {
fColor = [IJSVGColor changeAlphaOnColor:fColor
to:path.fillOpacity.value];
}
// anything changed by user?
@@ -495,7 +486,6 @@
if(path.fillOpacity.value != 0.f) {
gradLayer.opacity = path.fillOpacity.value;
}
gradLayer.masksToBounds = YES;
return gradLayer;
}
@@ -517,10 +507,10 @@
CGRect bounds = CGPathGetBoundingBox(layer.path);
bounds = [self correctBounds:bounds forStrokedPath:path];
patternLayer.frame = bounds;
patternLayer.masksToBounds = YES;
// display
[patternLayer setNeedsDisplay];
return patternLayer;
}
@@ -545,7 +535,6 @@
// display
[patternLayer setNeedsDisplay];
patternLayer.masksToBounds = YES;
return patternLayer;
}
+34 -30
View File
@@ -51,7 +51,7 @@
// create the gradient with the colours
NSGradient * grad = [[NSGradient alloc] initWithColors:colors
atLocations:stopsParams
colorSpace:IJSVGColor.defaultColorSpace];
colorSpace:[NSColorSpace genericRGBColorSpace]];
free(stopsParams);
return [grad autorelease];
@@ -70,37 +70,41 @@
CGAffineTransform selfTransform = IJSVGConcatTransforms(self.transforms);
#pragma mark User Space On Use
if(inUserSpace == YES) {
CGFloat width = CGRectGetWidth(viewBox);
CGFloat height = CGRectGetHeight(viewBox);
gradientStartPoint = CGPointMake([self.x1 computeValue:width],
[self.y1 computeValue:height]);
gradientEndPoint = CGPointMake([self.x2 computeValue:width],
[self.y2 computeValue:height]);
// transform absolute - due to user space
CGContextConcatCTM(ctx, absTransform);
} else {
CGContextSaveGState(ctx);
{
if(inUserSpace == YES) {
CGFloat width = CGRectGetWidth(viewBox);
CGFloat height = CGRectGetHeight(viewBox);
gradientStartPoint = CGPointMake([self.x1 computeValue:width],
[self.y1 computeValue:height]);
gradientEndPoint = CGPointMake([self.x2 computeValue:width],
[self.y2 computeValue:height]);
// transform absolute - due to user space
CGContextConcatCTM(ctx, absTransform);
} else {
#pragma mark Object Bounding Box
CGFloat width = CGRectGetWidth(objectRect);
CGFloat height = CGRectGetHeight(objectRect);
gradientStartPoint = CGPointMake([self.x1 computeValue:width],
[self.y1 computeValue:height]);
CGFloat width = CGRectGetWidth(objectRect);
CGFloat height = CGRectGetHeight(objectRect);
gradientStartPoint = CGPointMake([self.x1 computeValue:width],
[self.y1 computeValue:height]);
gradientEndPoint = CGPointMake([self.x2 computeValue:width],
[self.y2 computeValue:height]);
}
// transform the context
CGContextConcatCTM(ctx, selfTransform);
gradientEndPoint = CGPointMake([self.x2 computeValue:width],
[self.y2 computeValue:height]);
}
// transform the context
CGContextConcatCTM(ctx, selfTransform);
// draw the gradient
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation|
kCGGradientDrawsAfterEndLocation;
CGContextDrawLinearGradient(ctx, self.CGGradient, gradientStartPoint,
gradientEndPoint, options);
// draw the gradient
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation|
kCGGradientDrawsAfterEndLocation;
CGContextDrawLinearGradient(ctx, self.CGGradient, gradientStartPoint,
gradientEndPoint, options);
};
CGContextRestoreGState(ctx);
}
@end
-2
View File
@@ -146,7 +146,6 @@
{
if( ( self = [self initWithDef:YES] ) != nil )
{
self.opacity = [IJSVGUnitLength unitWithFloat:1];
}
return self;
}
@@ -215,7 +214,6 @@
{
self.opacity = [IJSVGUnitLength unitWithFloat:0.f];
self.fillOpacity = [IJSVGUnitLength unitWithFloat:1.f];
self.fillOpacity.inherit = YES;
self.strokeDashOffset = [IJSVGUnitLength unitWithFloat:0.f];
self.shouldRender = YES;
-2
View File
@@ -107,8 +107,6 @@ handleForeignObject:(IJSVGForeignObject *)foreignObject
@property ( nonatomic, readonly ) NSRect viewBox;
@property ( nonatomic, readonly ) NSSize proposedViewSize;
+ (BOOL)isDataSVG:(NSData *)data;
- (id)initWithSVGString:(NSString *)string
error:(NSError **)error
delegate:(id<IJSVGParserDelegate>)delegate;
+28 -45
View File
@@ -116,18 +116,6 @@
}
+ (BOOL)isDataSVG:(NSData *)data
{
@try {
NSError * error;
NSXMLDocument * doc = [[[NSXMLDocument alloc] initWithData:data
options:0
error:&error] autorelease];
return doc != nil && error == nil;
} @catch(NSException * exception) {}
return NO;
}
- (id)initWithFileURL:(NSURL *)aURL
error:(NSError **)error
delegate:(id<IJSVGParserDelegate>)delegate
@@ -152,11 +140,10 @@
- (void *)_handleErrorWithCode:(NSUInteger)code
error:(NSError **)error
{
if( error ) {
if( error )
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
code:code
userInfo:nil] autorelease];
}
[_document release], _document = nil;
[self release], self = nil;
return nil;
@@ -170,12 +157,12 @@
// check the viewbox
if( NSEqualRects( self.viewBox, NSZeroRect ) ||
self.size.width == 0 || self.size.height == 0 ) {
if( error != NULL ) {
self.size.width == 0 || self.size.height == 0 )
{
if( error != NULL )
*error = [[[NSError alloc] initWithDomain:IJSVGErrorDomain
code:IJSVGErrorInvalidViewBox
userInfo:nil] autorelease];
}
return NO;
}
return YES;
@@ -198,19 +185,21 @@
// find the sizebox!
NSXMLNode * attribute = nil;
if( ( attribute = [svgElement attributeForName:(NSString *)IJSVGAttributeViewBox] ) != nil ) {
// we have a viewbox...
CGFloat * box = [IJSVGUtils parseViewBox:[attribute stringValue]];
viewBox = NSMakeRect( box[0], box[1], box[2], box[3]);
free(box);
} else {
// there is no view box so find the width and height
CGFloat w = [[[svgElement attributeForName:(NSString *)IJSVGAttributeWidth] stringValue] floatValue];
CGFloat h = [[[svgElement attributeForName:(NSString *)IJSVGAttributeHeight] stringValue] floatValue];
if( h == 0.f && w != 0.f ) {
if( h == 0.f && w != 0.f )
h = w;
} else if( w == 0.f && h != 0.f ) {
else if( w == 0.f && h != 0.f )
w = h;
}
viewBox = NSMakeRect( 0.f, 0.f, w, h );
}
@@ -225,7 +214,7 @@
} else if( h == 0 && w != 0.f ) {
h = viewBox.size.height;
}
proposedViewSize = NSMakeSize(w, h);
proposedViewSize = NSMakeSize( w, h );
// the root element is SVG, so iterate over its children
// recursively
@@ -262,8 +251,8 @@
if([ignoredAttributes containsObject:key]) {
return;
}
NSString * v = [style property:key] ?:
[element attributeForName:key].stringValue;
NSString * v = [element attributeForName:key].stringValue
?: [style property:key];
if(v != nil && v.length != 0) {
block(v);
}
@@ -614,21 +603,16 @@
// sub SVG
case IJSVGNodeTypeSVG: {
IJSVGGroup * path = [[[IJSVGGroup alloc] init] autorelease];
IJSVGPath * path = [[[IJSVGPath alloc] init] autorelease];
path.type = aType;
path.name = subName;
path.parentNode = parentGroup;
// grab common attributes
[self _setupDefaultsForNode:path];
[self _parseElementForCommonAttributes:element
node:path
ignoreAttributes:nil];
// if its a sub svg, we can remove the attributes for x and y
// this is required or it could go out of bounds before the exporter
// hits the layers from the groups :)
[element removeAttributeForName:@"x"];
[element removeAttributeForName:@"y"];
// work out the SVG
NSError * error = nil;
@@ -892,12 +876,6 @@
NSString * xlinkID = [xlink substringFromIndex:1];
IJSVGNode * node = [self definedObjectForID:xlinkID];
// there was no specified link ID, well, not that we could find,
// so just break
if(node == nil) {
break;
}
// due to this being a carbon clone, we need to clear the ID
if([element attributeForName:(NSString *)IJSVGAttributeID] == nil) {
node.identifier = nil;
@@ -1118,14 +1096,16 @@
NSUInteger len = [command length];
// allocate memory for the string buffer for reading
const char * buffer = [command cStringUsingEncoding:NSUTF8StringEncoding];
unichar * buffer = (unichar *)calloc( len+1, sizeof(unichar));
[command getCharacters:buffer
range:NSMakeRange(0, len)];
int defaultBufferSize = 200;
int currentBufferSize = 0;
int currentSize = defaultBufferSize;
unichar * commandBuffer = NULL;
if(len != 0) {
if( len != 0 ) {
commandBuffer = (unichar *)calloc(defaultBufferSize,sizeof(unichar));
}
@@ -1133,18 +1113,17 @@
for( int i = 0; i < len; i++ ) {
unichar currentChar = buffer[i];
unichar nextChar = buffer[i+1];
BOOL atEnd = i == len-1;
BOOL isStartCommand = IJSVGIsLegalCommandCharacter(nextChar);
if( ( currentBufferSize + 1 ) == currentSize ) {
currentSize += defaultBufferSize;
commandBuffer = (unichar *)realloc(commandBuffer, sizeof(unichar)*currentSize);
commandBuffer = (unichar *)realloc( commandBuffer, sizeof(unichar)*currentSize);
}
commandBuffer[currentBufferSize++] = currentChar;
if(isStartCommand == YES || atEnd == YES) {
if( isStartCommand || atEnd ) {
NSString * commandString = [NSString stringWithCharacters:commandBuffer
length:currentBufferSize];
// previous command is actual subcommand
IJSVGCommand * previousCommand = [_currentCommand subCommands].lastObject;
IJSVGCommand * cCommand = [self _parseCommandString:commandString
@@ -1156,15 +1135,19 @@
_currentCommand = cCommand;
}
if(atEnd == NO) {
free(commandBuffer);
commandBuffer = NULL;
if( !atEnd ) {
currentBufferSize = 0;
memset(commandBuffer,'\0', sizeof(unichar)*currentSize);
currentSize = defaultBufferSize;
commandBuffer = (unichar *)calloc(defaultBufferSize,sizeof(unichar));
}
}
}
// free the buffer
free(commandBuffer);
free(buffer);
}
- (IJSVGCommand *)_parseCommandString:(NSString *)string
-1
View File
@@ -24,7 +24,6 @@
{
if((self = [super init]) != nil) {
self.requiresBackingScaleHelp = YES;
self.shouldRasterize = YES;
}
return self;
}
+24
View File
@@ -0,0 +1,24 @@
//
// IJSVGRenderer.h
// IJSVGExample
//
// Created by Curtis Hard on 31/01/2018.
// Copyright © 2018 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
@class IJSVGLayer;
@interface IJSVGQuartzRenderer : NSObject {
}
@property (nonatomic, assign) CGFloat scale;
@property (nonatomic, assign) CGFloat backingScale;
@property (nonatomic, assign) CGRect viewPort;
- (void)renderLayer:(IJSVGLayer *)layer
inContext:(CGContextRef)context;
@end
+247
View File
@@ -0,0 +1,247 @@
//
// 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
+54 -50
View File
@@ -88,7 +88,7 @@
CGFloat * colorStops = [[self class] computeColorStopsFromString:element colors:&colors];
NSGradient * ret = [[[NSGradient alloc] initWithColors:colors
atLocations:colorStops
colorSpace:IJSVGColor.defaultColorSpace] autorelease];
colorSpace:[NSColorSpace genericRGBColorSpace]] autorelease];
free(colorStops);
return ret;
}
@@ -107,61 +107,65 @@
// transforms
CGAffineTransform selfTransform = IJSVGConcatTransforms(self.transforms);
CGContextSaveGState(ctx);
{
#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 {
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));
// 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);
}
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);
// transform the context
CGContextConcatCTM(ctx, selfTransform);
// draw the gradient
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation|
kCGGradientDrawsAfterEndLocation;
CGContextDrawRadialGradient(ctx, self.CGGradient,
gradientEndPoint, 0, gradientStartPoint,
radius, options);
};
CGContextRestoreGState(ctx);
}
@end
-21
View File
@@ -1,21 +0,0 @@
//
// IJSVGRendering.h
// IJSVGExample
//
// Created by Curtis Hard on 14/03/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef CGFloat (^IJSVGRenderingBackingScaleFactorHelper)();
typedef NS_ENUM(NSInteger, IJSVGRenderQuality) {
IJSVGRenderQualityFullResolution, // slowest to render
IJSVGRenderQualityOptimized, // best of both worlds
IJSVGRenderQualityLow // fast rendering
};
@interface IJSVGRendering : NSObject
@end
-13
View File
@@ -1,13 +0,0 @@
//
// IJSVGRendering.m
// IJSVGExample
//
// Created by Curtis Hard on 14/03/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import "IJSVGRendering.h"
@implementation IJSVGRendering
@end
-1
View File
@@ -23,7 +23,6 @@
@property (nonatomic, assign) IJSVGPatternLayer * patternStrokeLayer;
@property (nonatomic, assign) BOOL requiresBackingScaleHelp;
@property (nonatomic, assign) CGFloat backingScaleFactor;
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, assign) CGBlendMode blendingMode;
@property (nonatomic, assign) CGPoint absoluteOrigin;
@property (nonatomic, assign) CGPoint originalPathOrigin;
+2 -6
View File
@@ -21,12 +21,13 @@
@synthesize blendingMode;
@synthesize convertMasksToPaths;
@synthesize originalPathOrigin;
@synthesize renderQuality;
- (void)dealloc
{
IJSVGBeginTransactionLock();
[_maskingLayer release], _maskingLayer = nil;
[super dealloc];
IJSVGEndTransactionLock();
}
- (void)addSublayer:(CALayer *)layer {
@@ -154,9 +155,4 @@
return point;
}
- (id<CAAction>)actionForKey:(NSString *)event
{
return nil;
}
@end
+2 -13
View File
@@ -9,24 +9,13 @@
#import "IJSVGTransaction.h"
void IJSVGBeginTransactionLock() {
if(NSThread.isMainThread == YES) {
return;
}
[CATransaction begin];
if(@available(macOS 10.14, *)) {} else {
[CATransaction lock];
}
[CATransaction lock];
[CATransaction setDisableActions:YES];
};
void IJSVGEndTransactionLock() {
if(NSThread.isMainThread == YES) {
return;
}
if(@available(macOS 10.14, *)) {} else {
[CATransaction unlock];
}
[CATransaction unlock];
[CATransaction commit];
};
-1
View File
@@ -7,7 +7,6 @@
//
#import <Foundation/Foundation.h>
#include <xlocale.h>
#import "IJSVGCommand.h"
#import "IJSVGGradientUnitLength.h"
+21 -15
View File
@@ -178,8 +178,14 @@ NSString * IJSVGShortFloatStringWithPrecision(CGFloat f, NSInteger precision)
BOOL IJSVGIsLegalCommandCharacter(unichar aChar)
{
const char * validChars = "MmZzLlHhVvCcSsQqTtAa";
return strchr(validChars, aChar) != NULL;
char * validChars = "MmZzLlHhVvCcSsQqTtAa";
NSUInteger length = strlen(validChars);
for(NSUInteger i = 0; i < length; i++) {
if(aChar == validChars[i]) {
return YES;
}
}
return NO;
}
BOOL IJSVGIsSVGLayer(CALayer * layer)
@@ -214,7 +220,7 @@ CGFloat degrees_to_radians( CGFloat degrees )
+ (IJSVGCommandType)typeForCommandString:(NSString *)string
{
return isupper([string characterAtIndex:0]) ? IJSVGCommandTypeAbsolute : IJSVGCommandTypeRelative;
return [string isEqualToString:[string uppercaseString]] ? IJSVGCommandTypeAbsolute : IJSVGCommandTypeRelative;
}
+ (NSString *)defURL:(NSString *)string
@@ -393,7 +399,8 @@ CGFloat degrees_to_radians( CGFloat degrees )
+ (CGFloat *)commandParameters:(NSString *)command
count:(NSInteger *)count
{
if( [command isKindOfClass:[NSNumber class]] ) {
if( [command isKindOfClass:[NSNumber class]] )
{
CGFloat * ret = (CGFloat *)malloc(1*sizeof(CGFloat));
ret[0] = [(NSNumber *)command floatValue];
*count = 1;
@@ -408,11 +415,11 @@ CGFloat degrees_to_radians( CGFloat degrees )
{
// default sizes and memory
// sizes for the string buffer
const NSInteger defFloatSize = 30;
const NSInteger defSize = 15;
// default memory size for the float
NSInteger defSize = 50;
NSInteger size = defSize;
// default memory size for the floats
NSInteger defFloatSize = 100;
NSInteger floatSize = defFloatSize;
NSInteger i = 0;
@@ -439,7 +446,7 @@ CGFloat degrees_to_radians( CGFloat degrees )
nextChar = cString[i+1];
}
bool isValid = strchr(validChars, currentChar) != NULL;
bool isValid = strchr(validChars, currentChar);
// in order to work out the split, its either because the next char is
// a hyphen or a plus, or next char is a decimal and the current number is a decimal
@@ -476,7 +483,7 @@ CGFloat degrees_to_radians( CGFloat degrees )
// is at end of string, or wants to be stopped
// buffer has to actually exist or its completly
// useless and will cause a crash
if((buffer != NULL && bufferCount != 0) && (wantsEnd || i == sLength-1)) {
if(buffer != NULL && (wantsEnd || i == sLength-1)) {
// make sure there is enough room in the float pool
if((counter+1) == floatSize) {
floatSize += defFloatSize;
@@ -484,18 +491,17 @@ CGFloat degrees_to_radians( CGFloat degrees )
}
// add the float
floats[counter++] = strtod_l(buffer, NULL, NULL);
floats[counter++] = atof(buffer);
// memory clean and counter resets
memset(buffer, '\0', sizeof(*buffer)*size);
free(buffer);
size = defSize;
isDecimal = false;
bufferCount = 0;
buffer = NULL;
}
i++;
}
if(buffer != NULL) {
free(buffer);
}
*length = counter;
return floats;
}
+3 -3
View File
@@ -11,10 +11,10 @@
IB_DESIGNABLE
@interface IJSVGView : NSView {
IBInspectable NSString * imageName;
IBInspectable NSColor * tintColor;
IJSVG * SVG;
IBInspectable NSString * imageName;
IJSVG * SVG;
}
@property (nonatomic, retain) IJSVG * SVG;
+2 -3
View File
@@ -41,9 +41,8 @@
// image was set via IB
if(imageName != nil) {
IJSVG * anSVG = [IJSVG svgNamed:imageName];
if(tintColor != nil) {
anSVG.fillColor = tintColor;
}
// dont need the dom, so clean it
[anSVG discardDOM];
self.SVG = anSVG;
}
}