diff --git a/Framework/IJSVG/IJSVG.xcodeproj/project.pbxproj b/Framework/IJSVG/IJSVG.xcodeproj/project.pbxproj index b033e22..eb065a3 100644 --- a/Framework/IJSVG/IJSVG.xcodeproj/project.pbxproj +++ b/Framework/IJSVG/IJSVG.xcodeproj/project.pbxproj @@ -7,6 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + 590AA08D280DDD90002BBE12 /* IJSVGFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 590AA08B280DDD90002BBE12 /* IJSVGFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 590AA08E280DDD90002BBE12 /* IJSVGFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 590AA08C280DDD90002BBE12 /* IJSVGFilter.m */; }; + 590AA091280DDDA3002BBE12 /* IJSVGFilterEffect.h in Headers */ = {isa = PBXBuildFile; fileRef = 590AA08F280DDDA3002BBE12 /* IJSVGFilterEffect.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 590AA092280DDDA3002BBE12 /* IJSVGFilterEffect.m in Sources */ = {isa = PBXBuildFile; fileRef = 590AA090280DDDA3002BBE12 /* IJSVGFilterEffect.m */; }; + 590AA096280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.h in Headers */ = {isa = PBXBuildFile; fileRef = 590AA094280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 590AA097280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.m in Sources */ = {isa = PBXBuildFile; fileRef = 590AA095280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.m */; }; 590EB7A427F637EF0047CECF /* IJSVGTransformLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 590EB7A227F637EF0047CECF /* IJSVGTransformLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 590EB7A527F637EF0047CECF /* IJSVGTransformLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 590EB7A327F637EF0047CECF /* IJSVGTransformLayer.m */; }; 5919E65723F47FF60051873A /* IJSVGUnitRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 5919E65523F47FF60051873A /* IJSVGUnitRect.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -20,6 +26,8 @@ 594CF55F238FF462009B251B /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF55E238FF462009B251B /* AppKit.framework */; }; 594CF561238FF46C009B251B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF560238FF46C009B251B /* Foundation.framework */; }; 594CF563238FF473009B251B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594CF562238FF473009B251B /* QuartzCore.framework */; }; + 595BA91C280EB29B00AD1C89 /* IJSVGFilterLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 595BA91A280EB29B00AD1C89 /* IJSVGFilterLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 595BA91D280EB29B00AD1C89 /* IJSVGFilterLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 595BA91B280EB29B00AD1C89 /* IJSVGFilterLayer.m */; }; 5978C46A280A241200D25296 /* IJSVGRootLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 5978C468280A241200D25296 /* IJSVGRootLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5978C46B280A241200D25296 /* IJSVGRootLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5978C469280A241200D25296 /* IJSVGRootLayer.m */; }; 599EB4D3238FF570004CB6BC /* libobjc.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 599EB4D2238FF535004CB6BC /* libobjc.tbd */; }; @@ -136,6 +144,8 @@ 59EB764623905F7300F5AE63 /* IJSVGRendering.m in Sources */ = {isa = PBXBuildFile; fileRef = 59EB75D523905F7300F5AE63 /* IJSVGRendering.m */; }; 59F36508262F1ABB00BCE3FD /* IJSVGColorType.h in Headers */ = {isa = PBXBuildFile; fileRef = 59F36506262F1ABB00BCE3FD /* IJSVGColorType.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59F36509262F1ABB00BCE3FD /* IJSVGColorType.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F36507262F1ABB00BCE3FD /* IJSVGColorType.m */; }; + 59F38A63280ED99200804FE4 /* IJSVGBasicLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 59F38A61280ED99200804FE4 /* IJSVGBasicLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 59F38A64280ED99200804FE4 /* IJSVGBasicLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F38A62280ED99200804FE4 /* IJSVGBasicLayer.m */; }; 59F9EAF82808BB5F00188ACB /* IJSVGViewBox.h in Headers */ = {isa = PBXBuildFile; fileRef = 59F9EAF62808BB5F00188ACB /* IJSVGViewBox.h */; settings = {ATTRIBUTES = (Public, ); }; }; 59F9EAF92808BB5F00188ACB /* IJSVGViewBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F9EAF72808BB5F00188ACB /* IJSVGViewBox.m */; }; 59FCC09427F2394D00BB924E /* IJSVGRootNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 59FCC09227F2394D00BB924E /* IJSVGRootNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -145,6 +155,12 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 590AA08B280DDD90002BBE12 /* IJSVGFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGFilter.h; sourceTree = ""; }; + 590AA08C280DDD90002BBE12 /* IJSVGFilter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGFilter.m; sourceTree = ""; }; + 590AA08F280DDDA3002BBE12 /* IJSVGFilterEffect.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGFilterEffect.h; sourceTree = ""; }; + 590AA090280DDDA3002BBE12 /* IJSVGFilterEffect.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGFilterEffect.m; sourceTree = ""; }; + 590AA094280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGFilterEffectGaussianBlur.h; sourceTree = ""; }; + 590AA095280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGFilterEffectGaussianBlur.m; sourceTree = ""; }; 590EB7A227F637EF0047CECF /* IJSVGTransformLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGTransformLayer.h; sourceTree = ""; }; 590EB7A327F637EF0047CECF /* IJSVGTransformLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGTransformLayer.m; sourceTree = ""; }; 5919E65523F47FF60051873A /* IJSVGUnitRect.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGUnitRect.h; sourceTree = ""; }; @@ -160,6 +176,8 @@ 594CF55E238FF462009B251B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 594CF560238FF46C009B251B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 594CF562238FF473009B251B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 595BA91A280EB29B00AD1C89 /* IJSVGFilterLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGFilterLayer.h; sourceTree = ""; }; + 595BA91B280EB29B00AD1C89 /* IJSVGFilterLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGFilterLayer.m; sourceTree = ""; }; 5978C468280A241200D25296 /* IJSVGRootLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGRootLayer.h; sourceTree = ""; }; 5978C469280A241200D25296 /* IJSVGRootLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGRootLayer.m; sourceTree = ""; }; 599EB4D2238FF535004CB6BC /* libobjc.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libobjc.tbd; path = usr/lib/libobjc.tbd; sourceTree = SDKROOT; }; @@ -276,6 +294,8 @@ 59EB75D523905F7300F5AE63 /* IJSVGRendering.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IJSVGRendering.m; sourceTree = ""; }; 59F36506262F1ABB00BCE3FD /* IJSVGColorType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGColorType.h; sourceTree = ""; }; 59F36507262F1ABB00BCE3FD /* IJSVGColorType.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGColorType.m; sourceTree = ""; }; + 59F38A61280ED99200804FE4 /* IJSVGBasicLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGBasicLayer.h; sourceTree = ""; }; + 59F38A62280ED99200804FE4 /* IJSVGBasicLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGBasicLayer.m; sourceTree = ""; }; 59F9EAF62808BB5F00188ACB /* IJSVGViewBox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGViewBox.h; sourceTree = ""; }; 59F9EAF72808BB5F00188ACB /* IJSVGViewBox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IJSVGViewBox.m; sourceTree = ""; }; 59FCC09227F2394D00BB924E /* IJSVGRootNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IJSVGRootNode.h; sourceTree = ""; }; @@ -299,6 +319,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 590AA093280DE648002BBE12 /* Filter Effects */ = { + isa = PBXGroup; + children = ( + 590AA094280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.h */, + 590AA095280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.m */, + ); + path = "Filter Effects"; + sourceTree = ""; + }; 592ABBE223979F9B00F44380 /* Commands */ = { isa = PBXGroup; children = ( @@ -379,6 +408,10 @@ 590EB7A327F637EF0047CECF /* IJSVGTransformLayer.m */, 5978C468280A241200D25296 /* IJSVGRootLayer.h */, 5978C469280A241200D25296 /* IJSVGRootLayer.m */, + 595BA91A280EB29B00AD1C89 /* IJSVGFilterLayer.h */, + 595BA91B280EB29B00AD1C89 /* IJSVGFilterLayer.m */, + 59F38A61280ED99200804FE4 /* IJSVGBasicLayer.h */, + 59F38A62280ED99200804FE4 /* IJSVGBasicLayer.m */, ); path = Layers; sourceTree = ""; @@ -397,6 +430,7 @@ 592ABBE72397A02900F44380 /* Nodes */ = { isa = PBXGroup; children = ( + 590AA093280DE648002BBE12 /* Filter Effects */, 59EB75CE23905F7200F5AE63 /* IJSVGForeignObject.h */, 59EB75C923905F7200F5AE63 /* IJSVGForeignObject.m */, 59EB758623905F6C00F5AE63 /* IJSVGDef.h */, @@ -423,6 +457,10 @@ 59FCC09327F2394D00BB924E /* IJSVGRootNode.m */, 59FDBEFE27F3454800AF7038 /* IJSVGColorNode.h */, 59FDBEFF27F3454800AF7038 /* IJSVGColorNode.m */, + 590AA08B280DDD90002BBE12 /* IJSVGFilter.h */, + 590AA08C280DDD90002BBE12 /* IJSVGFilter.m */, + 590AA08F280DDDA3002BBE12 /* IJSVGFilterEffect.h */, + 590AA090280DDDA3002BBE12 /* IJSVGFilterEffect.m */, ); path = Nodes; sourceTree = ""; @@ -631,6 +669,11 @@ 594A10DA248D7C90001A3181 /* NSImage+IJSVGAdditions.h in Headers */, 59EB762D23905F7300F5AE63 /* IJSVGLayerTree.h in Headers */, 59FDBF0027F3454800AF7038 /* IJSVGColorNode.h in Headers */, + 590AA091280DDDA3002BBE12 /* IJSVGFilterEffect.h in Headers */, + 590AA08D280DDD90002BBE12 /* IJSVGFilter.h in Headers */, + 59F38A63280ED99200804FE4 /* IJSVGBasicLayer.h in Headers */, + 595BA91C280EB29B00AD1C89 /* IJSVGFilterLayer.h in Headers */, + 590AA096280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.h in Headers */, 590EB7A427F637EF0047CECF /* IJSVGTransformLayer.h in Headers */, 5978C46A280A241200D25296 /* IJSVGRootLayer.h in Headers */, 59F9EAF82808BB5F00188ACB /* IJSVGViewBox.h in Headers */, @@ -712,7 +755,9 @@ 59EB75FD23905F7300F5AE63 /* IJSVGPatternLayer.m in Sources */, 59EB763923905F7300F5AE63 /* IJSVGCommandSmoothQuadraticCurve.m in Sources */, 59EB75D823905F7300F5AE63 /* IJSVGStyleSheetRule.m in Sources */, + 590AA092280DDDA3002BBE12 /* IJSVGFilterEffect.m in Sources */, 59EB75FB23905F7300F5AE63 /* IJSVGPath.m in Sources */, + 590AA097280DE668002BBE12 /* IJSVGFilterEffectGaussianBlur.m in Sources */, 59F36509262F1ABB00BCE3FD /* IJSVGColorType.m in Sources */, 59EB760123905F7300F5AE63 /* IJSVGLayerTree.m in Sources */, 59EB763A23905F7300F5AE63 /* IJSVGForeignObject.m in Sources */, @@ -727,6 +772,7 @@ 59EB75E523905F7300F5AE63 /* IJSVGStrokeLayer.m in Sources */, 59EB763223905F7300F5AE63 /* IJSVGView.m in Sources */, 59EB763523905F7300F5AE63 /* IJSVGStyleSheetSelector.m in Sources */, + 590AA08E280DDD90002BBE12 /* IJSVGFilter.m in Sources */, 59E7CFB023B148600077D599 /* IJSVGCommandParser.m in Sources */, 59EB760723905F7300F5AE63 /* IJSVGNode.m in Sources */, 5919E65C23F480330051873A /* IJSVGUnitPoint.m in Sources */, @@ -735,9 +781,11 @@ 59EB761723905F7300F5AE63 /* IJSVGRadialGradient.m in Sources */, 59EB75E223905F7300F5AE63 /* IJSVGColorList.m in Sources */, 59EB75ED23905F7300F5AE63 /* IJSVGFontConverter.m in Sources */, + 595BA91D280EB29B00AD1C89 /* IJSVGFilterLayer.m in Sources */, 59EB763C23905F7300F5AE63 /* IJSVGCommandEllipticalArc.m in Sources */, 59EB75E723905F7300F5AE63 /* IJSVGImage.m in Sources */, 59EB75E623905F7300F5AE63 /* IJSVGStyle.m in Sources */, + 59F38A64280ED99200804FE4 /* IJSVGBasicLayer.m in Sources */, 59EB75EB23905F7300F5AE63 /* IJSVGShapeLayer.m in Sources */, 59EB75F623905F7300F5AE63 /* IJSVGColor.m in Sources */, 59EB75F323905F7300F5AE63 /* IJSVGGroupLayer.m in Sources */, diff --git a/Framework/IJSVG/IJSVG/Source/Core/IJSVG.m b/Framework/IJSVG/IJSVG/Source/Core/IJSVG.m index 56abbcf..9286b11 100644 --- a/Framework/IJSVG/IJSVG/Source/Core/IJSVG.m +++ b/Framework/IJSVG/IJSVG/Source/Core/IJSVG.m @@ -750,6 +750,7 @@ CGContextSaveGState(ref); // make sure we setup a transaction CGFloat backingScale = [self backingScaleFactor]; + [IJSVGLayer logLayer:self.rootLayer]; [self.rootLayer renderInContext:ref viewPort:rect backingScale:backingScale diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGBasicLayer.h b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGBasicLayer.h new file mode 100644 index 0000000..3a04122 --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGBasicLayer.h @@ -0,0 +1,14 @@ +// +// IJSVGBasicLayer.h +// IJSVG +// +// Created by Curtis Hard on 19/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import +#import + +@interface IJSVGBasicLayer : CALayer + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGBasicLayer.m b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGBasicLayer.m new file mode 100644 index 0000000..ed65810 --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGBasicLayer.m @@ -0,0 +1,23 @@ +// +// IJSVGBasicLayer.m +// IJSVG +// +// Created by Curtis Hard on 19/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import "IJSVGBasicLayer.h" + +@implementation IJSVGBasicLayer + +@synthesize backingScaleFactor; +@synthesize renderQuality; +@synthesize debugLayers; + + +- (void)performRenderInContext:(CGContextRef)ctx { +} + + + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGFilterLayer.h b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGFilterLayer.h new file mode 100644 index 0000000..6e81fb1 --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGFilterLayer.h @@ -0,0 +1,23 @@ +// +// IJSVGFilterLayer.h +// IJSVG +// +// Created by Curtis Hard on 19/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import +#import +#import + +@interface IJSVGFilterLayer : IJSVGLayer { + +@private + IJSVGBasicLayer* _hostingLayer; + CGImageRef _image; + +} + +@property (nonatomic, retain) CALayer* sublayer; + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGFilterLayer.m b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGFilterLayer.m new file mode 100644 index 0000000..1d96221 --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGFilterLayer.m @@ -0,0 +1,70 @@ +// +// IJSVGFilterLayer.m +// IJSVG +// +// Created by Curtis Hard on 19/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import "IJSVGFilterLayer.h" +#import + +@implementation IJSVGFilterLayer + +- (void)dealloc +{ + (void)[_sublayer release], _sublayer = nil; + (void)CGImageRelease(_image), _image = nil; + [super dealloc]; +} + +- (instancetype)init +{ + if((self = [super init]) != nil) { + _hostingLayer = [IJSVGBasicLayer layer]; + [self addSublayer:_hostingLayer]; + } + return self; +} + +- (void)setBackingScaleFactor:(CGFloat)backingScaleFactor +{ + // we are responsible for recursively calling the sublayer + // with the new backing scale factor + [IJSVGLayer recursivelyWalkLayer:_sublayer + withBlock:^(CALayer* layer, BOOL *stop) { + layer.backingScaleFactor = backingScaleFactor; + }]; + BOOL needsChange = self.backingScaleFactor != backingScaleFactor; + [super setBackingScaleFactor:backingScaleFactor]; + if(needsChange == YES) { + [self updateImage]; + } +} + +- (void)updateImage +{ + if(_image != nil) { + (void)CGImageRelease(_image), _image = nil; + } + _image = [self.filter newImageByApplyFilterToLayer:_sublayer + scale:1.f]; +} + +- (void)layoutSublayers +{ + CGFloat width = CGImageGetWidth(_image); + CGFloat height = CGImageGetHeight(_image); + CGRect frame = _sublayer.innerBoundingBox; + _hostingLayer.frame = CGRectMake(frame.origin.x + (frame.size.width / 2.f - width / 2.f), + frame.origin.y + (frame.size.height / 2.f - height / 2.f), + width, height); + _hostingLayer.contents = (id)_image; +} + +- (NSArray*>*)debugLayers +{ + return @[_sublayer]; +} + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGGroupLayer.m b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGGroupLayer.m index 7cc4723..33a7e28 100644 --- a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGGroupLayer.m +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGGroupLayer.m @@ -19,4 +19,9 @@ [super dealloc]; } +- (CGRect)innerBoundingBox +{ + return self.outerBoundingBox; +} + @end diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGImageLayer.h b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGImageLayer.h index a9ebe24..884ab43 100644 --- a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGImageLayer.h +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGImageLayer.h @@ -9,13 +9,14 @@ #import #import #import +#import #import #import @interface IJSVGImageLayer : IJSVGLayer { @private - IJSVGLayer* _imageLayer; + CALayer* _imageLayer; } @property (nonatomic, retain) IJSVGImage* image; diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGImageLayer.m b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGImageLayer.m index 0cac549..2cd89d2 100644 --- a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGImageLayer.m +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGImageLayer.m @@ -40,7 +40,7 @@ - (void)reloadContent { if(_imageLayer == nil) { - _imageLayer = [IJSVGLayer layer].retain; + _imageLayer = [IJSVGBasicLayer layer].retain; _imageLayer.contentsGravity = kCAGravityResize; _imageLayer.affineTransform = CGAffineTransformMakeScale(1.f, -1.f); _imageLayer.contents = (id)_image.CGImage; diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGLayer.h b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGLayer.h index a87ae0d..169f8e3 100644 --- a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGLayer.h +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGLayer.h @@ -16,6 +16,7 @@ @class IJSVGStrokeLayer; @class IJSVGGroupLayer; @class IJSVGLayer; +@class IJSVGFilter; typedef NS_ENUM(NSUInteger, IJSVGLayerFillType) { IJSVGLayerFillTypeColor, @@ -35,7 +36,15 @@ typedef NS_OPTIONS(NSUInteger, IJSVGLayerTraits) { @end -@protocol IJSVGDrawableLayer +@protocol IJSVGBasicLayer +@required +@property (nonatomic, assign) CGFloat backingScaleFactor; +@property (nonatomic, assign) IJSVGRenderQuality renderQuality; +@property (nonatomic, readonly) NSArray*>* debugLayers; + +@end + +@protocol IJSVGDrawableLayer @required @property (nonatomic, assign) CGBlendMode blendingMode; @@ -44,8 +53,6 @@ typedef NS_OPTIONS(NSUInteger, IJSVGLayerTraits) { @property (nonatomic, copy) CAShapeLayerFillRule clipRule; @property (nonatomic, copy) CAShapeLayerFillRule fillRule; @property (nonatomic, readonly) CGPoint absoluteOrigin; -@property (nonatomic, assign) IJSVGRenderQuality renderQuality; -@property (nonatomic, assign) CGFloat backingScaleFactor; @property (nonatomic, readonly) BOOL requiresBackingScaleHelp; @property (nonatomic, readonly) CALayer* rootLayer; @property (nonatomic, readonly) CGRect absoluteFrame; @@ -53,8 +60,9 @@ typedef NS_OPTIONS(NSUInteger, IJSVGLayerTraits) { @property (nonatomic, readonly) CGRect strokeBoundingBox; @property (nonatomic, readonly) CGRect boundingBoxBounds; @property (nonatomic, assign) CGRect outerBoundingBox; +@property (nonatomic, readonly) CGRect innerBoundingBox; @property (nonatomic, assign) CALayer* referencingLayer; -@property (nonatomic, readonly) NSArray*>* debugLayers; +@property (nonatomic, retain) IJSVGFilter* filter; - (void)performRenderInContext:(CGContextRef)ctx; @@ -81,20 +89,21 @@ typedef NS_OPTIONS(NSUInteger, IJSVGLayerTraits) { @property (nonatomic, copy) CAShapeLayerFillRule clipRule; @property (nonatomic, copy) CAShapeLayerFillRule fillRule; @property (nonatomic, retain) CALayer* maskLayer; +@property (nonatomic, retain) IJSVGFilter* filter; @property (nonatomic, readonly) CALayer* rootLayer; @property (nonatomic, readonly) CGRect absoluteFrame; @property (nonatomic, assign) CGRect boundingBox; @property (nonatomic, readonly) CGRect boundingBoxBounds; @property (nonatomic, readonly) CGRect strokeBoundingBox; @property (nonatomic, assign) CGRect outerBoundingBox; -@property (nonatomic, readonly) CGRect outerBoundingBoxBounds; +@property (nonatomic, readonly) CGRect innerBoundingBox; @property (nonatomic, assign) CALayer* referencingLayer; + (IJSVGLayerFillType)fillTypeForFill:(id)fill; + (NSArray*)deepestSublayersOfLayer:(CALayer*)layer; -+ (void)recursivelyWalkLayer:(CALayer*)layer - withBlock:(void (^)(CALayer* layer, BOOL* stop))block; ++ (void)recursivelyWalkLayer:(CALayer*)layer + withBlock:(void (^)(CALayer* layer, BOOL* stop))block; - (void)applySublayerMaskToContext:(CGContextRef)context forSublayer:(IJSVGLayer*)sublayer diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGLayer.m b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGLayer.m index 19be8ec..a037e88 100644 --- a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGLayer.m +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGLayer.m @@ -17,6 +17,7 @@ - (void)dealloc { + (void)([_filter release]), _filter = nil; (void)([_clipLayer release]), _clipLayer = nil; (void)([_maskLayer release]), _maskLayer = nil; (void)([_clipRule release]), _clipRule = nil; @@ -61,6 +62,22 @@ return IJSVGLayerFillTypeUnknown; } ++ (void)applyAbsoluteTransformForLayer:(CALayer*)layer + toContext:(CGContextRef)ctx +{ + CALayer* parentLayer = layer; + while((parentLayer = parentLayer.referencingLayer) != nil) { + CGContextConcatCTM(ctx, parentLayer.affineTransform); + + // only go up until we find a root layer, at that point, we know + // we can stop looking + if([parentLayer isKindOfClass:IJSVGRootLayer.class] == YES) { + break; + } + } + CGContextConcatCTM(ctx, layer.affineTransform); +} + + (CGAffineTransform)absoluteTransformForLayer:(CALayer*)layer { CGAffineTransform identity = CGAffineTransformIdentity; @@ -72,8 +89,7 @@ // we can stop looking if([parentLayer isKindOfClass:IJSVGRootLayer.class] == YES) { break; - } - + } } return CGAffineTransformConcat(identity, layer.affineTransform); } @@ -88,8 +104,8 @@ return parentLayer; } -+ (void)renderLayer:(CALayer*)layer - inContext:(CGContextRef)ctx ++ (void)performBasicRenderOfLayer:(CALayer*)layer + inContext:(CGContextRef)ctx { dispatch_block_t drawingBlock = ^{ [layer performRenderInContext:ctx]; @@ -109,6 +125,13 @@ }]; } ++ (void)renderLayer:(CALayer*)layer + inContext:(CGContextRef)ctx +{ + [self performBasicRenderOfLayer:layer + inContext:ctx]; +} + + (void)applyBlendingMode:(CGBlendMode)blendMode toContext:(CGContextRef)ctx drawingBlock:(dispatch_block_t)drawingBlock @@ -224,17 +247,20 @@ bitmapInfo:(uint32_t)bitmapInfo scale:(CGFloat)scale { + CGRect frame = layer.outerBoundingBox; CGContextRef offscreenContext = CGBitmapContextCreate(NULL, ceilf(frame.size.width * scale), ceilf(frame.size.height * scale), 8, 0, colorSpace, bitmapInfo); - - CGContextSaveGState(offscreenContext); + CGContextConcatCTM(offscreenContext, [self absoluteTransformForLayer:layer]); CGContextScaleCTM(offscreenContext, scale, scale); - CGContextTranslateCTM(offscreenContext, -frame.origin.x, -frame.origin.y); - [layer renderInContext:offscreenContext]; + [IJSVGLayer renderLayer:layer + inContext:offscreenContext]; CGImageRef image = CGBitmapContextCreateImage(offscreenContext); + NSImage* prev = [[[NSImage alloc] initWithCGImage:image + size:NSMakeSize(CGImageGetWidth(image), CGImageGetHeight(image))] autorelease]; + CGContextRelease(offscreenContext); return image; } @@ -273,8 +299,8 @@ return arr; } -+ (void)recursivelyWalkLayer:(CALayer*)layer - withBlock:(void (^)(CALayer* layer, BOOL* stop))block ++ (void)recursivelyWalkLayer:(CALayer*)layer + withBlock:(void (^)(CALayer* layer, BOOL* stop))block { // call for layer and mask if there is one BOOL stop = NO; @@ -284,7 +310,7 @@ } // sublayers!! - for (CALayer* aLayer in layer.sublayers) { + for (CALayer* aLayer in layer.sublayers) { [self recursivelyWalkLayer:aLayer withBlock:block]; } @@ -466,14 +492,6 @@ return CGRectIsNull(_outerBoundingBox) == NO ? _outerBoundingBox : self.frame; } -- (CGRect)outerBoundingBoxBounds -{ - return (CGRect) { - .origin = CGPointZero, - .size = self.outerBoundingBox.size - }; -} - - (CGRect)boundingBoxBounds { return (CGRect) { @@ -482,6 +500,14 @@ }; } +- (CGRect)innerBoundingBox +{ + return (CGRect) { + .origin = CGPointZero, + .size = self.outerBoundingBox.size + }; +} + - (CGRect)strokeBoundingBox { return self.boundingBox; diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGRootLayer.m b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGRootLayer.m index 5fb46c5..63934e0 100644 --- a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGRootLayer.m +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGRootLayer.m @@ -18,7 +18,7 @@ IJSVGViewBoxDrawingBlock drawingBlock = ^(CGSize size) { // we have to make sure we set the backing scale factor once // we know how scale this will be drawn at - weakSelf.backingScaleFactor *= MAX(size.width, size.height); + weakSelf.backingScaleFactor *= MIN(size.width, size.height); [super performRenderInContext:ctx]; }; [IJSVGViewBox drawViewBox:viewBox diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGShapeLayer.h b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGShapeLayer.h index f65fbd0..e4860d3 100644 --- a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGShapeLayer.h +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGShapeLayer.h @@ -36,7 +36,9 @@ @property (nonatomic, readonly) CGRect absoluteFrame; @property (nonatomic, assign) CGRect boundingBox; @property (nonatomic, assign) CGRect outerBoundingBox; +@property (nonatomic, readonly) CGRect innerBoundingBox; @property (nonatomic, readonly) CGRect boundingBoxBounds; +@property (nonatomic, retain) IJSVGFilter* filter; @property (nonatomic, assign) CALayer* referencingLayer; @property (nonatomic, assign) CGRect strokeBoundingBox; @property (nonatomic, copy) CAShapeLayerFillRule clipRule; diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGShapeLayer.m b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGShapeLayer.m index 02fa904..b1ae5ad 100644 --- a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGShapeLayer.m +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGShapeLayer.m @@ -13,6 +13,7 @@ - (void)dealloc { + (void)([_filter release]), _filter = nil; (void)([_clipLayer release]), _clipLayer = nil; (void)([_maskLayer release]), _maskLayer = nil; (void)([_maskingLayer release]), _maskingLayer = nil; @@ -48,6 +49,11 @@ }; } +- (CGRect)innerBoundingBox +{ + return self.bounds; +} + - (BOOL)requiresBackingScaleHelp { return _maskLayer != nil || _clipLayer != nil; diff --git a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGTransformLayer.m b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGTransformLayer.m index ae79b49..c375302 100644 --- a/Framework/IJSVG/IJSVG/Source/Layers/IJSVGTransformLayer.m +++ b/Framework/IJSVG/IJSVG/Source/Layers/IJSVGTransformLayer.m @@ -21,9 +21,12 @@ @synthesize boundingBoxBounds; @synthesize strokeBoundingBox; @synthesize outerBoundingBox; +@synthesize filter = _filter; +@synthesize innerBoundingBox; - (void)dealloc { + (void)[_filter release], _filter = nil; (void)[_maskLayer release], _maskLayer = nil; (void)[_fillRule release], _fillRule = nil; (void)[_clipRule release], _clipRule = nil; diff --git a/Framework/IJSVG/IJSVG/Source/Nodes/Filter Effects/IJSVGFilterEffectGaussianBlur.h b/Framework/IJSVG/IJSVG/Source/Nodes/Filter Effects/IJSVGFilterEffectGaussianBlur.h new file mode 100644 index 0000000..dcb405f --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Nodes/Filter Effects/IJSVGFilterEffectGaussianBlur.h @@ -0,0 +1,13 @@ +// +// IJSVGFilterEffectGaussianBlur.h +// IJSVG +// +// Created by Curtis Hard on 18/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import + +@interface IJSVGFilterEffectGaussianBlur : IJSVGFilterEffect + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Nodes/Filter Effects/IJSVGFilterEffectGaussianBlur.m b/Framework/IJSVG/IJSVG/Source/Nodes/Filter Effects/IJSVGFilterEffectGaussianBlur.m new file mode 100644 index 0000000..21ff927 --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Nodes/Filter Effects/IJSVGFilterEffectGaussianBlur.m @@ -0,0 +1,22 @@ +// +// IJSVGFilterEffectGaussianBlur.m +// IJSVG +// +// Created by Curtis Hard on 18/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import "IJSVGFilterEffectGaussianBlur.h" + +@implementation IJSVGFilterEffectGaussianBlur + +- (CIImage*)processImage:(CIImage*)image +{ + CIFilter* filter = [CIFilter filterWithName:@"CIGaussianBlur"]; + [filter setDefaults]; + [filter setValue:image forKey:kCIInputImageKey]; + [filter setValue:@(self.stdDeviation.value) forKey:kCIInputRadiusKey]; + return [filter valueForKey:kCIOutputImageKey]; +} + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGColorNode.h b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGColorNode.h index a958419..341fd54 100644 --- a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGColorNode.h +++ b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGColorNode.h @@ -13,6 +13,7 @@ } @property (nonatomic, retain) NSColor* color; +@property (nonatomic, assign) BOOL isNoneOrTransparent; + (IJSVGNode*)colorNodeWithColor:(NSColor*)color; diff --git a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilter.h b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilter.h new file mode 100644 index 0000000..598d4b8 --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilter.h @@ -0,0 +1,17 @@ +// +// IJSVGFilter.h +// IJSVG +// +// Created by Curtis Hard on 18/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import +#import + +@interface IJSVGFilter : IJSVGGroup + +- (CGImageRef)newImageByApplyFilterToLayer:(CALayer*)layer + scale:(CGFloat)scale; + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilter.m b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilter.m new file mode 100644 index 0000000..d71791a --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilter.m @@ -0,0 +1,41 @@ +// +// IJSVGFilter.m +// IJSVG +// +// Created by Curtis Hard on 18/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import "IJSVGFilter.h" +#import "IJSVGFilterEffect.h" +#import "IJSVGLayer.h" + +@implementation IJSVGFilter + +- (CGImageRef)newImageByApplyFilterToLayer:(CALayer*)layer + scale:(CGFloat)scale +{ + IJSVGFilter* filter = layer.filter; + layer.filter = nil; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGImageRef originalImage = [IJSVGLayer newImageForLayer:layer + colorSpace:colorSpace + bitmapInfo:kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little + scale:1.f]; + CIImage* image = [CIImage imageWithCGImage:originalImage]; + CGColorSpaceRelease(colorSpace); + CGImageRelease(originalImage); + for(IJSVGFilterEffect* effect in self.children) { + image = [effect processImage:image]; + } + CIContext* context = [CIContext context]; + CGImageRef outputImage = [context createCGImage:image + fromRect:image.extent]; + NSImage* nimage = [[[NSImage alloc] initWithCGImage:outputImage + size:NSMakeSize(CGImageGetWidth(outputImage), + CGImageGetHeight(outputImage))] autorelease]; + layer.filter = filter; + return outputImage; +} + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilterEffect.h b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilterEffect.h new file mode 100644 index 0000000..1d2f1d6 --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilterEffect.h @@ -0,0 +1,40 @@ +// +// IJSVGFilterEffect.h +// IJSVG +// +// Created by Curtis Hard on 18/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import + +typedef NS_ENUM(NSInteger, IJSVGFilterEffectSource) { + IJSVGFilterEffectSourceGraphic, + IJSVGFilterEffectSourceAlpha, + IJSVGFilterEffectSourceBackgroundImage, + IJSVGFilterEffectSourceBackgroundAlpha, + IJSVGFilterEffectSourceFillPaint, + IJSVGFilterEffectSourceStrokePaint, + IJSVGFilterEffectSourcePrimitiveReference, +}; + +typedef NS_ENUM(NSInteger, IJSVGFilterEffectEdgeMode) { + IJSVGFilterEffectEdgeModeNone, + IJSVGFilterEffectEdgeModeWrap, + IJSVGFilterEffectEdgeModeDuplicate, +}; + +@interface IJSVGFilterEffect : IJSVGGroup + +@property (nonatomic, assign) IJSVGFilterEffectSource source; +@property (nonatomic, retain) IJSVGUnitLength* stdDeviation; +@property (nonatomic, assign) IJSVGFilterEffectEdgeMode edgeMode; +@property (nonatomic, copy) NSString* primitiveReference; + ++ (Class)effectClassForElementName:(NSString*)name; ++ (IJSVGFilterEffectSource)sourceForString:(NSString*)string; ++ (IJSVGFilterEffectEdgeMode)edgeModeForString:(NSString*)string; + +- (CIImage*)processImage:(CIImage*)image; + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilterEffect.m b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilterEffect.m new file mode 100644 index 0000000..7a643ba --- /dev/null +++ b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGFilterEffect.m @@ -0,0 +1,86 @@ +// +// IJSVGFilterEffect.m +// IJSVG +// +// Created by Curtis Hard on 18/04/2022. +// Copyright © 2022 Curtis Hard. All rights reserved. +// + +#import "IJSVGFilterEffect.h" +#import "IJSVGFilterEffectGaussianBlur.h" + +@implementation IJSVGFilterEffect + +static NSDictionary* _elementClassMap = nil; + ++ (void)load +{ + _elementClassMap = [@{ + @"fegaussianblur": IJSVGFilterEffectGaussianBlur.class + } retain]; +} + ++ (Class)effectClassForElementName:(NSString*)name +{ + NSString* key = name.lowercaseString; + return _elementClassMap[key] ?: IJSVGFilterEffect.class; +} + ++ (IJSVGFilterEffectSource)sourceForString:(NSString*)string +{ + const char* name = string.lowercaseString.UTF8String; + if(name == NULL) { + return IJSVGFilterEffectSourceGraphic; + } + if(strcmp(name, "sourcegraphic") == 0) { + return IJSVGFilterEffectSourceGraphic; + } + if(strcmp(name, "sourcealpha") == 0) { + return IJSVGFilterEffectSourceAlpha; + } + if(strcmp(name, "backgroundimage") == 0) { + return IJSVGFilterEffectSourceBackgroundImage; + } + if(strcmp(name, "backgroundalpha") == 0) { + return IJSVGFilterEffectSourceBackgroundAlpha; + } + if(strcmp(name, "fillpaint") == 0) { + return IJSVGFilterEffectSourceFillPaint; + } + if(strcmp(name, "strokepain") == 0) { + return IJSVGFilterEffectSourceStrokePaint; + } + return IJSVGFilterEffectSourcePrimitiveReference; +} + ++ (IJSVGFilterEffectEdgeMode)edgeModeForString:(NSString*)string +{ + const char* name = string.lowercaseString.UTF8String; + if(name == NULL) { + return IJSVGFilterEffectEdgeModeNone; + } + if(strcmp(name, "none") == 0) { + return IJSVGFilterEffectEdgeModeNone; + } + if(strcmp(name, "wrap") == 0) { + return IJSVGFilterEffectEdgeModeWrap; + } + if(strcmp(name, "duplicate") == 0) { + return IJSVGFilterEffectEdgeModeDuplicate; + } + return IJSVGFilterEffectEdgeModeNone; +} + +- (void)dealloc +{ + (void)[_primitiveReference release], _primitiveReference = nil; + (void)[_stdDeviation release], _stdDeviation = nil; + [super dealloc]; +} + +- (CIImage*)processImage:(CIImage*)image +{ + return image; +} + +@end diff --git a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGNode.h b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGNode.h index 4dad276..74b73c9 100644 --- a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGNode.h +++ b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGNode.h @@ -21,6 +21,7 @@ @class IJSVGTransform; @class IJSVGRootNode; @class IJSVGUnitRect; +@class IJSVGFilter; typedef void (^IJSVGNodeWalkHandler)(IJSVGNode* node, BOOL* allowChildNodes, BOOL* stop); @@ -58,6 +59,8 @@ typedef NS_ENUM(NSInteger, IJSVGNodeType) { IJSVGNodeTypeDesc, IJSVGNodeTypeStop, IJSVGNodeTypeNotFound, + IJSVGNodeTypeFilter, + IJSVGNodeTypeFilterEffect, IJSVGNodeTypeUnknown, }; @@ -151,6 +154,7 @@ void IJSVGAssertPaintableObject(id object); @property (nonatomic, assign) IJSVGLineCapStyle lineCapStyle; @property (nonatomic, assign) IJSVGLineJoinStyle lineJoinStyle; @property (nonatomic, retain) NSArray* transforms; +@property (nonatomic, retain) IJSVGFilter* filter; @property (nonatomic, assign) CGFloat* strokeDashArray; @property (nonatomic, assign) NSInteger strokeDashArrayCount; @property (nonatomic, readonly) NSArray* lineDashPattern; diff --git a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGNode.m b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGNode.m index 8628675..4b07657 100644 --- a/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGNode.m +++ b/Framework/IJSVG/IJSVG/Source/Nodes/IJSVGNode.m @@ -43,6 +43,7 @@ (void)([_svg release]), _svg = nil; (void)([_mask release]), _mask = nil; (void)([_viewBox release]), _viewBox = nil; + (void)([_filter release]), _filter = nil; [super dealloc]; } @@ -136,6 +137,12 @@ if(strcmp(name, "desc") == 0) { return IJSVGNodeTypeDesc; } + if(strcmp(name, "filter") == 0) { + return IJSVGNodeTypeFilter; + } + if(strcmp(name, "fegaussianblur") == 0) { + return IJSVGNodeTypeFilterEffect; + } return IJSVGNodeTypeUnknown; } @@ -442,7 +449,8 @@ - (NSString *)description { - return [NSString stringWithFormat:@"%@ %@ %@",self.name,self.classNameList,self.identifier]; + return [NSString stringWithFormat:@"%@ %@ %@ %@",NSStringFromClass(self.class), + self.name,self.classNameList,self.identifier]; } @end diff --git a/Framework/IJSVG/IJSVG/Source/Parsing/IJSVGParser.h b/Framework/IJSVG/IJSVG/Source/Parsing/IJSVGParser.h index ae24bee..2da48b1 100644 --- a/Framework/IJSVG/IJSVG/Source/Parsing/IJSVGParser.h +++ b/Framework/IJSVG/IJSVG/Source/Parsing/IJSVGParser.h @@ -23,6 +23,8 @@ #import #import #import +#import +#import #import #import @@ -77,6 +79,10 @@ extern NSString* const IJSVGAttributeStopColor; extern NSString* const IJSVGAttributeStopOpacity; extern NSString* const IJSVGAttributeHref; extern NSString* const IJSVGAttributeOverflow; +extern NSString* const IJSVGAttributeFilter; +extern NSString* const IJSVGAttributeStdDeviation; +extern NSString* const IJSVGAttributeIn; +extern NSString* const IJSVGAttributeEdgeMode; @class IJSVGParser; diff --git a/Framework/IJSVG/IJSVG/Source/Parsing/IJSVGParser.m b/Framework/IJSVG/IJSVG/Source/Parsing/IJSVGParser.m index 2c06b79..dda672f 100644 --- a/Framework/IJSVG/IJSVG/Source/Parsing/IJSVGParser.m +++ b/Framework/IJSVG/IJSVG/Source/Parsing/IJSVGParser.m @@ -64,6 +64,10 @@ NSString* const IJSVGAttributeStopColor = @"stop-color"; NSString* const IJSVGAttributeStopOpacity = @"stop-opacity"; NSString* const IJSVGAttributeHref = @"href"; NSString* const IJSVGAttributeOverflow = @"overflow"; +NSString* const IJSVGAttributeFilter = @"filter"; +NSString* const IJSVGAttributeStdDeviation = @"stdDeviation"; +NSString* const IJSVGAttributeIn = @"in"; +NSString* const IJSVGAttributeEdgeMode = @"edgeMode"; @implementation IJSVGParser @@ -466,7 +470,11 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil; return; } NSColor* color = [IJSVGColor colorFromString:value]; - node.stroke = [IJSVGColorNode colorNodeWithColor:color]; + IJSVGColorNode* colorNode = (IJSVGColorNode*)[IJSVGColorNode colorNodeWithColor:color]; + if(color == nil) { + colorNode.isNoneOrTransparent = [IJSVGColor isNoneOrTransparent:value]; + } + node.stroke = colorNode; }); // stroke dash array @@ -495,7 +503,11 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil; return; } NSColor* color = [IJSVGColor colorFromString:value]; - node.fill = [IJSVGColorNode colorNodeWithColor:color]; + IJSVGColorNode* colorNode = (IJSVGColorNode*)[IJSVGColorNode colorNodeWithColor:color]; + if(color == nil) { + colorNode.isNoneOrTransparent = [IJSVGColor isNoneOrTransparent:value]; + } + node.fill = colorNode; }); // fill opacity @@ -537,11 +549,15 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil; // stop-color IJSVGAttributeParse(IJSVGAttributeStopColor, ^(NSString* value) { NSColor* color = [IJSVGColor colorFromString:value]; - if(node.fillOpacity.value != 1.f) { + IJSVGColorNode* colorNode = (IJSVGColorNode*)[IJSVGColorNode colorNodeWithColor:color]; + if(color == nil) { + colorNode.isNoneOrTransparent = [IJSVGColor isNoneOrTransparent:value]; + } else if(node.fillOpacity.value != 1.f) { color = [IJSVGColor changeAlphaOnColor:color to:node.fillOpacity.value]; + colorNode.color = color; } - node.fill = [IJSVGColorNode colorNodeWithColor:color]; + node.fill = colorNode; }); // overflow @@ -571,6 +587,39 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil; node.viewBoxAlignment = alignment; node.viewBoxMeetOrSlice = meetOrSlice; }); + + // filters + IJSVGAttributeParse(IJSVGAttributeFilter, ^(NSString* value) { + NSString* filterIdentifier = [IJSVGUtils defURL:value]; + if(filterIdentifier != nil) { + IJSVGNode* filter = [self computeDetachedNodeWithIdentifier:filterIdentifier + referencingElement:element + referencingNode:node]; + node.filter = (IJSVGFilter*)filter; + } + }); + + if(node.type == IJSVGNodeTypeFilterEffect) { + IJSVGFilterEffect* effect = (IJSVGFilterEffect*)node; + + // in + IJSVGAttributeParse(IJSVGAttributeIn, ^(NSString* value) { + effect.source = [IJSVGFilterEffect sourceForString:value]; + if(effect.source == IJSVGFilterEffectSourcePrimitiveReference) { + effect.primitiveReference = value; + } + }); + + // edge mode + IJSVGAttributeParse(IJSVGAttributeEdgeMode, ^(NSString* value) { + effect.edgeMode = [IJSVGFilterEffect edgeModeForString:value]; + }); + + // deviation + IJSVGAttributeParse(IJSVGAttributeStdDeviation, ^(NSString* value) { + effect.stdDeviation = [IJSVGUnitLength unitWithString:value]; + }); + } } - (IJSVGNode*)parseElement:(NSXMLElement*)element @@ -693,6 +742,16 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil; parentNode:node]; break; } + case IJSVGNodeTypeFilter: { + computedNode = [self parseFilterElement:element + parentNode:node]; + break; + } + case IJSVGNodeTypeFilterEffect: { + computedNode = [self parseFilterEffectElement:element + parentNode:node]; + break; + } default: break; } @@ -787,6 +846,44 @@ static NSDictionary* _IJSVGAttributeDictionaryTransforms = nil; [_styleSheet parseStyleBlock:element.stringValue]; } +- (IJSVGNode*)parseFilterElement:(NSXMLElement*)element + parentNode:(IJSVGNode*)parentNode +{ + IJSVGFilter* node = [[[IJSVGFilter alloc] init] autorelease]; + node.type = IJSVGNodeTypeFilter; + node.name = element.localName; + + [self computeAttributesFromElement:element + onNode:node + ignoredAttributes:nil]; + + [self computeElement:element + parentNode:node]; + return node; +} + +- (IJSVGNode*)parseFilterEffectElement:(NSXMLElement*)element + parentNode:(IJSVGNode*)parentNode +{ + Class effectClass = [IJSVGFilterEffect effectClassForElementName:element.localName]; + IJSVGFilterEffect* node = [[[effectClass alloc] init] autorelease]; + node.type = IJSVGNodeTypeFilterEffect; + node.name = element.localName; + + if([parentNode isKindOfClass:IJSVGGroup.class] == YES) { + IJSVGGroup* group = (IJSVGGroup*)parentNode; + [group addChild:node]; + } + + [self computeAttributesFromElement:element + onNode:node + ignoredAttributes:nil]; + + [self computeElement:element + parentNode:node]; + return node; +} + - (IJSVGNode*)parseLinearGradientElement:(NSXMLElement*)element parentNode:(IJSVGNode*)parentNode { diff --git a/Framework/IJSVG/IJSVG/Source/Rendering/IJSVGLayerTree.m b/Framework/IJSVG/IJSVG/Source/Rendering/IJSVGLayerTree.m index 5ddb8c7..0419200 100644 --- a/Framework/IJSVG/IJSVG/Source/Rendering/IJSVGLayerTree.m +++ b/Framework/IJSVG/IJSVG/Source/Rendering/IJSVGLayerTree.m @@ -25,6 +25,7 @@ #import #import #import +#import @implementation IJSVGLayerTree @@ -91,6 +92,9 @@ if(layer != nil) { [self applyDefaultsToLayer:layer fromNode:node]; + layer = [self applyFilter:node.filter + toLayer:layer + fromNode:node]; return [self applyTransforms:node.transforms toLayer:layer fromNode:node]; @@ -164,10 +168,14 @@ IJSVGColorNode* colorNode = (IJSVGColorNode*)fill; NSColor* color = colorNode.color; - // change the fill color opacity if required - if(node.fillOpacity.value != 1.f) { - color = [IJSVGColor changeAlphaOnColor:color - to:node.fillOpacity.value]; + if(colorNode.isNoneOrTransparent == YES) { + color = nil; + } else { + // change the fill color opacity if required + if(node.fillOpacity.value != 1.f) { + color = [IJSVGColor changeAlphaOnColor:color + to:node.fillOpacity.value]; + } } // set the color against the layer — we cant just use fill layer due to how @@ -410,8 +418,8 @@ } - (CALayer*)drawableGradientLayerForPathNode:(IJSVGPath*)node - gradient:(IJSVGGradient*)gradient - layer:(CALayer*)layer + gradient:(IJSVGGradient*)gradient + layer:(CALayer*)layer { // gradient fill IJSVGGradientLayer* gradientLayer = [self drawableBasicGradientLayerForLayer:layer @@ -494,6 +502,23 @@ } } +#pragma mark Filters + +- (CALayer*)applyFilter:(IJSVGFilter*)filter + toLayer:(CALayer*)layer + fromNode:(IJSVGNode*)node +{ + if(filter == nil) { + return layer; + } + IJSVGFilterLayer* filterLayer = [IJSVGFilterLayer layer]; + filterLayer.filter = filter; + filterLayer.frame = layer.frame; + filterLayer.sublayer = layer; + layer.referencingLayer = filterLayer; + return filterLayer; +} + #pragma mark Transforms - (CALayer*)applyTransforms:(NSArray*)transforms diff --git a/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate b/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate index 45abb4b..06765cf 100644 Binary files a/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate and b/IJSVGExample/IJSVGExample.xcodeproj/project.xcworkspace/xcuserdata/curtishard.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/IJSVGExample/IJSVGExample.xcodeproj/xcuserdata/curtishard.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/IJSVGExample/IJSVGExample.xcodeproj/xcuserdata/curtishard.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 1695a6a..9414b3b 100644 --- a/IJSVGExample/IJSVGExample.xcodeproj/xcuserdata/curtishard.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/IJSVGExample/IJSVGExample.xcodeproj/xcuserdata/curtishard.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -23,17 +23,65 @@ + + + + + + + + + + diff --git a/IJSVGExample/IJSVGExample/NewTux.svg b/IJSVGExample/IJSVGExample/NewTux.svg index 291fc56..0718b1a 100644 --- a/IJSVGExample/IJSVGExample/NewTux.svg +++ b/IJSVGExample/IJSVGExample/NewTux.svg @@ -1,72 +1,75 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/IJSVGExample/IJSVGExample/SVGView.m b/IJSVGExample/IJSVGExample/SVGView.m index c023732..0a027ca 100644 --- a/IJSVGExample/IJSVGExample/SVGView.m +++ b/IJSVGExample/IJSVGExample/SVGView.m @@ -31,7 +31,7 @@ - (IJSVG *)svg { - return [IJSVG svgNamed:@"AJ_Digital_Camera"]; + return [IJSVG svgNamed:@"heart"]; } - (void)drawRect:(NSRect)dirtyRect