Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ffb55b0f31 | |||
| a4c032afa2 | |||
| abe8f4cba5 | |||
| a16842271b | |||
| 58126e06e4 | |||
| 5af5e054ab | |||
| edd3aa1f33 | |||
| 3366bc4fa5 | |||
| 104002183b | |||
| 7010b7ea50 | |||
| 5266a8c07a | |||
| caf55e8bdf | |||
| 8160d05eba | |||
| a6d6a06521 | |||
| 103a4d71f6 | |||
| 98874b1d2c | |||
| e742db31e0 | |||
| 198fd09f07 | |||
| 3493194b1b | |||
| 0016775eaf | |||
| 304a04cc22 | |||
| e4fd0af582 | |||
| 69a2a0c97e | |||
| 5299bb0479 | |||
| 4f1943cad1 | |||
| f02d186293 | |||
| 3291718cfb | |||
| 6fbaaf5884 | |||
| abc65797ea | |||
| af5a1c2718 | |||
| fb9a5282b9 | |||
| d83933a103 | |||
| bd7a0d5021 | |||
| a9a038568c | |||
| 77fbb38b6f | |||
| 12c3191569 | |||
| e3e9626ef7 | |||
| 1160d89f16 | |||
| 1575cbfde8 | |||
| 5c4c2eee91 | |||
| 087b13e58f | |||
| 1183e167aa | |||
| 4dbfc59437 | |||
| 409bd509fa | |||
| 8ae1d1b4e0 | |||
| 7243fbe5ff | |||
| 51b9a5e85f | |||
| 40098589de | |||
| 7cb96b21f2 | |||
| 1bff7c6970 |
@@ -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 */,
|
||||
);
|
||||
|
||||
BIN
Binary file not shown.
@@ -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"/>
|
||||
|
||||
@@ -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 |
@@ -11,9 +11,11 @@
|
||||
|
||||
@interface SVGView : NSView {
|
||||
|
||||
@private
|
||||
IJSVG * svg;
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)switchToCoreGraphics:(id)sender;
|
||||
- (IBAction)switchToCoreAnimation:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
@@ -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
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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:¶meterCount];
|
||||
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
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
|
||||
@implementation IJSVGCommandArc
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"a"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
@implementation IJSVGCommandCurve
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"c"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 6;
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
@implementation IJSVGCommandHorizontalLine
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"h"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 1;
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
@implementation IJSVGCommandLineTo
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"l"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 2;
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
|
||||
@implementation IJSVGCommandMove
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"m"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 2;
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
|
||||
@implementation IJSVGCommandQuadraticCurve
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"q"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 4;
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
|
||||
@implementation IJSVGCommandSmoothCurve
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"s"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 4;
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
@implementation IJSVGCommandVerticalLine
|
||||
|
||||
+ (void)load
|
||||
{
|
||||
[IJSVGCommand registerClass:[self class]
|
||||
forCommand:@"v"];
|
||||
}
|
||||
|
||||
+ (NSInteger)requiredParameterCount
|
||||
{
|
||||
return 1;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
{
|
||||
if((self = [super init]) != nil) {
|
||||
self.requiresBackingScaleHelp = YES;
|
||||
self.shouldRasterize = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <xlocale.h>
|
||||
#import "IJSVGCommand.h"
|
||||
#import "IJSVGGradientUnitLength.h"
|
||||
|
||||
|
||||
+21
-15
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user