Compare commits

..

50 Commits

Author SHA1 Message Date
Curtis Hard ffb55b0f31 Fixes viewBox origin translate 2018-03-02 13:28:40 +00:00
Curtis Hard a4c032afa2 Added clip to viewport 2018-02-28 21:29:11 +00:00
Curtis Hard abe8f4cba5 Fixes drawInRect not obeying origin 2018-02-28 21:00:20 +00:00
Curtis Hard a16842271b Resolves namespaces correctly + added common HTML list to be parsed as groups 2018-02-26 13:46:41 +00:00
Curtis Hard 58126e06e4 Fixes! 2018-02-25 21:58:49 +00:00
Curtis Hard 5af5e054ab Fixed defNode being removed 2018-02-23 22:37:53 +00:00
Curtis Hard edd3aa1f33 excluded various elements from diff 2018-02-23 16:29:54 +00:00
Curtis Hard 3366bc4fa5 Better optimaztion 2018-02-23 14:05:03 +00:00
Curtis Hard 104002183b Added rudimenatry inline styles -> stylesheet 2018-02-22 22:17:50 +00:00
Curtis Hard 7010b7ea50 Correct order of cleanup 2018-02-22 13:33:32 +00:00
Curtis Hard 5266a8c07a corrent length of string 2018-02-22 08:22:17 +00:00
Curtis Hard caf55e8bdf removes useless def if required 2018-02-21 20:45:17 +00:00
Curtis Hard 8160d05eba Refactor of a few methods 2018-02-20 14:12:02 +00:00
Curtis Hard a6d6a06521 Added collpasing of gradients 2018-02-20 09:40:21 +00:00
Curtis Hard 103a4d71f6 Reduced floats even more 2018-02-19 19:07:30 +00:00
Curtis Hard 98874b1d2c More compression goodness 2018-02-19 18:53:02 +00:00
Curtis Hard e742db31e0 Added intermediateParent 2018-02-19 14:02:15 +00:00
Curtis Hard 198fd09f07 Fixes! and performance increases 2018-02-19 11:51:37 +00:00
Curtis Hard 3493194b1b Scale computation 2018-02-19 08:20:02 +00:00
Curtis Hard 0016775eaf More goodness 2018-02-18 22:33:26 +00:00
Curtis Hard 304a04cc22 Vastly improved the exporter 2018-02-18 22:32:04 +00:00
Curtis Hard e4fd0af582 This is insanely important! 2018-02-18 15:20:28 +00:00
Curtis Hard 69a2a0c97e Refactor 2018-02-04 22:02:19 +00:00
Curtis Hard 5299bb0479 will continue to use CoreAnimation for the time being 2018-02-04 21:58:21 +00:00
Curtis Hard 4f1943cad1 Fixes gradient strokes 2018-02-04 21:57:46 +00:00
Curtis Hard f02d186293 trying to get masks to work 2018-02-04 11:15:04 +00:00
Curtis Hard 3291718cfb Beginning of quartz renderer 2018-02-02 22:35:22 +00:00
Curtis Hard 6fbaaf5884 Removed useless log 2018-01-29 22:32:08 +00:00
Curtis Hard abc65797ea I think gradients work :D 2018-01-29 21:37:45 +00:00
Curtis Hard af5a1c2718 Possible fx and fy things… 2018-01-28 22:18:02 +00:00
Curtis Hard fb9a5282b9 Even more gradient fixes 2018-01-28 19:22:40 +00:00
Curtis Hard d83933a103 Various improvements 2018-01-28 14:21:50 +00:00
Curtis Hard bd7a0d5021 Start to linear 2018-01-27 22:26:33 +00:00
Curtis Hard a9a038568c This kind of actually works... 2018-01-27 21:14:25 +00:00
Curtis Hard 77fbb38b6f I guess this could be a good start?
Posssible start of fixes?
2018-01-27 20:32:13 +00:00
Curtis Hard 12c3191569 Added method for findind absolute position 2018-01-27 15:32:15 +00:00
Curtis Hard e3e9626ef7 Fixes crash due to parentNode on temp groups being released 2018-01-27 14:25:03 +00:00
Curtis Hard 1160d89f16 Fixes use statements with transforms 2018-01-26 22:29:22 +00:00
Curtis Hard 1575cbfde8 Moved color tree over to modern syntax 2018-01-26 18:16:03 +00:00
Curtis Hard 5c4c2eee91 Added HSL/HSLA support 2018-01-25 18:23:53 +00:00
Curtis Hard 087b13e58f Various image loading issues resolved from base64 images 2018-01-24 22:24:43 +00:00
Curtis Hard 1183e167aa Rect issue fix 2018-01-24 21:21:41 +00:00
Curtis Hard 4dbfc59437 Moved transforms over from being concatinated to seperate calls 2018-01-24 21:19:24 +00:00
Curtis Hard 409bd509fa Fixes color issue 2018-01-24 20:30:38 +00:00
Curtis Hard 8ae1d1b4e0 Removed check as its not needed here 2018-01-24 18:41:08 +00:00
Curtis Hard 7243fbe5ff Added excludeAttributes list to parseCommonAttributes
added x and y to that list for rect
2018-01-24 18:35:43 +00:00
Curtis Hard 51b9a5e85f Added isSubcommand to IJSVGCommand
- Fixes move command going awol when preceding move commands are not subcommands (woah)
2018-01-23 19:53:04 +00:00
Curtis Hard 40098589de removed reverseObjectEnumerator
IJSVGTransform already deals with this at parse stage (was a test from earlier), spec states transforms are applied in reverse order (which parser already dealth with :-))
2018-01-22 22:04:48 +00:00
Curtis Hard 7cb96b21f2 Removed use of origin here as its computed in apply defaults 2018-01-22 22:01:56 +00:00
Curtis Hard 1bff7c6970 Various fixes… still going… 2018-01-22 21:48:59 +00:00
296 changed files with 26822 additions and 79010 deletions
-1
View File
@@ -1 +0,0 @@
.DS_Store
-5
View File
@@ -1,5 +0,0 @@
framework module IJSVG {
umbrella header "IJSVGUmbrella.h"
export *
module * { export * }
}
File diff suppressed because it is too large Load Diff
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:IJSVG.xcodeproj">
</FileRef>
</Workspace>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "594CF46E238FF38E009B251B"
BuildableName = "IJSVG.framework"
BlueprintName = "IJSVG"
ReferencedContainer = "container:IJSVG.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "594CF46E238FF38E009B251B"
BuildableName = "IJSVG.framework"
BlueprintName = "IJSVG"
ReferencedContainer = "container:IJSVG.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>IJSVG.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>594CF46E238FF38E009B251B</key>
<dict>
<key>primary</key>
<true />
</dict>
</dict>
</dict>
</plist>
-24
View File
@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2020 Curtis Hard. All rights reserved.</string>
</dict>
</plist>
@@ -1,19 +0,0 @@
//
// IJSVGStringAdditions.h
// IconJar
//
// Created by Curtis Hard on 07/06/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSString (IJSVGAdditions)
- (NSArray<NSString*>*)ijsvg_componentsSeparatedByChars:(const char*)aChar;
- (BOOL)ijsvg_isNumeric;
- (BOOL)ijsvg_containsAlpha;
- (NSArray*)ijsvg_componentsSplitByWhiteSpace;
- (BOOL)ijsvg_isHexString;
@end
@@ -1,71 +0,0 @@
//
// IJSVGStringAdditions.m
// IconJar
//
// Created by Curtis Hard on 07/06/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGStringAdditions.h>
#import <IJSVG/IJSVGUtils.h>
@implementation NSString (IJSVGAdditions)
- (NSArray<NSString*>*)ijsvg_componentsSeparatedByChars:(const char*)aChar
{
char* chars = (char*)self.UTF8String;
if(chars == NULL || strlen(chars) == 0) {
return @[];
}
NSMutableArray<NSString*>* strings = nil;
strings = [[NSMutableArray alloc] init];
char* copy = strdup(chars);
char* spt = NULL;
char* ptr = strtok_r(copy, aChar, &spt);
while(ptr != NULL) {
NSString* possibleString = nil;
if((possibleString = [NSString stringWithUTF8String:ptr]) != nil) {
[strings addObject:possibleString];
}
ptr = strtok_r(NULL, aChar, &spt);
}
(void)free(copy), copy = NULL;
return strings;
}
- (BOOL)ijsvg_containsAlpha
{
const char* buffer = self.UTF8String;
char currentChar;
while((currentChar = *buffer++) ) {
if(isalpha(currentChar)) {
return YES;
}
}
return NO;
}
- (BOOL)ijsvg_isNumeric
{
const char* buffer = self.UTF8String;
char currentChar;
while((currentChar = *buffer++) ) {
if(!isnumber(currentChar)) {
return NO;
}
}
return YES;
}
- (NSArray*)ijsvg_componentsSplitByWhiteSpace
{
return [self ijsvg_componentsSeparatedByChars:"\t\n\r "];
}
- (BOOL)ijsvg_isHexString
{
const char* chars = self.UTF8String;
return IJSVGCharBufferIsHEX((char*)chars);
}
@end
@@ -1,18 +0,0 @@
//
// NSImage+IJSVGAdditions.h
// IJSVG
//
// Created by Curtis Hard on 07/06/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import <AppKit/AppKit.h>
#import <Cocoa/Cocoa.h>
IJSVG* IJSVGGetFromNSImage(NSImage* image);
@interface NSImage (IJSVGAdditions)
+ (NSImage*)SVGImageNamed:(NSString*)imageName;
@end
@@ -1,53 +0,0 @@
//
// NSImage+IJSVGAdditions.m
// IJSVG
//
// Created by Curtis Hard on 07/06/2020.
// Copyright © 2020 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGImageRep.h>
#import <IJSVG/NSImage+IJSVGAdditions.h>
IJSVG* IJSVGGetFromNSImage(NSImage* image)
{
for (NSImageRep* rep in image.representations) {
if([rep isKindOfClass:IJSVGImageRep.class]) {
return ((IJSVGImageRep*)rep).SVG;
}
}
return nil;
}
@implementation NSImage (IJSVGAdditions)
+ (NSImage*)SVGImageNamed:(NSString*)imageName
{
// find the image
NSBundle* bundle = NSBundle.mainBundle;
NSString* str = nil;
NSString* ext = imageName.pathExtension;
if(ext == nil || ext.length == 0) {
ext = @"svg";
}
if((str = [bundle pathForResource:imageName.stringByDeletingPathExtension
ofType:ext])
!= nil) {
// work out if we can get the data
NSData* data = [[NSData alloc] initWithContentsOfFile:str];
if(data == nil) {
return nil;
}
// grab the image rep
IJSVGImageRep* rep = [[IJSVGImageRep alloc] initWithData:data];
NSImage* image = [[NSImage alloc] init];
[image addRepresentation:rep];
return image;
}
return nil;
}
@end
@@ -1,841 +0,0 @@
//
// IJSVGColor.m
// IconJar
//
// Created by Curtis Hard on 31/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGColor.h>
#import <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGStringAdditions.h>
#import <IJSVG/IJSVGParsing.h>
#import <IJSVG/IJSVGParser.h>
NSString* const IJSVGColorCurrentColorName = @"currentColor";
@implementation IJSVGColor
static NSDictionary* _colorTree = nil;
CGFloat* IJSVGColorCSSHSLToHSB(CGFloat hue, CGFloat saturation, CGFloat lightness)
{
hue *= (1.f / 360.f);
hue = (hue - floorf(hue));
saturation *= 0.01;
lightness *= 0.01;
lightness *= 2.f;
CGFloat s = saturation * ((lightness < 1.f) ? lightness : (2.f - lightness));
CGFloat brightness = (lightness + s) * .5f;
if(s != 0.f) {
s = (2.f * s) / (lightness + s);
}
CGFloat* floats = (CGFloat*)malloc(3 * sizeof(CGFloat));
floats[0] = hue;
floats[1] = s;
floats[2] = brightness;
return floats;
};
+ (void)load
{
[self.class _generateTree];
}
+ (NSColorSpace*)defaultColorSpace
{
return NSColorSpace.deviceRGBColorSpace;
}
+ (NSColor*)computeColorSpace:(NSColor*)color
{
NSColorSpace* space = [self defaultColorSpace];
if(color.colorSpace != space) {
color = [color colorUsingColorSpace:space];
}
return color;
}
+ (void)_generateTree
{
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)
};
});
}
+ (NSColor*)computeColor:(id)colour
{
if([colour isKindOfClass:[NSColor class]])
return colour;
return nil;
}
+ (NSColor*)colorFromRString:(NSString*)rString
gString:(NSString*)gString
bString:(NSString*)bString
aString:(NSString*)aString
{
return [self colorFromRUnit:[IJSVGUnitLength unitWithString:rString]
gUnit:[IJSVGUnitLength unitWithString:gString]
bUnit:[IJSVGUnitLength unitWithString:bString]
aUnit:[IJSVGUnitLength unitWithString:aString]];
}
+ (NSColor*)colorFromRUnit:(IJSVGUnitLength*)rUnit
gUnit:(IJSVGUnitLength*)gUnit
bUnit:(IJSVGUnitLength*)bUnit
aUnit:(IJSVGUnitLength*)aUnit
{
CGFloat r = rUnit.type == IJSVGUnitLengthTypePercentage ? [rUnit computeValue:255.f] : [rUnit computeValue:1.f];
CGFloat g = gUnit.type == IJSVGUnitLengthTypePercentage ? [gUnit computeValue:255.f] : [gUnit computeValue:1.f];
CGFloat b = bUnit.type == IJSVGUnitLengthTypePercentage ? [bUnit computeValue:255.f] : [bUnit computeValue:1.f];
CGFloat a = [aUnit computeValue:100.f];
return [self computeColorSpace:[NSColor colorWithDeviceRed:(r / 255.f)
green:(g / 255.f)
blue:(b / 255.f)
alpha:a]];
}
+ (BOOL)isNoneOrTransparent:(NSString*)string
{
const char* str = string.UTF8String;
return IJSVGCharBufferCaseInsensitiveCompare(str, "none") == YES ||
IJSVGCharBufferCaseInsensitiveCompare(str, "transparent") == YES;
}
+ (NSColor*)colorFromString:(NSString*)string
{
// swap over to C for performance
if(string == nil) {
return nil;
}
const char* oString = string.UTF8String;
if(strlen(oString) == 0) {
return nil;
}
char* str = IJSVGTimmedCharBufferCreate(oString);
if(IJSVGCharBufferIsHEX(str) == YES) {
string = [NSString stringWithUTF8String:str];
(void)free(str), str = NULL;
return [self.class colorFromHEXString:string];
}
// is it RGB?
if(IJSVGCharBufferHasPrefix(str, "rgb") == YES) {
NSUInteger count = 0;
IJSVGParsingStringMethod** methods = NULL;
methods = IJSVGParsingMethodParseString(str, &count);
IJSVGParsingStringMethod* method = methods[0];
// memory clean for the string
(void)free(str), str = NULL;
// nothing to return, just mem clean and get out of here
if(count == 0 || methods == NULL) {
if(methods != NULL) {
IJSVGParsingStringMethodsRelease(methods, count);
methods = NULL;
}
return nil;
}
// Make sure we have actual floats within the parameters
NSInteger floatCount = 0;
CGFloat *params = [IJSVGUtils scanFloatsFromCString:method->parameters
size:&floatCount];
(void)free(params), params = NULL;
if(floatCount < 3) {
return [self computeColorSpace:NSColor.blackColor];
}
// Make sure the floats are not negative
// parse the parameters
NSString* parameters = [NSString stringWithUTF8String:method->parameters];
NSArray* parts = [parameters ijsvg_componentsSeparatedByChars:","];
NSString* alpha = @"100%";
if(parts.count == 4) {
alpha = parts[3];
}
IJSVGParsingStringMethodsRelease(methods, count);
methods = NULL;
return [self colorFromRString:parts[0]
gString:parts[1]
bString:parts[2]
aString:alpha];
}
// is it HSL?
if(IJSVGCharBufferHasPrefix(str, "hsl")) {
NSInteger count = 0;
CGFloat* params = [IJSVGUtils scanFloatsFromCString:str
size:&count];
CGFloat alpha = 1;
if(count == 4) {
alpha = params[3];
}
// convert HSL to HSB
CGFloat* hsb = IJSVGColorCSSHSLToHSB(params[0], params[1], params[2]);
NSColor* color = [NSColor colorWithDeviceHue:hsb[0]
saturation:hsb[1]
brightness:hsb[2]
alpha:alpha];
color = [self computeColorSpace:color];
// memory clean!
(void)free(str), str = NULL;
(void)free(hsb), hsb = NULL;
(void)free(params), params = NULL;
return color;
}
// is simply a clear color, dont fill
if(IJSVGCharBufferCompare(str, "none") == YES ||
IJSVGCharBufferCompare(str, "transparent") == YES) {
(void)free(str), str = NULL;
return nil;
}
// could return nil
(void)free(str), str = NULL;
return [self.class colorFromPredefinedColorName:string];
}
+ (NSColor*)colorFromPredefinedColorName:(NSString*)name
{
NSNumber* hex = nil;
name = [name.lowercaseString stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
if((hex = _colorTree[name]) == nil) {
return nil;
}
return [self.class colorFromHEXInteger:hex.integerValue];
}
+ (NSString*)colorStringFromColor:(NSColor*)color
{
IJSVGColorStringOptions options = IJSVGColorStringOptionDefault;
return [self colorStringFromColor:color
options:options];
}
+ (NSString*)colorStringFromColor:(NSColor*)color
options:(IJSVGColorStringOptions)options
{
// convert to RGB
color = [self computeColorSpace:color];
int red = color.redComponent * 0xFF;
int green = color.greenComponent * 0xFF;
int blue = color.blueComponent * 0xFF;
int alpha = (int)(color.alphaComponent * 100);
BOOL forceHex = (options & IJSVGColorStringOptionForceHEX) != 0;
BOOL allowShortHand = (options & IJSVGColorStringOptionAllowShortHand) != 0;
BOOL allowRRGGBBAA = (options & IJSVGColorStringOptionAllowRRGGBBAA) != 0;
// jsut return none
if(alpha == 0 && forceHex == NO) {
return IJSVGStringNone;
}
// always return hex unless criteria is met
if(forceHex == YES || allowRRGGBBAA == YES || alpha == 100 || (red == 0 && green == 0 && blue == 0 && alpha == 0) || (red == 255 && green == 255 && blue == 255 && alpha == 100)) {
// we need to make sure the last 2 chars
// are the same or we cant enable shorthand
if(allowRRGGBBAA == YES) {
NSString* alphaHexString = [NSString stringWithFormat:@"%02X",
(int)(color.alphaComponent * 0xFF)];
if([alphaHexString characterAtIndex:0] !=
[alphaHexString characterAtIndex:1]) {
allowShortHand = NO;
}
}
if(allowShortHand == YES) {
NSString* r = [NSString stringWithFormat:@"%02X", red];
NSString* g = [NSString stringWithFormat:@"%02X", green];
NSString* b = [NSString stringWithFormat:@"%02X", blue];
if([r characterAtIndex:0] == [r characterAtIndex:1] &&
[g characterAtIndex:0] == [g characterAtIndex:1] &&
[b characterAtIndex:0] == [b characterAtIndex:1]) {
// allow shorthand alpha
if(allowRRGGBBAA == YES && alpha != 100) {
NSString* a = [NSString stringWithFormat:@"%02X",
(int)(color.alphaComponent * 0xFF)];
return [NSString stringWithFormat:@"#%c%c%c%c",
[r characterAtIndex:0], [g characterAtIndex:0],
[b characterAtIndex:0], [a characterAtIndex:0]];
}
return [NSString stringWithFormat:@"#%c%c%c", [r characterAtIndex:0],
[g characterAtIndex:0], [b characterAtIndex:0]];
}
}
if(allowRRGGBBAA == YES && alpha != 100) {
return [NSString stringWithFormat:@"#%02X%02X%02X%02X", red, green,
blue, (int)(color.alphaComponent * 0xFF)];
}
return [NSString stringWithFormat:@"#%02X%02X%02X", red, green, blue];
}
// note the %g, CSS alpha is 0 to 1, not 0 - 100, my bad!
return [NSString stringWithFormat:@"rgba(%d,%d,%d,%@)", red, green, blue,
IJSVGShortFloatString((float)alpha / 100.f)];
}
+ (NSString*)colorNameFromPredefinedColor:(IJSVGPredefinedColor)color
{
switch (color) {
case IJSVGColorAliceblue:
return @"aliceblue";
case IJSVGColorAntiquewhite:
return @"antiquewhite";
case IJSVGColorAqua:
return @"aqua";
case IJSVGColorAquamarine:
return @"aquamarine";
case IJSVGColorAzure:
return @"azure";
case IJSVGColorBeige:
return @"beige";
case IJSVGColorBisque:
return @"bisque";
case IJSVGColorBlack:
return @"black";
case IJSVGColorBlanchedalmond:
return @"blanchedalmond";
case IJSVGColorBlue:
return @"blue";
case IJSVGColorBlueviolet:
return @"blueviolet";
case IJSVGColorBrown:
return @"brown";
case IJSVGColorBurlywood:
return @"burlywood";
case IJSVGColorCadetblue:
return @"cadetblue";
case IJSVGColorChartreuse:
return @"chartreuse";
case IJSVGColorChocolate:
return @"chocolate";
case IJSVGColorCoral:
return @"coral";
case IJSVGColorCornflowerblue:
return @"cornflowerblue";
case IJSVGColorCornsilk:
return @"cornsilk";
case IJSVGColorCrimson:
return @"crimson";
case IJSVGColorCyan:
return @"cyan";
case IJSVGColorDarkblue:
return @"darkblue";
case IJSVGColorDarkcyan:
return @"darkcyan";
case IJSVGColorDarkgoldenrod:
return @"darkgoldenrod";
case IJSVGColorDarkgray:
return @"darkgray";
case IJSVGColorDarkgreen:
return @"darkgreen";
case IJSVGColorDarkgrey:
return @"darkgrey";
case IJSVGColorDarkkhaki:
return @"darkkhaki";
case IJSVGColorDarkmagenta:
return @"darkmagenta";
case IJSVGColorDarkolivegreen:
return @"darkolivegreen";
case IJSVGColorDarkorange:
return @"darkorange";
case IJSVGColorDarkorchid:
return @"darkorchid";
case IJSVGColorDarkred:
return @"darkred";
case IJSVGColorDarksalmon:
return @"darksalmon";
case IJSVGColorDarkseagreen:
return @"darkseagreen";
case IJSVGColorDarkslateblue:
return @"darkslateblue";
case IJSVGColorDarkslategray:
return @"darkslategray";
case IJSVGColorDarkslategrey:
return @"darkslategrey";
case IJSVGColorDarkturquoise:
return @"darkturquoise";
case IJSVGColorDarkviolet:
return @"darkviolet";
case IJSVGColorDeeppink:
return @"deeppink";
case IJSVGColorDeepskyblue:
return @"deepskyblue";
case IJSVGColorDimgray:
return @"dimgray";
case IJSVGColorDimgrey:
return @"dimgrey";
case IJSVGColorDodgerblue:
return @"dodgerblue";
case IJSVGColorFirebrick:
return @"firebrick";
case IJSVGColorFloralwhite:
return @"floralwhite";
case IJSVGColorForestgreen:
return @"forestgreen";
case IJSVGColorFuchsia:
return @"fuchsia";
case IJSVGColorGainsboro:
return @"gainsboro";
case IJSVGColorGhostwhite:
return @"ghostwhite";
case IJSVGColorGold:
return @"gold";
case IJSVGColorGoldenrod:
return @"goldenrod";
case IJSVGColorGray:
return @"gray";
case IJSVGColorGreen:
return @"green";
case IJSVGColorGreenyellow:
return @"greenyellow";
case IJSVGColorGrey:
return @"grey";
case IJSVGColorHoneydew:
return @"honeydew";
case IJSVGColorHotpink:
return @"hotpink";
case IJSVGColorIndianred:
return @"indianred";
case IJSVGColorIndigo:
return @"indigo";
case IJSVGColorIvory:
return @"ivory";
case IJSVGColorKhaki:
return @"khaki";
case IJSVGColorLavender:
return @"lavender";
case IJSVGColorLavenderblush:
return @"lavenderblush";
case IJSVGColorLawngreen:
return @"lawngreen";
case IJSVGColorLemonchiffon:
return @"lemonchiffon";
case IJSVGColorLightblue:
return @"lightblue";
case IJSVGColorLightcoral:
return @"lightcoral";
case IJSVGColorLightcyan:
return @"lightcyan";
case IJSVGColorLightgoldenrodyellow:
return @"lightgoldenrodyellow";
case IJSVGColorLightgray:
return @"lightgray";
case IJSVGColorLightgreen:
return @"lightgreen";
case IJSVGColorLightgrey:
return @"lightgrey";
case IJSVGColorLightpink:
return @"lightpink";
case IJSVGColorLightsalmon:
return @"lightsalmon";
case IJSVGColorLightseagreen:
return @"lightseagreen";
case IJSVGColorLightskyblue:
return @"lightskyblue";
case IJSVGColorLightslategray:
return @"lightslategray";
case IJSVGColorLightslategrey:
return @"lightslategrey";
case IJSVGColorLightsteelblue:
return @"lightsteelblue";
case IJSVGColorLightyellow:
return @"lightyellow";
case IJSVGColorLime:
return @"lime";
case IJSVGColorLimegreen:
return @"limegreen";
case IJSVGColorLinen:
return @"linen";
case IJSVGColorMagenta:
return @"magenta";
case IJSVGColorMaroon:
return @"maroon";
case IJSVGColorMediumaquamarine:
return @"mediumaquamarine";
case IJSVGColorMediumblue:
return @"mediumblue";
case IJSVGColorMediumorchid:
return @"mediumorchid";
case IJSVGColorMediumpurple:
return @"mediumpurple";
case IJSVGColorMediumseagreen:
return @"mediumseagreen";
case IJSVGColorMediumslateblue:
return @"mediumslateblue";
case IJSVGColorMediumspringgreen:
return @"mediumspringgreen";
case IJSVGColorMediumturquoise:
return @"mediumturquoise";
case IJSVGColorMediumvioletred:
return @"mediumvioletred";
case IJSVGColorMidnightblue:
return @"midnightblue";
case IJSVGColorMintcream:
return @"mintcream";
case IJSVGColorMistyrose:
return @"mistyrose";
case IJSVGColorMoccasin:
return @"moccasin";
case IJSVGColorNavajowhite:
return @"navajowhite";
case IJSVGColorNavy:
return @"navy";
case IJSVGColorOldlace:
return @"oldlace";
case IJSVGColorOlive:
return @"olive";
case IJSVGColorOlivedrab:
return @"olivedrab";
case IJSVGColorOrange:
return @"orange";
case IJSVGColorOrangered:
return @"orangered";
case IJSVGColorOrchid:
return @"orchid";
case IJSVGColorPalegoldenrod:
return @"palegoldenrod";
case IJSVGColorPalegreen:
return @"palegreen";
case IJSVGColorPaleturquoise:
return @"paleturquoise";
case IJSVGColorPalevioletred:
return @"palevioletred";
case IJSVGColorPapayawhip:
return @"papayawhip";
case IJSVGColorPeachpuff:
return @"peachpuff";
case IJSVGColorPeru:
return @"peru";
case IJSVGColorPink:
return @"pink";
case IJSVGColorPlum:
return @"plum";
case IJSVGColorPowderblue:
return @"powderblue";
case IJSVGColorPurple:
return @"purple";
case IJSVGColorRed:
return @"red";
case IJSVGColorRosybrown:
return @"rosybrown";
case IJSVGColorRoyalblue:
return @"royalblue";
case IJSVGColorSaddlebrown:
return @"saddlebrown";
case IJSVGColorSalmon:
return @"salmon";
case IJSVGColorSandybrown:
return @"sandybrown";
case IJSVGColorSeagreen:
return @"seagreen";
case IJSVGColorSeashell:
return @"seashell";
case IJSVGColorSienna:
return @"sienna";
case IJSVGColorSilver:
return @"silver";
case IJSVGColorSkyblue:
return @"skyblue";
case IJSVGColorSlateblue:
return @"slateblue";
case IJSVGColorSlategray:
return @"slategray";
case IJSVGColorSlategrey:
return @"slategrey";
case IJSVGColorSnow:
return @"snow";
case IJSVGColorSpringgreen:
return @"springgreen";
case IJSVGColorSteelblue:
return @"steelblue";
case IJSVGColorTan:
return @"tan";
case IJSVGColorTeal:
return @"teal";
case IJSVGColorThistle:
return @"thistle";
case IJSVGColorTomato:
return @"tomato";
case IJSVGColorTurquoise:
return @"turquoise";
case IJSVGColorViolet:
return @"violet";
case IJSVGColorWheat:
return @"wheat";
case IJSVGColorWhite:
return @"white";
case IJSVGColorWhitesmoke:
return @"whitesmoke";
case IJSVGColorYellow:
return @"yellow";
case IJSVGColorYellowgreen:
return @"yellowgreen";
}
return nil;
}
+ (NSColor*)changeAlphaOnColor:(NSColor*)color
to:(CGFloat)alphaValue
{
color = [self computeColorSpace:color];
return [self computeColorSpace:[NSColor colorWithDeviceRed:color.redComponent
green:color.greenComponent
blue:color.blueComponent
alpha:alphaValue]];
}
+ (BOOL)isColor:(NSString*)string
{
return [string hasPrefix:@"#"] || [string hasPrefix:@"rgb"];
}
+ (BOOL)isHex:(NSString*)string
{
return string.ijsvg_isHexString;
}
+ (unsigned long)lengthOfHEXInteger:(NSUInteger)hex
{
char* buffer;
asprintf(&buffer, "%lX", (long)hex);
unsigned long length = strlen(buffer);
free(buffer);
return length;
}
+ (BOOL)HEXContainsAlphaComponent:(NSUInteger)hex
{
return [self lengthOfHEXInteger:hex] == 8;
}
+ (NSColor*)colorFromHEXInteger:(NSInteger)hex
{
CGFloat alpha = 1.f;
if([self HEXContainsAlphaComponent:hex] == YES) {
alpha = (hex & 0xFF) / 255.f;
hex = hex >> 8;
}
return [self computeColorSpace:[NSColor colorWithDeviceRed:((hex >> 16) & 0xFF) / 255.f
green:((hex >> 8) & 0xFF) / 255.f
blue:(hex & 0xFF) / 255.f
alpha:alpha]];
}
+ (unsigned long)HEXFromArbitraryHexString:(NSString*)aString
{
const char* hexString = [aString cStringUsingEncoding:NSUTF8StringEncoding];
return strtoul(hexString, NULL, 16);
}
+ (NSColor*)colorFromHEXString:(NSString*)string
{
return [self colorFromHEXString:string
containsAlphaComponent:nil];
}
+ (NSColor*)colorFromHEXString:(NSString*)string
containsAlphaComponent:(BOOL*)containsAlphaComponent
{
// absolutely no string
if(string == nil) {
return nil;
}
char* str = (char*)string.UTF8String;
size_t length = strlen(str);
if(length == 0 || IJSVGCharBufferIsHEX(str) == NO) {
return nil;
}
// remove the hash from the front of the string
if(str[0] == '#') {
length--;
str++;
}
unsigned long hex;
// we need to work out if its shorthand
// if it is, the length needs to be length*2
if(length == 3 || length == 4) {
char* chars = NULL;
chars = (char*)calloc(sizeof(char),length*2+1);
for(int i = 0; i < length; i++) {
chars[i*2] = chars[i*2+1] = str[i];
}
hex = strtoul(chars, NULL, 16);
(void)free(chars), chars = NULL;
} else {
hex = strtoul(str, NULL, 16);
}
// now convert rest to hex
if(containsAlphaComponent != nil) {
*containsAlphaComponent = [self HEXContainsAlphaComponent:hex];
}
return [self colorFromHEXInteger:hex];
}
@end
@@ -1,32 +0,0 @@
//
// IJSVGColorType.h
// IJSVG
//
// Created by Curtis Hard on 20/04/2021.
// Copyright © 2021 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
typedef NS_OPTIONS(NSInteger, IJSVGColorUsageTraits) {
IJSVGColorUsageTraitNone = 0,
IJSVGColorUsageTraitUnknown = 1 << 0,
IJSVGColorUsageTraitFill = 1 << 1,
IJSVGColorUsageTraitStroke = 1 << 2,
IJSVGColorUsageTraitGradientStop = 1 << 3,
IJSVGColorUsageTraitAll = IJSVGColorUsageTraitFill | IJSVGColorUsageTraitGradientStop |
IJSVGColorUsageTraitStroke
};
@interface IJSVGTraitedColor : NSObject {
}
@property (nonatomic, strong) NSColor* color;
@property (nonatomic, assign) IJSVGColorUsageTraits traits;
+ (IJSVGTraitedColor*)typeWithColor:(NSColor*)color
traits:(IJSVGColorUsageTraits)mask;
@end
@@ -1,36 +0,0 @@
//
// IJSVGColorType.h
// IJSVG
//
// Created by Curtis Hard on 20/04/2021.
// Copyright © 2021 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
typedef NS_OPTIONS(NSInteger, IJSVGColorUsageTraits) {
IJSVGColorUsageTraitNone = 0,
IJSVGColorUsageTraitUnknown = 1 << 0,
IJSVGColorUsageTraitFill = 1 << 1,
IJSVGColorUsageTraitStroke = 1 << 2,
IJSVGColorUsageTraitGradientStop = 1 << 3,
IJSVGColorUsageTraitAll = IJSVGColorUsageTraitFill | IJSVGColorUsageTraitGradientStop |
IJSVGColorUsageTraitStroke
};
@interface IJSVGTraitedColor : NSObject {
}
@property (nonatomic, strong) NSColor* color;
@property (nonatomic, assign) IJSVGColorUsageTraits traits;
+ (IJSVGTraitedColor*)colorWithColor:(NSColor*)color
traits:(IJSVGColorUsageTraits)mask;
- (void)addTraits:(IJSVGColorUsageTraits)traits;
- (void)removeTraits:(IJSVGColorUsageTraits)traits;
- (BOOL)matchesTraits:(IJSVGColorUsageTraits)traits;
@end
@@ -1,64 +0,0 @@
//
// IJSVGColorType.m
// IJSVG
//
// Created by Curtis Hard on 20/04/2021.
// Copyright © 2021 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGTraitedColor.h>
#import <IJSVG/IJSVGColor.h>
@implementation IJSVGTraitedColor
+ (IJSVGTraitedColor*)colorWithColor:(NSColor*)color
traits:(IJSVGColorUsageTraits)traits
{
IJSVGTraitedColor* type = [[self alloc] init];
type.color = color;
type.traits = traits;
return type;
}
- (instancetype)init
{
if((self = [super init]) != nil) {
_traits = IJSVGColorUsageTraitNone;
}
return self;
}
- (BOOL)isEqual:(id)object
{
if([object isKindOfClass:IJSVGTraitedColor.class] == NO) {
return NO;
}
return [self.color isEqual:((IJSVGTraitedColor*)object).color];
}
- (void)setColor:(NSColor *)color
{
_color = [IJSVGColor computeColorSpace:color];
}
- (NSUInteger)hash
{
return self.color.hash;
}
- (void)addTraits:(IJSVGColorUsageTraits)traits
{
_traits |= traits;
}
- (void)removeTraits:(IJSVGColorUsageTraits)traits
{
_traits = _traits & ~traits;
}
- (BOOL)matchesTraits:(IJSVGColorUsageTraits)traits
{
return (self.traits & traits) == traits;
}
@end
@@ -1,43 +0,0 @@
//
// IJSVGColorList.h
// IconJar
//
// Created by Curtis Hard on 07/07/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGColor.h>
#import <IJSVG/IJSVGTraitedColor.h>
#import <Foundation/Foundation.h>
@interface IJSVGReplacementColor : IJSVGTraitedColor
@property (nonatomic, strong) NSColor* replacementColor;
@end
@interface IJSVGTraitedColorStorage : NSObject {
@private
NSMutableArray<IJSVGReplacementColor*>* _replacementColors;
NSMutableSet<IJSVGTraitedColor*>* _colors;
IJSVGColorUsageTraits _traits;
IJSVGColorUsageTraits _replacementTraits;
}
@property (nonatomic, readonly) NSUInteger count;
@property (nonatomic, readonly) NSSet<IJSVGTraitedColor*>* colors;
@property (nonatomic, readonly) NSUInteger replacedColorCount;
- (void)addTraits:(IJSVGColorUsageTraits)traits;
- (void)addColor:(IJSVGTraitedColor*)color;
- (void)replaceColor:(NSColor*)replaceColor
withColor:(NSColor*)withColor
traits:(IJSVGColorUsageTraits)traits;
- (void)unionColorStorage:(IJSVGTraitedColorStorage*)colorList;
- (NSColor*)colorForColor:(NSColor*)color
matchingTraits:(IJSVGColorUsageTraits)traits;
- (BOOL)matchesReplacementTraits:(IJSVGColorUsageTraits)traits;
- (BOOL)matchesTraits:(IJSVGColorUsageTraits)traits;
@end
@@ -1,146 +0,0 @@
//
// IJSVGColorList.m
// IconJar
//
// Created by Curtis Hard on 07/07/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGTraitedColorStorage.h>
@implementation IJSVGReplacementColor
- (void)setReplacementColor:(NSColor*)replacementColor
{
_replacementColor = [IJSVGColor computeColorSpace:replacementColor];
}
@end
@implementation IJSVGTraitedColorStorage
- (instancetype)init
{
if((self = [super init]) != nil) {
_replacementColors = [[NSMutableArray alloc] init];
_colors = [[NSMutableSet alloc] init];
_replacementTraits = IJSVGColorUsageTraitNone;
_traits = IJSVGColorUsageTraitNone;
}
return self;
}
- (void)addColor:(IJSVGTraitedColor*)color
{
if([_colors containsObject:color] == YES) {
void (^handler)(IJSVGTraitedColor * _Nonnull obj, BOOL * _Nonnull stop) =
^(IJSVGTraitedColor * _Nonnull obj, BOOL * _Nonnull stop) {
if([obj isEqual:color] == YES) {
[obj addTraits:color.traits];
*stop = YES;
}
};
[_colors enumerateObjectsUsingBlock:handler];
return;
}
_traits |= color.traits;
[_colors addObject:color];
}
- (void)addTraits:(IJSVGColorUsageTraits)traits
{
for(IJSVGTraitedColor* color in _colors) {
[color addTraits:traits];
}
}
- (void)replaceColor:(NSColor*)replaceColor
withColor:(NSColor*)withColor
traits:(IJSVGColorUsageTraits)traits
{
replaceColor = [IJSVGColor computeColorSpace:replaceColor];
withColor = [IJSVGColor computeColorSpace:withColor];
IJSVGReplacementColor* repColor = [self replacementColorForColor:replaceColor
withColor:withColor];
if(repColor == nil) {
repColor = [[IJSVGReplacementColor alloc] init];
repColor.color = replaceColor;
repColor.replacementColor = withColor;
[_replacementColors addObject:repColor];
}
_replacementTraits |= traits;
[repColor addTraits:traits];
}
- (IJSVGReplacementColor*)replacementColorForColor:(NSColor*)replacementColor
withColor:(NSColor*)withColor
{
for(IJSVGReplacementColor* color in _replacementColors) {
if([color.color isEqual:color] == YES &&
[color.replacementColor isEqual:withColor] == YES) {
return color;
}
}
return nil;
}
- (IJSVGReplacementColor*)replacementColorForColor:(NSColor*)color
matchingTraits:(IJSVGColorUsageTraits)traits
{
for(IJSVGReplacementColor* replacementColor in _replacementColors) {
if([replacementColor.color isEqual:color] &&
[replacementColor matchesTraits:traits] == YES) {
return replacementColor;
}
}
return nil;
}
- (NSColor*)colorForColor:(NSColor*)color
matchingTraits:(IJSVGColorUsageTraits)traits
{
if([self matchesReplacementTraits:traits] == NO) {
return nil;
}
color = [IJSVGColor computeColorSpace:color];
IJSVGReplacementColor* repColor = nil;
if((repColor = [self replacementColorForColor:color
matchingTraits:traits]) != nil) {
return repColor.replacementColor;
}
return nil;
}
- (void)unionColorStorage:(IJSVGTraitedColorStorage*)colorList
{
for(IJSVGTraitedColor* traitedColor in colorList.colors) {
[self addColor:traitedColor];
}
}
- (BOOL)matchesTraits:(IJSVGColorUsageTraits)traits
{
return (_traits & traits) == traits;
}
- (BOOL)matchesReplacementTraits:(IJSVGColorUsageTraits)traits
{
return (_replacementTraits & traits) == traits;
}
- (NSUInteger)replacedColorCount
{
return _replacementColors.count;
}
- (NSUInteger)count
{
return _colors.count;
}
- (NSSet<IJSVGTraitedColor*>*)colors
{
return _colors;
}
@end
@@ -1,73 +0,0 @@
//
// IJSVGCommand.h
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandParser.h>
#import <IJSVG/IJSVGPath.h>
#import <Foundation/Foundation.h>
static const NSInteger IJSVGCustomVariableParameterCount = NSNotFound;
typedef NS_ENUM(NSInteger, IJSVGCommandType) {
kIJSVGCommandTypeAbsolute,
kIJSVGCommandTypeRelative
};
@interface IJSVGCommand : NSObject <NSCopying> {
@private
NSInteger _currentIndex;
}
@property (nonatomic, assign) char command;
@property (nonatomic, assign) CGFloat* parameters;
@property (nonatomic, assign) NSInteger parameterCount;
@property (nonatomic, assign) IJSVGCommandType type;
@property (nonatomic, strong) NSArray<IJSVGCommand*>* subCommands;
@property (nonatomic, assign) IJSVGCommand* previousCommand;
@property (nonatomic, assign) BOOL isSubCommand;
+ (Class)commandClassForCommandChar:(char)aChar;
+ (NSInteger)requiredParameterCount;
+ (NSPoint)readCoordinatePair:(CGFloat*)pairs
index:(NSInteger)index;
+ (IJSVGPathDataSequence*)pathDataSequence;
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path;
+ (void)parseParams:(CGFloat*)params
paramCount:(NSInteger)paramCount
intoArray:(NSMutableArray<IJSVGCommand*>*)commands
parentCommand:(IJSVGCommand*)parentCommand;
+ (NSArray<IJSVGCommand*>*)commandsForDataCharacters:(const char*)buffer;
+ (NSArray<IJSVGCommand*>*)commandsForDataCharacters:(const char*)buffer
dataStream:(IJSVGPathDataStream*)dataStream;
+ (CGMutablePathRef)newPathForCommandsArray:(NSArray<IJSVGCommand*>*)commands;
+ (NSArray<IJSVGCommand*>*)convertCommands:(NSArray<IJSVGCommand*>*)commands
toUnits:(IJSVGUnitType)unitType
bounds:(CGRect)bounds;
- (id)initWithCommandStringBuffer:(const char*)str
dataStream:(IJSVGPathDataStream*)dataStream;
- (IJSVGCommand*)subcommandWithParameters:(CGFloat*)subParams
paramCount:(NSInteger)paramCount
previousCommand:(IJSVGCommand*)command;
- (void)convertToUnits:(IJSVGUnitType)units
boundingBox:(CGRect)boundingBox;
- (IJSVGCommand*)commandByConvertingToUnits:(IJSVGUnitType)unitType
boundingBox:(CGRect)boundingBox;
- (CGFloat)readFloat;
- (NSPoint)readPoint;
- (BOOL)readBOOL;
- (void)resetRead;
@end
@@ -1,365 +0,0 @@
//
// IJSVGCommand.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommand.h>
#import <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGCommandClose.h>
#import <IJSVG/IJSVGCommandCurve.h>
#import <IJSVG/IJSVGCommandEllipticalArc.h>
#import <IJSVG/IJSVGCommandHorizontalLine.h>
#import <IJSVG/IJSVGCommandLineTo.h>
#import <IJSVG/IJSVGCommandMove.h>
#import <IJSVG/IJSVGCommandQuadraticCurve.h>
#import <IJSVG/IJSVGCommandSmoothCurve.h>
#import <IJSVG/IJSVGCommandSmoothQuadraticCurve.h>
#import <IJSVG/IJSVGCommandVerticalLine.h>
#import <IJSVG/IJSVGThreadManager.h>
@implementation IJSVGCommand
+ (BOOL)requiresCustomParameterParsing
{
return NO;
}
+ (NSInteger)requiredParameterCount
{
return 1;
}
+ (IJSVGPathDataSequence*)pathDataSequence
{
return NULL;
}
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
}
+ (void)parseParams:(CGFloat*)params
paramCount:(NSInteger)paramCount
intoArray:(NSMutableArray<IJSVGCommand*>*)commands
parentCommand:(IJSVGCommand*)parentCommand
{
}
+ (NSPoint)readCoordinatePair:(CGFloat*)pairs
index:(NSInteger)index
{
return NSMakePoint(pairs[index * 2], pairs[index * 2 + 1]);
}
+ (void)load
{
// register here...
}
+ (Class)commandClassForCommandChar:(char)aChar
{
aChar = tolower(aChar);
switch (aChar) {
case 'a':
return IJSVGCommandEllipticalArc.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 IJSVGCommandSmoothQuadraticCurve.class;
case 'v':
return IJSVGCommandVerticalLine.class;
case 'z':
return IJSVGCommandClose.class;
}
return nil;
}
+ (CGMutablePathRef)newPathForCommandsArray:(NSArray<IJSVGCommand*>*)commands
{
CGMutablePathRef path = CGPathCreateMutable();
IJSVGCommand* preCommand = nil;
for(IJSVGCommand* command in commands) {
for (IJSVGCommand* subCommand in command.subCommands) {
[command.class runWithParams:subCommand.parameters
paramCount:subCommand.parameterCount
command:subCommand
previousCommand:preCommand
type:subCommand.type
path:path];
preCommand = subCommand;
}
}
return path;
}
+ (NSArray<IJSVGCommand*>*)commandsForDataCharacters:(const char*)buffer
{
IJSVGThreadManager* manager = IJSVGThreadManager.currentManager;
NSArray<IJSVGCommand*>* commands = [self commandsForDataCharacters:buffer
dataStream:manager.pathDataStream];
return commands;
}
+ (NSArray<IJSVGCommand*>*)commandsForDataCharacters:(const char*)unsafeBuffer
dataStream:(IJSVGPathDataStream*)dataStream
{
NSMutableArray<IJSVGCommand*>* commands = [[NSMutableArray alloc] init];
NSUInteger len = strlen(unsafeBuffer);
NSUInteger lastIndex = len - 1;
// we need to trim the buffer first as any given string given to us
// could be unsafe and have random whitespace at the extremes.
size_t bufferLength = sizeof(char)*(len + 1);
char* buffer = (char*)malloc(bufferLength);
memcpy(buffer, unsafeBuffer, bufferLength);
IJSVGTrimCharBuffer(buffer);
// make sure we plus 1 for the null byte
char* charBuffer = (char*)malloc(bufferLength);
NSInteger start = 0;
for (NSInteger i = start; i < len; i++) {
char nextChar = buffer[i + 1];
BOOL atEnd = i == lastIndex;
BOOL isStartCommand = IJSVGIsLegalCommandCharacter(nextChar);
if(isStartCommand == YES || atEnd == YES) {
// copy memory from current buffer
NSInteger index = ((i + 1) - start);
memcpy(&charBuffer[0], &buffer[start], sizeof(char)*index);
charBuffer[index] = '\0';
// create the command from the substring
unsigned long length = index + 1;
size_t mlength = sizeof(char)*length;
char* commandString = (char*)malloc(mlength);
memcpy(commandString, &charBuffer[0], mlength);
// reset start position
start = (i + 1);
// previous command is actual subcommand
Class commandClass = [IJSVGCommand commandClassForCommandChar:commandString[0]];
if(commandClass != nil) {
IJSVGCommand* command = nil;
command = (IJSVGCommand*)[[commandClass alloc] initWithCommandStringBuffer:commandString
dataStream:dataStream];
[commands addObject:command];
}
#if DEBUG
else {
// if we get here then the command buffer is invalid, nothing we can
// do about it, just invalid data.
NSLog(@"\"%s\" is not a valid command instruction set", commandString);
}
#endif
// free the memory as at this point, we are done with it
(void)free(commandString), commandString = NULL;
}
}
(void)free(charBuffer), charBuffer = NULL;
(void)free(buffer), buffer = NULL;
return commands;
}
+ (NSArray<IJSVGCommand*>*)convertCommands:(NSArray<IJSVGCommand*>*)commands
toUnits:(IJSVGUnitType)unitType
bounds:(CGRect)bounds
{
NSMutableArray<IJSVGCommand*>* newCommands = nil;
newCommands = [[NSMutableArray alloc] initWithCapacity:commands.count];
for(IJSVGCommand* command in commands) {
IJSVGCommand* nCommand = [command commandByConvertingToUnits:unitType
boundingBox:bounds];
[newCommands addObject:nCommand];
}
return newCommands;
}
- (void)dealloc
{
if(_parameters) {
(void)(free(_parameters)), _parameters = nil;
}
}
- (id)initWithCommandStringBuffer:(const char*)str
dataStream:(IJSVGPathDataStream*)dataStream
{
if((self = [super init]) != nil) {
// work out the basics
_currentIndex = 0;
_command = str[0];
_type = [IJSVGUtils typeForCommandChar:_command];
NSInteger sets = 0;
NSInteger paramCount = [self.class requiredParameterCount];
IJSVGPathDataSequence* sequence = [self.class pathDataSequence];
_parameters = IJSVGParsePathDataStreamSequence(str, strlen(str),
dataStream, sequence, paramCount, &sets);
if(sets <= 1) {
CGFloat* subParams = [self parametersFromIndexOffset:0];
IJSVGCommand* command = [self subcommandWithParameters:subParams
paramCount:paramCount
previousCommand:nil];
_subCommands = @[ command ];
} else {
NSMutableArray<IJSVGCommand*>* subCommandArray = nil;
subCommandArray = [[NSMutableArray alloc] initWithCapacity:sets];
// interate over the sets
IJSVGCommand* lastCommand = nil;
for (NSInteger i = 0; i < sets; i++) {
// memory for this will be handled by the created subcommand
CGFloat* subParams = [self parametersFromIndexOffset:i];
// generate the subcommand
IJSVGCommand* command = [self subcommandWithParameters:subParams
paramCount:paramCount
previousCommand:lastCommand];
// make sure we assign the last command or hell breaks
// lose and the firey demons will run wild, namely, commands will break
// if they are multiples of a set
lastCommand = command;
[subCommandArray addObject:command];
}
// store the retained value
_subCommands = subCommandArray.copy;
}
}
return self;
}
- (void)setSubCommands:(NSArray<IJSVGCommand*>*)subCommands
{
_subCommands = subCommands;
}
- (id)copyWithZone:(NSZone *)zone
{
IJSVGCommand* command = [[self.class alloc] init];
command.type = self.type;
command.command = self.command;
command.isSubCommand = self.isSubCommand;
command.parameterCount = self.parameterCount;
size_t memsize = sizeof(CGFloat)*self.parameterCount;
command.parameters = (CGFloat*)malloc(memsize);
memcpy(command.parameters, self.parameters, memsize);
IJSVGCommand* lastCommand = nil;
NSMutableArray<IJSVGCommand*>* subcommands = nil;
subcommands = [[NSMutableArray alloc] initWithCapacity:self.subCommands.count];
for(IJSVGCommand* subcommand in self.subCommands) {
IJSVGCommand* subCopy = subcommand.copy;
subCopy.previousCommand = lastCommand;
subCopy.isSubCommand = lastCommand != nil;
[subcommands addObject:subCopy];
}
command.subCommands = subcommands;
return command;
}
- (CGFloat*)parametersFromIndexOffset:(NSInteger)index
{
CGFloat* subParams = 0;
NSInteger req = [self.class requiredParameterCount];
if(req != 0) {
subParams = (CGFloat*)malloc(req * sizeof(CGFloat));
memcpy(subParams, &self.parameters[index * req], sizeof(CGFloat) * req);
}
return subParams;
}
- (IJSVGCommand*)subcommandWithParameters:(CGFloat*)subParams
paramCount:(NSInteger)paramCount
previousCommand:(IJSVGCommand*)aPreviousCommand
{
// create a subcommand per set
IJSVGCommand* c = [[self.class alloc] init];
c.parameterCount = paramCount;
c.parameters = subParams;
c.type = self.type;
c.command = self.command;
c.previousCommand = aPreviousCommand;
c.isSubCommand = aPreviousCommand != nil;
return c;
}
- (CGFloat)readFloat
{
CGFloat f = _parameters[_currentIndex];
_currentIndex++;
return f;
}
- (NSPoint)readPoint
{
CGFloat x = _parameters[_currentIndex];
CGFloat y = _parameters[_currentIndex + 1];
_currentIndex += 2;
return NSMakePoint(x, y);
}
- (BOOL)readBOOL
{
return [self readFloat] == 1;
}
- (void)resetRead
{
_currentIndex = 0;
}
- (void)convertToUnits:(IJSVGUnitType)units
boundingBox:(CGRect)boundingBox
{
for(IJSVGCommand* command in _subCommands) {
[command convertToUnits:units
boundingBox:boundingBox];
}
}
- (NSString *)description
{
NSMutableString* str = [[NSMutableString alloc] init];
[str appendFormat:@"%c ",_command];
NSMutableArray* args = [[NSMutableArray alloc] initWithCapacity:_parameterCount];
for(int i = 0; i < _parameterCount; i++) {
[args addObject:[NSString stringWithFormat:@"%f",_parameters[i]]];
}
[str appendString:[args componentsJoinedByString:@", "]];
return str;
}
- (IJSVGCommand*)commandByConvertingToUnits:(IJSVGUnitType)unitType
boundingBox:(CGRect)boundingBox
{
IJSVGCommand* copy = self.copy;
[copy convertToUnits:unitType
boundingBox:boundingBox];
return copy;
}
@end
@@ -1,38 +0,0 @@
//
// IJSVGCommandCurve.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandCurve.h>
@implementation IJSVGCommandCurve
+ (NSInteger)requiredParameterCount
{
return 6;
}
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
if(type == kIJSVGCommandTypeAbsolute) {
CGPathAddCurveToPoint(path, NULL, params[0], params[1],
params[2], params[3],
params[4], params[5]);
return;
}
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddCurveToPoint(path, NULL,
currentPoint.x + params[0], currentPoint.y + params[1],
currentPoint.x + params[2], currentPoint.y + params[3],
currentPoint.x + params[4], currentPoint.y + params[5]);
}
@end
@@ -1,139 +0,0 @@
//
// IJSVGCommandArc.m
// IJSVGExample
//
// Created by Curtis Hard on 03/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandEllipticalArc.h>
#import <IJSVG/IJSVGUtils.h>
@implementation IJSVGCommandEllipticalArc
static IJSVGPathDataSequence* _sequence;
+ (NSInteger)requiredParameterCount
{
return 7;
}
+ (IJSVGPathDataSequence*)pathDataSequence
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sequence = (IJSVGPathDataSequence*)malloc(sizeof(IJSVGPathDataSequence) * 7);
_sequence[0] = kIJSVGPathDataSequenceTypeFloat;
_sequence[1] = kIJSVGPathDataSequenceTypeFloat;
_sequence[2] = kIJSVGPathDataSequenceTypeFloat;
_sequence[3] = kIJSVGPathDataSequenceTypeFlag;
_sequence[4] = kIJSVGPathDataSequenceTypeFlag;
_sequence[5] = kIJSVGPathDataSequenceTypeFloat;
_sequence[6] = kIJSVGPathDataSequenceTypeFloat;
});
return _sequence;
}
// modified from https://github.com/SVGKit/SVGKit/blob/880c94a5b77b6f22beb491a7a7e02ace220c32af/Source/Parsers/SVGKPointsAndPathsParser.m
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
CGPoint radii = CGPointZero;
CGPoint arcEndPoint = CGPointZero;
CGPoint pathCurrentPoint = CGPathGetCurrentPoint(path);
CGFloat xAxisRotation = 0.f;
BOOL largeArcFlag = NO;
BOOL sweepFlag = NO;
radii = currentCommand.readPoint;
xAxisRotation = currentCommand.readFloat;
largeArcFlag = currentCommand.readBOOL;
sweepFlag = currentCommand.readBOOL;
arcEndPoint = currentCommand.readPoint;
CGFloat rx = fabs(radii.x);
CGFloat ry = fabs(radii.y);
xAxisRotation *= M_PI / 180.f;
xAxisRotation = fmod(xAxisRotation, 2.f * M_PI);
if(type == kIJSVGCommandTypeRelative) {
arcEndPoint.x += pathCurrentPoint.x;
arcEndPoint.y += pathCurrentPoint.y;
}
CGFloat x1 = pathCurrentPoint.x;
CGFloat y1 = pathCurrentPoint.y;
CGFloat x2 = arcEndPoint.x;
CGFloat y2 = arcEndPoint.y;
if(rx == 0.f || ry == 0.f) {
CGPathAddLineToPoint(path, NULL, x2, y2);
return;
}
CGFloat cosPhi = cos(xAxisRotation);
CGFloat sinPhi = sin(xAxisRotation);
CGFloat x1p = cosPhi * (x1 - x2) / 2.f + sinPhi * (y1 - y2) / 2.f;
CGFloat y1p = -sinPhi * (x1 - x2) / 2.f + cosPhi * (y1 - y2) / 2.f;
CGFloat rx_2 = rx * rx;
CGFloat ry_2 = ry * ry;
CGFloat xp_2 = x1p * x1p;
CGFloat yp_2 = y1p * y1p;
CGFloat delta = xp_2 / rx_2 + yp_2 / ry_2;
if(delta > 1.f) {
rx *= sqrt(delta);
ry *= sqrt(delta);
rx_2 = rx * rx;
ry_2 = ry * ry;
}
CGFloat sign = (largeArcFlag == sweepFlag) ? -1.f : 1.f;
CGFloat numerator = MAX(0.f, rx_2 * ry_2 - rx_2 * yp_2 - ry_2 * xp_2);
CGFloat denom = rx_2 * yp_2 + ry_2 * xp_2;
CGFloat lhs = denom == 0.f ? 0.f : sign * sqrt(numerator / denom);
CGFloat cxp = lhs * (rx * y1p) / ry;
CGFloat cyp = lhs * -((ry * x1p) / rx);
CGFloat cx = cosPhi * cxp + -sinPhi * cyp + (x1 + x2) / 2.f;
CGFloat cy = cxp * sinPhi + cyp * cosPhi + (y1 + y2) / 2.f;
CGAffineTransform transform = CGAffineTransformMakeScale(1.f / rx, 1.f / ry);
transform = CGAffineTransformRotate(transform, -xAxisRotation);
transform = CGAffineTransformTranslate(transform, -cx, -cy);
CGPoint arcPt1 = CGPointApplyAffineTransform(CGPointMake(x1, y1), transform);
CGPoint arcPt2 = CGPointApplyAffineTransform(CGPointMake(x2, y2), transform);
CGFloat startAngle = atan2(arcPt1.y, arcPt1.x);
CGFloat endAngle = atan2(arcPt2.y, arcPt2.x);
CGFloat angleDelta = endAngle - startAngle;;
if(sweepFlag == YES) {
if(angleDelta < 0.f) {
angleDelta += 2.f * M_PI;
}
} else if(angleDelta > 0.f) {
angleDelta = angleDelta - 2.f * M_PI;
}
transform = CGAffineTransformMakeTranslation(cx, cy);
transform = CGAffineTransformRotate(transform, xAxisRotation);
transform = CGAffineTransformScale(transform, rx, ry);
CGPathAddRelativeArc(path, &transform, 0.f, 0.f, 1.f,
startAngle, angleDelta);
}
@end
@@ -1,34 +0,0 @@
//
// IJSVGCommandHorizontalLine.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandHorizontalLine.h>
@implementation IJSVGCommandHorizontalLine
+ (NSInteger)requiredParameterCount
{
return 1;
}
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
if(type == kIJSVGCommandTypeAbsolute) {
CGPathAddLineToPoint(path, NULL, params[0], CGPathGetCurrentPoint(path).y);
return;
}
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddLineToPoint(path, NULL, currentPoint.x + params[0],
currentPoint.y);
}
@end
@@ -1,45 +0,0 @@
//
// IJSVGCommandLineTo.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandLineTo.h>
@implementation IJSVGCommandLineTo
+ (NSInteger)requiredParameterCount
{
return 2;
}
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
if(type == kIJSVGCommandTypeAbsolute) {
CGPathAddLineToPoint(path, NULL, params[0], params[1]);
return;
}
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddLineToPoint(path, NULL, currentPoint.x + params[0],
currentPoint.y + params[1]);
}
- (void)convertToUnits:(IJSVGUnitType)units
boundingBox:(CGRect)boundingBox
{
if(units == IJSVGUnitObjectBoundingBox) {
self.parameters[0] = [[IJSVGUnitLength unitWithPercentageFloat:self.parameters[0]] computeValue:boundingBox.size.width];
self.parameters[1] = [[IJSVGUnitLength unitWithPercentageFloat:self.parameters[1]] computeValue:boundingBox.size.height];
}
[super convertToUnits:units
boundingBox:boundingBox];
}
@end
@@ -1,65 +0,0 @@
//
// IJSVGCommandMove.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandLineTo.h>
#import <IJSVG/IJSVGCommandMove.h>
@implementation IJSVGCommandMove
+ (NSInteger)requiredParameterCount
{
return 2;
}
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
// move to's allow more then one move to, but if there are more then one,
// we need to run the line to instead...who knew!
if(command.class == self.class && currentCommand.isSubCommand == YES) {
[IJSVGCommandLineTo runWithParams:params
paramCount:count
command:currentCommand
previousCommand:command
type:type
path:path];
return;
}
// actual move to command - do a moveToPoint only
// if the type is absolute, or its possible the type is
// relative but there is no previous command which means
// there is no current point. Asking for current point on an empty
// path will result in an exception being thrown
if(type == kIJSVGCommandTypeAbsolute || command == nil) {
CGPathMoveToPoint(path, NULL,
params[0], params[1]);
return;
}
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathMoveToPoint(path, NULL,
currentPoint.x + params[0],
currentPoint.y + params[1]);
}
- (void)convertToUnits:(IJSVGUnitType)units
boundingBox:(CGRect)boundingBox
{
if(units == IJSVGUnitObjectBoundingBox) {
self.parameters[0] = [[IJSVGUnitLength unitWithPercentageFloat:self.parameters[0]] computeValue:boundingBox.size.width];
self.parameters[1] = [[IJSVGUnitLength unitWithPercentageFloat:self.parameters[1]] computeValue:boundingBox.size.height];
}
[super convertToUnits:units
boundingBox:boundingBox];
}
@end
@@ -1,37 +0,0 @@
//
// IJSVGCommandQuadraticCurve.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandQuadraticCurve.h>
#import <IJSVG/IJSVGUtils.h>
@implementation IJSVGCommandQuadraticCurve
+ (NSInteger)requiredParameterCount
{
return 4;
}
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
if(type == kIJSVGCommandTypeAbsolute) {
CGPathAddQuadCurveToPoint(path, NULL, params[0], params[1],
params[2], params[3]);
return;
}
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddQuadCurveToPoint(path, NULL,
currentPoint.x + params[0], currentPoint.y + params[1],
currentPoint.x + params[2], currentPoint.y + params[3]);
}
@end
@@ -1,64 +0,0 @@
//
// IJSVGCommandSmoothCurve.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandCurve.h>
#import <IJSVG/IJSVGCommandSmoothCurve.h>
#import <IJSVG/IJSVGUtils.h>
@implementation IJSVGCommandSmoothCurve
+ (NSInteger)requiredParameterCount
{
return 4;
}
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPoint firstControl = CGPointMake(currentPoint.x, currentPoint.y);
if(command != nil) {
if(command.class == [IJSVGCommandCurve class] || command.class == self.class) {
if(command.class == [IJSVGCommandCurve class]) {
if(command.type == kIJSVGCommandTypeAbsolute) {
firstControl = CGPointMake(-1 * command.parameters[2] + 2 * currentPoint.x,
-1 * command.parameters[3] + 2 * currentPoint.y);
} else {
NSPoint oldPoint = CGPointMake(currentPoint.x - command.parameters[4],
currentPoint.y - command.parameters[5]);
firstControl = CGPointMake(-1 * (command.parameters[2] + oldPoint.x) + 2 * currentPoint.x,
-1 * (command.parameters[3] + oldPoint.y) + 2 * currentPoint.y);
}
} else {
if(command.type == kIJSVGCommandTypeAbsolute) {
firstControl = CGPointMake(-1 * command.parameters[0] + 2 * currentPoint.x,
-1 * command.parameters[1] + 2 * currentPoint.y);
} else {
NSPoint oldPoint = CGPointMake(currentPoint.x - command.parameters[2],
currentPoint.y - command.parameters[3]);
firstControl = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * currentPoint.x,
-1 * (command.parameters[1] + oldPoint.y) + 2 * currentPoint.y);
}
}
}
}
if(type == kIJSVGCommandTypeAbsolute) {
CGPathAddCurveToPoint(path, NULL, firstControl.x, firstControl.y,
params[0], params[1], params[2], params[3]);
return;
}
CGPathAddCurveToPoint(path, NULL, firstControl.x, firstControl.y,
currentPoint.x + params[0], currentPoint.y + params[1],
currentPoint.x + params[2], currentPoint.y + params[3]);
}
@end
@@ -1,58 +0,0 @@
//
// IJSVGCommandCommandQuadraticCurve.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandQuadraticCurve.h>
#import <IJSVG/IJSVGCommandSmoothQuadraticCurve.h>
#import <IJSVG/IJSVGUtils.h>
@implementation IJSVGCommandSmoothQuadraticCurve
+ (NSInteger)requiredParameterCount
{
return 2;
}
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
CGPoint lastControlPoint = IJSVGPathGetLastQuadraticCommandPoint(path);
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPoint commandPoint = CGPointMake(currentPoint.x, currentPoint.y);
if(command != nil) {
if(command.class == IJSVGCommandQuadraticCurve.class) {
// quadratic curve
if(command.type == kIJSVGCommandTypeAbsolute) {
commandPoint = NSMakePoint(-1 * command.parameters[0] + 2 * currentPoint.x,
-1 * command.parameters[1] + 2 * currentPoint.y);
} else {
CGPoint oldPoint = CGPointMake(currentPoint.x - command.parameters[2],
currentPoint.y - command.parameters[3]);
commandPoint = CGPointMake(-1 * (command.parameters[0] + oldPoint.x) + 2 * (currentPoint.x),
-1 * (command.parameters[1] + oldPoint.y) + 2 * currentPoint.y);
}
} else if(command.class == self.class) {
// smooth quadratic curve
commandPoint = CGPointMake(-1 * (lastControlPoint.x) + 2 * (currentPoint.x),
-1 * (lastControlPoint.y) + 2 * currentPoint.y);
}
}
// path.lastControlPoint = commandPoint;
if(type == kIJSVGCommandTypeAbsolute) {
CGPathAddQuadCurveToPoint(path, NULL, commandPoint.x, commandPoint.y,
params[0], params[1]);
return;
}
CGPathAddQuadCurveToPoint(path, NULL, commandPoint.x, commandPoint.y,
currentPoint.x + params[0], currentPoint.y + params[1]);
}
@end
@@ -1,33 +0,0 @@
//
// IJSVGCommandVerticalLine.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandVerticalLine.h>
@implementation IJSVGCommandVerticalLine
+ (NSInteger)requiredParameterCount
{
return 1;
}
+ (void)runWithParams:(CGFloat*)params
paramCount:(NSInteger)count
command:(IJSVGCommand*)currentCommand
previousCommand:(IJSVGCommand*)command
type:(IJSVGCommandType)type
path:(CGMutablePathRef)path
{
if(type == kIJSVGCommandTypeAbsolute) {
CGPathAddLineToPoint(path, NULL, CGPathGetCurrentPoint(path).x, params[0]);
return;
}
CGPoint currentPoint = CGPathGetCurrentPoint(path);
CGPathAddLineToPoint(path, NULL, currentPoint.x, currentPoint.y + params[0]);
}
@end
-162
View File
@@ -1,162 +0,0 @@
//
// IJSVGImage.h
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGTraitedColorStorage.h>
#import <IJSVG/IJSVGRootNode.h>
#import <IJSVG/IJSVGExporter.h>
#import <IJSVG/IJSVGGradientLayer.h>
#import <IJSVG/IJSVGGroupLayer.h>
#import <IJSVG/IJSVGRootLayer.h>
#import <IJSVG/IJSVGImageLayer.h>
#import <IJSVG/IJSVGLayerTree.h>
#import <IJSVG/IJSVGParser.h>
#import <IJSVG/IJSVGRendering.h>
#import <IJSVG/IJSVGStyle.h>
#import <IJSVG/IJSVGTransaction.h>
#import <Foundation/Foundation.h>
@class IJSVG;
@class IJSVGParser;
@interface IJSVG : NSObject <NSPasteboardWriting> {
@private
IJSVGRootNode* _rootNode;
IJSVGLayerTree* _layerTree;
CGRect _viewBox;
CGFloat _backingScale;
IJSVGUnitSize* _intrinsicSize;
IJSVGParser* _parser;
}
// set this to be called when the layer is about to draw, it will call this
// and ask for the scale of the backing store where its going to be drawn
// and apply the scale to each layer that has custom drawing against it, mainly
// pattern and gradient layers
@property (nonatomic, copy) IJSVGRenderingBackingScaleFactorHelper renderingBackingScaleHelper;
// global overwriting rules for when rendering an SVG, this will overide any
// fillColor, strokeColor, pattern and gradient fill
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, strong) IJSVGStyle* style;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* desc;
@property (nonatomic, strong) IJSVGLayerTree* layerTree;
@property (nonatomic, strong) IJSVGRootLayer* rootLayer;
@property (nonatomic, assign) BOOL ignoreIntrinsicSize;
@property (nonatomic, readonly) IJSVGTraitedColorStorage* colors;
// The size of the SVG either computed by its intrinsicSize of its viewBox
// If the size if % values, it will use the defaultSize
@property (nonatomic, readonly) CGSize size;
// Will return true if the intrinsic size is a % value
@property (nonatomic, readonly) BOOL hasDynamicSize;
// This is used when the intrinsic size is a % value, e.g. 100% x 100%
@property (nonatomic, assign) CGSize defaultSize;
// bitmask of which dimentions were implicitly set on the SVG
@property (nonatomic, readonly) IJSVGIntrinsicDimensions intrinsicDimensions;
- (void)prepForDrawingInView:(NSView*)view;
- (IJSVGRootNode*)rootNode;
- (CGRect)viewBox;
- (CGSize)sizeWithDefaultSize:(CGSize)size;
- (CGSize)sizeByMaintainingAspectRatioWithSize:(CGSize)aSize;
- (NSString*)identifier;
- (NSSet<IJSVG*>*)directDescendSVGs;
- (IJSVGExporter*)exporterWithSize:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (NSString*)SVGStringWithSize:(CGSize)size
options:(IJSVGExporterOptions)options;
- (NSString*)SVGStringWithSize:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options;
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (id)SVGNamed:(NSString*)string;
+ (IJSVG*)SVGFromCGPathRef:(CGPathRef)path;
+ (IJSVG*)SVGFromCGPathRef:(CGPathRef)path
flipped:(BOOL)flipped;
- (id)initWithImage:(NSImage*)image;
- (id)initWithRootNode:(IJSVGRootNode*)rootNode;
- (id)initWithSVGLayer:(IJSVGGroupLayer*)group
viewBox:(CGRect)viewBox;
- (id)initWithSVGString:(NSString*)string;
- (id)initWithSVGString:(NSString*)string
error:(NSError**)error;
- (id)initWithSVGData:(NSData*)data;
- (id)initWithSVGData:(NSData*)data
error:(NSError**)error;
- (id)initWithFile:(NSString*)file;
- (id)initWithFile:(NSString*)file
error:(NSError**)error;
- (id)initWithFilePathURL:(NSURL*)aURL;
- (id)initWithFilePathURL:(NSURL*)aURL
error:(NSError**)error;
- (id)initWithDataAssetNamed:(NSDataAssetName)name
error:(NSError**)error;
- (id)initWithDataAssetNamed:(NSDataAssetName)name
bundle:(NSBundle*)bundle
error:(NSError**)error;
- (NSImage*)imageWithSize:(CGSize)aSize;
- (NSImage*)imageWithSize:(CGSize)aSize
error:(NSError**)error;
- (NSImage*)imageWithSize:(CGSize)aSize
flipped:(BOOL)flipped;
- (NSImage*)imageWithSize:(CGSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error;
- (NSImage*)imageByMaintainingAspectRatioWithSize:(CGSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error;
- (CGImageRef)newCGImageRefWithSize:(CGSize)size
flipped:(BOOL)flipped
error:(NSError**)error;
- (BOOL)drawAtPoint:(CGPoint)point
size:(CGSize)size;
- (BOOL)drawAtPoint:(CGPoint)point
size:(CGSize)aSize
error:(NSError**)error;
- (BOOL)drawInRect:(CGRect)rect;
- (BOOL)drawInRect:(CGRect)rect
error:(NSError**)error;
- (void)drawInRect:(CGRect)rect
context:(CGContextRef)context;
- (NSData*)PDFData;
- (NSData*)PDFData:(NSError**)error;
- (NSData*)PDFDataWithRect:(CGRect)rect;
- (NSData*)PDFDataWithRect:(CGRect)rect
error:(NSError**)error;
// call this to invalidate the render tree when you change the style
- (void)setNeedsDisplay;
// colors
- (void)performBlock:(dispatch_block_t)block;
// matching
- (BOOL)containsNodesMatchingTraits:(IJSVGNodeTraits)mask;
@end
-747
View File
@@ -1,747 +0,0 @@
//
// IJSVGImage.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
#import <IJSVG/IJSVGExporter.h>
#import <IJSVG/IJSVGTransaction.h>
#import <IJSVG/IJSVGThreadManager.h>
@interface IJSVG (private)
@property (nonatomic, strong) IJSVGParser* parser;
@end
@implementation IJSVG
// these are explicitly implemented
@synthesize title = _title;
@synthesize desc = _desc;
- (void)dealloc
{
// thread manager will deal with this for us, but if we are main thread,
// we want to kick this off as soon as possible, or if the memory is set
// to quick.
IJSVGThreadManager* threadManager = IJSVGThreadManager.currentManager;
BOOL flag = IJSVGBeginTransaction();
_layerTree = nil;
_rootLayer = nil;
if(flag == YES) {
IJSVGEndTransaction();
}
// tell the thread manager we are done with
[threadManager remove:self];
}
+ (id)SVGNamed:(NSString*)string
{
return [self.class SVGNamed:string
error:nil];
}
+ (id)SVGNamed:(NSString*)string
error:(NSError**)error
{
NSBundle* bundle = NSBundle.mainBundle;
NSString* str = nil;
NSString* ext = [string pathExtension];
if(ext == nil || ext.length == 0) {
ext = @"svg";
}
if((str = [bundle pathForResource:[string stringByDeletingPathExtension]
ofType:ext]) != nil) {
return [[self alloc] initWithFile:str
error:error];
}
// check the asset catalogues
return [[self alloc] initWithDataAssetNamed:string
error:error];
}
+ (IJSVG*)SVGFromCGPathRef:(CGPathRef)path
{
return [self SVGFromCGPathRef:path
flipped:NO];
}
+ (IJSVG*)SVGFromCGPathRef:(CGPathRef)path
flipped:(BOOL)flipped
{
CGRect box = CGPathGetPathBoundingBox(path);
IJSVGRootNode* rootNode = [[IJSVGRootNode alloc] init];
rootNode.viewBox = [IJSVGUnitRect rectWithCGRect:box];
CGMutablePathRef nPath = NULL;
if(flipped) {
CGPathRef transformedPath = [IJSVGUtils newFlippedCGPath:path];
nPath = CGPathCreateMutableCopy(transformedPath);
CGPathRelease(transformedPath);
} else {
nPath = CGPathCreateMutableCopy(path);
}
IJSVGPath* childPath = [[IJSVGPath alloc] init];
childPath.path = nPath;
[rootNode addChild:childPath];
CGPathRelease(nPath);
return [[self.class alloc] initWithRootNode:rootNode];
}
- (id)initWithDataAssetNamed:(NSDataAssetName)name
error:(NSError**)error
{
return [self initWithDataAssetNamed:name
bundle:NSBundle.mainBundle
error:error];
}
- (id)initWithDataAssetNamed:(NSDataAssetName)name
bundle:(NSBundle*)bundle
error:(NSError**)error
{
NSDataAsset* dataAsset = [[NSDataAsset alloc] initWithName:name
bundle:bundle];
if(dataAsset != nil) {
return [self initWithSVGData:dataAsset.data
error:error];
}
return nil;
}
- (id)initWithImage:(NSImage*)image
{
IJSVGRootNode* rootNode = [[IJSVGRootNode alloc] init];
IJSVGImage* imageNode = [[IJSVGImage alloc] init];
imageNode.image = image;
IJSVGUnitSize* size = [IJSVGUnitSize sizeWithCGSize:imageNode.intrinsicSize];
IJSVGUnitRect* viewBox = [IJSVGUnitRect rectWithOrigin:IJSVGUnitPoint.zeroPoint
size:size];
imageNode.width = size.width.copy;
imageNode.height = size.height.copy;
rootNode.viewBox = viewBox;
[rootNode addChild:imageNode];
return [self initWithRootNode:rootNode];
}
- (id)initWithSVGLayer:(IJSVGGroupLayer*)group
viewBox:(CGRect)viewBox
{
// this completely bypasses passing of files
if((self = [super init]) != nil) {
// keep the layer tree
_viewBox = viewBox;
// any setups
[self _setupBasicsFromAnyInitializer];
}
return self;
}
- (id)initWithRootNode:(IJSVGRootNode*)rootNode
{
if((self = [super init]) != nil) {
_rootNode = rootNode;
[self _setupBasicInfoFromGroup];
[self _setupBasicsFromAnyInitializer];
}
return self;
}
- (id)initWithFile:(NSString*)file
{
return [self initWithFile:file
error:nil];
}
- (id)initWithFile:(NSString*)file
error:(NSError**)error
{
return [self initWithFilePathURL:[NSURL fileURLWithPath:file isDirectory:NO]
error:error];
}
- (id)initWithFilePathURL:(NSURL*)aURL
{
return [self initWithFilePathURL:aURL
error:nil];
}
- (id)initWithFilePathURL:(NSURL*)aURL
error:(NSError**)error
{
// create the object
if((self = [super init]) != nil) {
NSError* anError = nil;
// create the group
IJSVGParser *parser = [IJSVGParser parserForFileURL:aURL
error:&anError];
self.parser = parser;
[self _setupBasicInfoFromGroup];
[self _setupBasicsFromAnyInitializer];
// something went wrong...
if(_rootNode == nil) {
if(error != NULL) {
*error = anError;
}
self = nil;
return nil;
}
}
return self;
}
- (id)initWithSVGData:(NSData*)data
{
return [self initWithSVGData:data
error:nil];
}
- (void)setParser:(IJSVGParser*)parser {
_rootNode = [parser rootNodeWithSize:CGSizeZero];
// if the rootNode has any form of relative units, we need to keep hold of
// the parser so when we render, we can ask for new values.
if(_rootNode.viewBoxContainsRelativeUnits) {
_parser = parser;
}
}
- (id)initWithSVGData:(NSData*)data
error:(NSError**)error
{
NSString* svgString = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
return [self initWithSVGString:svgString
error:error];
}
- (id)initWithSVGString:(NSString*)string
{
return [self initWithSVGString:string
error:nil];
}
- (id)initWithSVGString:(NSString*)string
error:(NSError**)error
{
if((self = [super init]) != nil) {
// this is basically the same as init with URL just
// bypasses the loading of a file
NSError* anError = nil;
// setup the parser
IJSVGParser* parser = [[IJSVGParser alloc] initWithSVGString:string
error:&anError];
self.parser = parser;
[self _setupBasicInfoFromGroup];
[self _setupBasicsFromAnyInitializer];
// something went wrong :(
if(_rootNode == nil) {
if(error != NULL) {
*error = anError;
}
self = nil;
return nil;
}
}
return self;
}
- (void)performBlock:(dispatch_block_t)block
{
IJSVGPerformTransactionBlock(^{
block();
});
}
- (void)_setupBasicInfoFromGroup
{
_viewBox = [_rootNode.viewBox computeValue:CGSizeZero];
_intrinsicSize = _rootNode.intrinsicSize;
}
- (CGSize)size
{
return [_intrinsicSize computeValue:self.defaultSize];
}
- (CGSize)sizeWithDefaultSize:(CGSize)size
{
return [_intrinsicSize computeValue:size];
}
- (void)_setupBasicsFromAnyInitializer
{
self.style = [[IJSVGStyle alloc] init];
self.ignoreIntrinsicSize = YES;
self.renderQuality = kIJSVGRenderQualityFullResolution;
self.defaultSize = CGSizeMake(200.f, 200.f);
self.renderingBackingScaleHelper = ^CGFloat {
if(NSScreen.mainScreen != nil) {
return NSScreen.mainScreen.backingScaleFactor;
}
return 1.f;
};
// tell the thread manager we exist
[IJSVGThreadManager.currentManager adopt:self];
}
- (BOOL)hasDynamicSize
{
IJSVGUnitSize* size = _intrinsicSize;
return size.width.type == IJSVGUnitLengthTypePercentage ||
size.height.type == IJSVGUnitLengthTypePercentage;
}
- (IJSVGIntrinsicDimensions)intrinsicDimensions
{
return self.rootNode.intrinsicDimensions;
}
- (void)setTitle:(NSString*)title
{
_rootNode.title = title;
}
- (NSString*)title
{
return _rootNode.title;
}
- (void)setDesc:(NSString*)description
{
_rootNode.desc = description;
}
- (NSString*)desc
{
return _rootNode.desc;
}
- (NSString*)identifier
{
return _rootNode.identifier;
}
- (CGRect)viewBox
{
return _viewBox;
}
- (IJSVGRootNode*)rootNode
{
return _rootNode;
}
- (NSSet<IJSVG*>*)directDescendSVGs
{
NSMutableSet<IJSVG*>* svgs = [[NSMutableSet alloc] init];
NSSet<IJSVGNode*>* nodes = [self.rootNode childrenOfType:IJSVGNodeTypeSVG];
for(IJSVGNode* node in nodes) {
IJSVG* newSVG = nil;
newSVG = [[self.class alloc] initWithRootNode:(IJSVGRootNode*)node];
[svgs addObject:newSVG];
}
return svgs;
}
- (IJSVGExporter*)exporterWithSize:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
return [[IJSVGExporter alloc] initWithSVG:self
size:size
options:options
floatingPointOptions:floatingPointOptions];
}
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
{
IJSVGFloatingPointOptions fpo = IJSVGFloatingPointOptionsDefault();
return [self exporterWithSize:_viewBox.size
options:options
floatingPointOptions:fpo].SVGString;
}
- (NSString*)SVGStringWithOptions:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
return [self exporterWithSize:_viewBox.size
options:options
floatingPointOptions:floatingPointOptions].SVGString;
}
- (NSString *)SVGStringWithSize:(CGSize)size
options:(IJSVGExporterOptions)options
{
IJSVGFloatingPointOptions fpo = IJSVGFloatingPointOptionsDefault();
return [self exporterWithSize:size
options:options
floatingPointOptions:fpo].SVGString;
}
- (NSString *)SVGStringWithSize:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
return [self exporterWithSize:size
options:options
floatingPointOptions:floatingPointOptions].SVGString;
}
- (NSImage*)imageWithSize:(CGSize)aSize
{
return [self imageWithSize:aSize
flipped:NO
error:nil];
}
- (NSImage*)imageWithSize:(CGSize)aSize
error:(NSError**)error;
{
return [self imageWithSize:aSize
flipped:NO
error:error];
}
- (NSImage*)imageWithSize:(CGSize)aSize
flipped:(BOOL)flipped
{
return [self imageWithSize:aSize
flipped:flipped
error:nil];
}
- (CGImageRef)newCGImageRefWithSize:(CGSize)size
flipped:(BOOL)flipped
error:(NSError**)error
{
// setup the drawing rect, this is used for both the intial drawing
// and the backing scale helper block
CGRect rect = (CGRect) {
.origin = CGPointZero,
.size = (CGSize)size
};
// make sure we setup the scale based on the backing scale factor
CGFloat scale = [self backingScaleFactor];
// create the context and colorspace
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ref = CGBitmapContextCreate(NULL, (int)size.width * scale,
(int)size.height * scale, 8, 0, colorSpace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
// scale the context
CGContextScaleCTM(ref, scale, scale);
if(flipped == YES) {
CGContextTranslateCTM(ref, 0.f, size.height);
CGContextScaleCTM(ref, 1.f, -1.f);
}
// draw the SVG into the context
[self _drawInRect:rect
context:ref
error:error];
// create the image from the context
CGImageRef imageRef = CGBitmapContextCreateImage(ref);
// release all things!
CGColorSpaceRelease(colorSpace);
CGContextRelease(ref);
return imageRef;
}
- (NSImage*)imageWithSize:(CGSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error
{
CGImageRef ref = [self newCGImageRefWithSize:aSize
flipped:flipped
error:error];
NSImage* image = [[NSImage alloc] initWithCGImage:ref
size:aSize];
CGImageRelease(ref);
return image;
}
- (CGSize)sizeByMaintainingAspectRatioWithSize:(CGSize)aSize
{
CGSize ogSize = [_rootNode.intrinsicSize computeValue:aSize];
CGFloat ratio = 0.f;
CGFloat imageWidth = ogSize.width;
CGFloat imageHeight = ogSize.height;
CGFloat maxWidth = aSize.width;
CGFloat maxHeight = aSize.height;
if(imageWidth > imageHeight) {
ratio = maxWidth / imageWidth;
} else {
ratio = maxHeight / imageHeight;
}
ogSize.width = ceilf(imageWidth * ratio);
ogSize.height = ceilf(imageHeight * ratio);
return ogSize;
}
- (NSImage*)imageByMaintainingAspectRatioWithSize:(CGSize)aSize
flipped:(BOOL)flipped
error:(NSError**)error
{
CGSize ogSize = [self sizeByMaintainingAspectRatioWithSize:aSize];
return [self imageWithSize:ogSize
flipped:flipped
error:error];
}
- (NSData*)PDFData
{
return [self PDFData:nil];
}
- (NSData*)PDFData:(NSError**)error
{
return [self
PDFDataWithRect:(CGRect) { .origin = NSZeroPoint, .size = _viewBox.size }
error:error];
}
- (NSData*)PDFDataWithRect:(CGRect)rect
{
return [self PDFDataWithRect:rect error:nil];
}
- (NSData*)PDFDataWithRect:(CGRect)rect
error:(NSError**)error
{
// create the data for the PDF
NSMutableData* data = [[NSMutableData alloc] init];
// assign the data to the consumer
CGDataConsumerRef dataConsumer = CGDataConsumerCreateWithCFData((CFMutableDataRef)data);
const CGRect box = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width,
rect.size.height);
// create the context
CGContextRef context = CGPDFContextCreate(dataConsumer, &box, NULL);
CGContextBeginPage(context, &box);
// the context is currently upside down, doh! flip it...
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -box.size.height);
// make sure we set the masks to path bits n bobs
// draw the icon
[self _drawInRect:(CGRect)box
context:context
error:error];
CGContextEndPage(context);
// clean up
CGPDFContextClose(context);
CGContextRelease(context);
CGDataConsumerRelease(dataConsumer);
return data;
}
- (void)prepForDrawingInView:(NSView*)view
{
// kill the render
if(view == nil) {
self.renderingBackingScaleHelper = nil;
return;
}
// construct the layer before drawing
[self rootLayer];
// set the scale
__weak NSView* weakView = view;
self.renderingBackingScaleHelper = ^CGFloat {
return weakView.window.screen.backingScaleFactor;
};
}
- (BOOL)drawAtPoint:(CGPoint)point
size:(CGSize)aSize
{
return [self drawAtPoint:point
size:aSize
error:nil];
}
- (BOOL)drawAtPoint:(CGPoint)point
size:(CGSize)aSize
error:(NSError**)error
{
return [self drawInRect:NSMakeRect(point.x, point.y,
aSize.width, aSize.height)
error:error];
}
- (BOOL)drawInRect:(CGRect)rect
{
return [self drawInRect:rect error:nil];
}
- (BOOL)drawInRect:(CGRect)rect
error:(NSError**)error
{
CGContextRef currentCGContext;
currentCGContext = NSGraphicsContext.currentContext.CGContext;
return [self _drawInRect:rect
context:currentCGContext
error:error];
}
- (void)drawInRect:(CGRect)rect
context:(CGContextRef)context
{
[self _drawInRect:rect
context:context
error:nil];
}
- (BOOL)_drawInRect:(CGRect)rect
context:(CGContextRef)ctx
error:(NSError**)error
{
BOOL transaction = IJSVGBeginTransaction();
CGContextSaveGState(ctx);
CGFloat backingScale = MAX([self backingScaleFactor], 1.f);
CGInterpolationQuality quality;
switch (_renderQuality) {
case kIJSVGRenderQualityLow: {
quality = kCGInterpolationLow;
break;
}
case kIJSVGRenderQualityOptimized: {
quality = kCGInterpolationMedium;
break;
}
default: {
quality = kCGInterpolationHigh;
}
}
CGContextSetInterpolationQuality(ctx, quality);
IJSVGRootLayer* rootLayer = [self rootLayerWithRect:rect];
[rootLayer renderInContext:ctx
viewPort:rect
backingScale:backingScale
quality:_renderQuality
ignoreIntrinsicSize:_ignoreIntrinsicSize];
CGContextRestoreGState(ctx);
if(transaction == YES) {
IJSVGEndTransaction();
}
return YES;
}
- (IJSVGLayerTree*)layerTree
{
if(_layerTree == nil) {
_layerTree = [[IJSVGLayerTree alloc] init];
_layerTree.style = _style;
}
return _layerTree;
}
- (IJSVGRootLayer*)rootLayerWithRect:(CGRect)rect {
// no parser, which means there is no need to recompute the value
if(_parser == nil || !_rootNode.viewBoxContainsRelativeUnits ||
CGSizeEqualToSize(_rootNode.clientSize, rect.size)) {
return self.rootLayer;
}
// if we do have a parser, that means the node has relative values, lets recompute
__weak IJSVG* weakSelf = self;
[self performBlock:^{
IJSVG* strongSelf = weakSelf;
strongSelf->_rootNode = [strongSelf->_parser rootNodeWithSize:rect.size];
strongSelf->_rootLayer = [strongSelf.layerTree rootLayerForRootNode:strongSelf->_rootNode];
}];
return _rootLayer;
}
- (IJSVGRootLayer*)rootLayer
{
if(_rootLayer != nil) {
return _rootLayer;
}
__weak IJSVG* weakSelf = self;
[self performBlock:^{
IJSVG* strongSelf = weakSelf;
strongSelf->_rootLayer = [strongSelf.layerTree rootLayerForRootNode:strongSelf->_rootNode];
}];
return _rootLayer;
}
- (CGFloat)backingScaleFactor
{
__block CGFloat scale = 1.f;
if(self.renderingBackingScaleHelper != nil) {
scale = (self.renderingBackingScaleHelper)();
}
scale = MAX(1.f, scale);
return _backingScale = scale;
}
- (void)setNeedsDisplay
{
[self invalidateLayerTree];
}
- (void)invalidateLayerTree
{
__weak IJSVG* weakSelf = self;
[self performBlock:^{
IJSVG* strongSelf = weakSelf;
strongSelf->_rootLayer = nil;
strongSelf->_layerTree = nil;
}];
}
- (IJSVGTraitedColorStorage*)colors
{
return self.rootLayer.colors;
}
#pragma mark NSPasteboard
- (NSArray*)writableTypesForPasteboard:(NSPasteboard*)pasteboard
{
return @[ NSPasteboardTypePDF ];
}
- (id)pasteboardPropertyListForType:(NSString*)type
{
if([type isEqualToString:NSPasteboardTypePDF]) {
return [self PDFData];
}
return nil;
}
#pragma mark matching
- (BOOL)containsNodesMatchingTraits:(IJSVGNodeTraits)traits
{
return [_rootNode containsNodesMatchingTraits:traits];
}
@end
@@ -1,25 +0,0 @@
//
// IJSVGImageRep.h
// IJSVGExample
//
// Created by Curtis Hard on 15/03/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGParser.h>
#import <Cocoa/Cocoa.h>
@class IJSVG;
@interface IJSVGImageRep : NSImageRep {
@private
IJSVG* _svg;
}
- (instancetype)initWithData:(NSData*)data;
@property (nonatomic, readonly) CGRect viewBox;
@property (nonatomic, readonly) IJSVG* SVG;
@end
@@ -1,108 +0,0 @@
//
// IJSVGImageRep.m
// IJSVGExample
//
// Created by Curtis Hard on 15/03/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
#import <IJSVG/IJSVGImageRep.h>
@implementation IJSVGImageRep
+ (void)load
{
[NSBitmapImageRep registerImageRepClass:self];
}
+ (BOOL)canInitWithData:(NSData*)data
{
return [IJSVGParser isDataSVG:data];
}
+ (NSArray<NSString*>*)imageTypes
{
if(@available(macOS 10.10, *)) {
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
} else {
return @[ @"public.svg-image", @"svg" ];
}
}
+ (NSArray<NSString*>*)imageUnfilteredTypes
{
if(@available(macOS 10.10, *)) {
return @[ (NSString*)kUTTypeScalableVectorGraphics, @"svg" ];
} else {
return @[ @"public.svg-image", @"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];
}
- (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];
_svg = [[IJSVG alloc] initWithSVGString:string];
// no valid SVG, just return nil;
if(_svg == nil) {
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;
}
- (IJSVG*)SVG
{
return _svg;
}
@end
@@ -1,37 +0,0 @@
//
// IJSVGUmbrella.h
// IJSVG
//
// Created by Curtis Hard on 16/04/2023.
// Copyright © 2023 Curtis Hard. All rights reserved.
//
#ifndef IJSVGUmbrella_h
#define IJSVGUmbrella_h
#import <IJSVG/IJSVG.h>
#import <IJSVG/IJSVGCommandClose.h>
#import <IJSVG/IJSVGCommandCurve.h>
#import <IJSVG/IJSVGCommandEllipticalArc.h>
#import <IJSVG/IJSVGCommandHorizontalLine.h>
#import <IJSVG/IJSVGCommandLineTo.h>
#import <IJSVG/IJSVGCommandMove.h>
#import <IJSVG/IJSVGCommandQuadraticCurve.h>
#import <IJSVG/IJSVGCommandSmoothQuadraticCurve.h>
#import <IJSVG/IJSVGCommandSmoothCurve.h>
#import <IJSVG/IJSVGCommandVerticalLine.h>
#import <IJSVG/IJSVGExporterPathInstruction.h>
#import <IJSVG/IJSVGFeatureFlag.h>
#import <IJSVG/IJSVGFeatureFlags.h>
#import <IJSVG/IJSVGFilterEffectGaussianBlur.h>
#import <IJSVG/IJSVGFilterLayer.h>
#import <IJSVG/IJSVGImageRep.h>
#import <IJSVG/IJSVGMath.h>
#import <IJSVG/IJSVGParsing.h>
#import <IJSVG/IJSVGPatternLayer.h>
#import <IJSVG/IJSVGStrokeLayer.h>
#import <IJSVG/IJSVGThreadManager.h>
#import <IJSVG/IJSVGView.h>
#import <IJSVG/NSImage+IJSVGAdditions.h>
#endif /* IJSVGUmbrella_h */
@@ -1,25 +0,0 @@
//
// IJSVGView.h
// IconJar
//
// Created by Curtis Hard on 04/04/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
#import <Cocoa/Cocoa.h>
IB_DESIGNABLE
@interface IJSVGView : NSView {
IBInspectable NSString* imageName;
IBInspectable NSColor* tintColor;
IJSVG* SVG;
}
@property (nonatomic, strong) IJSVG* SVG;
+ (IJSVGView*)viewWithSVGNamed:(NSString*)name;
- (id)initWithSVG:(IJSVG*)anSvg;
@end
@@ -1,120 +0,0 @@
//
// IJSVGExporter.h
// IJSVGExample
//
// Created by Curtis Hard on 06/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGTraitedColor.h>
@class IJSVG;
@class IJSVGExporter;
@class IJSVGLayer;
@class IJSVGNode;
NS_ASSUME_NONNULL_BEGIN
typedef void (^IJSVGCGPathHandler)(const CGPathElement* pathElement);
typedef void (^IJSVGPathElementEnumerationBlock)(const CGPathElement* pathElement, CGPoint currentPoint);
void IJSVGExporterPathCaller(void* info, const CGPathElement* pathElement);
typedef NS_OPTIONS(NSInteger, IJSVGExporterOptions) {
IJSVGExporterOptionNone = 1 << 0,
IJSVGExporterOptionRemoveUselessGroups = 1 << 1,
IJSVGExporterOptionRemoveUselessDef = 1 << 2,
IJSVGExporterOptionMoveAttributesToGroup = 1 << 3,
IJSVGExporterOptionCreateUseForPaths = 1 << 4,
IJSVGExporterOptionSortAttributes = 1 << 5,
IJSVGExporterOptionCollapseGroups = 1 << 6,
IJSVGExporterOptionCleanupPaths = 1 << 7,
IJSVGExporterOptionRemoveHiddenElements = 1 << 8,
IJSVGExporterOptionScaleToSizeIfNecessary DEPRECATED_ATTRIBUTE = 1 << 9,
IJSVGExporterOptionCompressOutput = 1 << 10,
IJSVGExporterOptionCollapseGradients = 1 << 11,
IJSVGExporterOptionCreateClasses DEPRECATED_ATTRIBUTE = 1 << 12,
IJSVGExporterOptionRemoveWidthHeightAttributes = 1 << 13,
IJSVGExporterOptionColorAllowRRGGBBAA = 1 << 14,
IJSVGExporterOptionRemoveComments = 1 << 15,
IJSVGExporterOptionCenterWithinViewBox = 1 << 16,
IJSVGExporterOptionRemoveXMLDeclaration = 1 << 17,
IJSVGExporterOptionConvertArcs = 1 << 18,
IJSVGExporterOptionConvertShapesToPaths = 1 << 19,
IJSVGExporterOptionRoundTransforms = 1 << 20,
IJSVGExporterOptionRemoveDefaultValues = 1 << 21,
IJSVGExporterOptionConvertStrokesToPaths = 1 << 22,
IJSVGExporterOptionAll = IJSVGExporterOptionRemoveUselessDef | IJSVGExporterOptionRemoveUselessGroups |
IJSVGExporterOptionCreateUseForPaths | IJSVGExporterOptionMoveAttributesToGroup |
IJSVGExporterOptionSortAttributes | IJSVGExporterOptionCollapseGroups |
IJSVGExporterOptionCleanupPaths | IJSVGExporterOptionRemoveHiddenElements | IJSVGExporterOptionCompressOutput |
IJSVGExporterOptionCollapseGradients | IJSVGExporterOptionRemoveWidthHeightAttributes |
IJSVGExporterOptionColorAllowRRGGBBAA | IJSVGExporterOptionRemoveComments |
IJSVGExporterOptionCenterWithinViewBox | IJSVGExporterOptionRemoveXMLDeclaration |
IJSVGExporterOptionConvertArcs | IJSVGExporterOptionConvertShapesToPaths |
IJSVGExporterOptionRoundTransforms | IJSVGExporterOptionRemoveDefaultValues |
IJSVGExporterOptionConvertStrokesToPaths
};
BOOL IJSVGExporterHasOption(IJSVGExporterOptions options, NSInteger option);
void IJSVGEnumerateCGPathElements(CGPathRef path, IJSVGPathElementEnumerationBlock enumBlock);
const NSArray<NSString*>* IJSVGShortCharacterArray(void);
const NSDictionary<NSString*, NSString*>* IJSVGDefaultAttributes(void);
@protocol IJSVGExporterDelegate <NSObject>
@optional
- (NSString* _Nullable)svgExporter:(IJSVGExporter*)exporter
identifierForElement:(NSXMLElement* _Nullable)element
type:(IJSVGNodeType)type
defaultID:(NSString* (^)(void))defaultID;
- (NSString* _Nullable)svgExporter:(IJSVGExporter*)exporter
stringForColor:(NSColor*)color
flags:(IJSVGColorUsageTraits)flag
options:(IJSVGColorStringOptions)options;
@end
@interface IJSVGExporter : NSObject {
@private
IJSVG* _svg;
CGSize _size;
IJSVGExporterOptions _options;
NSXMLDocument* _dom;
NSXMLElement* _defElement;
NSInteger _idCount;
NSInteger _shortIdCount;
BOOL _appliedXLink;
IJSVGThreadManager* _threadManager;
struct {
unsigned int identifierForElement: 1;
unsigned int stringForColor: 1;
} _respondsTo;
}
@property (nonatomic, assign) id<IJSVGExporterDelegate> delegate;
@property (nonatomic, assign) IJSVGFloatingPointOptions floatingPointOptions;
@property (nonatomic, copy, nullable) NSString* title;
@property (nonatomic, copy, nullable) NSString* desc;
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options;
- (id)initWithSVG:(IJSVG*)svg
size:(CGSize)size
options:(IJSVGExporterOptions)options
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (NSString*)SVGString;
- (NSData*)SVGData;
- (IJSVG*)SVG:(NSError**)error;
@end
NS_ASSUME_NONNULL_END
File diff suppressed because it is too large Load Diff
@@ -1,63 +0,0 @@
//
// IJSVGExporterPathInstruction.h
// IconJar
//
// Created by Curtis Hard on 08/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface IJSVGExporterPathInstructionCommand : NSObject {
}
@property (nonatomic, assign) char instruction;
@property (nonatomic, strong) NSArray<NSString*>* params;
@end
@interface IJSVGExporterPathInstruction : NSObject {
@private
NSInteger _dataCount;
CGFloat* _data;
CGFloat* _base;
CGFloat* _coords;
}
@property (nonatomic, assign) char instruction;
void IJSVGExporterPathInstructionRoundData(CGFloat* data, NSInteger length, IJSVGFloatingPointOptions options);
CGFloat IJSVGExporterPathFloatToFixed(CGFloat number, int precision);
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
- (id)initWithInstruction:(char)instruction
dataCount:(NSInteger)floatCount;
- (CGFloat*)data;
- (NSInteger)dataLength;
+ (NSArray<IJSVGExporterPathInstruction*>*)convertInstructionsCurves:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (void)convertInstructionsToMixedAbsoluteRelative:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (void)convertInstructionsDataToRounded:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
+ (NSString*)pathStringWithInstructionSet:(NSArray<NSValue*>*)instructionSets
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions;
@end
NS_ASSUME_NONNULL_END
static NSInteger const kIJSVGExporterPathInstructionFloatPrecision = 3;
static CGFloat const kIJSVGExporterPathInstructionErrorThreshold = 1e-2;
#define IJ_SVG_EXPORT_ROUND(value) IJSVGExporterPathFloatToFixed(value, kIJSVGExporterPathInstructionFloatPrecision)
@@ -1,652 +0,0 @@
//
// IJSVGExporterPathInstruction.m
// IconJar
//
// Created by Curtis Hard on 08/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGExporter.h>
#import <IJSVG/IJSVGExporterPathInstruction.h>
#import <IJSVG/IJSVGUtils.h>
#import <math.h>
@implementation IJSVGExporterPathInstructionCommand
@end
@implementation IJSVGExporterPathInstruction
@synthesize instruction = _instruction;
- (void)dealloc
{
if(_data != NULL) {
(void)free(_data), _data = NULL;
}
if(_base != NULL) {
(void)free(_base), _base = NULL;
}
if(_coords != NULL) {
(void)free(_coords), _coords = NULL;
}
}
- (id)initWithInstruction:(char)instruction
dataCount:(NSInteger)floatCount
{
if((self = [super init]) != nil) {
_instruction = instruction;
// only allocate if not zero
if(floatCount != 0) {
_dataCount = floatCount;
_data = (CGFloat*)calloc(sizeof(CGFloat), floatCount);
}
// setup base and coords
_base = (CGFloat*)malloc(sizeof(CGFloat) * 2);
_coords = (CGFloat*)malloc(sizeof(CGFloat) * 2);
}
return self;
}
- (NSInteger)dataLength
{
return _dataCount;
}
- (CGFloat*)data
{
return _data;
}
- (CGFloat*)base
{
return _base;
}
- (CGFloat*)coords
{
return _coords;
}
+ (NSString*)pathStringWithInstructionSet:(NSArray<IJSVGExporterPathInstructionCommand*>*)instructionSets
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
IJSVGExporterPathInstructionCommand* lastCommand = NULL;
NSMutableString* string = [[NSMutableString alloc] init];
for (IJSVGExporterPathInstructionCommand* command in instructionSets) {
// read back the bytes
// add on the instruction character only if there is no current command
// or the last command is not the same as the current command
// if they both are the same, we still need to seperate them via a space
if(lastCommand == nil || (lastCommand != nil && lastCommand.instruction != command.instruction)) {
[string appendFormat:@"%c", command.instruction];
} else {
[string appendString:@" "];
}
// compresses the floats
NSString* compressedFloats = IJSVGCompressFloatParameterArray(command.params);
[string appendString:compressedFloats];
// store last command
lastCommand = command;
}
return string;
}
+ (NSString*)pathStringFromInstructions:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
NSMutableArray* pathInstructions = [[NSMutableArray alloc] init];
for (IJSVGExporterPathInstruction* instruction in instructions) {
CGFloat* data = instruction.data;
const char lowerInstruction = tolower(instruction.instruction);
NSArray<NSString*>* set = nil;
switch (lowerInstruction) {
case 't':
case 'm':
case 'l': {
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions)
];
break;
}
case 'v':
case 'h': {
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions)
];
break;
}
case 'c': {
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[4], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[5], floatingPointOptions)
];
break;
}
case 's':
case 'q': {
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions)
];
break;
}
case 'a': {
set = @[
IJSVGShortFloatStringWithOptions(data[0], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[1], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[2], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[3], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[4], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[5], floatingPointOptions),
IJSVGShortFloatStringWithOptions(data[6], floatingPointOptions),
];
break;
}
// close path
case 'z': {
set = @[];
}
}
// wrap into the command and give to the array
IJSVGExporterPathInstructionCommand* wrapperCommand = nil;
wrapperCommand = [[IJSVGExporterPathInstructionCommand alloc] init];
wrapperCommand.instruction = instruction.instruction;
wrapperCommand.params = set ?: @[];
[pathInstructions addObject:wrapperCommand];
}
return [self pathStringWithInstructionSet:pathInstructions
floatingPointOptions:floatingPointOptions];
}
CGFloat IJSVGExporterPathFloatToFixed(CGFloat number, int precision)
{
return floorf(pow(10, precision) * number) / pow(10, precision);
}
void IJSVGExporterPathInstructionRoundData(CGFloat* data, NSInteger length,
IJSVGFloatingPointOptions options)
{
for (NSInteger i = length; i-- > 0;) {
CGFloat d = data[i];
CGFloat proposed = IJSVGExporterPathFloatToFixed(d, options.precision);
if(proposed != d) {
CGFloat rounded = +IJSVGExporterPathFloatToFixed(d, options.precision - 1);
data[i] = IJSVGExporterPathFloatToFixed(+fabs(rounded - d), options.precision + 1)
>= kIJSVGExporterPathInstructionErrorThreshold
? +IJSVGExporterPathFloatToFixed(d, options.precision)
: rounded;
}
}
}
+ (void)convertInstructionsToRoundRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
CGFloat relSubPoint[2] = { 0.f, 0.f };
for (IJSVGExporterPathInstruction* instruction in instructions) {
char instructionChar = instruction.instruction;
NSInteger length = instruction.dataLength;
CGFloat* data = instruction.data;
if(strchr("mltqsc", instructionChar) != NULL) {
for (NSInteger i = length; i--;) {
data[i] += instruction.base[i % 2] - relSubPoint[i % 2];
}
} else if(instructionChar == 'h') {
data[0] += instruction.base[0] - relSubPoint[0];
} else if(instructionChar == 'v') {
data[0] += instruction.base[1] - relSubPoint[1];
} else if(instructionChar == 'a') {
data[5] += instruction.base[0] - relSubPoint[0];
data[5] += instruction.base[1] - relSubPoint[1];
}
IJSVGExporterPathInstructionRoundData(data, length, floatingPointOptions);
if(instructionChar == 'h') {
relSubPoint[0] += data[0];
} else if(instructionChar == 'v') {
relSubPoint[1] += data[0];
} else {
relSubPoint[0] += data[length - 2];
relSubPoint[1] += data[length - 1];
}
IJSVGExporterPathInstructionRoundData(relSubPoint, 2, floatingPointOptions);
}
}
+ (void)convertInstructionsToMixedAbsoluteRelative:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
IJSVGExporterPathInstruction* prevInstruction = nil;
for (IJSVGExporterPathInstruction* instruction in instructions) {
if(prevInstruction == nil || instruction.dataLength == 0) {
prevInstruction = instruction;
continue;
}
char instructionChar = instruction.instruction;
CGFloat* data = instruction.data;
NSInteger length = instruction.dataLength;
CGFloat* adata = (CGFloat*)malloc(sizeof(CGFloat) * length);
memcpy(adata, data, sizeof(CGFloat) * length);
if(strchr("mltqsc", instructionChar) != NULL) {
for (NSInteger i = length; i--;) {
adata[i] += instruction.base[i % 2];
}
} else if(instructionChar == 'h') {
adata[0] += instruction.base[0];
} else if(instructionChar == 'v') {
adata[0] += instruction.base[1];
} else if(instructionChar == 'a') {
adata[5] += instruction.base[0];
adata[6] += instruction.base[1];
}
IJSVGExporterPathInstructionRoundData(adata, length, floatingPointOptions);
IJSVGExporterPathInstruction* ainstruction = nil;
ainstruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:instructionChar
dataCount:length];
memcpy(ainstruction.data, adata, sizeof(CGFloat) * length);
// run these through our default string runner
// to compare the outputs
NSString* orig = [self pathStringFromInstructions:@[ instruction ]
floatingPointOptions:floatingPointOptions];
NSString* comp = [self pathStringFromInstructions:@[ ainstruction ]
floatingPointOptions:floatingPointOptions];
if(comp.length < orig.length && !(instructionChar == prevInstruction.instruction &&
prevInstruction.instruction > 96 &&
comp.length == orig.length - 1 &&
data[0] < 0.f &&
fmod(prevInstruction.data[prevInstruction.dataLength - 1], 1) != 0.f)) {
instruction.instruction = toupper(instructionChar);
memcpy(data, adata, sizeof(CGFloat) * length);
}
(void)free(adata), adata = NULL;
prevInstruction = instruction;
}
}
+ (void)convertInstructionsDataToRounded:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
for (IJSVGExporterPathInstruction* instruction in instructions) {
CGFloat* data = instruction.data;
IJSVGExporterPathInstructionRoundData(data, instruction.dataLength,
floatingPointOptions);
}
}
+ (NSArray<IJSVGExporterPathInstruction*>*)convertInstructionsCurves:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
NSMutableArray<IJSVGExporterPathInstruction*>* nInstructions = nil;
nInstructions = [[NSMutableArray alloc] initWithCapacity:instructions.count];
IJSVGExporterPathInstruction* lastInstruction = nil;
for (IJSVGExporterPathInstruction* instruction in instructions) {
lastInstruction = nInstructions.lastObject;
if(lastInstruction == nil) {
[nInstructions addObject:instruction];
continue;
}
if(instruction.instruction == 'c') {
if(lastInstruction.instruction == 'c' &&
instruction.data[0] == -(lastInstruction.data[2] - lastInstruction.data[4]) &&
instruction.data[1] == -(lastInstruction.data[3] - lastInstruction.data[5])) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'s'
dataCount:4];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
nInstruction.data[2] = instruction.data[4];
nInstruction.data[3] = instruction.data[5];
[nInstructions addObject:nInstruction];
continue;
} else if(lastInstruction.instruction == 's' &&
instruction.data[0] == -(lastInstruction.data[0] - lastInstruction.data[2]) &&
instruction.data[1] == -(lastInstruction.data[1] - lastInstruction.data[3])) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'s'
dataCount:4];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
nInstruction.data[2] = instruction.data[4];
nInstruction.data[3] = instruction.data[5];
[nInstructions addObject:nInstruction];
continue;
} else if(lastInstruction.instruction != 'c' &&
lastInstruction.instruction != 's' &&
instruction.data[0] == 0.f && instruction.data[1] == 0.f) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'s'
dataCount:4];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
nInstruction.data[2] = instruction.data[4];
nInstruction.data[3] = instruction.data[5];
[nInstructions addObject:nInstruction];
continue;
}
} else if(instruction.instruction == 'q') {
if(lastInstruction.instruction == 'q' &&
instruction.data[0] == (lastInstruction.data[2] - lastInstruction.data[0]) &&
instruction.data[1] == (lastInstruction.data[3] - lastInstruction.data[1])) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'t'
dataCount:2];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
[nInstructions addObject:nInstruction];
continue;
} else if(lastInstruction.instruction == 't' &&
instruction.data[2] == lastInstruction.data[0] &&
instruction.data[3] == lastInstruction.data[1]) {
IJSVGExporterPathInstruction* nInstruction = nil;
nInstruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'t'
dataCount:2];
nInstruction.data[0] = instruction.data[2];
nInstruction.data[1] = instruction.data[3];
[nInstructions addObject:nInstruction];
continue;
}
}
[nInstructions addObject:instruction];
}
return nInstructions;
}
+ (void)convertInstructionsToRelativeCoordinates:(NSArray<IJSVGExporterPathInstruction*>*)instructions
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
CGFloat start[2] = {0, 0};
CGFloat cursor[2] = {0, 0};
CGFloat prevCoords[2] = {0, 0};
NSInteger index = 0;
for (IJSVGExporterPathInstruction* anInstruction in instructions) {
char instruction = anInstruction.instruction;
CGFloat* data = anInstruction.data;
CGFloat* base = anInstruction.base;
CGFloat* coords = anInstruction.coords;
switch(instruction) {
case 'm': {
cursor[0] += data[0];
cursor[1] += data[1];
start[0] = cursor[0];
start[1] = cursor[1];
break;
}
case 'M': {
if(index != 0) {
instruction = 'm';
}
data[0] -= cursor[0];
data[1] -= cursor[1];
cursor[0] += data[0];
cursor[1] += data[1];
start[0] = cursor[0];
start[1] = cursor[1];
break;
}
case 'l': {
cursor[0] += data[0];
cursor[1] += data[1];
break;
}
case 'L': {
instruction = 'l';
data[0] -= cursor[0];
data[1] -= cursor[1];
cursor[0] += data[0];
cursor[1] += data[1];
break;
}
case 'h': {
cursor[0] += data[0];
break;
}
case 'H': {
instruction = 'h';
data[0] -= cursor[0];
cursor[0] += data[0];
break;
}
case 'v': {
cursor[1] += data[0];
break;
}
case 'V': {
instruction = 'v';
data[0] -= cursor[1];
cursor[1] += data[0];
break;
}
case 'c': {
cursor[0] += data[4];
cursor[1] += data[5];
break;
}
case 'C': {
instruction = 'c';
data[0] -= cursor[0];
data[1] -= cursor[1];
data[2] -= cursor[0];
data[3] -= cursor[1];
data[4] -= cursor[0];
data[5] -= cursor[1];
cursor[0] += data[4];
cursor[1] += data[5];
break;
}
case 's': {
cursor[0] += data[2];
cursor[1] += data[3];
break;
}
case 'S': {
instruction = 's';
data[0] -= cursor[0];
data[1] -= cursor[1];
data[2] -= cursor[0];
data[3] -= cursor[1];
cursor[0] += data[2];
cursor[1] += data[3];
break;
}
case 'q': {
cursor[0] += data[2];
cursor[1] += data[3];
break;
}
case 'Q': {
instruction = 'q';
data[0] -= cursor[0];
data[1] -= cursor[1];
data[2] -= cursor[0];
data[3] -= cursor[1];
cursor[0] += data[2];
cursor[1] += data[3];
break;
}
case 't': {
cursor[0] += data[0];
cursor[1] += data[1];
break;
}
case 'T': {
instruction = 't';
data[0] -= cursor[0];
data[1] -= cursor[1];
cursor[0] += data[0];
cursor[1] += data[1];
break;
}
case 'a': {
cursor[0] += data[5];
cursor[1] += data[6];
break;
}
case 'A': {
instruction = 'a';
data[5] -= cursor[0];
data[6] -= cursor[1];
cursor[0] += data[5];
cursor[1] += data[6];
break;
}
case 'Z':
case 'z': {
cursor[0] = start[0];
cursor[1] = start[1];
break;
}
}
// set the instruction back
anInstruction.instruction = instruction;
base[0] = prevCoords[0];
base[1] = prevCoords[1];
coords[0] = cursor[0];
coords[1] = cursor[1];
prevCoords[0] = cursor[0];
prevCoords[1] = cursor[1];
index++;
}
}
+ (NSArray<IJSVGExporterPathInstruction*>*)instructionsFromPath:(CGPathRef)path
floatingPointOptions:(IJSVGFloatingPointOptions)floatingPointOptions
{
// keep track of the current point
__block CGPoint currentPoint = CGPointZero;
NSMutableArray* instructions = [[NSMutableArray alloc] init];
// create the path callback
IJSVGCGPathHandler callback = ^(const CGPathElement* pathElement) {
IJSVGExporterPathInstruction* instruction = nil;
// work out what to do
switch (pathElement->type) {
case kCGPathElementMoveToPoint: {
// move to command
instruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'M'
dataCount:2];
CGPoint point = pathElement->points[0];
instruction.data[0] = point.x;
instruction.data[1] = point.y;
currentPoint = point;
[instructions addObject:instruction];
break;
}
case kCGPathElementAddLineToPoint: {
// line to command
CGPoint point = pathElement->points[0];
if(point.x == currentPoint.x) {
instruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'V'
dataCount:1];
instruction.data[0] = point.y;
} else if(point.y == currentPoint.y) {
instruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'H'
dataCount:1];
instruction.data[0] = point.x;
} else {
instruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'L'
dataCount:2];
instruction.data[0] = point.x;
instruction.data[1] = point.y;
}
currentPoint = point;
[instructions addObject:instruction];
break;
}
case kCGPathElementAddQuadCurveToPoint: {
// quad curve to command
CGPoint controlPoint = pathElement->points[0];
CGPoint point = pathElement->points[1];
instruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'Q'
dataCount:4];
instruction.data[0] = controlPoint.x;
instruction.data[1] = controlPoint.y;
instruction.data[2] = point.x;
instruction.data[3] = point.y;
currentPoint = point;
[instructions addObject:instruction];
break;
}
case kCGPathElementAddCurveToPoint: {
// curve to command
CGPoint controlPoint1 = pathElement->points[0];
CGPoint controlPoint2 = pathElement->points[1];
CGPoint point = pathElement->points[2];
currentPoint = point;
instruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'C'
dataCount:6];
instruction.data[0] = controlPoint1.x;
instruction.data[1] = controlPoint1.y;
instruction.data[2] = controlPoint2.x;
instruction.data[3] = controlPoint2.y;
instruction.data[4] = point.x;
instruction.data[5] = point.y;
[instructions addObject:instruction];
break;
}
case kCGPathElementCloseSubpath: {
// close command
instruction = [[IJSVGExporterPathInstruction alloc] initWithInstruction:'Z'
dataCount:0];
[instructions addObject:instruction];
break;
}
}
};
// apply the
CGPathApply(path, (__bridge void*)callback, IJSVGExporterPathCaller);
// remove last instruction if it was Z -> M
IJSVGExporterPathInstruction* lastInstruction = instructions.lastObject;
if(lastInstruction.instruction == 'M' || lastInstruction.instruction == 'm') {
if(instructions.count >= 2) {
NSInteger index = [instructions indexOfObject:lastInstruction] - 1;
IJSVGExporterPathInstruction* prevInstruction = instructions[index];
if(prevInstruction.instruction == 'z' || prevInstruction.instruction == 'Z') {
[instructions removeLastObject];
}
}
}
return instructions;
}
@end
@@ -1,14 +0,0 @@
//
// IJSVGBasicLayer.h
// IJSVG
//
// Created by Curtis Hard on 19/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
#import <IJSVG/IJSVGLayer.h>
@interface IJSVGBasicLayer : CALayer <IJSVGBasicLayer>
@end
@@ -1,30 +0,0 @@
//
// IJSVGBasicLayer.m
// IJSVG
//
// Created by Curtis Hard on 19/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGBasicLayer.h>
@implementation IJSVGBasicLayer
@synthesize backingScaleFactor;
@synthesize requiresBackingScale;
@synthesize renderQuality;
@synthesize debugLayers;
- (id<CAAction>)actionForKey:(NSString*)event
{
return nil;
}
- (void)performRenderInContext:(CGContextRef)ctx
{
}
@end
@@ -1,23 +0,0 @@
//
// IJSVGFilterLayer.h
// IJSVG
//
// Created by Curtis Hard on 19/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGFilter.h>
#import <IJSVG/IJSVGBasicLayer.h>
@interface IJSVGFilterLayer : IJSVGLayer {
@private
IJSVGBasicLayer* _hostingLayer;
CGImageRef _image;
}
@property (nonatomic, strong) CALayer<IJSVGDrawableLayer>* sublayer;
@end
@@ -1,69 +0,0 @@
//
// IJSVGFilterLayer.m
// IJSVG
//
// Created by Curtis Hard on 19/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGFilterLayer.h>
#import <IJSVG/IJSVGTransform.h>
@implementation IJSVGFilterLayer
- (void)dealloc
{
(void)CGImageRelease(_image), _image = nil;
}
- (instancetype)init
{
if((self = [super init]) != nil) {
_hostingLayer = [IJSVGBasicLayer layer];
[self addSublayer:_hostingLayer];
}
return self;
}
- (BOOL)requiresBackingScale
{
return YES;
}
- (void)setBackingScaleFactor:(CGFloat)backingScaleFactor
{
// we are responsible for recursively calling the sublayer
// with the new backing scale factor
[IJSVGLayer setBackingScaleFactor:backingScaleFactor
renderQuality:self.renderQuality
recursivelyToLayer:_sublayer];
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:self.backingScaleFactor];
}
- (void)layoutSublayers
{
CGRect frame = _sublayer.innerBoundingBox;
_hostingLayer.frame = frame;
_hostingLayer.contents = (__bridge id)_image;
}
- (NSArray<CALayer<IJSVGBasicLayer>*>*)debugLayers
{
return @[_sublayer];
}
@end
@@ -1,107 +0,0 @@
//
// IJSVGGradientLayer.m
// IJSVGExample
//
// Created by Curtis Hard on 29/12/2016.
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGradientLayer.h>
#import <IJSVG/IJSVGRootLayer.h>
@implementation IJSVGGradientLayer
- (BOOL)requiresBackingScale
{
return YES;
}
- (void)setGradient:(IJSVGGradient*)newGradient
{
_gradient = newGradient;
// lets check its alpha properties on the colors
BOOL hasAlphaChannel = NO;
for (NSColor* color in newGradient.colors) {
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 kIJSVGRenderQualityOptimized: {
backingScaleFactor = (backingScaleFactor * .35f);
break;
}
case kIJSVGRenderQualityLow: {
backingScaleFactor = (backingScaleFactor * .05f);
break;
}
default: {
break;
}
}
[super setBackingScaleFactor:backingScaleFactor];
}
- (CALayer<IJSVGDrawableLayer> *)referencingLayer
{
return [super referencingLayer] ?: self.superlayer;
}
- (void)drawInContext:(CGContextRef)ctx
{
[super drawInContext:ctx];
// nothing to do :(
if(self.gradient == nil) {
return;
}
// perform the draw
CGRect bounds = CGRectZero;
CGAffineTransform transform = CGAffineTransformIdentity;
CALayer<IJSVGDrawableLayer>* layer = (CALayer<IJSVGDrawableLayer>*)self.referencingLayer;
if(self.gradient.units == IJSVGUnitUserSpaceOnUse) {
IJSVGRootLayer* rootNode = (IJSVGRootLayer*)[IJSVGLayer rootLayerForLayer:self];
bounds = [rootNode.viewBox computeValue:CGSizeZero];
transform = [IJSVGLayer userSpaceTransformForLayer:layer];
} else {
bounds = IJSVGLayerGetBoundingBoxBounds(layer);
}
// its possible that this layer is shifted inwards due to a stroke on the
// parent layer
transform = CGAffineTransformConcat(transform, [IJSVGLayer userSpaceTransformForLayer:self]);
[self.gradient drawInContextRef:ctx
bounds:bounds
transform:transform];
}
- (IJSVGTraitedColorStorage*)colors
{
IJSVGTraitedColorStorage* list = [[IJSVGTraitedColorStorage alloc] init];
for(NSColor* color in self.gradient.colors) {
IJSVGTraitedColor* traited = nil;
traited = [IJSVGTraitedColor colorWithColor:color
traits:IJSVGColorUsageTraitNone];
[list addColor:traited];
}
return list;
}
@end
@@ -1,23 +0,0 @@
//
// IJSVGGroupLayer.h
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGShapeLayer.h>
#import <IJSVG/IJSVGUnitRect.h>
#import <QuartzCore/QuartzCore.h>
@interface IJSVGGroupLayer : IJSVGLayer <IJSVGMaskingLayer> {
}
@property (nonatomic, strong) IJSVGUnitSize* intrinsicSize;
@property (nonatomic, strong) IJSVGUnitRect* viewBox;
@property (nonatomic, assign) IJSVGViewBoxAlignment viewBoxAlignment;
@property (nonatomic, assign) IJSVGViewBoxMeetOrSlice viewBoxMeetOrSlice;
@end
@@ -1,21 +0,0 @@
//
// IJSVGGroupLayer.m
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGroupLayer.h>
#import <IJSVG/IJSVGViewBox.h>
#import <IJSVG/IJSVGUnitRect.h>
#import <IJSVG/IJSVGLayer.h>
@implementation IJSVGGroupLayer
- (CGRect)innerBoundingBox
{
return self.outerBoundingBox;
}
@end
@@ -1,24 +0,0 @@
//
// IJSVGImageLayer.h
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGImage.h>
#import <IJSVG/IJSVGTransformLayer.h>
#import <IJSVG/IJSVGTileLayer.h>
#import <IJSVG/IJSVGBasicLayer.h>
#import <AppKit/AppKit.h>
#import <QuartzCore/QuartzCore.h>
@interface IJSVGImageLayer : IJSVGTileLayer {
}
@property (nonatomic, strong) IJSVGImage* image;
- (id)initWithImage:(IJSVGImage*)image;
@end
@@ -1,48 +0,0 @@
//
// IJSVGImageLayer.m
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGImageLayer.h>
@implementation IJSVGImageLayer
- (id)initWithImage:(IJSVGImage*)image
{
if((self = [super init]) != nil) {
self.image = image;
}
return self;
}
- (BOOL)requiresBackingScale
{
return YES;
}
- (void)setImage:(IJSVGImage *)image
{
_image = image;
[self setNeedsDisplay];
}
- (void)drawInContext:(CGContextRef)ctx
{
CGImageRef image = _image.CGImage;
CGRect imageDrawRect = _image.bounds;
CGRect currentBounds = self.bounds;
IJSVGViewBoxDrawingBlock drawBlock = ^(CGFloat scale[]) {
// image will be upside down, so just translate it back on itself
CGContextConcatCTM(ctx, CGAffineTransformMakeScale(1.f, -1.f));
CGContextTranslateCTM(ctx, 0.f, -CGRectGetHeight(imageDrawRect));
CGContextDrawImage(ctx, imageDrawRect, image);
};
IJSVGContextDrawViewBox(ctx, imageDrawRect, currentBounds,
_image.viewBoxAlignment,
_image.viewBoxMeetOrSlice, drawBlock);
}
@end
@@ -1,191 +0,0 @@
//
// IJSVGLayer.h
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGRendering.h>
#import <IJSVG/IJSVGTransaction.h>
#import <QuartzCore/QuartzCore.h>
@class IJSVGShapeLayer;
@class IJSVGGradientLayer;
@class IJSVGPatternLayer;
@class IJSVGStrokeLayer;
@class IJSVGGroupLayer;
@class IJSVGLayer;
@class IJSVGFilter;
@class IJSVGTraitedColorStorage;
typedef NS_OPTIONS(NSUInteger, IJSVGLayerTraits) {
IJSVGLayerTraitNone = 0,
IJSVGLayerTraitFilled = 1 << 1,
IJSVGLayerTraitStroked = 1 << 2
};
typedef NS_OPTIONS(NSUInteger, IJSVGLayerDrawingOptions) {
IJSVGLayerDrawingOptionNone = 0,
IJSVGLayerDrawingOptionIgnoreClipping = 1 << 1
};
typedef NS_ENUM(NSUInteger, IJSVGLayerFillType) {
IJSVGLayerFillTypeColor,
IJSVGLayerFillTypePattern,
IJSVGLayerFillTypeGradient,
IJSVGLayerFillTypeUnknown
};
typedef NS_ENUM(NSUInteger, IJSVGLayerUsageType) {
IJSVGLayerUsageTypeFillGeneric,
IJSVGLayerUsageTypeFillPattern,
IJSVGLayerUsageTypeFillGradient,
IJSVGLayerUsageTypeStrokeGeneric,
IJSVGLayerUsageTypeStrokePattern,
IJSVGLayerUsageTypeStrokeGradient,
IJSVGLayerUsageTypeStroke
};
@protocol IJSVGPathableLayer <NSObject>
@required
@property (nonatomic, assign) CGPathRef path;
@end
@protocol IJSVGBasicLayer <NSObject>
@required
@property (nonatomic, assign) CGFloat backingScaleFactor;
@property (nonatomic, readonly) BOOL requiresBackingScale;
@property (nonatomic, assign) IJSVGRenderQuality renderQuality;
@property (nonatomic, readonly) NSArray<CALayer<IJSVGBasicLayer>*>* debugLayers;
@end
@protocol IJSVGMaskingLayer <NSObject>
@required
@property (nonatomic, assign) CGRect maskingBoundingBox;
@property (nonatomic, assign) CGRect maskingClippingRect;
@end
@protocol IJSVGClippingLayer <NSObject>
@required
@property (nonatomic, assign) CGRect clippingBoundingBox;
@property (nonatomic, assign) CGAffineTransform clippingTransform;
@property (nonatomic, assign) CGPathRef clipPath;
@property (nonatomic, assign) CGAffineTransform clipPathTransform;
@end
@protocol IJSVGDrawableLayer <NSObject, IJSVGBasicLayer, IJSVGMaskingLayer, IJSVGClippingLayer>
@required
@property (nonatomic, readonly) BOOL treatImplicitOriginAsTransform;
@property (nonatomic, assign) CGBlendMode blendingMode;
@property (nonatomic, strong) NSArray<CALayer<IJSVGDrawableLayer>*>* clipLayers;
@property (nonatomic, strong) CALayer<IJSVGDrawableLayer>* maskLayer;
@property (nonatomic, copy) CAShapeLayerFillRule clipRule;
@property (nonatomic, copy) CAShapeLayerFillRule fillRule;
@property (nonatomic, readonly) CGPoint absoluteOrigin;
@property (nonatomic, readonly) CGRect absoluteFrame;
@property (nonatomic, assign) CGRect boundingBox;
@property (nonatomic, assign) CGRect outerBoundingBox;
@property (nonatomic, readonly) CGRect innerBoundingBox;
@property (nonatomic, weak) CALayer<IJSVGDrawableLayer>* referencingLayer;
@property (nonatomic, strong) IJSVGFilter* filter;
@property (nonatomic, readonly) IJSVGLayerTraits layerTraits;
@property (nonatomic, readonly) NSMapTable<NSNumber*, CALayer<IJSVGDrawableLayer>*>* layerUsageMapTable;
@property (nonatomic, readonly) IJSVGTraitedColorStorage* colors;
- (void)performRenderInContext:(CGContextRef)ctx;
- (void)setLayer:(CALayer<IJSVGDrawableLayer>*)layer
forUsageType:(IJSVGLayerUsageType)type;
- (CALayer<IJSVGDrawableLayer>*)layerForUsageType:(IJSVGLayerUsageType)type;
- (CALayer<IJSVGDrawableLayer>*)strokeLayer:(IJSVGLayerUsageType*)usageType;
- (void)addTraits:(IJSVGLayerTraits)traits;
- (void)removeTraits:(IJSVGLayerTraits)traits;
- (BOOL)matchesTraits:(IJSVGLayerTraits)traits;
- (CALayer<IJSVGDrawableLayer>*)firstSublayerOfClass:(Class)aClass;
@end
CGRect IJSVGLayerGetBoundingBoxBounds(CALayer<IJSVGDrawableLayer>* drawableLayer);
NSMapTable<NSNumber*, CALayer<IJSVGDrawableLayer>*>* IJSVGLayerDefaultUsageMapTable(void);
@interface IJSVGLayer : CALayer <IJSVGDrawableLayer, IJSVGMaskingLayer> {
@private
IJSVGLayer* _maskingLayer;
NSMapTable<NSNumber*, CALayer<IJSVGDrawableLayer>*>* _layerUsageMapTable;
}
+ (IJSVGLayerFillType)fillTypeForFill:(id)fill;
+ (NSArray*)deepestSublayersOfLayer:(CALayer*)layer;
+ (void)recursivelyWalkLayer:(CALayer<IJSVGBasicLayer>*)layer
withBlock:(void (^)(CALayer<IJSVGBasicLayer>* layer, BOOL* stop))block;
+ (CALayer<IJSVGDrawableLayer>*)firstSublayerOfClass:(Class)aClass
fromLayer:(CALayer<IJSVGDrawableLayer>*)layer;
+ (void)setBackingScaleFactor:(CGFloat)scale
renderQuality:(IJSVGRenderQuality)quality
recursivelyToLayer:(CALayer<IJSVGDrawableLayer>*)layer;
- (void)applySublayerMaskToContext:(CGContextRef)context
forSublayer:(IJSVGLayer*)sublayer
withOffset:(CGPoint)offset;
+ (CGImageRef)newMaskImageForLayer:(CALayer<IJSVGDrawableLayer>*)layer
options:(IJSVGLayerDrawingOptions)options
scale:(CGFloat)scale;
+ (CGImageRef)newImageForLayer:(CALayer<IJSVGDrawableLayer>*)layer
options:(IJSVGLayerDrawingOptions)options
colorSpace:(CGColorSpaceRef)colorSpace
bitmapInfo:(uint32_t)bitmapInfo
scale:(CGFloat)scale;
+ (CGImageRef)newImageWithSize:(CGSize)size
drawBlock:(void (^)(CGContextRef context))drawBlock
colorSpace:(CGColorSpaceRef)colorSpace
bitmapInfo:(uint32_t)bitmapInfo
scale:(CGFloat)scale;
+ (void)renderLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
options:(IJSVGLayerDrawingOptions)options;
+ (void)applyBlendingMode:(CGBlendMode)blendMode
toContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock;
+ (void)clipContextWithClipLayers:(NSArray<CALayer<IJSVGDrawableLayer>*>*)clipLayers
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock;
+ (void)clipContextWithMask:(CALayer<IJSVGMaskingLayer>*)maskLayer
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)context
drawingBlock:(dispatch_block_t)drawingBlock;
+ (CALayer<IJSVGDrawableLayer>*)rootLayerForLayer:(CALayer<IJSVGDrawableLayer>*)layer;
+ (CGAffineTransform)absoluteTransformForLayer:(CALayer*)layer;
+ (CGRect)absoluteFrameForLayer:(CALayer<IJSVGDrawableLayer>*)layer;
+ (CGRect)calculateFrameForSublayers:(NSArray<CALayer<IJSVGDrawableLayer>*>*)layers;
+ (CGAffineTransform)userSpaceTransformForLayer:(CALayer<IJSVGDrawableLayer>*)layer;
+ (void)transformLayer:(CALayer<IJSVGDrawableLayer>*)layer
intoUserSpaceUnitsFrom:(CALayer<IJSVGDrawableLayer>*)fromLayer;
+ (void)logLayer:(CALayer<IJSVGDrawableLayer>*)layer;
@end
@@ -1,780 +0,0 @@
//
// IJSVGLayer.m
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
#import <IJSVG/IJSVGGroupLayer.h>
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGShapeLayer.h>
#import <IJSVG/IJSVGTransformLayer.h>
#import <IJSVG/IJSVGRootLayer.h>
CGRect IJSVGLayerGetBoundingBoxBounds(CALayer<IJSVGDrawableLayer>* drawableLayer)
{
return (CGRect) {
.origin = CGPointZero,
.size = drawableLayer.boundingBox.size
};
}
NSMapTable<NSNumber*, CALayer<IJSVGDrawableLayer>*>* IJSVGLayerDefaultUsageMapTable(void)
{
return [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
valueOptions:NSPointerFunctionsWeakMemory
capacity:3];
}
@implementation IJSVGLayer
@synthesize backingScaleFactor = _backingScaleFactor;
@synthesize requiresBackingScale;
@synthesize renderQuality;
@synthesize blendingMode;
@synthesize absoluteOrigin;
@synthesize clipPath = _clipPath;
@synthesize clipRule = _clipRule;
@synthesize clipLayers = _clipLayers;
@synthesize clippingTransform;
@synthesize clippingBoundingBox;
@synthesize maskingClippingRect;
@synthesize clipPathTransform;
@synthesize colors;
@synthesize boundingBox = _boundingBox;
@synthesize layerTraits = _layerTraits;
@synthesize maskingBoundingBox;
@synthesize filter;
@synthesize referencingLayer = _referencingLayer;
@synthesize outerBoundingBox = _outerBoundingBox;
@synthesize maskLayer = _maskLayer;
@synthesize treatImplicitOriginAsTransform;
@synthesize fillRule;
- (void)dealloc
{
if(_clipPath != NULL) {
CGPathRelease(_clipPath);
}
}
- (instancetype)init
{
if((self = [super init]) != nil) {
_boundingBox = CGRectNull;
_outerBoundingBox = CGRectNull;
}
return self;
}
- (id<CAAction>)actionForKey:(NSString*)event
{
return nil;
}
- (CAShapeLayerFillRule)fillRule
{
return kCAFillRuleNonZero;
}
- (CAShapeLayerFillRule)clipRule
{
if(_clipRule == nil) {
return self.fillRule;
}
return _clipRule;
}
+ (IJSVGLayerFillType)fillTypeForFill:(id)fill
{
if([fill isKindOfClass:IJSVGColorNode.class]) {
return IJSVGLayerFillTypeColor;
}
if([fill isKindOfClass:IJSVGGradient.class]) {
return IJSVGLayerFillTypeGradient;
}
if([fill isKindOfClass:IJSVGPattern.class]) {
return IJSVGLayerFillTypePattern;
}
return IJSVGLayerFillTypeUnknown;
}
+ (CALayer<IJSVGDrawableLayer>*)firstSublayerOfClass:(Class)aClass
fromLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
for(CALayer<IJSVGDrawableLayer>* sublayer in layer.sublayers) {
if(sublayer.class == aClass) {
return sublayer;
}
}
return nil;
}
+ (void)applyAbsoluteTransformForLayer:(CALayer<IJSVGDrawableLayer>*)layer
toContext:(CGContextRef)ctx
{
CALayer<IJSVGDrawableLayer>* 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<IJSVGDrawableLayer>*)layer
{
CGAffineTransform identity = CGAffineTransformIdentity;
CALayer<IJSVGDrawableLayer>* parentLayer = layer;
while((parentLayer = parentLayer.referencingLayer) != nil) {
identity = [self absoluteTransformForLayer:parentLayer];
// 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;
}
}
return CGAffineTransformConcat(identity, layer.affineTransform);
}
+ (void)transformLayer:(CALayer<IJSVGDrawableLayer>*)layer
intoUserSpaceUnitsFrom:(CALayer<IJSVGDrawableLayer>*)fromLayer
{
CGAffineTransform transform = layer.affineTransform;
CGAffineTransform userSpaceTransform = [IJSVGLayer userSpaceTransformForLayer:fromLayer];
layer.affineTransform = CGAffineTransformConcat(transform, userSpaceTransform);
}
+ (CGAffineTransform)userSpaceTransformForLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
CGRect absolutePosition = layer.outerBoundingBox;
return CGAffineTransformTranslate(CGAffineTransformIdentity,
-CGRectGetMinX(absolutePosition),
-CGRectGetMinY(absolutePosition));
}
+ (CALayer<IJSVGDrawableLayer>*)rootLayerForLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
CALayer<IJSVGDrawableLayer>* parentLayer = (CALayer<IJSVGDrawableLayer>*)layer.referencingLayer;
while([parentLayer isKindOfClass:IJSVGRootLayer.class] == NO &&
parentLayer.referencingLayer != nil) {
parentLayer = (CALayer<IJSVGDrawableLayer>*)parentLayer.referencingLayer;
}
return parentLayer;
}
+ (void)clipContext:(CGContextRef)ctx
path:(CGPathRef)path
rule:(CAShapeLayerFillRule)rule
drawingBlock:(dispatch_block_t)block
{
CGContextSaveGState(ctx);
CGContextAddPath(ctx, path);
if([rule isEqualToString:kCAFillRuleEvenOdd] == YES) {
CGContextEOClip(ctx);
} else {
CGContextClip(ctx);
}
block();
CGContextRestoreGState(ctx);
}
+ (void)performBasicRenderOfLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
options:(IJSVGLayerDrawingOptions)options
{
dispatch_block_t drawingBlock = ^{
if(layer.clipPath != NULL) {
[self clipContext:ctx
path:layer.clipPath
rule:layer.clipRule
drawingBlock:^{
[layer performRenderInContext:ctx];
}];
return;
}
[layer performRenderInContext:ctx];
};
[self applyBlendingMode:layer.blendingMode
toContext:ctx
drawingBlock:^{
// we need to clip first
IJSVGLayerDrawingOptions opt = IJSVGLayerDrawingOptionIgnoreClipping;
BOOL ignoreClipping = (options & opt) == opt;
if(ignoreClipping == YES || layer.clipLayers == nil) {
drawingBlock();
return;
}
[self clipContextWithClipLayers:layer.clipLayers
toLayer:layer
inContext:ctx
drawingBlock:drawingBlock];
}];
}
+ (void)renderLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
options:(IJSVGLayerDrawingOptions)options
{
[self performBasicRenderOfLayer:layer
inContext:ctx
options:options];
}
+ (void)applyBlendingMode:(CGBlendMode)blendMode
toContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock
{
if(blendMode == kCGBlendModeNormal) {
drawingBlock();
return;
}
CGContextSaveGState(ctx);
CGContextSetBlendMode(ctx, blendMode);
drawingBlock();
CGContextRestoreGState(ctx);
}
///// Shape layers are the only thing we can clip, as they contain a path, however
///// the layer passed to us from a clip could have groups contained with in it that
///// have transforms on them, so simply recursively iterate over them and concat
///// their transforms down to the path and clip at the end with the path.
//+ (void)recursivelyClip:(CALayer<IJSVGDrawableLayer>*)layer
// transform:(CGAffineTransform)transform
// inContext:(CGContextRef)ctx
//{
// if([layer isKindOfClass:IJSVGShapeLayer.class]) {
// [self clipContextWithShapeLayer:(IJSVGShapeLayer*)layer
// transform:transform
// inContext:ctx];
// return;
// }
//
// for(CALayer<IJSVGDrawableLayer>* drawableLayer in layer.sublayers) {
// CGAffineTransform drawTransform = CGAffineTransformConcat(transform,
// drawableLayer.affineTransform);
//
// // If its not a shape layer, just go down the tree until we find one
// if([drawableLayer isKindOfClass:IJSVGShapeLayer.class] == NO) {
// [self recursivelyClip:drawableLayer
// transform:drawTransform
// inContext:ctx];
// continue;
// }
// IJSVGShapeLayer* shapeLayer = (IJSVGShapeLayer*)drawableLayer;
// [self clipContextWithShapeLayer:shapeLayer
// transform:transform
// inContext:ctx];
// }
//}
//
//+ (void)clipContextWithShapeLayer:(IJSVGShapeLayer*)shapeLayer
// transform:(CGAffineTransform)transform
// inContext:(CGContextRef)ctx
//{
// CGAffineTransform drawTransform = CGAffineTransformConcat(transform,
// shapeLayer.affineTransform);
// // Shape layers paths are reset back to 0,0 origin, so in order to be
// // correct path, we need to shift it back to where it belongs.
// drawTransform = CGAffineTransformTranslate(transform,
// shapeLayer.frame.origin.x,
// shapeLayer.frame.origin.y);
// CGPathRef path = CGPathCreateCopyByTransformingPath(shapeLayer.path,
// &drawTransform);
// CGContextAddPath(ctx, path);
// CGPathRelease(path);
//}
//
//+ (void)clipContextWithClip:(CALayer<IJSVGDrawableLayer>*)clipLayer
// toLayer:(CALayer<IJSVGDrawableLayer>*)layer
// inContext:(CGContextRef)ctx
// drawingBlock:(dispatch_block_t)drawingBlock
//{
// CGContextSaveGState(ctx);
// [self recursivelyClip:clipLayer
// transform:clipLayer.affineTransform
// inContext:ctx];
// if([layer.clipRule isEqualToString:kCAFillRuleEvenOdd]) {
// CGContextEOClip(ctx);
// } else {
// CGContextClip(ctx);
// }
// drawingBlock();
// CGContextRestoreGState(ctx);
//}
+ (void)clipContextWithClipLayers:(NSArray<CALayer<IJSVGDrawableLayer>*>*)clipLayers
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock
{
CGContextSaveGState(ctx);
const CGFloat maskTolerance = .5f;
const CGFloat scale = layer.backingScaleFactor;
CGRect rect = layer.clippingBoundingBox;
// weed out the no empty clipPaths
if(CGRectEqualToRect(rect, CGRectZero) == YES) {
drawingBlock();
return;
}
CGSize size = CGSizeMake(CGRectGetWidth(rect),
CGRectGetHeight(rect));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
void (^drawBlock)(CGContextRef maskCtx) = ^(CGContextRef maskCtx) {
CGImageRef maskImage = NULL;
for(CALayer<IJSVGDrawableLayer>* clipLayer in clipLayers) {
CGContextSaveGState(maskCtx);
CGRect layerRect = clipLayer.outerBoundingBox;
layerRect = CGRectApplyAffineTransform(layerRect, layer.clippingTransform);
CGImageRef layerMask = [self newMaskImageForLayer:clipLayer
options:IJSVGLayerDrawingOptionIgnoreClipping
scale:scale];
if(maskImage != NULL) {
CGRect maskRect = CGRectInset(rect, -maskTolerance, -maskTolerance);
CGContextClipToMask(maskCtx, maskRect, maskImage);
}
CGContextDrawImage(maskCtx, layerRect, layerMask);
CGImageRelease(layerMask);
if(maskImage != NULL) {
CGImageRelease(maskImage);
}
maskImage = CGBitmapContextCreateImage(maskCtx);
CGContextRestoreGState(maskCtx);
}
CGImageRelease(maskImage);
};
CGImageRef image = [self newImageWithSize:size
drawBlock:drawBlock
colorSpace:colorSpace
bitmapInfo:kCGImageAlphaNone
scale:scale];
// we need to transform the mask rect back based on the inner bounding
// box of the layer, as this could be a group layer that inner box is
// not at 0,0.
CGRect layerRect = layer.innerBoundingBox;
CGAffineTransform transform = CGAffineTransformMakeTranslation(CGRectGetMinX(layerRect),
CGRectGetMinY(layerRect));
rect = CGRectApplyAffineTransform(rect, transform);
CGContextClipToMask(ctx, rect, image);
drawingBlock();
CGImageRelease(image);
CGColorSpaceRelease(colorSpace);
CGContextRestoreGState(ctx);
}
+ (void)clipContextWithMask:(CALayer<IJSVGDrawableLayer>*)maskLayer
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
inContext:(CGContextRef)ctx
drawingBlock:(dispatch_block_t)drawingBlock
{
CGContextSaveGState(ctx);
CGFloat scale = layer.backingScaleFactor;
CGImageRef maskImage = [self newMaskImageForLayer:maskLayer
options:IJSVGLayerDrawingOptionNone
scale:scale];
CGContextClipToRect(ctx, maskLayer.maskingClippingRect);
CGContextClipToMask(ctx, maskLayer.maskingBoundingBox, maskImage);
drawingBlock();
CGImageRelease(maskImage);
CGContextRestoreGState(ctx);
}
+ (CGImageRef)newMaskImageForLayer:(CALayer<IJSVGDrawableLayer>*)layer
options:(IJSVGLayerDrawingOptions)options
scale:(CGFloat)scale
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGImageRef alphaMask = [self newImageForLayer:layer
options:options
colorSpace:colorSpace
bitmapInfo:kCGImageAlphaNone
scale:scale];
// low - high pairs
const CGFloat colors[6] = {
0.f, 11.f,
0.f, 11.f,
0.f, 11.f
};
CGImageRef masked = CGImageCreateWithMaskingColors(alphaMask, colors);
CGImageRelease(alphaMask);
CGColorSpaceRelease(colorSpace);
return masked;
}
+ (CGImageRef)newImageWithSize:(CGSize)size
drawBlock:(void (^)(CGContextRef context))drawBlock
colorSpace:(CGColorSpaceRef)colorSpace
bitmapInfo:(uint32_t)bitmapInfo
scale:(CGFloat)scale
{
CGContextRef offscreenContext = CGBitmapContextCreate(NULL,
ceilf(size.width*scale),
ceilf(size.height*scale),
8, 0, colorSpace, bitmapInfo);
CGContextScaleCTM(offscreenContext, scale, scale);
drawBlock(offscreenContext);
CGImageRef image = CGBitmapContextCreateImage(offscreenContext);
CGContextRelease(offscreenContext);
return image;
}
+ (CGImageRef)newImageForLayer:(CALayer<IJSVGDrawableLayer>*)layer
options:(IJSVGLayerDrawingOptions)options
colorSpace:(CGColorSpaceRef)colorSpace
bitmapInfo:(uint32_t)bitmapInfo
scale:(CGFloat)scale
{
CALayer<IJSVGDrawableLayer>* referenceLayer = layer.referencingLayer ?: layer;
CGRect frame = layer.outerBoundingBox;
CGRect bounds = CGRectApplyAffineTransform(layer.innerBoundingBox,
[self userSpaceTransformForLayer:referenceLayer]);
CGContextRef offscreenContext = CGBitmapContextCreate(NULL,
ceilf(frame.size.width*scale),
ceilf(frame.size.height*scale),
8, 0, colorSpace, bitmapInfo);
CGContextScaleCTM(offscreenContext, scale, scale);
CGContextConcatCTM(offscreenContext, CGAffineTransformMakeTranslation(-bounds.origin.x,
-bounds.origin.y));
[IJSVGLayer renderLayer:layer
inContext:offscreenContext
options:options];
CGImageRef image = CGBitmapContextCreateImage(offscreenContext);
CGContextRelease(offscreenContext);
return image;
}
+ (CGRect)absoluteFrameForLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
return (CGRect) {
.origin = [self absoluteOriginForLayer:layer],
.size = layer.frame.size
};
}
+ (CGPoint)absoluteOriginForLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
CGPoint point = CGPointZero;
CALayer<IJSVGDrawableLayer>* pLayer = layer;
while (pLayer != nil) {
point.x += pLayer.frame.origin.x;
point.y += pLayer.frame.origin.y;
pLayer = pLayer.referencingLayer;
}
return point;
}
+ (NSArray*)deepestSublayersOfLayer:(CALayer*)layer
{
NSMutableArray* arr = [[NSMutableArray alloc] init];
for (CALayer* subLayer in layer.sublayers) {
if(subLayer.sublayers.count != 0) {
NSArray* moreLayers = [self deepestSublayersOfLayer:(IJSVGLayer*)subLayer];
[arr addObjectsFromArray:moreLayers];
} else {
[arr addObject:subLayer];
}
}
return arr;
}
+ (void)recursivelyWalkLayer:(CALayer<IJSVGBasicLayer>*)layer
withBlock:(void (^)(CALayer<IJSVGBasicLayer>* layer, BOOL* stop))block
{
// call for layer and mask if there is one
BOOL stop = NO;
block(layer, &stop);
if(stop == YES) {
return;
}
// sublayers!!
for (CALayer<IJSVGBasicLayer>* aLayer in layer.sublayers) {
[self recursivelyWalkLayer:aLayer
withBlock:block];
}
}
+ (void)setBackingScaleFactor:(CGFloat)scale
renderQuality:(IJSVGRenderQuality)quality
recursivelyToLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
[self recursivelyWalkLayer:layer
withBlock:^(CALayer<IJSVGDrawableLayer>*sublayer, BOOL *stop) {
if(sublayer.requiresBackingScale == YES) {
sublayer.backingScaleFactor = scale;
}
sublayer.renderQuality = quality;
}];
}
+ (void)logLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
[self logLayer:layer
depth:0];
}
+ (void)logLayer:(CALayer<IJSVGDrawableLayer>*)layer
depth:(NSUInteger)depth
{
NSLog(@"%@ %@ frame: %@ transform: %@",[@"" stringByPaddingToLength:depth
withString:@"- - "
startingAtIndex:0], layer,
NSStringFromRect(layer.frame),
[IJSVGTransform affineTransformToSVGTransformComponentString:layer.affineTransform]);
for(CALayer<IJSVGDrawableLayer>* sublayer in layer.debugLayers) {
[self logLayer:sublayer
depth:depth+1];
}
}
+ (CGRect)calculateFrameForSublayers:(NSArray<CALayer<IJSVGDrawableLayer>*>*)layers
{
CGRect rect = CGRectNull;
for(CALayer<IJSVGDrawableLayer>* layer in layers) {
CGRect layerFrame = layer.outerBoundingBox;
// if we are a transform layer, we can just apply its transform
// to its sublayers and keep going down the tree
if([layer isKindOfClass:IJSVGTransformLayer.class] == YES) {
CGRect frame = [self calculateFrameForSublayers:layer.sublayers];
frame = CGRectApplyAffineTransform(frame, layer.affineTransform);
layerFrame = frame;
}
if(CGRectIsNull(rect)) {
rect = layerFrame;
continue;
}
rect = CGRectUnion(rect, layerFrame);
}
return rect;
}
- (void)setBackingScaleFactor:(CGFloat)newFactor
{
if(_backingScaleFactor == newFactor) {
return;
}
_backingScaleFactor = newFactor;
self.contentsScale = newFactor;
self.rasterizationScale = newFactor;
// make sure its applied to any mask or clipPath
_maskLayer.backingScaleFactor = newFactor;
for(CALayer<IJSVGDrawableLayer>* clipLayer in _clipLayers) {
clipLayer.backingScaleFactor = newFactor;
}
}
- (void)performRenderInContext:(CGContextRef)ctx
{
if(_maskLayer != nil) {
[self.class clipContextWithMask:_maskLayer
toLayer:self
inContext:ctx
drawingBlock:^{
[super renderInContext:ctx];
}];
return;
}
[super renderInContext:ctx];
}
- (void)applySublayerMaskToContext:(CGContextRef)context
forSublayer:(IJSVGLayer*)sublayer
withOffset:(CGPoint)offset
{
// apply any transforms needed
CGPoint layerOffset = offset;
CGAffineTransform sublayerTransform = CATransform3DGetAffineTransform(sublayer.transform);
CGContextConcatCTM(context, CGAffineTransformInvert(sublayerTransform));
// walk up the superlayer chain
CALayer* superlayer = self.superlayer;
if(IJSVGIsSVGLayer(superlayer) == YES) {
[(IJSVGLayer*)superlayer applySublayerMaskToContext:context
forSublayer:(IJSVGLayer*)self
withOffset:layerOffset];
}
// grab the masking layer
IJSVGShapeLayer* maskingLayer = [self maskingLayer];
// if its a group we need to get the lowest level children
// and walk up the chain again
if([maskingLayer isKindOfClass:[IJSVGGroupLayer class]]) {
NSArray* subs = [IJSVGLayer deepestSublayersOfLayer:maskingLayer];
for (IJSVGLayer* subLayer in subs) {
[subLayer applySublayerMaskToContext:context
forSublayer:(IJSVGLayer*)self
withOffset:layerOffset];
}
} else if([maskingLayer isKindOfClass:[IJSVGShapeLayer class]]) {
// is a shape, go for it!
CGPathRef maskPath = maskingLayer.path;
CGContextTranslateCTM(context, -layerOffset.x, -layerOffset.y);
CGContextAddPath(context, maskPath);
CGContextClip(context);
CGContextTranslateCTM(context, layerOffset.x, layerOffset.y);
}
CGContextConcatCTM(context, sublayerTransform);
}
- (CGAffineTransform)absoluteTransform
{
return [IJSVGLayer absoluteTransformForLayer:self];
}
- (BOOL)requiresBackingScale
{
return _maskLayer != nil || (_clipLayers != nil && _clipLayers.count != 0);
}
- (IJSVGShapeLayer*)maskingLayer
{
return (IJSVGShapeLayer*)_maskingLayer ?: nil;
}
- (void)renderInContext:(CGContextRef)ctx
{
[IJSVGLayer renderLayer:self
inContext:ctx
options:IJSVGLayerDrawingOptionNone];
}
- (CGRect)absoluteFrame
{
return [self.class absoluteFrameForLayer:self];
}
- (CGRect)boundingBox
{
return CGRectIsNull(_boundingBox) == NO ? _boundingBox : self.frame;
}
- (CGRect)outerBoundingBox
{
return CGRectIsNull(_outerBoundingBox) == NO ? _outerBoundingBox : self.frame;
}
- (CGRect)boundingBoxBounds
{
return (CGRect) {
.origin = CGPointZero,
.size = self.boundingBox.size
};
}
- (CGRect)innerBoundingBox
{
return (CGRect) {
.origin = CGPointZero,
.size = self.outerBoundingBox.size
};
}
- (CALayer<IJSVGDrawableLayer> *)referencingLayer
{
return _referencingLayer ?: self.superlayer;
}
-(NSArray<CALayer<IJSVGDrawableLayer>*>*)debugLayers
{
return self.sublayers;
}
- (void)setClipPath:(CGPathRef)clipPath
{
if(_clipPath != NULL) {
CGPathRelease(_clipPath);
}
_clipPath = CGPathRetain(clipPath);
}
- (NSMapTable<NSNumber*,CALayer<IJSVGDrawableLayer>*>*)layerUsageMapTable
{
if(_layerUsageMapTable == nil) {
_layerUsageMapTable = IJSVGLayerDefaultUsageMapTable();
}
return _layerUsageMapTable;
}
- (void)setLayer:(CALayer<IJSVGDrawableLayer>*)layer
forUsageType:(IJSVGLayerUsageType)type
{
[self.layerUsageMapTable setObject:layer
forKey:@(type)];
}
- (CALayer<IJSVGDrawableLayer>*)layerForUsageType:(IJSVGLayerUsageType)type
{
return [self.layerUsageMapTable objectForKey:@(type)];
}
- (CALayer<IJSVGDrawableLayer>*)strokeLayer:(IJSVGLayerUsageType*)usageType
{
CALayer<IJSVGDrawableLayer>* layer = nil;
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokeGeneric]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokeGeneric;
return layer;
}
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokeGradient]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokeGradient;
return layer;
}
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokePattern]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokePattern;
return layer;
}
return nil;
}
- (void)addTraits:(IJSVGLayerTraits)traits
{
_layerTraits |= traits;
}
- (void)removeTraits:(IJSVGLayerTraits)traits
{
_layerTraits = _layerTraits & ~traits;
}
- (BOOL)matchesTraits:(IJSVGLayerTraits)traits
{
return (_layerTraits & traits) == traits;
}
- (BOOL)treatImplicitOriginAsTransform
{
return YES;
}
- (IJSVGTraitedColorStorage*)colors
{
IJSVGTraitedColorStorage* colorList = [[IJSVGTraitedColorStorage alloc] init];
for(CALayer<IJSVGDrawableLayer>* layer in self.sublayers) {
[colorList unionColorStorage:layer.colors];
}
return colorList;
}
- (CALayer<IJSVGDrawableLayer>*)firstSublayerOfClass:(Class)aClass
{
return [self.class firstSublayerOfClass:aClass
fromLayer:self];
}
@end
@@ -1,22 +0,0 @@
//
// IJSVGPatternLayer.h
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGPattern.h>
#import <QuartzCore/QuartzCore.h>
@interface IJSVGPatternLayer : IJSVGLayer
@property (nonatomic, strong) CALayer<IJSVGDrawableLayer>* pattern;
@property (nonatomic, strong) IJSVGPattern* patternNode;
- (void)computeCellSize:(CGSize*)cellSize
viewBox:(CGRect*)viewBox
origin:(CGPoint*)origin;
@end
@@ -1,163 +0,0 @@
//
// IJSVGPatternLayer.m
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGPatternLayer.h>
#import <IJSVG/IJSVGTransform.h>
#import <IJSVG/IJSVGUnitRect.h>
#import <IJSVG/IJSVGViewBox.h>
@interface IJSVGPatternLayer ()
@property (nonatomic, assign, readonly) CGSize cellSize;
@property (nonatomic, assign) CGRect viewBox;
@end
@implementation IJSVGPatternLayer
- (BOOL)requiresBackingScale
{
return YES;
}
- (void)setBackingScaleFactor:(CGFloat)backingScaleFactor
{
[super setBackingScaleFactor:backingScaleFactor];
[IJSVGLayer setBackingScaleFactor:backingScaleFactor
renderQuality:self.renderQuality
recursivelyToLayer:self.pattern];
}
void IJSVGPatternDrawingCallBack(void* info, CGContextRef ctx)
{
// reassign the layer
IJSVGPatternLayer* layer = (__bridge IJSVGPatternLayer*)info;
CGSize size = layer.cellSize;
CGContextSaveGState(ctx);
CGRect rect = CGRectMake(0.f, 0.f, size.width, size.height);
CGContextClipToRect(ctx, rect);
IJSVGViewBoxAlignment alignment = layer.patternNode.viewBoxAlignment;
IJSVGViewBoxMeetOrSlice meetOrSlice = layer.patternNode.viewBoxMeetOrSlice;
CGRect viewBox = layer.viewBox;
IJSVGViewBoxDrawingBlock drawBlock = ^(CGFloat scale[]) {
[IJSVGLayer renderLayer:layer.pattern
inContext:ctx
options:IJSVGLayerDrawingOptionNone];
};
IJSVGContextDrawViewBox(ctx, viewBox, rect, alignment, meetOrSlice,
drawBlock);
CGContextSaveGState(ctx);
};
- (CALayer<IJSVGDrawableLayer>*)referencingLayer
{
return [super referencingLayer] ?: self.superlayer;
}
- (void)computeCellSize:(CGSize*)cellSize
viewBox:(CGRect*)viewBox
origin:(CGPoint*)origin
{
CALayer<IJSVGDrawableLayer>* layer = (CALayer<IJSVGDrawableLayer>*)self.referencingLayer;
CGRect rect = IJSVGLayerGetBoundingBoxBounds(layer);
// get the bounds, we need these as when we render we might need to swap
// the coordinates over to objectBoundingBox
IJSVGUnitLength* xLength = _patternNode.x;
IJSVGUnitLength* yLength = _patternNode.y;
IJSVGUnitLength* wLength = _patternNode.width;
IJSVGUnitLength* hLength = _patternNode.height;
// actually do the swap if required
if(_patternNode.units == IJSVGUnitObjectBoundingBox) {
wLength = wLength.lengthByMatchingPercentage;
hLength = hLength.lengthByMatchingPercentage;
xLength = xLength.lengthByMatchingPercentage;
yLength = yLength.lengthByMatchingPercentage;
}
*origin = CGPointMake([xLength computeValue:rect.size.width],
[yLength computeValue:rect.size.height]);
CGFloat width = [wLength computeValue:rect.size.width];
CGFloat height = [hLength computeValue:rect.size.height];
*cellSize = CGSizeMake(width, height);
// who knew that patterns have viewBoxes? Not me, but here is an implementation
// of it anyway
if(_patternNode.viewBox != nil && _patternNode.viewBox.isZeroRect == NO) {
IJSVGUnitRect* nViewBox = _patternNode.viewBox;
if(_patternNode.contentUnits == IJSVGUnitObjectBoundingBox) {
nViewBox = [nViewBox copyByConvertingToUnitsLengthType:IJSVGUnitLengthTypePercentage];
}
*viewBox = [nViewBox computeValue:rect.size];
} else {
// no viewbox is assigned, so just map it 1:1 with its cellSize
*viewBox = CGRectMake(0.f, 0.f, cellSize->width, cellSize->height);
}
}
- (void)drawInContext:(CGContextRef)ctx
{
// holder for callback
static const CGPatternCallbacks callbacks = { 0, &IJSVGPatternDrawingCallBack, NULL };
// create base pattern space
CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(NULL);
CGContextSetFillColorSpace(ctx, patternSpace);
CGColorSpaceRelease(patternSpace);
CALayer<IJSVGDrawableLayer>* layer = (CALayer<IJSVGDrawableLayer>*)self.referencingLayer;
// transform us back into the correct space
CGAffineTransform transform = CGAffineTransformIdentity;
if(_patternNode.units == IJSVGUnitUserSpaceOnUse) {
transform = [IJSVGLayer userSpaceTransformForLayer:layer];
}
CGPoint origin = CGPointZero;
[self computeCellSize:&_cellSize
viewBox:&_viewBox
origin:&origin];
// transform the X and Y shift
transform = CGAffineTransformConcat(transform, IJSVGConcatTransforms(self.patternNode.transforms));
transform = CGAffineTransformTranslate(transform, origin.x, origin.y);
// its possible that this layer is shifted inwards due to a stroke on the
// parent layer
transform = CGAffineTransformConcat(transform, [IJSVGLayer userSpaceTransformForLayer:self]);
// create the pattern
CGRect selfBounds = IJSVGLayerGetBoundingBoxBounds(self);
CGPatternRef ref = CGPatternCreate((void*)self, selfBounds,
transform, _cellSize.width, _cellSize.height,
kCGPatternTilingConstantSpacing,
true, &callbacks);
// set the pattern then release it
CGFloat alpha = 1.f;
CGContextSetFillPattern(ctx, ref, &alpha);
CGPatternRelease(ref);
// fill it
CGContextFillRect(ctx, selfBounds);
}
- (NSArray<CALayer<IJSVGDrawableLayer>*>*)debugLayers
{
return @[self.pattern];
}
- (IJSVGTraitedColorStorage*)colors
{
return _pattern.colors;
}
@end
@@ -1,24 +0,0 @@
//
// IJSVGRootLayer.h
// IJSVG
//
// Created by Curtis Hard on 15/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
@interface IJSVGRootLayer : IJSVGGroupLayer {
@private
BOOL _disableBackingScalePropagation;
}
- (void)renderInContext:(CGContextRef)ctx
viewPort:(CGRect)viewPort
backingScale:(CGFloat)backingScale
quality:(IJSVGRenderQuality)quality
ignoreIntrinsicSize:(BOOL)ignoreIntrinsicSize;
@end
@@ -1,93 +0,0 @@
//
// IJSVGRootLayer.m
// IJSVG
//
// Created by Curtis Hard on 15/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGRootLayer.h>
@implementation IJSVGRootLayer
- (void)performRenderInContext:(CGContextRef)ctx
{
if(self.viewBox != nil) {
CGRect viewBox = [self.viewBox computeValue:CGSizeZero];
__weak IJSVGRootLayer* weakSelf = self;
IJSVGViewBoxDrawingBlock drawingBlock = ^(CGFloat scale[]) {
CGContextSaveGState(ctx);
// we have to make sure we set the backing scale factor once
// we know how scale this will be drawn at
CGFloat nScale = MIN(scale[0], scale[1]);
nScale += weakSelf.backingScaleFactor;
weakSelf.backingScaleFactor = nScale;
// perform the actual render now we have computed backing scale
[super performRenderInContext:ctx];
CGContextRestoreGState(ctx);
};
IJSVGContextDrawViewBox(ctx, viewBox, IJSVGLayerGetBoundingBoxBounds(self),
self.viewBoxAlignment,
self.viewBoxMeetOrSlice, drawingBlock);
return;
}
[super performRenderInContext:ctx];
}
- (void)setBackingScaleFactor:(CGFloat)backingScaleFactor
{
// get nearest .5f
backingScaleFactor = MAX(round(backingScaleFactor * 2.f) / 2.f, .5f);
[super setBackingScaleFactor:backingScaleFactor];
if(_disableBackingScalePropagation == YES) {
return;
}
[self propagateBackingScalePropertiesToSublayers];
}
- (void)propagateBackingScalePropertiesToSublayers
{
for(CALayer<IJSVGDrawableLayer>* layer in self.sublayers) {
[IJSVGLayer setBackingScaleFactor:self.backingScaleFactor
renderQuality:self.renderQuality
recursivelyToLayer:layer];
}
}
- (void)renderInContext:(CGContextRef)ctx
viewPort:(CGRect)viewPort
backingScale:(CGFloat)backingScale
quality:(IJSVGRenderQuality)quality
ignoreIntrinsicSize:(BOOL)ignoreIntrinsicSize
{
CGRect frame = viewPort;
IJSVGUnitSize* size = nil;
// The SVG might have an intrinsic size against it, if so, we need to use
// that instead of the viewPort size to make sure we obey the render correctly.
if(ignoreIntrinsicSize == NO && (size = self.intrinsicSize) != nil) {
CGFloat width = 0.f;
CGFloat height = 0.f;
if((width = [size.width computeValue:frame.size.width]) != 0.f) {
frame.size.width = width;
}
if((height = [size.height computeValue:frame.size.height]) != 0.f) {
frame.size.height = height;
}
}
self.frame = frame;
_disableBackingScalePropagation = YES;
self.backingScaleFactor = backingScale;
self.renderQuality = quality;
_disableBackingScalePropagation = NO;
[self renderInContext:ctx];
}
- (BOOL)treatImplicitOriginAsTransform
{
return NO;
}
@end
@@ -1,26 +0,0 @@
//
// IJSVGShapeLayer.h
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGUtils.h>
#import <QuartzCore/QuartzCore.h>
@interface IJSVGShapeLayer : CAShapeLayer <IJSVGDrawableLayer, IJSVGPathableLayer> {
@private
IJSVGLayer* _maskingLayer;
NSMapTable<NSNumber*, CALayer<IJSVGDrawableLayer>*>* _layerUsageMapTable;
}
@property (nonatomic, assign) IJSVGPrimitivePathType primitiveType;
- (void)applySublayerMaskToContext:(CGContextRef)context
forSublayer:(IJSVGLayer*)sublayer
withOffset:(CGPoint)offset;
@end
@@ -1,312 +0,0 @@
//
// IJSVGShapeLayer.m
// IJSVGExample
//
// Created by Curtis Hard on 07/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGroupLayer.h>
#import <IJSVG/IJSVGShapeLayer.h>
#import <IJSVG/IJSVGStrokeLayer.h>
#import <IJSVG/IJSVGGradientLayer.h>
#import <IJSVG/IJSVGTraitedColorStorage.h>
#import <IJSVG/IJSVGPatternLayer.h>
@implementation IJSVGShapeLayer
@synthesize backingScaleFactor = _backingScaleFactor;
@synthesize requiresBackingScale;
@synthesize renderQuality;
@synthesize blendingMode;
@synthesize absoluteOrigin;
@synthesize clipPath = _clipPath;
@synthesize clipRule;
@synthesize clipLayers = _clipLayers;
@synthesize clippingTransform;
@synthesize clippingBoundingBox;
@synthesize maskingClippingRect;
@synthesize clipPathTransform;
@synthesize colors;
@synthesize boundingBox;
@synthesize layerTraits = _layerTraits;
@synthesize maskingBoundingBox;
@synthesize filter;
@synthesize referencingLayer = _referencingLayer;
@synthesize outerBoundingBox;
@synthesize maskLayer = _maskLayer;
@synthesize treatImplicitOriginAsTransform;
- (void)dealloc
{
if(_clipPath != NULL) {
CGPathRelease(_clipPath);
}
}
- (id<CAAction>)actionForKey:(NSString*)event
{
return nil;
}
- (CGRect)absoluteFrame
{
return [IJSVGLayer absoluteFrameForLayer:self];
}
- (CGAffineTransform)absoluteTransform
{
return [IJSVGLayer absoluteTransformForLayer:self];
}
- (CALayer<IJSVGDrawableLayer> *)referencingLayer
{
return _referencingLayer ?: self.superlayer;
}
- (CGRect)boundingBoxBounds
{
return (CGRect) {
.origin = CGPointZero,
.size = self.boundingBox.size
};
}
- (CGRect)innerBoundingBox
{
return self.bounds;
}
- (BOOL)requiresBackingScale
{
return _maskLayer != nil || (_clipLayers != nil && _clipLayers.count != 0);
}
- (void)setClipPath:(CGPathRef)clipPath
{
if(_clipPath != NULL) {
CGPathRelease(_clipPath);
}
_clipPath = CGPathRetain(clipPath);
}
- (void)setBackingScaleFactor:(CGFloat)newFactor
{
if(_backingScaleFactor == newFactor) {
return;
}
_backingScaleFactor = newFactor;
self.contentsScale = newFactor;
self.rasterizationScale = newFactor;
// make sure its applied to any mask or clipPath
_maskLayer.backingScaleFactor = newFactor;
for(CALayer<IJSVGDrawableLayer>* clipLayer in _clipLayers) {
clipLayer.backingScaleFactor = newFactor;
}
[self setNeedsDisplay];
}
- (void)performRenderInContext:(CGContextRef)ctx
{
if(_maskLayer != nil) {
[IJSVGLayer clipContextWithMask:_maskLayer
toLayer:self
inContext:ctx
drawingBlock:^{
[super renderInContext:ctx];
}];
return;
}
[super renderInContext:ctx];
}
- (void)applySublayerMaskToContext:(CGContextRef)context
forSublayer:(IJSVGLayer*)sublayer
withOffset:(CGPoint)offset
{
// apply any transforms needed
CGPoint layerOffset = offset;
CGAffineTransform sublayerTransform = CATransform3DGetAffineTransform(sublayer.transform);
CGContextConcatCTM(context, CGAffineTransformInvert(sublayerTransform));
// walk up the superlayer chain
CALayer* superlayer = self.superlayer;
if(IJSVGIsSVGLayer(superlayer) == YES) {
[(IJSVGLayer*)superlayer applySublayerMaskToContext:context
forSublayer:(IJSVGLayer*)self
withOffset:layerOffset];
}
// grab the masking layer
IJSVGShapeLayer* maskingLayer = [self maskingLayer];
// if its a group we need to get the lowest level children
// and walk up the chain again
if([maskingLayer isKindOfClass:[IJSVGGroupLayer class]]) {
NSArray* subs = [IJSVGLayer deepestSublayersOfLayer:maskingLayer];
for (IJSVGLayer* subLayer in subs) {
[subLayer applySublayerMaskToContext:context
forSublayer:(IJSVGLayer*)self
withOffset:layerOffset];
}
} else if([maskingLayer isKindOfClass:[IJSVGShapeLayer class]]) {
// is a shape, go for it!
CGPathRef maskPath = maskingLayer.path;
CGContextTranslateCTM(context, -layerOffset.x, -layerOffset.y);
CGContextAddPath(context, maskPath);
CGContextClip(context);
CGContextTranslateCTM(context, layerOffset.x, layerOffset.y);
}
CGContextConcatCTM(context, sublayerTransform);
}
- (IJSVGShapeLayer*)maskingLayer
{
return (IJSVGShapeLayer*)_maskingLayer ?: nil;
}
- (void)renderInContext:(CGContextRef)ctx
{
[IJSVGLayer renderLayer:(IJSVGLayer*)self
inContext:ctx
options:IJSVGLayerDrawingOptionNone];
}
- (NSMapTable<NSNumber*,CALayer<IJSVGDrawableLayer>*>*)layerUsageMapTable
{
if(_layerUsageMapTable == nil) {
_layerUsageMapTable = IJSVGLayerDefaultUsageMapTable();
}
return _layerUsageMapTable;
}
- (void)setLayer:(CALayer<IJSVGDrawableLayer>*)layer
forUsageType:(IJSVGLayerUsageType)type
{
[self.layerUsageMapTable setObject:layer
forKey:@(type)];
}
- (CALayer<IJSVGDrawableLayer>*)layerForUsageType:(IJSVGLayerUsageType)type
{
return [self.layerUsageMapTable objectForKey:@(type)];
}
- (CALayer<IJSVGDrawableLayer>*)strokeLayer:(IJSVGLayerUsageType*)usageType
{
CALayer<IJSVGDrawableLayer>* layer = nil;
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokeGeneric]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokeGeneric;
return layer;
}
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokeGradient]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokeGradient;
return layer;
}
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokePattern]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokePattern;
return layer;
}
return nil;
}
-(NSArray<CALayer<IJSVGDrawableLayer>*>*)debugLayers
{
return self.sublayers;
}
- (BOOL)treatImplicitOriginAsTransform
{
return YES;
}
- (void)addTraits:(IJSVGLayerTraits)traits
{
_layerTraits |= traits;
}
- (void)removeTraits:(IJSVGLayerTraits)traits
{
_layerTraits = _layerTraits & ~traits;
}
- (BOOL)matchesTraits:(IJSVGLayerTraits)traits
{
return (_layerTraits & traits) == traits;
}
- (CALayer<IJSVGDrawableLayer>*)firstSublayerOfClass:(Class)aClass
{
return [IJSVGLayer firstSublayerOfClass:aClass
fromLayer:self];
}
- (IJSVGTraitedColorStorage*)colors
{
IJSVGTraitedColorStorage* list = [[IJSVGTraitedColorStorage alloc] init];
// we have a fill color
if([self matchesTraits:IJSVGLayerTraitFilled] == YES) {
IJSVGShapeLayer* fillLayer = nil;
if((fillLayer = (IJSVGShapeLayer*)[self layerForUsageType:IJSVGLayerUsageTypeFillGeneric]) != nil) {
CGColorRef colorRef = NULL;
if((colorRef = fillLayer.fillColor) != NULL) {
NSColor* nsColor = [NSColor colorWithCGColor:colorRef];
IJSVGTraitedColor* color = [IJSVGTraitedColor colorWithColor:nsColor
traits:IJSVGColorUsageTraitFill];
[list addColor:color];
}
}
// patterns
if((fillLayer = (IJSVGShapeLayer*)[self layerForUsageType:IJSVGLayerUsageTypeFillPattern]) != nil) {
IJSVGTraitedColorStorage* storage = fillLayer.colors;
[storage addTraits:IJSVGColorUsageTraitFill];
[list unionColorStorage:storage];
}
// gradients
if((fillLayer = (IJSVGShapeLayer*)[self layerForUsageType:IJSVGLayerUsageTypeFillGradient]) != nil) {
IJSVGTraitedColorStorage* storage = fillLayer.colors;
[storage addTraits:IJSVGColorUsageTraitGradientStop];
[list unionColorStorage:storage];
}
}
// we have a stroke color
if([self matchesTraits:IJSVGLayerTraitStroked] == YES) {
IJSVGStrokeLayer* strokeLayer = nil;
if((strokeLayer = (IJSVGStrokeLayer*)[self layerForUsageType:IJSVGLayerUsageTypeStrokeGeneric]) != nil) {
CGColorRef colorRef = NULL;
if((colorRef = strokeLayer.strokeColor) != NULL) {
NSColor* nsColor = [NSColor colorWithCGColor:colorRef];
IJSVGTraitedColor* color = [IJSVGTraitedColor colorWithColor:nsColor
traits:IJSVGColorUsageTraitStroke];
[list addColor:color];
}
}
// patterns
if((strokeLayer = (IJSVGStrokeLayer*)[self layerForUsageType:IJSVGLayerUsageTypeStrokePattern]) != nil) {
IJSVGTraitedColorStorage* storage = strokeLayer.colors;
[storage addTraits:IJSVGColorUsageTraitFill];
[list unionColorStorage:storage];
}
// gradients
if((strokeLayer = (IJSVGStrokeLayer*)[self layerForUsageType:IJSVGLayerUsageTypeStrokeGradient]) != nil) {
IJSVGTraitedColorStorage* storage = strokeLayer.colors;
[storage addTraits:IJSVGColorUsageTraitGradientStop];
[list unionColorStorage:storage];
}
}
return list;
}
@end
@@ -1,17 +0,0 @@
//
// IJSVGTileLayer.h
// IJSVG
//
// Created by Curtis Hard on 29/06/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
#import <IJSVG/IJSVGLayer.h>
@interface IJSVGTileLayer : CATiledLayer <IJSVGDrawableLayer> {
@private
NSMapTable<NSNumber*, CALayer<IJSVGDrawableLayer>*>* _layerUsageMapTable;
}
@end
@@ -1,150 +0,0 @@
//
// IJSVGTileLayer.m
// IJSVG
//
// Created by Curtis Hard on 29/06/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import "IJSVGTileLayer.h"
@implementation IJSVGTileLayer
@synthesize clipLayers;
@synthesize backingScaleFactor;
@synthesize renderQuality;
@synthesize requiresBackingScale;
@synthesize maskLayer = _maskLayer;
@synthesize fillRule = _fillRule;
@synthesize clipRule = _clipRule;
@synthesize absoluteFrame;
@synthesize boundingBox;
@synthesize outerBoundingBox;
@synthesize filter = _filter;
@synthesize innerBoundingBox;
@synthesize maskingBoundingBox;
@synthesize maskingClippingRect;
@synthesize clippingBoundingBox;
@synthesize clippingTransform;
@synthesize layerTraits = _layerTraits;
@synthesize clipPath = _clipPath;
@synthesize clipPathTransform;
@synthesize referencingLayer = _referencingLayer;
@synthesize absoluteOrigin;
@synthesize blendingMode;
@synthesize colors;
- (void)dealloc
{
if(_clipPath != NULL) {
CGPathRelease(_clipPath);
}
}
- (id<CAAction>)actionForKey:(NSString*)event
{
return nil;
}
- (CALayer<IJSVGDrawableLayer> *)referencedLayer
{
return self.sublayers.firstObject;
}
- (CALayer<IJSVGDrawableLayer> *)referencingLayer
{
return _referencingLayer ?: self.superlayer;
}
- (void)renderInContext:(CGContextRef)ctx
{
[IJSVGLayer renderLayer:self
inContext:ctx
options:IJSVGLayerDrawingOptionNone];
}
- (void)performRenderInContext:(CGContextRef)ctx
{
[super renderInContext:ctx];
}
- (NSArray<CALayer<IJSVGDrawableLayer>*>*)debugLayers
{
return self.sublayers;
}
- (void)setClipPath:(CGPathRef)clipPath
{
if(_clipPath != NULL) {
CGPathRelease(_clipPath);
}
_clipPath = CGPathRetain(clipPath);
}
- (NSMapTable<NSNumber*,CALayer<IJSVGDrawableLayer>*>*)layerUsageMapTable
{
if(_layerUsageMapTable == nil) {
_layerUsageMapTable = IJSVGLayerDefaultUsageMapTable();
}
return _layerUsageMapTable;
}
- (CALayer<IJSVGDrawableLayer>*)layerForUsageType:(IJSVGLayerUsageType)type
{
return [self.layerUsageMapTable objectForKey:@(type)];
}
- (CALayer<IJSVGDrawableLayer>*)strokeLayer:(IJSVGLayerUsageType*)usageType
{
CALayer<IJSVGDrawableLayer>* layer = nil;
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokeGeneric]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokeGeneric;
return layer;
}
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokeGradient]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokeGradient;
return layer;
}
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokePattern]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokePattern;
return layer;
}
return nil;
}
- (void)setLayer:(CALayer<IJSVGDrawableLayer>*)layer
forUsageType:(IJSVGLayerUsageType)type
{
[self.layerUsageMapTable setObject:layer
forKey:@(type)];
}
- (void)addTraits:(IJSVGLayerTraits)traits
{
_layerTraits |= traits;
}
- (void)removeTraits:(IJSVGLayerTraits)traits
{
_layerTraits = _layerTraits & ~traits;
}
- (BOOL)matchesTraits:(IJSVGLayerTraits)traits
{
return (_layerTraits & traits) == traits;
}
- (BOOL)treatImplicitOriginAsTransform
{
return YES;
}
- (CALayer<IJSVGDrawableLayer>*)firstSublayerOfClass:(Class)aClass
{
return [IJSVGLayer firstSublayerOfClass:aClass
fromLayer:self];
}
@end
@@ -1,17 +0,0 @@
//
// IJSVGTransformLayer.h
// IJSVG
//
// Created by Curtis Hard on 31/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
#import <IJSVG/IJSVGLayer.h>
@interface IJSVGTransformLayer : CATransformLayer <IJSVGDrawableLayer> {
@private
NSMapTable<NSNumber*, CALayer<IJSVGDrawableLayer>*>* _layerUsageMapTable;
}
@end
@@ -1,160 +0,0 @@
//
// IJSVGTransformLayer.m
// IJSVG
//
// Created by Curtis Hard on 31/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGTransformLayer.h>
#import <IJSVG/IJSVGTraitedColorStorage.h>
@implementation IJSVGTransformLayer
@synthesize clipLayers;
@synthesize backingScaleFactor;
@synthesize renderQuality;
@synthesize requiresBackingScale;
@synthesize maskLayer = _maskLayer;
@synthesize fillRule = _fillRule;
@synthesize clipRule = _clipRule;
@synthesize absoluteFrame;
@synthesize boundingBox;
@synthesize outerBoundingBox;
@synthesize filter = _filter;
@synthesize innerBoundingBox;
@synthesize maskingBoundingBox;
@synthesize maskingClippingRect;
@synthesize clippingBoundingBox;
@synthesize clippingTransform;
@synthesize layerTraits = _layerTraits;
@synthesize clipPath = _clipPath;
@synthesize clipPathTransform;
@synthesize colors;
@synthesize absoluteOrigin;
@synthesize blendingMode;
@synthesize referencingLayer = _referencingLayer;
- (void)dealloc
{
if(_clipPath != NULL) {
CGPathRelease(_clipPath);
}
}
- (id<CAAction>)actionForKey:(NSString*)event
{
return nil;
}
- (CALayer<IJSVGDrawableLayer> *)referencedLayer
{
return self.sublayers.firstObject;
}
- (CALayer<IJSVGDrawableLayer> *)referencingLayer
{
return _referencingLayer ?: self.superlayer;
}
- (void)renderInContext:(CGContextRef)ctx
{
[IJSVGLayer renderLayer:self
inContext:ctx
options:IJSVGLayerDrawingOptionNone];
}
- (void)performRenderInContext:(CGContextRef)ctx
{
[super renderInContext:ctx];
}
- (NSArray<CALayer<IJSVGDrawableLayer>*>*)debugLayers
{
return self.sublayers;
}
- (void)setClipPath:(CGPathRef)clipPath
{
if(_clipPath != NULL) {
CGPathRelease(_clipPath);
}
_clipPath = CGPathRetain(clipPath);
}
- (NSMapTable<NSNumber*,CALayer<IJSVGDrawableLayer>*>*)layerUsageMapTable
{
if(_layerUsageMapTable == nil) {
_layerUsageMapTable = IJSVGLayerDefaultUsageMapTable();
}
return _layerUsageMapTable;
}
- (CALayer<IJSVGDrawableLayer>*)layerForUsageType:(IJSVGLayerUsageType)type
{
return [self.layerUsageMapTable objectForKey:@(type)];
}
- (CALayer<IJSVGDrawableLayer>*)strokeLayer:(IJSVGLayerUsageType*)usageType
{
CALayer<IJSVGDrawableLayer>* layer = nil;
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokeGeneric]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokeGeneric;
return layer;
}
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokeGradient]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokeGradient;
return layer;
}
if((layer = [self layerForUsageType:IJSVGLayerUsageTypeStrokePattern]) != nil) {
*usageType = IJSVGLayerUsageTypeStrokePattern;
return layer;
}
return nil;
}
- (void)setLayer:(CALayer<IJSVGDrawableLayer>*)layer
forUsageType:(IJSVGLayerUsageType)type
{
[self.layerUsageMapTable setObject:layer
forKey:@(type)];
}
- (void)addTraits:(IJSVGLayerTraits)traits
{
_layerTraits |= traits;
}
- (void)removeTraits:(IJSVGLayerTraits)traits
{
_layerTraits = _layerTraits & ~traits;
}
- (BOOL)matchesTraits:(IJSVGLayerTraits)traits
{
return (_layerTraits & traits) == traits;
}
- (BOOL)treatImplicitOriginAsTransform
{
return YES;
}
- (CALayer<IJSVGDrawableLayer>*)firstSublayerOfClass:(Class)aClass
{
return [IJSVGLayer firstSublayerOfClass:aClass
fromLayer:self];
}
- (IJSVGTraitedColorStorage*)colors
{
IJSVGTraitedColorStorage* colorList = [[IJSVGTraitedColorStorage alloc] init];
for(CALayer<IJSVGDrawableLayer>* layer in self.sublayers) {
[colorList unionColorStorage:layer.colors];
}
return colorList;
}
@end
@@ -1,13 +0,0 @@
//
// IJSVGFilterEffectGaussianBlur.h
// IJSVG
//
// Created by Curtis Hard on 18/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGFilterEffect.h>
@interface IJSVGFilterEffectGaussianBlur : IJSVGFilterEffect
@end
@@ -1,36 +0,0 @@
//
// IJSVGFilterEffectGaussianBlur.m
// IJSVG
//
// Created by Curtis Hard on 18/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGFilterEffectGaussianBlur.h>
#import <IJSVG/IJSVGThreadManager.h>
@implementation IJSVGFilterEffectGaussianBlur
+ (CIFilter*)sharedFilter
{
IJSVGThreadManager* manager = IJSVGThreadManager.currentManager;
NSString* key = @"CIFilterGaussianBlur";
CIFilter* filter = nil;
if((filter = [manager userInfoObjectForKey:key]) == nil) {
filter = [CIFilter filterWithName:@"CIGaussianBlur"];
[manager setUserInfoObject:filter
forKey:key];
}
return filter;
}
- (CIImage*)processImage:(CIImage*)image
{
CIFilter* filter = [self.class sharedFilter];
[filter setDefaults];
[filter setValue:image forKey:kCIInputImageKey];
[filter setValue:@(self.stdDeviation.value) forKey:kCIInputRadiusKey];
return [filter valueForKey:kCIOutputImageKey];
}
@end
@@ -1,16 +0,0 @@
//
// IJSVGClipPath.h
// IJSVG
//
// Created by Curtis Hard on 29/05/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGroup.h>
@interface IJSVGClipPath : IJSVGGroup {
}
@property (nonatomic, readonly) IJSVGWindingRule computedClipRule;
@end
@@ -1,88 +0,0 @@
//
// IJSVGClipPath.m
// IJSVG
//
// Created by Curtis Hard on 29/05/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGClipPath.h>
#import <IJSVG/IJSVGRootNode.h>
@implementation IJSVGClipPath
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributeX];
[storage setBit:IJSVGNodeAttributeY];
[storage setBit:IJSVGNodeAttributeWidth];
[storage setBit:IJSVGNodeAttributeHeight];
[storage setBit:IJSVGNodeAttributeClipPathUnits];
[storage setBit:IJSVGNodeAttributeClipRule];
return storage;
}
- (void)setDefaults
{
self.units = IJSVGUnitObjectBoundingBox;
self.contentUnits = IJSVGUnitUserSpaceOnUse;
self.windingRule = IJSVGWindingRuleNonZero;
self.overflowVisibility = IJSVGOverflowVisibilityHidden;
self.fill = [IJSVGColorNode colorNodeWithColor:NSColor.whiteColor];
}
- (IJSVGUnitType)contentUnitsWithReferencingNodeBounds:(CGRect*)bounds
{
IJSVGNode* node = nil;
IJSVGUnitType units = [self contentUnitsWithReferencingNode:&node];
if(units == IJSVGUnitUserSpaceOnUse) {
*bounds = node.rootNode.bounds;
} else {
*bounds = node.parentNode.bounds;
}
return units;
}
- (void)postProcess
{
// clip paths only allow shapes in them, nothing else, we can simply
// check the node types for the trait of pathed
IJSVGNodeTraits childTraits = IJSVGNodeTraitPathed;
for(IJSVGNode* childNode in self.children.copy) {
if([childNode matchesTraits:childTraits] == NO) {
BOOL remove = YES;
IJSVGNodeType type = childNode.type;
if(type == IJSVGNodeTypeUse) {
remove = [(IJSVGGroup*)childNode childrenMatchTraits:childTraits] == NO;
}
if(remove == YES) {
[self removeChild:childNode];
}
}
}
}
- (IJSVGWindingRule)computedClipRule
{
// find the first use of a clipRule that is useful
__block IJSVGWindingRule rule = IJSVGWindingRuleInherit;
__weak IJSVGClipPath* weakSelf = self;
IJSVGNodeWalkHandler handler = ^(IJSVGNode *node, BOOL *allowChildNodes,
BOOL *stop) {
if(node == weakSelf) {
return;
}
IJSVGWindingRule clipRule = node.clipRule;
if(clipRule != IJSVGWindingRuleInherit) {
rule = clipRule;
*stop = YES;
}
};
[IJSVGNode walkNodeTree:self
handler:handler];
return rule;
}
@end
@@ -1,22 +0,0 @@
//
// IJSVGColorNode.h
// IJSVG
//
// Created by Curtis Hard on 29/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGNode.h>
@interface IJSVGColorNode : IJSVGNode {
}
@property (nonatomic, strong) NSColor* color;
@property (nonatomic, assign) BOOL isNoneOrTransparent;
+ (IJSVGNode*)colorNodeWithColor:(NSColor*)color;
- (id)initWithColor:(NSColor*)color;
@end
@@ -1,33 +0,0 @@
//
// IJSVGColorNode.m
// IJSVG
//
// Created by Curtis Hard on 29/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGColorNode.h>
@implementation IJSVGColorNode
+ (IJSVGNode*)colorNodeWithColor:(NSColor *)color
{
return [[self alloc] initWithColor:color];
}
- (id)initWithColor:(NSColor*)color {
if((self = [super init]) != nil) {
[self addTraits:IJSVGNodeTraitPaintable];
self.color = color;
}
return self;
}
- (void)applyPropertiesFromNode:(IJSVGNode*)node
{
if([node isKindOfClass:self.class]) {
self.color = ((IJSVGColorNode*)node).color;
}
}
@end
@@ -1,19 +0,0 @@
//
// IJSVGFilter.h
// IJSVG
//
// Created by Curtis Hard on 18/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGLayer.h>
@interface IJSVGFilter : IJSVGGroup
- (CGImageRef)newImageByApplyFilterToLayer:(CALayer<IJSVGDrawableLayer>*)layer
scale:(CGFloat)scale;
@property (nonatomic, readonly) BOOL valid;
@end
@@ -1,57 +0,0 @@
//
// IJSVGFilter.m
// IJSVG
//
// Created by Curtis Hard on 18/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGFilter.h>
#import <IJSVG/IJSVGFilterEffect.h>
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGThreadManager.h>
@implementation IJSVGFilter
- (BOOL)valid
{
return self.children.count != 0;
}
- (CGImageRef)newImageByApplyFilterToLayer:(CALayer<IJSVGDrawableLayer>*)layer
scale:(CGFloat)scale
{
IJSVGFilter* filter = layer.filter;
layer.filter = nil;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
uint32_t info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little;
CGImageRef originalImage = [IJSVGLayer newImageForLayer:layer
options:IJSVGLayerDrawingOptionNone
colorSpace:colorSpace
bitmapInfo:info
scale:scale];
CIImage* image = [CIImage imageWithCGImage:originalImage];
CGColorSpaceRelease(colorSpace);
CGImageRelease(originalImage);
for(IJSVGFilterEffect* effect in self.children) {
image = [effect processImage:image];
}
IJSVGThreadManager* manager = IJSVGThreadManager.currentManager;
CIContext* context = manager.CIContext;
CGImageRef outputImage = [context createCGImage:image
fromRect:image.extent];
layer.filter = filter;
return outputImage;
}
- (void)addChild:(IJSVGNode*)child
{
if([child isKindOfClass:IJSVGFilterEffect.class] == NO) {
return;
}
[super addChild:child];
}
@end
@@ -1,40 +0,0 @@
//
// IJSVGFilterEffect.h
// IJSVG
//
// Created by Curtis Hard on 18/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGroup.h>
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, strong) 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
@@ -1,80 +0,0 @@
//
// IJSVGFilterEffect.m
// IJSVG
//
// Created by Curtis Hard on 18/04/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGFilterEffect.h>
#import <IJSVG/IJSVGFilterEffectGaussianBlur.h>
#import <IJSVG/IJSVGUtils.h>
@implementation IJSVGFilterEffect
static NSDictionary<NSString*, Class>* _elementClassMap = nil;
+ (void)load
{
_elementClassMap = @{
@"fegaussianblur": IJSVGFilterEffectGaussianBlur.class
};
}
+ (Class)effectClassForElementName:(NSString*)name
{
NSString* key = name.lowercaseString;
return _elementClassMap[key] ?: IJSVGFilterEffect.class;
}
+ (IJSVGFilterEffectSource)sourceForString:(NSString*)string
{
const char* name = string.UTF8String;
if(name == NULL) {
return IJSVGFilterEffectSourceGraphic;
}
if(IJSVGCharBufferCaseInsensitiveCompare(name, "sourcegraphic") == YES) {
return IJSVGFilterEffectSourceGraphic;
}
if(IJSVGCharBufferCaseInsensitiveCompare(name, "sourcealpha") == YES) {
return IJSVGFilterEffectSourceAlpha;
}
if(IJSVGCharBufferCaseInsensitiveCompare(name, "backgroundimage") == YES) {
return IJSVGFilterEffectSourceBackgroundImage;
}
if(IJSVGCharBufferCaseInsensitiveCompare(name, "backgroundalpha") == YES) {
return IJSVGFilterEffectSourceBackgroundAlpha;
}
if(IJSVGCharBufferCaseInsensitiveCompare(name, "fillpaint") == YES) {
return IJSVGFilterEffectSourceFillPaint;
}
if(IJSVGCharBufferCaseInsensitiveCompare(name, "strokepain") == YES) {
return IJSVGFilterEffectSourceStrokePaint;
}
return IJSVGFilterEffectSourcePrimitiveReference;
}
+ (IJSVGFilterEffectEdgeMode)edgeModeForString:(NSString*)string
{
const char* name = string.lowercaseString.UTF8String;
if(name == NULL) {
return IJSVGFilterEffectEdgeModeNone;
}
if(IJSVGCharBufferCompare(name, "none") == YES) {
return IJSVGFilterEffectEdgeModeNone;
}
if(IJSVGCharBufferCompare(name, "wrap") == YES) {
return IJSVGFilterEffectEdgeModeWrap;
}
if(IJSVGCharBufferCompare(name, "duplicate") == YES) {
return IJSVGFilterEffectEdgeModeDuplicate;
}
return IJSVGFilterEffectEdgeModeNone;
}
- (CIImage*)processImage:(CIImage*)image
{
return image;
}
@end
@@ -1,33 +0,0 @@
//
// IJSVGGradient.h
// IJSVGExample
//
// Created by Curtis Hard on 03/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <IJSVG/IJSVGTraitedColorStorage.h>
#import <IJSVG/IJSVGTransform.h>
#import <IJSVG/IJSVGGroup.h>
@interface IJSVGGradient : IJSVGGroup
@property (nonatomic, strong) NSArray<NSColor*>* colors;
@property (nonatomic, assign) CGFloat* locations;
@property (nonatomic, assign) NSUInteger numberOfStops;
@property (nonatomic, assign) CGGradientRef CGGradient;
@property (nonatomic, strong) IJSVGUnitLength* x1;
@property (nonatomic, strong) IJSVGUnitLength* x2;
@property (nonatomic, strong) IJSVGUnitLength* y1;
@property (nonatomic, strong) IJSVGUnitLength* y2;
+ (CGFloat*)computeColorStops:(IJSVGGradient*)gradient
colors:(NSArray**)someColors;
- (CGGradientRef)CGGradient;
- (void)drawInContextRef:(CGContextRef)ctx
bounds:(NSRect)objectRect
transform:(CGAffineTransform)absoluteTransform;
@end
@@ -1,114 +0,0 @@
//
// IJSVGGradient.m
// IJSVGExample
//
// Created by Curtis Hard on 03/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGradient.h>
#import <IJSVG/IJSVGParser.h>
@implementation IJSVGGradient
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributeGradientUnits];
[storage setBit:IJSVGNodeAttributeGradientTransform];
return storage;
}
- (void)dealloc
{
if(_locations != NULL) {
(void)free(_locations), _locations = NULL;
}
if(_CGGradient != NULL) {
(void)CGGradientRelease(_CGGradient), _CGGradient = NULL;
}
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGGradient* clone = [[self.class alloc] init];
[clone applyPropertiesFromNode:self];
return clone;
}
- (void)applyPropertiesFromNode:(IJSVGGradient*)node
{
[super applyPropertiesFromNode:node];
self.numberOfStops = node.numberOfStops;
self.colors = node.colors.copy;
size_t length = sizeof(CGFloat)*node.numberOfStops;
self.locations = (CGFloat*)malloc(length);
memcpy(self.locations, node.locations, length);
self.x1 = node.x1.copy;
self.x2 = node.x2.copy;
self.y1 = node.y1.copy;
self.y2 = node.y2.copy;
}
- (void)setLocations:(CGFloat*)locations
{
if(_locations != NULL) {
(void)free(_locations), _locations = NULL;
}
_locations = locations;
}
+ (CGFloat*)computeColorStops:(IJSVGGradient*)gradient
colors:(NSArray**)someColors
{
NSArray<IJSVGNode*>* stops = gradient.children;
NSMutableArray* colors = [[NSMutableArray alloc] initWithCapacity:stops.count];
CGFloat* stopsParams = (CGFloat*)malloc(stops.count * sizeof(CGFloat));
NSInteger i = 0;
for(IJSVGNode* stopNode in stops) {
NSColor* color = ((IJSVGColorNode*)(stopNode.fill)).color;
CGFloat opacity = stopNode.fillOpacity.value;
CGFloat offset = stopNode.offset.value;
stopsParams[i++] = offset;
if(color == nil) {
color = [IJSVGColor colorFromHEXInteger:0x000000];
if(opacity != 1.f) {
color = [IJSVGColor changeAlphaOnColor:color
to:opacity];
}
}
[colors addObject:color];
}
*someColors = (NSArray*)colors;
return stopsParams;
}
- (CGGradientRef)CGGradient
{
// store it in the cache
if(_CGGradient != nil) {
return _CGGradient;
}
// actually create the gradient
NSInteger num = self.numberOfStops;
CFMutableArrayRef colors = CFArrayCreateMutable(kCFAllocatorDefault, (CFIndex)num,
&kCFTypeArrayCallBacks);
for (NSColor* color in _colors) {
CFArrayAppendValue(colors, color.CGColor);
}
CGGradientRef result = CGGradientCreateWithColors(IJSVGColor.defaultColorSpace.CGColorSpace,
colors, _locations);
CFRelease(colors);
return _CGGradient = result;
}
- (void)drawInContextRef:(CGContextRef)ctx
bounds:(NSRect)objectRect
transform:(CGAffineTransform)absoluteTransform
{
}
@end
@@ -1,30 +0,0 @@
//
// IJSVGGroup.h
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGNode.h>
#import <IJSVG/IJSVGPath.h>
#import <Foundation/Foundation.h>
@interface IJSVGGroup : IJSVGNode {
@private
NSMutableArray<IJSVGNode*>* _children;
}
@property (weak, nonatomic, readonly) NSArray<IJSVGNode*>* children;
- (void)addChild:(IJSVGNode*)child;
- (void)addChildren:(NSArray<IJSVGNode*>*)children;
- (void)removeChild:(IJSVGNode*)child;
- (void)removeChildren:(NSArray<IJSVGNode*>*)children;
- (BOOL)childrenMatchTraits:(IJSVGNodeTraits)traits;
- (BOOL)containsNodesMatchingTraits:(IJSVGNodeTraits)traits;
- (NSArray<IJSVGNode*>*)nodesMatchingTraits:(IJSVGNodeTraits)traits;
- (NSSet<IJSVGNode*>*)childrenOfType:(IJSVGNodeType)type;
@end
@@ -1,138 +0,0 @@
//
// IJSVGGroup.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGImage.h>
@implementation IJSVGGroup
- (id)init
{
if((self = [super init]) != nil) {
_children = [[NSMutableArray alloc] init];
}
return self;
}
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage addBits:[IJSVGPath allowedAttributes]];
[storage addBits:[IJSVGImage allowedAttributes]];
return storage;
}
- (void)prepareFromCopy
{
_children = [[NSMutableArray alloc] init];
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGGroup* node = [super copyWithZone:zone];
[node prepareFromCopy];
for (__strong IJSVGNode* childNode in _children) {
childNode = childNode.copy;
childNode.parentNode = node;
[node addChild:childNode];
}
return node;
}
- (void)addChild:(IJSVGNode*)child
{
if(child == nil || child == self ||
(child.parentNode == self && [_children containsObject:child])) {
return;
}
child.parentNode = self;
[_children addObject:child];
}
- (void)removeChild:(IJSVGNode*)child
{
if(child.parentNode == self) {
[child detach];
}
[_children removeObject:child];
}
- (void)addChildren:(NSArray<IJSVGNode*>*)children
{
for(IJSVGNode* node in children) {
[self addChild:node];
}
}
- (void)removeChildren:(NSArray<IJSVGNode*>*)children
{
for(IJSVGNode* node in children) {
[self removeChild:node];
}
}
- (BOOL)childrenMatchTraits:(IJSVGNodeTraits)traits
{
for(IJSVGNode* node in _children) {
if([node matchesTraits:traits] == NO) {
return NO;
}
}
return YES;
}
- (NSSet<IJSVGNode*>*)childrenOfType:(IJSVGNodeType)type
{
NSMutableSet<IJSVGNode*>* nodes = [[NSMutableSet alloc] init];
for(IJSVGNode* node in self.children) {
if(node.type == type) {
[nodes addObject:node];
}
}
return nodes;
}
- (BOOL)containsNodesMatchingTraits:(IJSVGNodeTraits)traits
{
return [self.class node:self
containsNodesMatchingTraits:traits];
}
- (NSArray<IJSVGNode*>*)nodesMatchingTraits:(IJSVGNodeTraits)traits
{
return [self.class node:self
nodesMatchingTraits:traits];
}
- (CGRect)bounds
{
CGRect rect = CGRectNull;
for(IJSVGNode* node in self.children) {
if(CGRectIsNull(rect)) {
rect = node.bounds;
} else {
rect = CGRectUnion(rect, node.bounds);
}
}
return rect;
}
- (NSArray<IJSVGNode*>*)children
{
return _children;
}
- (NSString*)description
{
return [NSString stringWithFormat:@"%@ - %@",
[super description], self.children];
}
@end
@@ -1,107 +0,0 @@
//
// IJSVGImage.m
// IJSVGExample
//
// Created by Curtis Hard on 28/05/2016.
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGImage.h>
#import <IJSVG/IJSVGPath.h>
#import <IJSVG/IJSVGTransform.h>
@implementation IJSVGImage
- (void)dealloc
{
(void)(CGImageRelease(CGImage)), CGImage = nil;
}
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributeX];
[storage setBit:IJSVGNodeAttributeY];
[storage setBit:IJSVGNodeAttributeWidth];
[storage setBit:IJSVGNodeAttributeHeight];
[storage setBit:IJSVGNodeAttributePreserveAspectRatio];
return storage;
}
- (void)setDefaults
{
[super setDefaults];
self.viewBoxAlignment = IJSVGViewBoxAlignmentXMidYMid;
self.viewBoxMeetOrSlice = IJSVGViewBoxMeetOrSliceMeet;
}
- (void)loadFromString:(NSString*)encodedString
{
if([encodedString hasPrefix:@"data:"]) {
encodedString = [encodedString stringByReplacingOccurrencesOfString:@"\\s+"
withString:@""
options:NSRegularExpressionSearch
range:NSMakeRange(0, encodedString.length)];
}
NSURL* url = [NSURL URLWithString:encodedString];
if(url != nil) {
[self loadFromURL:url];
}
}
- (void)loadFromURL:(NSURL*)aURL
{
NSData* data = [NSData dataWithContentsOfURL:aURL];
// no data, just ignore...invalid probably
if(data == nil) {
return;
}
// set the image against the container
NSImage* anImage = [[NSImage alloc] initWithData:data];
[self setImage:anImage];
}
- (void)setImage:(NSImage*)anImage
{
_image = anImage;
_intrinsicSize = (CGSize)_image.size;
if(CGImage != nil) {
CGImageRelease(CGImage);
CGImage = nil;
}
CGRect rect = CGRectMake(0.f, 0.f,
_intrinsicSize.width,
_intrinsicSize.height);
CGImage = [_image CGImageForProposedRect:&rect
context:nil
hints:nil];
CGImageRetain(CGImage);
}
- (CGImageRef)CGImage
{
return CGImage;
}
- (CGRect)intrinsicBounds
{
CGRect rect = CGRectZero;
rect.size.width = _intrinsicSize.width;
rect.size.height = _intrinsicSize.height;
return rect;
}
- (CGRect)bounds
{
return CGRectMake(0.f, 0.f,
self.width.value,
self.height.value);
}
@end
@@ -1,87 +0,0 @@
//
// IJSVGGradient.m
// IJSVGExample
//
// Created by Curtis Hard on 03/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGLinearGradient.h>
#import <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGParser.h>
@implementation IJSVGLinearGradient
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributeX1];
[storage setBit:IJSVGNodeAttributeX2];
[storage setBit:IJSVGNodeAttributeY1];
[storage setBit:IJSVGNodeAttributeY2];
return storage;
}
+ (void)parseGradient:(NSXMLElement*)element
gradient:(IJSVGLinearGradient*)aGradient
{
// just ask unit for the value
NSString* x1 = ([element attributeForName:IJSVGAttributeX1].stringValue ?: @"0");
NSString* x2 = ([element attributeForName:IJSVGAttributeX2].stringValue ?: @"100%");
NSString* y1 = ([element attributeForName:IJSVGAttributeY1].stringValue ?: @"0");
NSString* y2 = ([element attributeForName:IJSVGAttributeY2].stringValue ?: @"0");
aGradient.x1 = [IJSVGGradientUnitLength unitWithString:x1 fromUnitType:aGradient.units];
aGradient.x2 = [IJSVGGradientUnitLength unitWithString:x2 fromUnitType:aGradient.units];
aGradient.y1 = [IJSVGGradientUnitLength unitWithString:y1 fromUnitType:aGradient.units];
aGradient.y2 = [IJSVGGradientUnitLength unitWithString:y2 fromUnitType:aGradient.units];
// compute the color stops and colours
NSArray* colors = nil;
CGFloat* stopsParams = [self.class computeColorStops:aGradient
colors:&colors];
aGradient.colors = colors;
aGradient.locations = stopsParams;
aGradient.numberOfStops = colors.count;
}
- (void)drawInContextRef:(CGContextRef)ctx
bounds:(NSRect)objectRect
transform:(CGAffineTransform)absoluteTransform
{
BOOL inUserSpace = self.units == IJSVGUnitUserSpaceOnUse;
CGPoint gradientStartPoint = CGPointZero;
CGPoint gradientEndPoint = CGPointZero;
CGRect boundingBox = objectRect;
// make sure we apply the absolute position to
// transform us back into the correct space
CGFloat width = CGRectGetWidth(boundingBox);
CGFloat height = CGRectGetHeight(boundingBox);
if(inUserSpace == YES) {
CGContextConcatCTM(ctx, absoluteTransform);
} else {
width = 1.f;
height = 1.f;
CGContextConcatCTM(ctx, CGAffineTransformMakeScale(boundingBox.size.width,
boundingBox.size.height));
}
gradientStartPoint = CGPointMake([self.x1 computeValue:width],
[self.y1 computeValue:height]);
gradientEndPoint = CGPointMake([self.x2 computeValue:width],
[self.y2 computeValue:height]);
// concat the gradient transform into the context
IJSVGConcatTransformsCTM(ctx, self.transforms);
// draw the gradient
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGContextDrawLinearGradient(ctx, self.CGGradient, gradientStartPoint,
gradientEndPoint, options);
}
@end
@@ -1,13 +0,0 @@
//
// IJSVGMask.h
// IJSVG
//
// Created by Curtis Hard on 28/05/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGroup.h>
@interface IJSVGMask : IJSVGGroup
@end
@@ -1,51 +0,0 @@
//
// IJSVGMask.m
// IJSVG
//
// Created by Curtis Hard on 28/05/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGMask.h>
#import <IJSVG/IJSVGRootNode.h>
@implementation IJSVGMask
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributeX];
[storage setBit:IJSVGNodeAttributeY];
[storage setBit:IJSVGNodeAttributeWidth];
[storage setBit:IJSVGNodeAttributeHeight];
[storage setBit:IJSVGNodeAttributeMaskUnits];
[storage setBit:IJSVGNodeAttributeMaskContentUnits];
return storage;
}
- (void)setDefaults
{
[super setDefaults];
self.x = [IJSVGUnitLength unitWithPercentageFloat:-.2f];
self.y = [IJSVGUnitLength unitWithPercentageFloat:-.2f];
self.width = [IJSVGUnitLength unitWithPercentageFloat:1.2f];
self.height = [IJSVGUnitLength unitWithPercentageFloat:1.2f];
self.units = IJSVGUnitObjectBoundingBox;
self.contentUnits = IJSVGUnitUserSpaceOnUse;
self.overflowVisibility = IJSVGOverflowVisibilityHidden;
}
- (IJSVGUnitType)contentUnitsWithReferencingNodeBounds:(CGRect *)bounds
{
IJSVGNode* node = nil;
IJSVGUnitType units = [self contentUnitsWithReferencingNode:&node];
if(units == IJSVGUnitUserSpaceOnUse) {
*bounds = node.rootNode.bounds;
} else {
*bounds = node.parentNode.bounds;
}
return units;
}
@end
@@ -1,293 +0,0 @@
//
// IJSVGNode.h
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGStyleSheetStyle.h>
#import <IJSVG/IJSVGUnitLength.h>
#import <IJSVG/IJSVGViewBox.h>
#import <IJSVG/IJSVGBitFlags64.h>
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@class IJSVGNode;
@class IJSVG;
@class IJSVGGroup;
@class IJSVGGradient;
@class IJSVGGroup;
@class IJSVGPattern;
@class IJSVGTransform;
@class IJSVGRootNode;
@class IJSVGUnitRect;
@class IJSVGFilter;
@class IJSVGMask;
@class IJSVGClipPath;
@class IJSVGThreadManager;
typedef void (^IJSVGNodeWalkHandler)(IJSVGNode* node, BOOL* allowChildNodes, BOOL* stop);
typedef NS_ENUM(NSInteger, IJSVGNodeAttribute) {
IJSVGNodeAttributeVersion,
IJSVGNodeAttributeXMLNS,
IJSVGNodeAttributeXMLNSXlink,
IJSVGNodeAttributeViewBox,
IJSVGNodeAttributePreserveAspectRatio,
IJSVGNodeAttributeID,
IJSVGNodeAttributeClass,
IJSVGNodeAttributeX,
IJSVGNodeAttributeY,
IJSVGNodeAttributeWidth,
IJSVGNodeAttributeHeight,
IJSVGNodeAttributeOpacity,
IJSVGNodeAttributeStrokeOpacity,
IJSVGNodeAttributeStrokeWidth,
IJSVGNodeAttributeStrokeDashOffset,
IJSVGNodeAttributeFillOpacity,
IJSVGNodeAttributeClipPath,
IJSVGNodeAttributeClipPathUnits,
IJSVGNodeAttributeClipRule,
IJSVGNodeAttributeMask,
IJSVGNodeAttributeGradientUnits,
IJSVGNodeAttributePatternUnits,
IJSVGNodeAttributePatternContentUnits,
IJSVGNodeAttributePatternTransform,
IJSVGNodeAttributeMaskUnits,
IJSVGNodeAttributeMaskContentUnits,
IJSVGNodeAttributeTransform,
IJSVGNodeAttributeGradientTransform,
IJSVGNodeAttributeUnicode,
IJSVGNodeAttributeStrokeLineCap,
IJSVGNodeAttributeStrokeLineJoin,
IJSVGNodeAttributeStroke,
IJSVGNodeAttributeStrokeDashArray,
IJSVGNodeAttributeStrokeMiterLimit,
IJSVGNodeAttributeFill,
IJSVGNodeAttributeFillRule,
IJSVGNodeAttributeBlendMode,
IJSVGNodeAttributeDisplay,
IJSVGNodeAttributeStyle,
IJSVGNodeAttributeD,
IJSVGNodeAttributeXLink,
IJSVGNodeAttributeX1,
IJSVGNodeAttributeX2,
IJSVGNodeAttributeY1,
IJSVGNodeAttributeY2,
IJSVGNodeAttributeRX,
IJSVGNodeAttributeRY,
IJSVGNodeAttributeCX,
IJSVGNodeAttributeCY,
IJSVGNodeAttributeR,
IJSVGNodeAttributeFX,
IJSVGNodeAttributeFY,
IJSVGNodeAttributeFR,
IJSVGNodeAttributePoints,
IJSVGNodeAttributeOffset,
IJSVGNodeAttributeStopColor,
IJSVGNodeAttributeStopOpacity,
IJSVGNodeAttributeHref,
IJSVGNodeAttributeOverflow,
IJSVGNodeAttributeFilter,
IJSVGNodeAttributeStdDeviation,
IJSVGNodeAttributeIn,
IJSVGNodeAttributeEdgeMode,
IJSVGNodeAttributeMarker
};
static const int kIJSVGNodeAttributeStorageLength = 64;
typedef NS_OPTIONS(NSInteger, IJSVGIntrinsicDimensions) {
IJSVGIntrinsicDimensionNone = 0,
IJSVGIntrinsicDimensionWidth = 1 << 1,
IJSVGIntrinsicDimensionHeight = 1 << 2,
IJSVGIntrinsicDimensionBoth = IJSVGIntrinsicDimensionWidth | IJSVGIntrinsicDimensionHeight
};
typedef NS_OPTIONS(NSInteger, IJSVGNodeTraits) {
IJSVGNodeTraitNone = 0,
IJSVGNodeTraitStroked = 1 << 0,
IJSVGNodeTraitPaintable = 1 << 1,
IJSVGNodeTraitPathed = 1 << 2
};
typedef NS_ENUM(NSInteger, IJSVGNodeType) {
IJSVGNodeTypeUnknown = 0,
IJSVGNodeTypeGroup,
IJSVGNodeTypePath,
IJSVGNodeTypeDef,
IJSVGNodeTypePolygon,
IJSVGNodeTypePolyline,
IJSVGNodeTypeRect,
IJSVGNodeTypeLine,
IJSVGNodeTypeCircle,
IJSVGNodeTypeEllipse,
IJSVGNodeTypeUse,
IJSVGNodeTypeLinearGradient,
IJSVGNodeTypeRadialGradient,
IJSVGNodeTypeClipPath,
IJSVGNodeTypeFont,
IJSVGNodeTypeGlyph,
IJSVGNodeTypeMask,
IJSVGNodeTypeImage,
IJSVGNodeTypePattern,
IJSVGNodeTypeSVG,
IJSVGNodeTypeText,
IJSVGNodeTypeTextSpan,
IJSVGNodeTypeStyle,
IJSVGNodeTypeSwitch,
IJSVGNodeTypeTitle,
IJSVGNodeTypeDesc,
IJSVGNodeTypeStop,
IJSVGNodeTypeNotFound,
IJSVGNodeTypeFilter,
IJSVGNodeTypeFilterEffect,
IJSVGNodeTypeForeignObject
};
typedef NS_ENUM(NSInteger, IJSVGWindingRule) {
IJSVGWindingRuleNonZero,
IJSVGWindingRuleEvenOdd,
IJSVGWindingRuleInherit
};
typedef NS_ENUM(NSInteger, IJSVGLineCapStyle) {
IJSVGLineCapStyleNone,
IJSVGLineCapStyleButt,
IJSVGLineCapStyleRound,
IJSVGLineCapStyleSquare,
IJSVGLineCapStyleInherit
};
typedef NS_ENUM(NSInteger, IJSVGLineJoinStyle) {
IJSVGLineJoinStyleNone,
IJSVGLineJoinStyleMiter,
IJSVGLineJoinStyleRound,
IJSVGLineJoinStyleBevel,
IJSVGLineJoinStyleInherit
};
typedef NS_OPTIONS(NSInteger, IJSVGFontTraits) {
IJSVGFontTraitNone = 1 << 0,
IJSVGFontTraitBold = 1 << 1,
IJSVGFontTraitItalic = 1 << 2
};
typedef NS_ENUM(NSInteger, IJSVGBlendMode) {
IJSVGBlendModeNormal = kCGBlendModeNormal,
IJSVGBlendModeMultiply = kCGBlendModeMultiply,
IJSVGBlendModeScreen = kCGBlendModeScreen,
IJSVGBlendModeOverlay = kCGBlendModeOverlay,
IJSVGBlendModeDarken = kCGBlendModeDarken,
IJSVGBlendModeLighten = kCGBlendModeLighten,
IJSVGBlendModeColorDodge = kCGBlendModeColorDodge,
IJSVGBlendModeColorBurn = kCGBlendModeColorBurn,
IJSVGBlendModeHardLight = kCGBlendModeHardLight,
IJSVGBlendModeSoftLight = kCGBlendModeSoftLight,
IJSVGBlendModeDifference = kCGBlendModeDifference,
IJSVGBlendModeExclusion = kCGBlendModeExclusion,
IJSVGBlendModeHue = kCGBlendModeHue,
IJSVGBlendModeSaturation = kCGBlendModeSaturation,
IJSVGBlendModeColor = kCGBlendModeColor,
IJSVGBlendModeLuminosity = kCGBlendModeLuminosity
};
typedef NS_ENUM(NSInteger, IJSVGOverflowVisibility) {
IJSVGOverflowVisibilityHidden,
IJSVGOverflowVisibilityVisible
};
static CGFloat IJSVGInheritedFloatValue = -99.9999991;
static CGFloat IJSVGInheritedIntegerValue = INT_MIN;
@interface IJSVGNode : NSObject <NSCopying> {
@private
BOOL _computedTraits;
}
void IJSVGAssertPaintableObject(id object);
@property (nonatomic, assign) IJSVGNodeTraits traits;
@property (nonatomic, assign, readonly) CGRect bounds;
@property (nonatomic, strong) IJSVGUnitRect* viewBox;
@property (nonatomic, assign) IJSVGViewBoxAlignment viewBoxAlignment;
@property (nonatomic, assign) IJSVGViewBoxMeetOrSlice viewBoxMeetOrSlice;
@property (nonatomic, copy) NSString* title;
@property (nonatomic, copy) NSString* desc;
@property (nonatomic, assign) IJSVGNodeType type;
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* className;
@property (nonatomic, strong) NSSet<NSString*>* classNameList;
@property (nonatomic, copy) NSString* unicode;
@property (nonatomic, assign) BOOL shouldRender;
@property (nonatomic, strong) IJSVGUnitLength* x;
@property (nonatomic, strong) IJSVGUnitLength* y;
@property (nonatomic, strong) IJSVGUnitLength* width;
@property (nonatomic, strong) IJSVGUnitLength* height;
@property (nonatomic, strong) IJSVGUnitLength* opacity;
@property (nonatomic, strong) IJSVGUnitLength* fillOpacity;
@property (nonatomic, strong) IJSVGUnitLength* strokeOpacity;
@property (nonatomic, strong) IJSVGUnitLength* strokeWidth;
@property (nonatomic, strong) IJSVGUnitLength* offset;
@property (nonatomic, strong) IJSVGNode* fill;
@property (nonatomic, strong) IJSVGNode* stroke;
@property (nonatomic, copy) NSString* identifier;
@property (nonatomic, assign) IJSVGNode* parentNode;
@property (nonatomic, strong) IJSVGClipPath* clipPath;
@property (nonatomic, strong) IJSVGMask* mask;
@property (nonatomic, assign) IJSVGWindingRule windingRule;
@property (nonatomic, assign) IJSVGWindingRule clipRule;
@property (nonatomic, assign) IJSVGLineCapStyle lineCapStyle;
@property (nonatomic, assign) IJSVGLineJoinStyle lineJoinStyle;
@property (nonatomic, strong) IJSVGUnitLength* strokeMiterLimit;
@property (nonatomic, strong) NSArray<IJSVGTransform*>* transforms;
@property (nonatomic, strong) IJSVGFilter* filter;
@property (nonatomic, assign) CGFloat* strokeDashArray;
@property (nonatomic, assign) NSInteger strokeDashArrayCount;
@property (nonatomic, readonly) NSArray<NSNumber*>* lineDashPattern;
@property (nonatomic, strong) IJSVGUnitLength* strokeDashOffset;
@property (nonatomic, strong) IJSVG* svg;
@property (nonatomic, assign) IJSVGUnitType contentUnits;
@property (nonatomic, assign) IJSVGUnitType units;
@property (nonatomic, assign) IJSVGBlendMode blendMode;
@property (nonatomic, assign) IJSVGOverflowVisibility overflowVisibility;
@property (nonatomic, readonly) BOOL detachedFromParentNode;
@property (nonatomic, readonly) IJSVGRootNode* rootNode;
+ (IJSVGBitFlags*)computedAllowedAttributes;
+ (IJSVGBitFlags*)allowedAttributes;
+ (void)walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler;
+ (NSArray<IJSVGNode*>*)node:(IJSVGNode*)node
nodesMatchingTraits:(IJSVGNodeTraits)traits;
+ (BOOL)node:(IJSVGNode*)node
containsNodesMatchingTraits:(IJSVGNodeTraits)traits;
+ (IJSVGNodeType)typeForString:(NSString*)string
kind:(NSXMLNodeKind)kind;
+ (BOOL)typeIsPathable:(IJSVGNodeType)type;
- (void)setDefaults;
- (void)postProcess;
- (void)applyPropertiesFromNode:(IJSVGNode*)node;
- (IJSVGUnitType)contentUnitsWithReferencingNodeBounds:(CGRect*)bounds;
- (IJSVGUnitType)contentUnitsWithReferencingNode:(IJSVGNode**)referencingNode;
- (instancetype)detach;
- (void)addTraits:(IJSVGNodeTraits)traits;
- (void)removeTraits:(IJSVGNodeTraits)traits;
- (BOOL)matchesTraits:(IJSVGNodeTraits)traits;
- (void)computeTraits;
- (NSSet<IJSVGNode*>*)nodesMatchingTypes:(NSIndexSet*)types;
- (instancetype)parentNodeMatchingClass:(Class)someClass;
- (instancetype)rootNodeMatchingClass:(Class)someClass;
@end
@@ -1,662 +0,0 @@
//
// IJSVGNode.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGNode.h>
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGRootNode.h>
#import <IJSVG/IJSVGThreadManager.h>
@implementation IJSVGNode
@synthesize fill = _fill;
@synthesize stroke = _stroke;
- (void)dealloc
{
if(_strokeDashArray != NULL) {
(void)free(_strokeDashArray), _strokeDashArray = NULL;
}
}
+ (IJSVGNodeType)typeForString:(NSString*)string
kind:(NSXMLNodeKind)kind
{
// if string is nil, or its not a generic element or some text span
if(string == nil || (kind != NSXMLElementKind && kind != NSXMLTextKind)) {
return IJSVGNodeTypeNotFound;
}
const char* nodeType = string.UTF8String;
if(nodeType == NULL) {
return IJSVGNodeTypeNotFound;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "g") == YES) {
return IJSVGNodeTypeGroup;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "path") == YES) {
return IJSVGNodeTypePath;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "style") == YES) {
return IJSVGNodeTypeStyle;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "switch") == YES) {
return IJSVGNodeTypeSwitch;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "defs") == YES) {
return IJSVGNodeTypeDef;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "polygon") == YES) {
return IJSVGNodeTypePolygon;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "polyline") == YES) {
return IJSVGNodeTypePolyline;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "rect") == YES) {
return IJSVGNodeTypeRect;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "line") == YES) {
return IJSVGNodeTypeLine;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "circle") == YES) {
return IJSVGNodeTypeCircle;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "ellipse") == YES) {
return IJSVGNodeTypeEllipse;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "use") == YES) {
return IJSVGNodeTypeUse;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "lineargradient") == YES) {
return IJSVGNodeTypeLinearGradient;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "radialgradient") == YES) {
return IJSVGNodeTypeRadialGradient;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "stop") == YES) {
return IJSVGNodeTypeStop;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "glyph") == YES) {
return IJSVGNodeTypeGlyph;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "font") == YES) {
return IJSVGNodeTypeFont;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "clippath") == YES) {
return IJSVGNodeTypeClipPath;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "mask") == YES) {
return IJSVGNodeTypeMask;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "image") == YES) {
return IJSVGNodeTypeImage;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "pattern") == YES) {
return IJSVGNodeTypePattern;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "svg") == YES) {
return IJSVGNodeTypeSVG;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "text") == YES) {
return IJSVGNodeTypeText;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "tspan") == YES || kind == NSXMLTextKind) {
return IJSVGNodeTypeTextSpan;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "title") == YES) {
return IJSVGNodeTypeTitle;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "desc") == YES) {
return IJSVGNodeTypeDesc;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "foreignobject") == YES) {
return IJSVGNodeTypeForeignObject;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "filter") == YES) {
return IJSVGNodeTypeFilter;
}
if(IJSVGCharBufferCaseInsensitiveCompare(nodeType, "fegaussianblur") == YES) {
return IJSVGNodeTypeFilterEffect;
}
return IJSVGNodeTypeUnknown;
}
+ (IJSVGBitFlags*)computedAllowedAttributes
{
static NSMutableDictionary<Class, IJSVGBitFlags*>* computed = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
computed = [[NSMutableDictionary alloc] init];
});
@synchronized (computed) {
IJSVGBitFlags* set = computed[self];
if(set == nil) {
set = [self allowedAttributes];
computed[(id<NSCopying>)self] = set;
}
return set;
}
}
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage setBit:IJSVGNodeAttributeStyle];
[storage setBit:IJSVGNodeAttributeClass];
[storage setBit:IJSVGNodeAttributeTransform];
[storage setBit:IJSVGNodeAttributeID];
[storage setBit:IJSVGNodeAttributeDisplay];
return storage;
}
+ (BOOL)typeIsPathable:(IJSVGNodeType)type
{
return type == IJSVGNodeTypePath || type == IJSVGNodeTypeRect ||
type == IJSVGNodeTypeCircle || type == IJSVGNodeTypeEllipse ||
type == IJSVGNodeTypePolygon || type == IJSVGNodeTypePolyline ||
type == IJSVGNodeTypeLine;
}
+ (void)walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler
{
BOOL allowChildNodes = YES;
BOOL stop = NO;
[self _walkNodeTree:node
handler:handler
allowChildNodes:&allowChildNodes
stop:&stop];
}
+ (void)_walkNodeTree:(IJSVGNode*)node
handler:(IJSVGNodeWalkHandler)handler
allowChildNodes:(BOOL*)allowChildNodes
stop:(BOOL*)stop
{
// run the handler and instantly stop
// if stop is set
handler(node, allowChildNodes, stop);
if(*stop == YES) {
return;
}
// child nodes only work for nodes
// that are type group
if(*allowChildNodes == NO ||
[node isKindOfClass:IJSVGGroup.class] == NO) {
*allowChildNodes = YES;
return;
}
// iterate over the childnodes
IJSVGGroup* group = (IJSVGGroup*)node;
for(IJSVGNode* childNode in group.children) {
[self _walkNodeTree:childNode
handler:handler
allowChildNodes:allowChildNodes
stop:stop];
if(*stop == YES) {
return;
}
}
}
+ (NSArray<IJSVGNode*>*)node:(IJSVGNode*)node
nodesMatchingTraits:(IJSVGNodeTraits)traits
{
NSMutableArray<IJSVGNode*>* nodes = [[NSMutableArray alloc] init];
IJSVGNodeWalkHandler handler = ^(IJSVGNode* node,
BOOL* allowChildNodes,
BOOL* stop) {
// dont compute nodes that are not designed
// to be rendered
if(node.shouldRender == NO) {
*allowChildNodes = NO;
return;
}
if([node matchesTraits:traits] == YES) {
[nodes addObject:node];
}
};
[IJSVGNode walkNodeTree:node
handler:handler];
return nodes;
}
+ (BOOL)node:(IJSVGNode*)node
containsNodesMatchingTraits:(IJSVGNodeTraits)traits
{
__block IJSVGNodeTraits matchedTraits = IJSVGNodeTraitNone;
IJSVGNodeWalkHandler handler = ^(IJSVGNode* node,
BOOL* allowChildNodes,
BOOL* stop) {
// dont compute nodes that are not designed
// to be rendered
if(node.shouldRender == NO) {
*allowChildNodes = NO;
return;
}
// check for stroke
if((traits & IJSVGNodeTraitStroked) == IJSVGNodeTraitStroked &&
[node matchesTraits:IJSVGNodeTraitStroked] == YES) {
matchedTraits |= IJSVGNodeTraitStroked;
}
// check for pathed
if((traits & IJSVGNodeTraitPathed) == IJSVGNodeTraitPathed &&
[node matchesTraits:IJSVGNodeTraitPathed] == YES) {
matchedTraits |= IJSVGNodeTraitPathed;
}
// check for paintable
if((traits & IJSVGNodeTraitPaintable) == IJSVGNodeTraitPaintable &&
[node matchesTraits:IJSVGNodeTraitPaintable] == YES) {
matchedTraits |= IJSVGNodeTraitPaintable;
}
// simply check if masks equal, if they are, stop this loop
// and return the evaluation
if(matchedTraits == traits) {
*stop = YES;
}
};
[IJSVGNode walkNodeTree:node
handler:handler];
return matchedTraits == traits;
}
- (id)init
{
if((self = [super init]) != nil) {
self.opacity = [IJSVGUnitLength unitWithFloat:1.f];
self.fillOpacity = [IJSVGUnitLength unitWithFloat:1.f];
self.fillOpacity.inherit = YES;
self.strokeDashArrayCount = IJSVGInheritedIntegerValue;
self.strokeDashOffset = [IJSVGUnitLength unitWithFloat:0.f];
self.shouldRender = YES;
self.strokeOpacity = [IJSVGUnitLength unitWithFloat:1.f];
self.strokeOpacity.inherit = YES;
self.strokeWidth = [IJSVGUnitLength unitWithFloat:1.f];
self.strokeWidth.inherit = YES;
self.windingRule = IJSVGWindingRuleInherit;
self.clipRule = IJSVGWindingRuleInherit;
self.lineCapStyle = IJSVGLineCapStyleInherit;
self.lineJoinStyle = IJSVGLineJoinStyleInherit;
self.units = IJSVGUnitInherit;
self.contentUnits = IJSVGUnitInherit;
self.blendMode = IJSVGBlendModeNormal;
self.overflowVisibility = IJSVGOverflowVisibilityVisible;
// peform basic init
[self setDefaults];
}
return self;
}
- (void)applyPropertiesFromNode:(IJSVGNode*)node
{
self.title = node.title;
self.desc = node.desc;
self.name = node.name;
self.type = node.type;
self.unicode = node.unicode;
self.className = node.className;
self.classNameList = node.classNameList;
self.viewBox = node.viewBox;
self.viewBoxAlignment = node.viewBoxAlignment;
self.viewBoxMeetOrSlice = node.viewBoxMeetOrSlice;
self.x = node.x;
self.y = node.y;
self.width = node.width;
self.height = node.height;
self.fill = node.fill;
self.stroke = node.stroke;
self.clipPath = node.clipPath;
self.units = node.units;
self.contentUnits = node.contentUnits;
self.opacity = node.opacity;
self.strokeWidth = node.strokeWidth;
self.fillOpacity = node.fillOpacity;
self.strokeOpacity = node.strokeOpacity;
self.strokeMiterLimit = node.strokeMiterLimit;
self.identifier = node.identifier;
self.transforms = node.transforms;
self.windingRule = node.windingRule;
self.clipRule = node.clipRule;
self.lineCapStyle = node.lineCapStyle;
self.lineJoinStyle = node.lineJoinStyle;
self.parentNode = node.parentNode;
self.shouldRender = node.shouldRender;
self.blendMode = node.blendMode;
self.overflowVisibility = node.overflowVisibility;
// dash array needs physical memory copied
CGFloat* nStrokeDashArray = (CGFloat*)malloc(node.strokeDashArrayCount * sizeof(CGFloat));
memcpy(self.strokeDashArray, nStrokeDashArray, node.strokeDashArrayCount * sizeof(CGFloat));
self.strokeDashArray = nStrokeDashArray;
self.strokeDashArrayCount = node.strokeDashArrayCount;
self.strokeDashOffset = node.strokeDashOffset;
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGNode* node = [self.class allocWithZone:zone];
[node applyPropertiesFromNode:self];
return node;
}
- (void)postProcess
{
}
- (void)setFill:(IJSVGNode*)fill
{
NSAssert([fill matchesTraits:IJSVGNodeTraitPaintable] || fill == nil, @"Fill must a paintable node.");
_fill = fill;
}
- (void)setStroke:(IJSVGNode*)stroke
{
NSAssert([stroke matchesTraits:IJSVGNodeTraitPaintable]|| stroke == nil, @"Stroke must be a paintable node.");
_stroke = stroke;
}
// winding rule can inherit..
- (IJSVGWindingRule)windingRule
{
if(_windingRule == IJSVGWindingRuleInherit && _parentNode != nil) {
return _parentNode.windingRule;
}
return _windingRule;
}
- (IJSVGWindingRule)clipRule
{
if(_clipRule == IJSVGWindingRuleInherit && _parentNode != nil) {
return _parentNode.clipRule;
}
return _clipRule;
}
- (IJSVGLineCapStyle)lineCapStyle
{
if(_lineCapStyle == IJSVGLineCapStyleInherit) {
if(_parentNode != nil) {
return _parentNode.lineCapStyle;
}
}
return _lineCapStyle;
}
- (IJSVGLineJoinStyle)lineJoinStyle
{
if(_lineJoinStyle == IJSVGLineJoinStyleInherit) {
if(_parentNode != nil) {
return _parentNode.lineJoinStyle;
}
}
return _lineJoinStyle;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGUnitLength*)opacity
{
if(_opacity.inherit && _parentNode != nil) {
return _parentNode.opacity;
}
return _opacity;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGUnitLength*)fillOpacity
{
if(_fillOpacity.inherit && _parentNode != nil) {
return _parentNode.fillOpacity;
}
return _fillOpacity;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGUnitLength*)strokeWidth
{
if(_strokeWidth.inherit && _parentNode != nil) {
return _parentNode.strokeWidth;
}
return _strokeWidth;
}
- (NSArray<NSNumber*>*)lineDashPattern
{
NSMutableArray* arr = [[NSMutableArray alloc] initWithCapacity:self.strokeDashArrayCount];
for (NSInteger i = 0; i < self.strokeDashArrayCount; i++) {
[arr addObject:@((CGFloat)self.strokeDashArray[i])];
}
return arr;
}
// these are all recursive, so go up the chain
// if they dont exist on this specific node
- (IJSVGNode*)stroke
{
if(_stroke == nil && _parentNode != nil) {
return _parentNode.stroke;
}
return _stroke;
}
- (CGFloat*)strokeDashArray
{
if(_strokeDashArray == NULL && _parentNode != nil) {
return _parentNode.strokeDashArray;
}
return _strokeDashArray;
}
- (NSInteger)strokeDashArrayCount
{
if(_strokeDashArrayCount == IJSVGInheritedIntegerValue && _parentNode != nil) {
return _parentNode.strokeDashArrayCount;
}
return _strokeDashArrayCount;
}
- (IJSVGUnitLength *)strokeDashOffset
{
if(_strokeDashOffset == nil && _parentNode != nil) {
return _parentNode.strokeDashOffset;
}
return _strokeDashOffset;
}
- (IJSVGUnitLength*)strokeOpacity
{
if(_strokeOpacity.inherit && _parentNode != nil) {
return _parentNode.strokeOpacity;
}
return _strokeOpacity;
}
// even though the spec explicity states fill color
// must be on the path, it can also be on the
- (IJSVGNode*)fill
{
if(_fill == nil && _parentNode != nil) {
return _parentNode.fill;
}
return _fill;
}
- (IJSVGUnitType)units
{
if(_units == IJSVGUnitInherit && _parentNode != nil) {
return _parentNode.units;
}
return _units;
}
- (IJSVGUnitType)contentUnits
{
if(_contentUnits == IJSVGUnitInherit && _parentNode != nil) {
return _parentNode.contentUnits;
}
return _contentUnits;
}
- (IJSVGUnitLength*)strokeMiterLimit
{
if(_strokeMiterLimit == nil && _parentNode != nil) {
return _parentNode.strokeMiterLimit;
}
return _strokeMiterLimit;
}
- (IJSVGUnitType)contentUnitsWithReferencingNodeBounds:(CGRect*)bounds
{
IJSVGNode* node = nil;
IJSVGUnitType type = [self contentUnitsWithReferencingNode:&node];
*bounds = node.parentNode.bounds;
return type;
}
- (IJSVGUnitType)contentUnitsWithReferencingNode:(IJSVGNode**)referencingNode
{
if(_contentUnits == IJSVGUnitInherit && _parentNode != nil) {
return [_parentNode contentUnitsWithReferencingNode:referencingNode];
}
*referencingNode = self;
return _contentUnits;
}
- (IJSVGNodeTraits)traits
{
if(_computedTraits == NO) {
_computedTraits = YES;
[self computeTraits];
}
return _traits;
}
- (void)addTraits:(IJSVGNodeTraits)traits
{
_traits |= traits;
}
- (void)removeTraits:(IJSVGNodeTraits)traits
{
_traits = _traits & ~traits;
}
- (BOOL)matchesTraits:(IJSVGNodeTraits)traits
{
return (self.traits & traits) == traits;
}
- (void)computeTraits
{
// by default this does nothing
}
- (void)setDefaults
{
}
- (instancetype)detach
{
self.parentNode = nil;
return self;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ %@ %@ %@",NSStringFromClass(self.class),
self.name,self.classNameList,self.identifier];
}
- (BOOL)detachedFromParentNode
{
return self.type == IJSVGNodeTypeClipPath ||
self.type == IJSVGNodeTypeMask;
}
- (IJSVGRootNode*)rootNode
{
IJSVGNode* parent = self;
while([parent isKindOfClass:IJSVGRootNode.class] == NO) {
parent = parent.parentNode;
if(parent == nil) {
return nil;
}
}
return (IJSVGRootNode*)parent;
}
- (NSSet<IJSVGNode*>*)nodesMatchingTypes:(NSIndexSet*)types
{
NSMutableSet<IJSVGNode*>* nodes = [[NSMutableSet alloc] init];
IJSVGNodeWalkHandler handler = ^(IJSVGNode* node,
BOOL* allowChildNodes,
BOOL* stop) {
if([types containsIndex:node.type] == YES) {
[nodes addObject:node];
}
};
[self.class walkNodeTree:self
handler:handler];
return nodes.copy;
}
- (instancetype)parentNodeMatchingClass:(Class)class
{
IJSVGNode* parent = self.parentNode;
if([parent isKindOfClass:class] == YES) {
return parent;
}
return [parent parentNodeMatchingClass:class];
}
- (instancetype)rootNodeMatchingClass:(Class)class
{
IJSVGRootNode* rootNode = self.rootNode;
IJSVGNode* parentNode = self.parentNode;
IJSVGNode* foundNode = nil;
while(parentNode != nil) {
// break on root node or if its matching
if(parentNode == rootNode || parentNode == nil) {
break;
}
if([parentNode isKindOfClass:class] == YES) {
foundNode = parentNode;
}
parentNode = parentNode.parentNode;
}
return foundNode;
}
@end
@@ -1,41 +0,0 @@
//
// IJSVGPath.h
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGNode.h>
#import <IJSVG/IJSVGColorNode.h>
#import <Foundation/Foundation.h>
@class IJSVGGroup;
typedef NS_ENUM(NSInteger, IJSVGPrimitivePathType) {
kIJSVGPrimitivePathTypePath,
kIJSVGPrimitivePathTypeRect,
kIJSVGPrimitivePathTypePolygon,
kIJSVGPrimitivePathTypePolyLine,
kIJSVGPrimitivePathTypeCircle,
kIJSVGPrimitivePathTypeEllipse,
kIJSVGPrimitivePathTypeLine
};
@interface IJSVGPath : IJSVGNode {
}
@property (nonatomic, assign) IJSVGPrimitivePathType primitiveType;
@property (nonatomic, assign) CGMutablePathRef path;
@property (nonatomic, assign) CGPoint lastControlPoint;
@property (nonatomic, readonly) CGRect controlPointBoundingBox;
@property (nonatomic, readonly) CGRect pathBoundingBox;
+ (void)recursivelyAddPathedNodesPaths:(NSArray<IJSVGNode*>*)nodes
transform:(CGAffineTransform)transform
toPath:(CGMutablePathRef)mutPath;
- (void)close;
- (NSPoint)currentPoint;
@end
@@ -1,141 +0,0 @@
//
// IJSVGPath.m
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGPath.h>
#import <IJSVG/IJSVGGroup.h>
@implementation IJSVGPath
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributeFill];
[storage setBit:IJSVGNodeAttributeFillOpacity];
[storage setBit:IJSVGNodeAttributeFillRule];
[storage setBit:IJSVGNodeAttributeX];
[storage setBit:IJSVGNodeAttributeY];
[storage setBit:IJSVGNodeAttributeStroke];
[storage setBit:IJSVGNodeAttributeStrokeWidth];
[storage setBit:IJSVGNodeAttributeStrokeOpacity];
[storage setBit:IJSVGNodeAttributeStrokeLineCap];
[storage setBit:IJSVGNodeAttributeStrokeLineJoin];
[storage setBit:IJSVGNodeAttributeStrokeDashArray];
[storage setBit:IJSVGNodeAttributeStrokeDashOffset];
[storage setBit:IJSVGNodeAttributeClipPath];
[storage setBit:IJSVGNodeAttributeMask];
[storage setBit:IJSVGNodeAttributeOpacity];
[storage setBit:IJSVGNodeAttributeBlendMode];
return storage;
}
+ (void)recursivelyAddPathedNodesPaths:(NSArray<IJSVGNode*>*)nodes
transform:(CGAffineTransform)transform
toPath:(CGMutablePathRef)mutPath
{
for(IJSVGPath* pathNode in nodes) {
// just a pathed node
if([pathNode matchesTraits:IJSVGNodeTraitPathed] == YES) {
CGPathAddPath(mutPath, &transform, pathNode.path);
continue;
}
// could be a use group
if([pathNode isKindOfClass:IJSVGGroup.class] == YES) {
IJSVGGroup* useGroup = (IJSVGGroup*)pathNode;
[self recursivelyAddPathedNodesPaths:useGroup.children
transform:transform
toPath:mutPath];
}
}
}
- (void)dealloc
{
if(_path != NULL) {
(void)CGPathRelease(_path), _path = NULL;
}
}
- (id)init
{
if((self = [super init]) != nil) {
_primitiveType = kIJSVGPrimitivePathTypePath;
_path = CGPathCreateMutable();
}
return self;
}
- (void)setDefaults
{
[self addTraits:IJSVGNodeTraitPathed];
}
- (CGRect)bounds
{
return self.pathBoundingBox;
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGPath* node = [super copyWithZone:zone];
node.path = _path;
return node;
}
- (void)setPath:(CGMutablePathRef)path
{
// this will automatically copy any path into a mutable path
// regardless of if it was a mutable path to begin with
if(_path != NULL) {
(void)CGPathRelease(_path), _path = NULL;
}
_path = CGPathCreateMutableCopy(path);
}
- (CGRect)pathBoundingBox
{
return CGPathGetBoundingBox(_path);
}
- (CGRect)controlPointBoundingBox
{
return CGPathGetPathBoundingBox(_path);
}
- (NSPoint)currentPoint
{
return CGPathGetCurrentPoint(_path);
}
- (void)close
{
CGPathCloseSubpath(_path);
}
#pragma mark Traits
- (void)computeTraits
{
if(self.stroke != nil) {
// by default we can just add this on
[self addTraits:IJSVGNodeTraitStroked];
// if we detect the stroke was a color, we need to check its alpha
// component to then remove the trait if its 0.f
if([self.stroke isKindOfClass:IJSVGColorNode.class] == YES) {
IJSVGColorNode* strokeColor = (IJSVGColorNode*)self.stroke;
if(strokeColor.color.alphaComponent == 0.f ||
strokeColor.isNoneOrTransparent == YES) {
[self removeTraits:IJSVGNodeTraitStroked];
}
}
}
}
@end
@@ -1,54 +0,0 @@
//
// IJSVGPattern.m
// IJSVGExample
//
// Created by Curtis Hard on 27/05/2016.
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGPattern.h>
#import <IJSVG/IJSVGUnitRect.h>
#import <IJSVG/IJSVGRootNode.h>
@implementation IJSVGPattern
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributePatternTransform];
[storage setBit:IJSVGNodeAttributePatternUnits];
[storage setBit:IJSVGNodeAttributePatternContentUnits];
[storage setBit:IJSVGNodeAttributeViewBox];
return storage;
}
- (instancetype)init
{
if((self = [super init]) != nil) {
self.viewBox = nil;
self.viewBoxAlignment = IJSVGViewBoxAlignmentXMidYMid;
self.viewBoxMeetOrSlice = IJSVGViewBoxMeetOrSliceMeet;
}
return self;
}
- (IJSVGUnitType)contentUnitsWithReferencingNodeBounds:(CGRect*)bounds
{
// as far as I can tell, units are inherited, so we need to go to the root
// level pattern if there is one (we could be nested) and if so, return those
// units as the actual units, but the bounds are based on the units of current
// units defined on this node... wtf?!
IJSVGNode* node = nil;
IJSVGPattern* parentPattern = [self rootNodeMatchingClass:self.class];
IJSVGUnitType units = [self contentUnitsWithReferencingNode:&node];
if(units == IJSVGUnitUserSpaceOnUse) {
*bounds = node.rootNode.bounds;
} else {
*bounds = node.parentNode.bounds;
}
return parentPattern ? [parentPattern contentUnitsWithReferencingNode:&node] : units;
}
@end
@@ -1,24 +0,0 @@
//
// IJSVGRadialGradient.h
// IJSVGExample
//
// Created by Curtis Hard on 03/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGGradient.h>
#import <Foundation/Foundation.h>
@interface IJSVGRadialGradient : IJSVGGradient
@property (nonatomic, strong) IJSVGUnitLength* cx;
@property (nonatomic, strong) IJSVGUnitLength* cy;
@property (nonatomic, strong) IJSVGUnitLength* fx;
@property (nonatomic, strong) IJSVGUnitLength* fy;
@property (nonatomic, strong) IJSVGUnitLength* fr;
@property (nonatomic, strong) IJSVGUnitLength* r;
+ (void)parseGradient:(NSXMLElement*)element
gradient:(IJSVGRadialGradient*)gradient;
@end
@@ -1,153 +0,0 @@
//
// IJSVGRadialGradient.m
// IJSVGExample
//
// Created by Curtis Hard on 03/09/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGRadialGradient.h>
#import <IJSVG/IJSVGParser.h>
@implementation IJSVGRadialGradient
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributeFX];
[storage setBit:IJSVGNodeAttributeFY];
[storage setBit:IJSVGNodeAttributeFR];
[storage setBit:IJSVGNodeAttributeCX];
[storage setBit:IJSVGNodeAttributeCY];
[storage setBit:IJSVGNodeAttributeR];
return storage;
}
- (id)copyWithZone:(NSZone*)zone
{
IJSVGRadialGradient* grad = [super copyWithZone:zone];
grad.fx = _fx;
grad.fy = _fy;
grad.fr = _fr;
grad.cx = _cx;
grad.cy = _cy;
grad.r = _r;
return grad;
}
+ (void)parseGradient:(NSXMLElement*)element
gradient:(IJSVGRadialGradient*)gradient
{
// cx defaults to 50% if not specified
NSDictionary* kv = @{
IJSVGAttributeCX : @"cx",
IJSVGAttributeCY : @"cy",
IJSVGAttributeR : @"r" };
for (NSString* key in kv.allKeys) {
NSString* str = [element attributeForName:key].stringValue;
IJSVGUnitLength* unit = nil;
if(str != nil) {
unit = [IJSVGUnitLength unitWithString:str
fromUnitType:gradient.units];
} else {
// spec says to say 50% for missing property default
unit = [IJSVGUnitLength unitWithPercentageFloat:.5f];
}
[gradient setValue:unit
forKey:kv[key]];
}
// fr
NSString* fr = [element attributeForName:IJSVGAttributeFR].stringValue;
if(fr != nil) {
gradient.fr = [IJSVGUnitLength unitWithString:fr
fromUnitType:gradient.units];
} else {
gradient.fr = [IJSVGUnitLength unitWithPercentageFloat:0.f];
}
// fx and fy are the same unless specified otherwise
gradient.fx = gradient.cx;
gradient.fy = gradient.cy;
// needs fixing
NSString* fx = [element attributeForName:IJSVGAttributeFX].stringValue;
if(fx != nil) {
gradient.fx = [IJSVGUnitLength unitWithString:fx
fromUnitType:gradient.units];
}
NSString* fy = [element attributeForName:IJSVGAttributeFY].stringValue;
if(fy != nil) {
gradient.fy = [IJSVGUnitLength unitWithString:fy
fromUnitType:gradient.units];
}
NSArray* colors = nil;
CGFloat* colorStops = [self.class computeColorStops:gradient
colors:&colors];
gradient.locations = colorStops;
gradient.colors = colors;
gradient.numberOfStops = colors.count;
}
- (void)drawInContextRef:(CGContextRef)ctx
bounds:(NSRect)objectRect
transform:(CGAffineTransform)absoluteTransform
{
CGContextSaveGState(ctx);
BOOL inUserSpace = self.units == IJSVGUnitUserSpaceOnUse;
CGFloat radius = 0.f;
CGPoint startPoint = CGPointZero;
CGPoint gradientStartPoint = CGPointZero;
CGPoint gradientEndPoint = CGPointZero;
CGRect boundingBox = objectRect;
// compute size based on percentages
CGFloat width = 0.f;
CGFloat height = 0.f;
if(inUserSpace == YES) {
width = CGRectGetWidth(boundingBox);
height = CGRectGetHeight(boundingBox);
} else {
width = 1.f;
height = 1.f;
}
CGFloat cx = [_cx computeValue:width];
CGFloat cy = [_cy computeValue:height];
startPoint = CGPointMake(cx, cy);
CGFloat val = MIN(width, height);
radius = [_r computeValue:val];
CGFloat focalRadius = [_fr computeValue:val];
CGFloat fx = [_fx computeValue:width];
CGFloat fy = [_fy computeValue:height];
gradientStartPoint = startPoint;
gradientEndPoint = CGPointMake(fx, fy);
// transform if width or height is not equal - this can only
// be done if we are using objectBoundingBox
if(inUserSpace == YES) {
CGContextConcatCTM(ctx, absoluteTransform);
} else {
CGContextConcatCTM(ctx, CGAffineTransformMakeScale(CGRectGetWidth(boundingBox),
CGRectGetHeight(boundingBox)));
}
// concat the gradient transform into the context
IJSVGConcatTransformsCTM(ctx, self.transforms);
// draw the gradient
CGGradientDrawingOptions options = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGContextDrawRadialGradient(ctx, self.CGGradient,
gradientEndPoint, focalRadius,
gradientStartPoint,
radius, options);
CGContextRestoreGState(ctx);
}
@end
@@ -1,21 +0,0 @@
//
// IJSVGRootNode.h
// IJSVG
//
// Created by Curtis Hard on 28/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGUnitSize.h>
@interface IJSVGRootNode : IJSVGGroup
@property (nonatomic, assign) CGSize clientSize;
@property (nonatomic, assign) BOOL viewBoxContainsRelativeUnits;
@property (nonatomic, assign) IJSVGIntrinsicDimensions intrinsicDimensions;
@property (nonatomic, strong) IJSVGUnitSize* intrinsicSize;
@property (nonatomic, readonly) CGRect bounds;
@end
@@ -1,59 +0,0 @@
//
// IJSVGRootNode.m
// IJSVG
//
// Created by Curtis Hard on 28/03/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGRootNode.h>
#import <IJSVG/IJSVGThreadManager.h>
@implementation IJSVGRootNode
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributeX];
[storage setBit:IJSVGNodeAttributeY];
[storage setBit:IJSVGNodeAttributeWidth];
[storage setBit:IJSVGNodeAttributeHeight];
[storage setBit:IJSVGNodeAttributePreserveAspectRatio];
[storage setBit:IJSVGNodeAttributeViewBox];
return storage;
}
- (instancetype)init
{
if((self = [super init]) != nil) {
self.viewBoxAlignment = IJSVGViewBoxAlignmentXMidYMid;
self.viewBoxMeetOrSlice = IJSVGViewBoxMeetOrSliceMeet;
self.lineCapStyle = IJSVGLineCapStyleButt;
self.lineJoinStyle = IJSVGLineJoinStyleMiter;
self.strokeMiterLimit = [IJSVGUnitLength unitWithFloat:4.f];
self.strokeDashArrayCount = 0;
self.intrinsicDimensions = IJSVGIntrinsicDimensionNone;
}
return self;
}
- (CGRect)bounds
{
return [self.viewBox computeValue:CGSizeZero];
}
- (IJSVGRootNode *)rootNode
{
IJSVGRootNode* rootNode = nil;
if((rootNode = super.rootNode) == self) {
IJSVGNode* parent = self.parentNode;
if([parent isKindOfClass:IJSVGRootNode.class]) {
return (IJSVGRootNode*)parent;
}
return parent.rootNode;
}
return rootNode;
}
@end
@@ -1,13 +0,0 @@
//
// IJSVGStop.h
// IJSVG
//
// Created by Curtis Hard on 05/09/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
@interface IJSVGStop : IJSVGNode
@end
@@ -1,23 +0,0 @@
//
// IJSVGStop.m
// IJSVG
//
// Created by Curtis Hard on 05/09/2022.
// Copyright © 2022 Curtis Hard. All rights reserved.
//
#import "IJSVGStop.h"
@implementation IJSVGStop
+ (IJSVGBitFlags*)allowedAttributes
{
IJSVGBitFlags64* storage = [[IJSVGBitFlags64 alloc] init];
[storage addBits:[super allowedAttributes]];
[storage setBit:IJSVGNodeAttributeStopColor];
[storage setBit:IJSVGNodeAttributeStopOpacity];
[storage setBit:IJSVGNodeAttributeOffset];
return storage;
}
@end
@@ -1,20 +0,0 @@
//
// IJSVGText.m
// IJSVGExample
//
// Created by Curtis Hard on 01/01/2017.
// Copyright © 2017 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGText.h>
@implementation IJSVGText
- (IJSVGText*)copyWithZone:(NSZone*)zone
{
IJSVGText* node = [super copyWithZone:zone];
node.text = _text;
return node;
}
@end
@@ -1,42 +0,0 @@
//
// IJSVGCommandParser.h
// IJSVG
//
// Created by Curtis Hard on 23/12/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <Foundation/Foundation.h>
#include <xlocale.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, IJSVGPathDataSequence) {
kIJSVGPathDataSequenceTypeFloat,
kIJSVGPathDataSequenceTypeFlag
};
static NSUInteger const IJSVG_STREAM_FLOAT_BLOCK_SIZE = 50;
static NSUInteger const IJSVG_STREAM_CHAR_BLOCK_SIZE = 20;
typedef struct {
CGFloat* floatBuffer;
NSInteger floatCount;
char* charBuffer;
NSInteger charCount;
} IJSVGPathDataStream;
@interface IJSVGCommandParser : NSObject
IJSVGPathDataStream* IJSVGPathDataStreamCreateDefault(void);
IJSVGPathDataStream* IJSVGPathDataStreamCreate(NSUInteger floatCount, NSUInteger charCount);
void IJSVGPathDataStreamRelease(IJSVGPathDataStream* buffer);
IJSVGPathDataSequence* IJSVGPathDataSequenceCreateWithType(IJSVGPathDataSequence type, NSInteger length);
CGFloat* _Nullable IJSVGParsePathDataStreamSequence(const char* commandChars, NSInteger commandCharLength,
IJSVGPathDataStream* dataStream, IJSVGPathDataSequence* _Nullable sequence,
NSInteger commandLength, NSInteger* _Nullable commandsFound);
@end
NS_ASSUME_NONNULL_END
@@ -1,259 +0,0 @@
//
// IJSVGCommandParser.m
// IJSVG
//
// Created by Curtis Hard on 23/12/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGCommandParser.h>
@implementation IJSVGCommandParser
#define VALID_DIGIT(c) ((c ^ '0') <= 9)
IJSVGPathDataSequence* IJSVGPathDataSequenceCreateWithType(IJSVGPathDataSequence type, NSInteger length)
{
size_t size = sizeof(IJSVGPathDataSequence) * length;
IJSVGPathDataSequence* sequence = (IJSVGPathDataSequence*)malloc(size);
memset(sequence, (int)type, size);
return sequence;
};
// Datastreams work by setting up one stream of bits/memory per SVG
// so that each SVG has a reusable memory block to read and parse paths into.
// As its all linear and one SVG per thread, this saves alot of memory allocation
// calls as we simple can just reuse the buffer that already exists - this also
// allows us to specify the default allocation size, so when parsing viewBox we
// can simply allocate (4*sizeof(CGFloat)) instead of the default 50 slots
IJSVGPathDataStream* IJSVGPathDataStreamCreateDefault(void)
{
return IJSVGPathDataStreamCreate(IJSVG_STREAM_FLOAT_BLOCK_SIZE,
IJSVG_STREAM_CHAR_BLOCK_SIZE);
}
IJSVGPathDataStream* IJSVGPathDataStreamCreate(NSUInteger floatCount, NSUInteger charCount)
{
floatCount = floatCount ?: IJSVG_STREAM_FLOAT_BLOCK_SIZE;
charCount = charCount ?: IJSVG_STREAM_CHAR_BLOCK_SIZE;
IJSVGPathDataStream* buffer = (IJSVGPathDataStream*)malloc(sizeof(IJSVGPathDataStream));
buffer->floatBuffer = (CGFloat*)malloc(sizeof(CGFloat) * floatCount);
buffer->floatCount = floatCount;
buffer->charBuffer = (char*)calloc(sizeof(char), charCount);
buffer->charCount = charCount;
return buffer;
}
void IJSVGPathDataStreamRelease(IJSVGPathDataStream* buffer)
{
free(buffer->charBuffer);
free(buffer->floatBuffer);
free(buffer);
};
CGFloat* _Nullable IJSVGParsePathDataStreamSequence(const char* commandChars, NSInteger commandCharLength,
IJSVGPathDataStream* dataStream, IJSVGPathDataSequence* _Nullable sequence,
NSInteger commandLength, NSInteger* _Nullable commandsFound)
{
// if no command length, its completely pointless function,
// so just return null and set commandsFound to 0, if we dont
// we get a arithmetic error later on due to zero
if(commandLength == 0) {
*commandsFound = 0;
return NULL;
}
// default memory size for the float
NSInteger i = 0;
NSInteger counter = 0;
const char* cString = commandChars;
const char* validChars = "+-.";
// this is much faster then doing strlen as it doesnt need
// to compute the length
NSInteger sLength = commandCharLength;
NSInteger sLengthMinusOne = sLength - 1;
bool isDecimal = false;
int bufferCount = 0;
while (i < sLength) {
char currentChar = *cString++;
// work out next char
char nextChar = (char)0;
if(i < sLengthMinusOne) {
nextChar = *cString++;
cString--;
}
// check for validator
bool isE = (currentChar | ('E' ^ 'e')) == 'e';
bool isValid = VALID_DIGIT(currentChar) || isE || strchr(validChars, currentChar) != NULL;
// 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
bool nIsSign = nextChar == '-' || nextChar == '+';
bool wantsEnd = nIsSign || (nextChar == '.' && isDecimal);
// work our what the sequence is...
IJSVGPathDataSequence seq = kIJSVGPathDataSequenceTypeFloat;
if(sequence != NULL) {
seq = sequence[counter % commandLength];
}
// is a flag, consists of one value
// if its invalid, make sure we free the memory
// and return null - or hell breaks lose
if(isValid == YES && seq == kIJSVGPathDataSequenceTypeFlag) {
if(bufferCount != 0 || (currentChar != '0' && currentChar != '1')) {
return NULL;
}
wantsEnd = YES;
}
// could be a float like 5.334e-5 so dont break on the hypen
if(wantsEnd && isE && nIsSign) {
wantsEnd = false;
}
// make sure its a valid string
if(isValid == YES) {
// alloc the buffer if needed
if((bufferCount + 1) == dataStream->charCount) {
// realloc the buffer, incase the string is overflowing the
// allocated memory
dataStream->charCount += IJSVG_STREAM_CHAR_BLOCK_SIZE;
dataStream->charBuffer = (char*)realloc(dataStream->charBuffer,
sizeof(char) * dataStream->charCount);
}
// set the actual char against it
if(currentChar == '.') {
isDecimal = true;
}
dataStream->charBuffer[bufferCount++] = currentChar;
} else {
// if its an invalid char, just stop it
wantsEnd = true;
}
// 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(bufferCount != 0 && (wantsEnd || i == sLengthMinusOne)) {
// make sure there is enough room in the float pool
if((counter + 1) == dataStream->floatCount) {
dataStream->floatCount += IJSVG_STREAM_FLOAT_BLOCK_SIZE;
dataStream->floatBuffer = (CGFloat*)realloc(dataStream->floatBuffer,
sizeof(CGFloat) * dataStream->floatCount);
}
// add the float - for performance reasons, we can simply set the
// null value of the end of the string instead of nulling out
// with memset \0 - huzzah!
dataStream->charBuffer[bufferCount] = '\0';
// lets check to make the buffer actually has a valid float, and
// not either just a sign or white space or some random char.
if(strlen(dataStream->charBuffer) == 1 && !VALID_DIGIT(dataStream->charBuffer[0])) {
isDecimal = false;
bufferCount = 0;
continue;
}
// actually add the float into the buffer
dataStream->floatBuffer[counter++] = IJSVGParseFloat(dataStream->charBuffer);
// reset
isDecimal = false;
bufferCount = 0;
}
i++;
}
// set commands found - only if there is one
if(commandsFound != NULL) {
*commandsFound = (NSInteger)round(counter / commandLength);
}
// allocate the new buffer from memory
CGFloat* floats = (CGFloat*)malloc(sizeof(CGFloat) * counter);
memcpy(floats, dataStream->floatBuffer, counter * sizeof(CGFloat));
// return the floats just set into the memory
return floats;
}
// this method is finely tuned to just handle the buffer
// that IJSVGParsePathDataSequence produces for each float
// it does not look or skip white space as the previous method
// handles this for us
// inspired and modified from http://www.leapsecond.com/tools/fast_atof.c
CGFloat IJSVGParseFloat(char* buffer)
{
int fraction;
double sign, value, scale;
// work out a sign, if any, might not be, who knows
sign = 1.f;
if(*buffer == '-') {
sign = -1.f;
buffer += 1;
} else if(*buffer == '+') {
buffer += 1;
}
// get numbers before decimal point or exponent
for (value = 0.f; VALID_DIGIT(*buffer); buffer += 1) {
value = value * 10.f + (*buffer - '0');
}
// get digits after decimal point
if(*buffer == '.') {
double pow10 = 10.f;
buffer += 1;
while (VALID_DIGIT(*buffer)) {
value += (*buffer - '0') / pow10;
pow10 *= 10.f;
buffer += 1;
}
}
// handle exponent
fraction = 0;
scale = 1.f;
if((*buffer | ('E' ^ 'e')) == 'e') {
unsigned int exponent;
buffer += 1;
if(*buffer == '-') {
fraction = 1;
buffer += 1;
} else if(*buffer == '+') {
buffer += 1;
}
for (exponent = 0; VALID_DIGIT(*buffer); buffer += 1) {
exponent = exponent * 10 + (*buffer - '0');
}
if(exponent > 308) {
exponent = 308;
}
while (exponent >= 50) {
scale *= 1E50;
exponent -= 50;
}
while (exponent >= 8) {
scale *= 1E8;
exponent -= 8;
}
while (exponent > 0) {
scale *= 10.f;
exponent -= 1;
}
}
// make sure we cast this to a CGFloat before return
return (CGFloat)(sign * (fraction ? (value / scale) : (value * scale)));
}
@end
@@ -1,149 +0,0 @@
//
// IJSVGParser.h
// IconJar
//
// Created by Curtis Hard on 30/08/2014.
// Copyright (c) 2014 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGColor.h>
#import <IJSVG/IJSVGCommand.h>
#import <IJSVG/IJSVGError.h>
#import <IJSVG/IJSVGForeignObject.h>
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGColorNode.h>
#import <IJSVG/IJSVGStop.h>
#import <IJSVG/IJSVGImage.h>
#import <IJSVG/IJSVGLinearGradient.h>
#import <IJSVG/IJSVGPath.h>
#import <IJSVG/IJSVGPattern.h>
#import <IJSVG/IJSVGMask.h>
#import <IJSVG/IJSVGClipPath.h>
#import <IJSVG/IJSVGRadialGradient.h>
#import <IJSVG/IJSVGStyleSheet.h>
#import <IJSVG/IJSVGText.h>
#import <IJSVG/IJSVGTransform.h>
#import <IJSVG/IJSVGUnitRect.h>
#import <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGFilter.h>
#import <IJSVG/IJSVGFilterEffect.h>
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
typedef void (^IJSVGNodeParserPostProcessBlock)(void);
extern NSString* const IJSVGStringObjectBoundingBox;
extern NSString* const IJSVGStringUserSpaceOnUse;
extern NSString* const IJSVGStringNone;
extern NSString* const IJSVGStringRound;
extern NSString* const IJSVGStringSquare;
extern NSString* const IJSVGStringBevel;
extern NSString* const IJSVGStringButt;
extern NSString* const IJSVGStringMiter;
extern NSString* const IJSVGStringInherit;
extern NSString* const IJSVGStringEvenOdd;
extern NSString* const IJSVGAttributeVersion;
extern NSString* const IJSVGAttributeXMLNS;
extern NSString* const IJSVGAttributeXMLNSXlink;
extern NSString* const IJSVGAttributeViewBox;
extern NSString* const IJSVGAttributePreserveAspectRatio;
extern NSString* const IJSVGAttributeID;
extern NSString* const IJSVGAttributeClass;
extern NSString* const IJSVGAttributeX;
extern NSString* const IJSVGAttributeY;
extern NSString* const IJSVGAttributeWidth;
extern NSString* const IJSVGAttributeHeight;
extern NSString* const IJSVGAttributeOpacity;
extern NSString* const IJSVGAttributeStrokeOpacity;
extern NSString* const IJSVGAttributeStrokeWidth;
extern NSString* const IJSVGAttributeStrokeDashOffset;
extern NSString* const IJSVGAttributeFillOpacity;
extern NSString* const IJSVGAttributeClipPath;
extern NSString* const IJSVGAttributeClipPathUnits;
extern NSString* const IJSVGAttributeClipRule;
extern NSString* const IJSVGAttributeMask;
extern NSString* const IJSVGAttributeGradientUnits;
extern NSString* const IJSVGAttributePatternUnits;
extern NSString* const IJSVGAttributePatternContentUnits;
extern NSString* const IJSVGAttributePatternTransform;
extern NSString* const IJSVGAttributeMaskUnits;
extern NSString* const IJSVGAttributeMaskContentUnits;
extern NSString* const IJSVGAttributeTransform;
extern NSString* const IJSVGAttributeGradientTransform;
extern NSString* const IJSVGAttributeUnicode;
extern NSString* const IJSVGAttributeStrokeLineCap;
extern NSString* const IJSVGAttributeStrokeLineJoin;
extern NSString* const IJSVGAttributeStroke;
extern NSString* const IJSVGAttributeStrokeDashArray;
extern NSString* const IJSVGAttributeStrokeMiterLimit;
extern NSString* const IJSVGAttributeFill;
extern NSString* const IJSVGAttributeFillRule;
extern NSString* const IJSVGAttributeBlendMode;
extern NSString* const IJSVGAttributeDisplay;
extern NSString* const IJSVGAttributeStyle;
extern NSString* const IJSVGAttributeD;
extern NSString* const IJSVGAttributeXLink;
extern NSString* const IJSVGAttributeX1;
extern NSString* const IJSVGAttributeX2;
extern NSString* const IJSVGAttributeY1;
extern NSString* const IJSVGAttributeY2;
extern NSString* const IJSVGAttributeRX;
extern NSString* const IJSVGAttributeRY;
extern NSString* const IJSVGAttributeCX;
extern NSString* const IJSVGAttributeCY;
extern NSString* const IJSVGAttributeR;
extern NSString* const IJSVGAttributeFR;
extern NSString* const IJSVGAttributeFX;
extern NSString* const IJSVGAttributeFY;
extern NSString* const IJSVGAttributePoints;
extern NSString* const IJSVGAttributeOffset;
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;
extern NSString* const IJSVGAttributeMarker;
@class IJSVGParser;
@class IJSVGThreadManager;
typedef struct {
char* nodeType;
} IJSVGParserMallocBuffers;
IJSVGParserMallocBuffers* IJSVGParserMallocBuffersCreate(void);
void IJSVGParserMallocBuffersFree(IJSVGParserMallocBuffers* buffers);
@interface IJSVGParser : NSObject {
@private
NSXMLDocument* _document;
IJSVGPathDataStream* _commandDataStream;
IJSVGStyleSheet* _styleSheet;
NSMutableDictionary<NSString*, NSXMLElement*>* _detachedReferences;
IJSVGThreadManager* _threadManager;
CGSize _rootSize;
IJSVGRootNode* _rootNode;
}
@property (nonatomic, assign) CGSize defaultSize;
+ (BOOL)isDataSVG:(NSData*)data;
- (id)initWithSVGString:(NSString*)string
error:(NSError**)error;
- (id)initWithFileURL:(NSURL*)aURL
error:(NSError**)error;
+ (IJSVGParser*)parserForFileURL:(NSURL*)aURL;
+ (IJSVGParser*)parserForFileURL:(NSURL*)aURL
error:(NSError**)error;
- (IJSVGRootNode*)rootNodeWithSize:(CGSize)size;
@end
File diff suppressed because it is too large Load Diff
@@ -1,30 +0,0 @@
//
// IJSVGLayerTree.h
// IJSVGExample
//
// Created by Curtis Hard on 29/12/2016.
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGNode.h>
#import <IJSVG/IJSVGStyle.h>
#import <QuartzCore/QuartzCore.h>
@class IJSVGLayer;
@class IJSVGRootLayer;
@class IJSVGRootNode;
@interface IJSVGLayerTree : NSObject {
@private
NSMutableArray<NSValue*>* _viewPortStack;
}
@property (nonatomic, assign) CGRect viewBox;
@property (nonatomic, assign) CGFloat backingScale;
@property (nonatomic, strong) IJSVGStyle* style;
+ (CGPathRef)newPathFromStrokedShapeLayer:(IJSVGShapeLayer*)shapeLayer;
- (IJSVGRootLayer*)rootLayerForRootNode:(IJSVGRootNode*)rootNode;
@end
@@ -1,801 +0,0 @@
//
// IJSVGLayerTree.m
// IJSVGExample
//
// Created by Curtis Hard on 29/12/2016.
// Copyright © 2016 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVG.h>
#import <IJSVG/IJSVGGradient.h>
#import <IJSVG/IJSVGGradientLayer.h>
#import <IJSVG/IJSVGGroup.h>
#import <IJSVG/IJSVGGroupLayer.h>
#import <IJSVG/IJSVGRootLayer.h>
#import <IJSVG/IJSVGImage.h>
#import <IJSVG/IJSVGImageLayer.h>
#import <IJSVG/IJSVGLayer.h>
#import <IJSVG/IJSVGLayerTree.h>
#import <IJSVG/IJSVGPath.h>
#import <IJSVG/IJSVGPattern.h>
#import <IJSVG/IJSVGPatternLayer.h>
#import <IJSVG/IJSVGShapeLayer.h>
#import <IJSVG/IJSVGStrokeLayer.h>
#import <IJSVG/IJSVGText.h>
#import <IJSVG/IJSVGTransform.h>
#import <IJSVG/IJSVGUtils.h>
#import <IJSVG/IJSVGTransformLayer.h>
#import <IJSVG/IJSVGFilterLayer.h>
#import <IJSVG/IJSVGThreadManager.h>
@implementation IJSVGLayerTree
@synthesize style = _style;
- (id)init
{
if((self = [super init]) != nil) {
_viewPortStack = [[NSMutableArray alloc] init];
}
return self;
}
- (void)pushViewPort:(CGRect)viewPort
{
NSValue* value = [NSValue valueWithRect:NSRectFromCGRect(viewPort)];
[_viewPortStack addObject:value];
}
- (CGRect)viewPort
{
NSValue* value = _viewPortStack.lastObject;
return (CGRect)NSRectToCGRect(value.rectValue);
}
- (void)popViewPort
{
[_viewPortStack removeLastObject];
}
- (IJSVGRootLayer*)rootLayerForRootNode:(IJSVGRootNode*)rootNode
{
return (IJSVGRootLayer*)[self drawableLayerForNode:rootNode];
}
- (CALayer<IJSVGDrawableLayer>*)drawableLayerForNode:(IJSVGNode*)node
{
CALayer<IJSVGDrawableLayer>* layer = nil;
if([node isKindOfClass:IJSVGPath.class]) {
layer = [self drawableLayerForPathNode:(IJSVGPath*)node];
} else if([node isKindOfClass:IJSVGRootNode.class]) {
layer = [self drawableLayerForRootNode:(IJSVGRootNode*)node];
} else if([node isKindOfClass:IJSVGGroup.class]) {
layer = [self drawableLayerForGroupNode:(IJSVGGroup*)node];
} else if([node isKindOfClass:IJSVGImage.class]) {
layer = [self drawableLayerForImageNode:(IJSVGImage*)node];
}
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];
}
return layer;
}
- (CALayer<IJSVGDrawableLayer>*)drawableBasicLayerForPathNode:(IJSVGPath*)node
{
IJSVGShapeLayer* layer = [IJSVGShapeLayer layer];
layer.primitiveType = node.primitiveType;
if(CGPathIsEmpty(node.path) == NO) {
[self applyTransformedPathToShapeLayer:layer
fromNode:node];
}
layer.fillColor = nil;
layer.fillRule = [IJSVGUtils CGFillRuleForWindingRule:node.windingRule];
return layer;
}
- (void)applyTransformedPathToShapeLayer:(CALayer<IJSVGPathableLayer, IJSVGDrawableLayer>*)layer
fromNode:(IJSVGPath*)node
{
CGRect pathBounds = CGPathGetPathBoundingBox(node.path);
pathBounds = pathBounds;
// this will move the path back to a 0 origin as we actually set the origin
// with the layer instead (which we can then move around)
if(pathBounds.origin.x != 0.f || pathBounds.origin.y != 0.f) {
CGAffineTransform transform = CGAffineTransformMakeTranslation(-pathBounds.origin.x,
-pathBounds.origin.y);
CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(node.path, &transform);
layer.path = transformedPath;
CGPathRelease(transformedPath);
} else {
layer.path = node.path;
}
// note that we store the bounding box at this point, as it can be modified later
// with strokes, however, SVG spec defined bounding box is the path without strokes
// and without control points.
layer.frame = pathBounds;
layer.outerBoundingBox = pathBounds;
layer.boundingBox = pathBounds;
}
+ (CGPathRef)newPathFromStrokedShapeLayer:(IJSVGShapeLayer*)shapeLayer
{
CGLineCap lineCap = [IJSVGUtils CGLineCapForCALineCap:shapeLayer.lineCap];
CGLineJoin lineJoin = [IJSVGUtils CGLineJoinForCALineJoin:shapeLayer.lineJoin];
CGPathRef dashedPath = NULL;
if(shapeLayer.lineDashPattern != nil && shapeLayer.lineDashPattern.count != 0.f) {
NSUInteger count = shapeLayer.lineDashPattern.count;
CGFloat* lengths = (CGFloat*)malloc(sizeof(CGFloat)*count);
NSUInteger i = 0;
for(NSNumber* number in shapeLayer.lineDashPattern) {
lengths[i++] = (CGFloat)number.floatValue;
}
dashedPath = CGPathCreateCopyByDashingPath(shapeLayer.path, NULL,
shapeLayer.lineDashPhase,
lengths, count);
(void)free(lengths), lengths = NULL;
}
CGPathRef path = dashedPath ?: shapeLayer.path;
CGPathRef newPath = CGPathCreateCopyByStrokingPath(path, NULL, shapeLayer.lineWidth,
lineCap, lineJoin,
shapeLayer.miterLimit);
if(dashedPath != NULL) {
CGPathRelease(dashedPath);
}
return newPath;
}
- (NSColor*)colorForColor:(NSColor*)color
matchingTraits:(IJSVGColorUsageTraits)traits
{
return [_style.colors colorForColor:color
matchingTraits:traits];
}
- (CALayer<IJSVGDrawableLayer>*)drawableLayerForPathNode:(IJSVGPath*)node
{
IJSVGShapeLayer* layer = (IJSVGShapeLayer*)[self drawableBasicLayerForPathNode:node];
// color the shape
id fill = node.fill;
// stroke the path
IJSVGStrokeLayer* strokeLayer = nil;
CGFloat strokeWidthDifference = 0.f;
if([node matchesTraits:IJSVGNodeTraitStroked]) {
// its highly likely that the stroke layer is larger than the layer its being
// drawing into, so we need to increase the layer size to match or any groups
// that this is inside wont be the correct frame
strokeLayer = (IJSVGStrokeLayer*)[self drawableStrokedLayerForPathNode:node];
strokeWidthDifference = strokeLayer.lineWidth * .5f;
[layer setLayer:strokeLayer
forUsageType:IJSVGLayerUsageTypeStroke];
// make sure we update the bounding box as it has changed
layer.frame = CGRectInset(layer.frame,
-strokeWidthDifference,
-strokeWidthDifference);
layer.outerBoundingBox = layer.frame;
}
// generic fill color
CALayer<IJSVGDrawableLayer>* fillLayer = nil;
IJSVGLayerFillType fillType = [IJSVGLayer fillTypeForFill:fill];
IJSVGLayerUsageType fillUsageType = IJSVGLayerUsageTypeFillGeneric;
switch(fillType) {
// just a generic fill color
default:
case IJSVGLayerFillTypeColor: {
fillUsageType = IJSVGLayerUsageTypeFillGeneric;
IJSVGColorNode* colorNode = (IJSVGColorNode*)fill;
NSColor* color = colorNode.color ?: NSColor.blackColor;
// could be an overall replaced fillColor from the style
if(_style.fillColor != nil) {
color = _style.fillColor;
}
if(colorNode.isNoneOrTransparent == YES) {
color = nil;
} else {
// compute any color that may have been changed via the styles
NSColor* repColor = [self colorForColor:color
matchingTraits:IJSVGColorUsageTraitFill];
color = repColor ?: color;
}
// set the color against the layer we cant just use fill layer due to how
// the stroke is position within the frame, we have to create another
// layer to draw the colour into!
IJSVGShapeLayer* shape = (IJSVGShapeLayer*)[self drawableBasicLayerForPathNode:node];
shape.fillColor = color.CGColor;
CGRect shapeRect = shape.frame;
// reset back to 0, later on this will move in enough for the stroke
// to be half over the edge
shapeRect.origin.x = 0.f;
shapeRect.origin.y = 0.f;
shape.frame = shapeRect;
fillLayer = shape;
break;
}
// pattern fill
case IJSVGLayerFillTypePattern: {
fillUsageType = IJSVGLayerUsageTypeFillPattern;
fillLayer = [self drawablePatternLayerForPathNode:node
pattern:(IJSVGPattern*)node.fill
layer:layer];
break;
}
// gradient fill
case IJSVGLayerFillTypeGradient: {
fillUsageType = IJSVGLayerUsageTypeFillGradient;
fillLayer = [self drawableGradientLayerForPathNode:node
gradient:(IJSVGGradient*)node.fill
layer:layer];
break;
}
}
if(fillLayer != nil) {
// fill opacity is precalculated for its colour when the type is fillColor,
// for fills such as gradients and patterns, just reduce the opacity down
[layer addTraits:IJSVGLayerTraitFilled];
if(node.fillOpacity.value != 1.f) {
fillLayer.opacity = node.fillOpacity.value;
}
fillLayer.affineTransform = CGAffineTransformTranslate(fillLayer.affineTransform,
strokeWidthDifference,
strokeWidthDifference);
[layer addSublayer:fillLayer];
[layer setLayer:fillLayer
forUsageType:fillUsageType];
}
// stroke the path
if(strokeLayer != nil) {
// we need to work out what type of fill we need for the layer
[layer addTraits:IJSVGLayerTraitStroked];
IJSVGLayerFillType type = [IJSVGLayer fillTypeForFill:node.stroke];
switch(type) {
// patterns
case IJSVGLayerFillTypePattern: {
IJSVGPatternLayer* patternLayer = nil;
patternLayer = [self drawableBasicPatternLayerForLayer:strokeLayer
pattern:(IJSVGPattern*)node.stroke];
patternLayer.referencingLayer = layer;
// clip the drawing to a stroked path
CGPathRef path = [self.class newPathFromStrokedShapeLayer:strokeLayer];
patternLayer.clipPath = path;
CGPathRelease(path);
[layer setLayer:patternLayer
forUsageType:IJSVGLayerUsageTypeStrokePattern];
[layer addSublayer:patternLayer];
break;
}
// gradients
case IJSVGLayerFillTypeGradient: {
IJSVGGradientLayer* gradientLayer = nil;
gradientLayer = [self drawableBasicGradientLayerForLayer:strokeLayer
gradient:(IJSVGGradient*)node.stroke];
gradientLayer.referencingLayer = layer;
// clip the drawing to a stroked path
CGPathRef path = [self.class newPathFromStrokedShapeLayer:strokeLayer];
gradientLayer.clipPath = path;
CGPathRelease(path);
[layer setLayer:gradientLayer
forUsageType:IJSVGLayerUsageTypeStrokeGradient];
[layer addSublayer:gradientLayer];
break;
}
// generic
default: {
[layer setLayer:strokeLayer
forUsageType:IJSVGLayerUsageTypeStrokeGeneric];
[layer addSublayer:strokeLayer];
break;
}
}
}
return layer;
}
- (CALayer<IJSVGDrawableLayer>*)drawableStrokedLayerForPathNode:(IJSVGPath*)node
{
IJSVGStrokeLayer* layer = [IJSVGStrokeLayer layer];
[self applyTransformedPathToShapeLayer:layer
fromNode:node];
// reset the frame back to zero
CGRect frame = layer.frame;
// compute the color
NSColor* strokeColor = NSColor.blackColor;
if([node.stroke isKindOfClass:IJSVGColorNode.class]) {
IJSVGColorNode* colorNode = (IJSVGColorNode*)node.stroke;
strokeColor = colorNode.color;
}
// replacement colour
NSColor* repColor = [self colorForColor:strokeColor
matchingTraits:IJSVGColorUsageTraitStroke];
strokeColor = repColor ?: strokeColor;
// use the users overriding color instead
if(_style.strokeColor != nil) {
strokeColor = _style.strokeColor;
}
// set the color
layer.fillColor = nil;
layer.strokeColor = strokeColor.CGColor;
// work out line width
CGFloat lineWidth = [node.strokeWidth computeValue:frame.size.width];
if(_style.lineWidth != IJSVGInheritedFloatValue) {
lineWidth = _style.lineWidth;
}
// work out line styles
IJSVGLineCapStyle lineCapStyle = node.lineCapStyle;
IJSVGLineJoinStyle lineJoinStyle = node.lineJoinStyle;
CGFloat miterLimit = node.strokeMiterLimit.value;
// use anything declared on the style
if(_style.lineCapStyle != IJSVGLineCapStyleNone &&
_style.lineCapStyle != IJSVGLineCapStyleInherit) {
lineCapStyle = _style.lineCapStyle;
}
if(_style.lineJoinStyle != IJSVGLineJoinStyleNone &&
_style.lineJoinStyle != IJSVGLineJoinStyleInherit) {
lineJoinStyle = _style.lineJoinStyle;
}
// miter limit can be set via the style
if(_style.miterLimit != IJSVGInheritedFloatValue) {
miterLimit = _style.miterLimit;
}
// apply the properties
layer.lineWidth = lineWidth;
layer.lineCap = [IJSVGUtils CGLineCapForCapStyle:lineCapStyle];
layer.lineJoin = [IJSVGUtils CGLineJoinForJoinStyle:lineJoinStyle];
layer.miterLimit = miterLimit;
CGFloat strokeOpacity = 1.f;
if(node.strokeOpacity.value != 1.f) {
strokeOpacity = node.strokeOpacity.value;
}
layer.opacity = strokeOpacity;
// dashing
layer.lineDashPhase = node.strokeDashOffset.value;
if(node.strokeDashArrayCount != IJSVGInheritedIntegerValue) {
layer.lineDashPattern = node.lineDashPattern;
}
// lets resize the layer as we have computed everything at this point
CGFloat increase = layer.lineWidth / 2.f;
frame = CGRectInset(frame, -increase, -increase);
// now we know what to do, we need to transform the path
CGAffineTransform transform = CGAffineTransformMakeTranslation(increase, increase);
CGPathRef path = CGPathCreateCopyByTransformingPath(layer.path, &transform);
// make sure we reset this back to zero
layer.frame = (CGRect) {
.origin = CGPointZero,
.size = frame.size
};
layer.outerBoundingBox = layer.frame;
layer.path = path;
CGPathRelease(path);
return layer;
}
- (CALayer<IJSVGDrawableLayer>*)drawableLayerForRootNode:(IJSVGRootNode*)node
{
IJSVGRootLayer* layer = [IJSVGRootLayer layer];
layer.viewBox = node.viewBox;
layer.intrinsicSize = node.intrinsicSize;
layer.viewBoxAlignment = node.viewBoxAlignment;
layer.viewBoxMeetOrSlice = node.viewBoxMeetOrSlice;
// we are the top most SVG, not a nested one,
// we can simply use the viewport given to us
CGRect frame = CGRectZero;
frame = CGRectMake(node.x.value, node.y.value,
node.intrinsicSize.width.value,
node.intrinsicSize.height.value);
layer.frame = frame;
[self pushViewPort:layer.frame];
layer.sublayers = [self drawableLayersForNodes:node.children];
[self popViewPort];
return layer;
}
- (CALayer<IJSVGDrawableLayer>*)drawableLayerForGroupNode:(IJSVGGroup*)node
{
NSArray<CALayer<IJSVGDrawableLayer>*>* layers = [self drawableLayersForNodes:node.children];
return [self drawableLayerForGroupNode:node
sublayers:layers];
}
- (CALayer<IJSVGDrawableLayer>*)drawableLayerForGroupNode:(IJSVGNode*)node
sublayers:(NSArray<CALayer<IJSVGDrawableLayer>*>*)sublayers
{
IJSVGGroupLayer* layer = [IJSVGGroupLayer layer];
layer.boundingBox = [IJSVGLayer calculateFrameForSublayers:sublayers];
layer.outerBoundingBox = layer.boundingBox;
layer.sublayers = sublayers;
return layer;
}
- (NSArray<CALayer<IJSVGDrawableLayer>*>*)drawableLayersForNodes:(NSArray<IJSVGNode*>*)nodes
{
NSMutableArray<CALayer<IJSVGDrawableLayer>*>* layers = nil;
layers = [[NSMutableArray alloc] initWithCapacity:nodes.count];
for(IJSVGNode* node in nodes) {
CALayer<IJSVGDrawableLayer>* layer = [self drawableLayerForNode:node];
if(layer != nil) {
[layers addObject:layer];
}
}
return layers;
}
#pragma mark Gradients and Patterns
- (IJSVGGradientLayer*)drawableBasicGradientLayerForLayer:(CALayer<IJSVGDrawableLayer>*)layer
gradient:(IJSVGGradient*)gradient
{
// gradient fill
IJSVGGradientLayer* gradientLayer = [IJSVGGradientLayer layer];
gradientLayer.backingScaleFactor = _backingScale;
// lets copy the gradient incase there are any style changes
IJSVGColorUsageTraits traits = IJSVGColorUsageTraitGradientStop;
if(_style.colors.replacedColorCount != 0 &&
[_style.colors matchesReplacementTraits:traits] == YES) {
gradient = gradient.copy;
NSMutableArray* colors = nil;
colors = [[NSMutableArray alloc] initWithCapacity:gradient.numberOfStops];
for(NSColor* color in gradient.colors) {
NSColor* repColor = [self colorForColor:color
matchingTraits:traits];
NSColor* compColor = repColor ?: color;
[colors addObject:compColor];
}
gradient.colors = colors;
}
gradientLayer.gradient = gradient;
gradientLayer.frame = layer.bounds;
gradientLayer.viewBox = self.viewPort;
gradientLayer.opacity = layer.opacity;
[gradientLayer setNeedsDisplay];
return gradientLayer;
}
- (CALayer<IJSVGDrawableLayer>*)drawableGradientLayerForPathNode:(IJSVGPath*)node
gradient:(IJSVGGradient*)gradient
layer:(CALayer<IJSVGDrawableLayer>*)layer
{
// gradient fill
IJSVGGradientLayer* gradientLayer = [self drawableBasicGradientLayerForLayer:layer
gradient:gradient];
// we must clip the fill to the path that we are drawing in, its simply just a matter
// of asking the tree for a path based on the layer passed in, but then moving
// it back to our current coordinate space
gradientLayer.clipRule = layer.fillRule;
gradientLayer.clipPath = ((IJSVGShapeLayer*)layer).path;
return gradientLayer;
}
- (IJSVGPatternLayer*)drawableBasicPatternLayerForLayer:(CALayer<IJSVGDrawableLayer>*)layer
pattern:(IJSVGPattern*)pattern
{
// pattern fill
IJSVGPatternLayer* patternLayer = [IJSVGPatternLayer layer];
patternLayer.patternNode = pattern;
patternLayer.frame = (CGRect) {
.origin = CGPointZero,
.size = layer.outerBoundingBox.size
};
CALayer<IJSVGDrawableLayer>* patternFill = [self drawableLayerForNode:pattern];
patternFill.referencingLayer = patternLayer;
patternLayer.pattern = patternFill;
patternLayer.opacity = layer.opacity;
[patternLayer setNeedsDisplay];
return patternLayer;
}
- (CALayer<IJSVGDrawableLayer>*)drawablePatternLayerForPathNode:(IJSVGPath*)node
pattern:(IJSVGPattern*)pattern
layer:(CALayer<IJSVGDrawableLayer>*)layer
{
// pattern fill
IJSVGPatternLayer* patternLayer = [self drawableBasicPatternLayerForLayer:layer
pattern:pattern];
// we must clip the fill to the path that we are drawing in, its simply just a matter
// of asking the tree for a path based on the layer passed in, but then moving
// it back to our current coordinate space
patternLayer.clipRule = layer.fillRule;
patternLayer.clipPath = ((IJSVGShapeLayer*)layer).path;
[patternLayer setNeedsDisplay];
return patternLayer;
}
- (CALayer<IJSVGDrawableLayer>*)maskLayerFromNode:(IJSVGMask*)mask
referencingLayer:(CALayer<IJSVGDrawableLayer>*)layer
fromLayer:(CALayer<IJSVGDrawableLayer>*)fromLayer
{
IJSVGMask* maskNode = mask;
CALayer<IJSVGDrawableLayer>* maskLayer = nil;
maskLayer = fromLayer ?: (CALayer<IJSVGDrawableLayer>*)[self drawableLayerForNode:maskNode];
CGRect viewPort = maskNode.units == IJSVGUnitUserSpaceOnUse ? self.viewPort : layer.boundingBox;
CGFloat width = CGRectGetWidth(viewPort);
CGFloat height = CGRectGetHeight(viewPort);
CGRect rect = CGRectZero;
IJSVGUnitLength* xUnit = maskNode.x;
IJSVGUnitLength* yUnit = maskNode.y;
IJSVGUnitLength* widthUnit = maskNode.width;
IJSVGUnitLength* heightUnit = maskNode.height;
// infer the fact that object bounding box must be % values of
// the box its being drawn into
if(maskNode.units == IJSVGUnitObjectBoundingBox) {
xUnit = [xUnit lengthWithUnitType:IJSVGUnitLengthTypePercentage];
yUnit = [yUnit lengthWithUnitType:IJSVGUnitLengthTypePercentage];
widthUnit = [widthUnit lengthWithUnitType:IJSVGUnitLengthTypePercentage];
heightUnit = [heightUnit lengthWithUnitType:IJSVGUnitLengthTypePercentage];
}
// calculate the rect, rect is the clipping rect
rect.origin.x = [xUnit computeValue:width];
rect.origin.y = [yUnit computeValue:height];
rect.size.width = [widthUnit computeValue:width];
rect.size.height = [heightUnit computeValue:height];
// calculate the actual masking bounds, maskingBounds is
// is the rect that the final mask is transformed into
CGRect layerBounds = layer.innerBoundingBox;
CGRect maskBounds = maskLayer.outerBoundingBox;
CGRect maskingBounds = layerBounds;
rect.origin.x += layerBounds.origin.x;
rect.origin.y += layerBounds.origin.y;
maskingBounds.size.width = maskBounds.size.width;
maskingBounds.size.height = maskBounds.size.height;
CGAffineTransform userSpaceTransform = [IJSVGLayer userSpaceTransformForLayer:layer];
if(maskNode.contentUnits == IJSVGUnitUserSpaceOnUse) {
maskingBounds.origin.x += maskBounds.origin.x;
maskingBounds.origin.y += maskBounds.origin.y;
maskingBounds = CGRectApplyAffineTransform(maskingBounds, userSpaceTransform);
// we need to move all the layers back if they are into the userSpace
// coordinate system
for(CALayer<IJSVGDrawableLayer> *childLayer in maskLayer.sublayers) {
CGRect innerBoundingBox = childLayer.innerBoundingBox;
CGAffineTransform innerTransform = CGAffineTransformMakeTranslation(-innerBoundingBox.origin.x,
-innerBoundingBox.origin.y);
childLayer.frame = CGRectApplyAffineTransform(childLayer.frame, userSpaceTransform);
childLayer.frame = CGRectApplyAffineTransform(childLayer.frame, innerTransform);
}
}
if(maskNode.units == IJSVGUnitUserSpaceOnUse) {
rect = CGRectApplyAffineTransform(rect, userSpaceTransform);
}
maskLayer.maskingBoundingBox = maskingBounds;
maskLayer.maskingClippingRect = rect;
maskLayer.referencingLayer = layer;
return maskLayer;
}
- (NSArray<CALayer<IJSVGDrawableLayer>*>*)clipLayersFromNode:(IJSVGClipPath*)node
referencingLayer:(CALayer<IJSVGDrawableLayer>*)layer
fromLayer:(CALayer<IJSVGDrawableLayer>*)fromLayer
{
NSMutableArray<CALayer<IJSVGDrawableLayer>*>* layers = nil;
layers = [[NSMutableArray alloc] init];
IJSVGClipPath* refClipPath = node;
IJSVGGroupLayer* groupLayer = nil;
while(refClipPath != nil) {
groupLayer = (IJSVGGroupLayer*)[self drawableLayerForNode:refClipPath];
if(groupLayer != nil) {
groupLayer.referencingLayer = layer;
[layers addObject:groupLayer];
}
refClipPath = refClipPath.clipPath;
}
CGRect clippingRect = [IJSVGLayer calculateFrameForSublayers:layers];
CGAffineTransform userSpaceTransform = [IJSVGLayer userSpaceTransformForLayer:layer];
CGAffineTransform clippingTransform = CGAffineTransformIdentity;
if(node.contentUnits == IJSVGUnitUserSpaceOnUse) {
clippingRect = CGRectApplyAffineTransform(clippingRect, userSpaceTransform);
clippingTransform = userSpaceTransform;
}
CGAffineTransform ident = CGAffineTransformMakeTranslation(-CGRectGetMinX(clippingRect),
-CGRectGetMinY(clippingRect));
clippingTransform = CGAffineTransformConcat(clippingTransform, ident);
layer.clippingTransform = clippingTransform;
layer.clippingBoundingBox = clippingRect;
return layers;
}
- (CGPathRef)newClipPathFromNode:(IJSVGClipPath*)node
fromLayer:(CALayer<IJSVGDrawableLayer>*)layer
{
CGMutablePathRef mPath = CGPathCreateMutable();
CGAffineTransform transform = CGAffineTransformIdentity;
if(node.contentUnits == IJSVGUnitUserSpaceOnUse) {
transform = [IJSVGLayer userSpaceTransformForLayer:layer];
}
CGRect layerRect = layer.innerBoundingBox;
CGAffineTransform layerTransform = CGAffineTransformMakeTranslation(CGRectGetMinX(layerRect),
CGRectGetMinY(layerRect));
transform = CGAffineTransformConcat(transform, layerTransform);
IJSVGClipPath* clipPath = node;
while(clipPath != nil) {
[IJSVGPath recursivelyAddPathedNodesPaths:clipPath.children
transform:transform
toPath:mPath];
clipPath = clipPath.clipPath;
}
return mPath;
}
#pragma mark Defaults
- (void)applyDefaultsToLayer:(CALayer<IJSVGDrawableLayer>*)layer
fromNode:(IJSVGNode*)node
{
// mask the layer
if(node.mask != nil) {
layer.maskLayer = [self maskLayerFromNode:node.mask
referencingLayer:layer
fromLayer:nil];
}
// add the clip mask if any
if(node.clipPath != nil) {
IJSVGClipPath* clipPath = node.clipPath;
CGPathRef path = [self newClipPathFromNode:clipPath
fromLayer:layer];
IJSVGWindingRule clipRule = node.clipRule;
if(clipRule == IJSVGWindingRuleInherit) {
clipRule = clipPath.computedClipRule;
}
layer.clipPath = path;
layer.clipRule = [IJSVGUtils CGFillRuleForWindingRule:clipRule];
CGPathRelease(path);
}
// setup the opacity
CGFloat opacity = node.opacity.value;
if(opacity != 1.f) {
layer.opacity = opacity;
}
// Blending mode
if(node.blendMode != IJSVGBlendModeNormal) {
layer.blendingMode = (CGBlendMode)node.blendMode;
}
// Should this even be displayed?
if(node.shouldRender == NO) {
layer.hidden = YES;
}
}
#pragma mark Filters
- (CALayer<IJSVGDrawableLayer>*)applyFilter:(IJSVGFilter*)filter
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
fromNode:(IJSVGNode*)node
{
if(IJSVGThreadManager.currentManager.featureFlags.filters.enabled == NO ||
(filter == nil || filter.valid == NO)) {
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<IJSVGDrawableLayer>*)applyTransforms:(NSArray<IJSVGTransform*>*)transforms
toLayer:(CALayer<IJSVGDrawableLayer>*)layer
fromNode:(IJSVGNode*)node
{
// any x and y?
CGRect frame = layer.bounds;
CGFloat x = 0.f;
CGFloat y = 0.f;
if(layer.treatImplicitOriginAsTransform == YES) {
x = [node.x computeValue:frame.size.width];
y = [node.y computeValue:frame.size.height];
}
// no need to do anything if no transform, or x or y == 0
if(transforms.count == 0 && x == 0.f && y == 0.f) {
return layer;
}
// simply cascade all the transforms onto the identity
CGAffineTransform identity = CGAffineTransformIdentity;
if(x != 0.f || y != 0.f) {
identity = CGAffineTransformTranslate(identity, x, y);
}
// this used to be done with each transform being added to its own
// group layer, but we can simply use one and then apply
// the transforms in reverse order, has same outcome with less memory
IJSVGTransformLayer* parentLayer = [IJSVGTransformLayer layer];
for(IJSVGTransform* transform in transforms.reverseObjectEnumerator) {
identity = CGAffineTransformConcat(identity,
transform.CGAffineTransform);
}
parentLayer.affineTransform = identity;
[parentLayer addSublayer:layer];
parentLayer.outerBoundingBox = [IJSVGLayer calculateFrameForSublayers:parentLayer.sublayers];
return parentLayer;
}
#pragma mark To Refactor
- (IJSVGLayer*)drawableLayerForImageNode:(IJSVGImage*)image
{
IJSVGImageLayer* layer = [[IJSVGImageLayer alloc] initWithImage:image];
// make sure we set the width and height correctly,
// as this may not be exactly the same as the size of the
// given image
CGRect frame = layer.frame;
frame.size.width = ceilf(image.width.value);
frame.size.height = ceilf(image.height.value);
layer.frame = frame;
[layer setNeedsLayout];
return (IJSVGLayer*)layer;
}
@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)(void);
typedef NS_ENUM(NSInteger, IJSVGRenderQuality) {
kIJSVGRenderQualityFullResolution, // slowest to render
kIJSVGRenderQualityOptimized, // best of both worlds
kIJSVGRenderQualityLow // 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 <IJSVG/IJSVGRendering.h>
@implementation IJSVGRendering
@end
@@ -1,25 +0,0 @@
//
// IJSVGStyleList.h
// IconJar
//
// Created by Curtis Hard on 09/07/2019.
// Copyright © 2019 Curtis Hard. All rights reserved.
//
#import <IJSVG/IJSVGTraitedColorStorage.h>
#import <IJSVG/IJSVGNode.h>
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface IJSVGStyle : NSObject
@property (nonatomic, assign) IJSVGLineCapStyle lineCapStyle;
@property (nonatomic, assign) IJSVGLineJoinStyle lineJoinStyle;
@property (nonatomic, assign) CGFloat lineWidth;
@property (nonatomic, assign) CGFloat miterLimit;
@property (nonatomic, strong) IJSVGTraitedColorStorage* colors;
@property (nonatomic, strong) NSColor* fillColor;
@property (nonatomic, strong) NSColor* strokeColor;
@end

Some files were not shown because too many files have changed in this diff Show More