├── .gitignore
├── Demo
├── Icon-76.1.png
└── Icon-76.png
├── stampicon.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── LICENSE
├── README.md
└── StampIcon
├── main.swift
├── NSColor+Hex.swift
├── Stamper.swift
└── CGRectExtensions.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | xcuserdata
2 | TestIcons
3 |
--------------------------------------------------------------------------------
/Demo/Icon-76.1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorystiefel/stampicon/HEAD/Demo/Icon-76.1.png
--------------------------------------------------------------------------------
/Demo/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jorystiefel/stampicon/HEAD/Demo/Icon-76.png
--------------------------------------------------------------------------------
/stampicon.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 JLS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # stampicon
2 | Applies a text label to PNG icons. Stampicon will automatically size the text to fit inside the ribbon, but exceedingly long text on small icons may become unreadable.
3 |
4 | This tool differs from other similar tools in that it renders the icon using Core Graphics, rather than requiring a dependency such as ImageMagick.
5 |
6 | 
7 | 
8 |
9 | ### Usage
10 |
11 | `stampicon inputfile.png --output outputfile.png --text "text to overlay"`
12 |
13 | option | meaning
14 | --------------|--------------------
15 | --text | the text to overlay, required
16 | --output | the file to write, required
17 | --font | font family, e.g, defaults to "Helvetica"
18 | --textcolor | the text hex color in hex #FFFFFF, defaults to white
19 | --badgecolor | the hex color of the ribbon behind the text, defaults to red
20 |
21 | ### Thanks
22 |
23 | This utility employs [SwiftCGRectExtensions](https://github.com/nschum/SwiftCGRectExtensions) by @nschum and [UIColor-Hex-Swift](https://github.com/yeahdongcn/UIColor-Hex-Swift/blob/master/UIColorExtension.swift) by @yeahdongcn
24 |
--------------------------------------------------------------------------------
/StampIcon/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // StampIcon
4 | //
5 | // Created by Jory Stiefel on 9/12/15.
6 | // Copyright © 2015 Jory Stiefel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | func generateConfigFromArguments() -> StampConfig {
12 |
13 | var config = StampConfig()
14 |
15 | guard Process.arguments.count > 1 else {
16 | print("stampicon by Jory Stiefel")
17 | print("Usage: stampicon iconfile.png --text \"stamp text\" --output outputfile.png\n")
18 | exit(0)
19 | }
20 |
21 | for (idx, argument) in Process.arguments.dropFirst().enumerate() {
22 | guard idx > 0 else {
23 | config.inputFile = argument
24 | continue
25 | }
26 |
27 | let argIdx = idx + 1
28 |
29 | switch argument {
30 | case "--text":
31 | config.text = Process.arguments[argIdx+1]
32 |
33 | case "--output":
34 | config.outputFile = Process.arguments[argIdx+1]
35 |
36 | case "--font":
37 | config.fontName = Process.arguments[argIdx+1]
38 |
39 | case "--textcolor":
40 | config.textColor = NSColor(rgba: Process.arguments[argIdx+1])
41 |
42 | case "--badgecolor":
43 | config.badgeColor = NSColor(rgba: Process.arguments[argIdx+1])
44 |
45 | default:
46 | continue
47 | }
48 |
49 | }
50 |
51 | return config
52 | }
53 |
54 | var config = generateConfigFromArguments()
55 | let stamper = Stamper(config: config)
56 | stamper.processStamp()
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/StampIcon/NSColor+Hex.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColorExtension.swift
3 | // UIColor-Hex-Swift
4 | //
5 | // Created by R0CKSTAR on 6/13/14.
6 | // Copyright (c) 2014 P.D.Q. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | extension NSColor {
12 | public convenience init(rgba: String) {
13 | var red: CGFloat = 0.0
14 | var green: CGFloat = 0.0
15 | var blue: CGFloat = 0.0
16 | var alpha: CGFloat = 1.0
17 |
18 | if rgba.hasPrefix("#") {
19 | let index = rgba.startIndex.advancedBy(1)
20 | let hex = rgba.substringFromIndex(index)
21 | let scanner = NSScanner(string: hex)
22 | var hexValue: CUnsignedLongLong = 0
23 | if scanner.scanHexLongLong(&hexValue) {
24 | switch (hex.characters.count) {
25 | case 3:
26 | red = CGFloat((hexValue & 0xF00) >> 8) / 15.0
27 | green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0
28 | blue = CGFloat(hexValue & 0x00F) / 15.0
29 | case 4:
30 | red = CGFloat((hexValue & 0xF000) >> 12) / 15.0
31 | green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0
32 | blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0
33 | alpha = CGFloat(hexValue & 0x000F) / 15.0
34 | case 6:
35 | red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0
36 | green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0
37 | blue = CGFloat(hexValue & 0x0000FF) / 255.0
38 | case 8:
39 | red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0
40 | green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0
41 | blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0
42 | alpha = CGFloat(hexValue & 0x000000FF) / 255.0
43 | default:
44 | print("Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8")
45 | }
46 | } else {
47 | print("Scan hex error")
48 | }
49 | } else {
50 | print("Invalid RGB string, missing '#' as prefix")
51 | }
52 | self.init(red:red, green:green, blue:blue, alpha:alpha)
53 | }
54 | }
--------------------------------------------------------------------------------
/StampIcon/Stamper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BadgeImage.swift
3 | // StampIcon
4 | //
5 | // Created by Jory Stiefel on 9/12/15.
6 | // Copyright © 2015 Jory Stiefel. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | let kVerticalPadding: CGFloat = 3.0
12 | let kHorizontalPadding: CGFloat = 7.0
13 | let kMaxFontSize: CGFloat = 22.0
14 | let π = CGFloat(M_PI)
15 |
16 | struct StampConfig {
17 | var inputFile = ""
18 | var outputFile = ""
19 | var text = ""
20 | var textColor = NSColor.whiteColor()
21 | var badgeColor = NSColor(red: 0.98, green: 0.13, blue: 0.15, alpha: 1.0)
22 | var fontName = "Helvetica"
23 | }
24 |
25 | struct Stamper {
26 |
27 | let config: StampConfig
28 |
29 | func generateBadgeImage(maxWidth: CGFloat, maxHeight: CGFloat) -> NSImage {
30 |
31 | var textFontSize = kMaxFontSize
32 | var textFontAttributes = [
33 | NSFontAttributeName: NSFont(name: config.fontName, size: textFontSize)!,
34 | NSForegroundColorAttributeName: config.textColor
35 | ]
36 |
37 | let drawText = config.text
38 |
39 | let badgeImage = NSImage(size: NSSize(width:maxWidth, height:maxHeight), flipped: false) { rect -> Bool in
40 |
41 | let context = NSGraphicsContext.currentContext()?.CGContext
42 |
43 | var textSize = drawText.sizeWithAttributes(textFontAttributes)
44 | while (textSize.width > maxWidth * 0.7) {
45 | textFontSize -= 0.25
46 | textFontAttributes[NSFontAttributeName] = NSFont(name: self.config.fontName, size: textFontSize)!
47 | textSize = drawText.sizeWithAttributes(textFontAttributes)
48 | }
49 |
50 | var badgeRect = rect.center(CGSize(width: maxWidth * 2, height: textSize.height + kVerticalPadding))
51 |
52 | badgeRect.origin.x += (1.0/6) * maxWidth
53 | badgeRect.origin.y -= (1.0/6) * maxHeight
54 | let textRect = badgeRect.center(textSize)
55 |
56 | CGContextTranslateCTM(context, badgeRect.center.x, badgeRect.center.y)
57 | CGContextRotateCTM(context, π / 4)
58 | CGContextTranslateCTM(context, -badgeRect.center.x, -badgeRect.center.y)
59 |
60 | self.config.badgeColor.setFill()
61 | NSRectFill(badgeRect)
62 |
63 | drawText.drawInRect(textRect, withAttributes: textFontAttributes)
64 |
65 | return true
66 | }
67 |
68 | return badgeImage
69 | }
70 |
71 | func generateOutputImage() -> NSImage? {
72 | guard let inputImage = NSImage(contentsOfFile: self.config.inputFile) else {
73 | print("Could not read input file")
74 | exit(1)
75 | }
76 |
77 | let badgeImage = stamper.generateBadgeImage(inputImage.size.width, maxHeight: inputImage.size.height)
78 |
79 | let outputImage = NSImage(size: inputImage.size, flipped: false, drawingHandler: { (rect) -> Bool in
80 | inputImage.drawInRect(rect)
81 | badgeImage.drawInRect(rect)
82 | return true
83 | })
84 |
85 | return outputImage
86 | }
87 |
88 | func writeImageFile(image: NSImage, filename: String) {
89 |
90 | if let imageData = image.TIFFRepresentation,
91 | let pngRepresentation: NSData = NSBitmapImageRep(data: imageData)?.representationUsingType(.NSPNGFileType, properties: [:])
92 | {
93 | let success = pngRepresentation.writeToFile(filename, atomically: true)
94 | if !success {
95 | print("Error writing file")
96 | exit(1)
97 | }
98 | }
99 | }
100 |
101 | func processStamp() {
102 |
103 | if let outputImage = self.generateOutputImage() {
104 | writeImageFile(outputImage, filename: config.outputFile)
105 | } else {
106 | print("Could not generate output image")
107 | }
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/stampicon.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0C9E141F1BA4790200B6EF0E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9E141E1BA4790200B6EF0E /* main.swift */; };
11 | 0C9E14261BA4828900B6EF0E /* Stamper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9E14251BA4828900B6EF0E /* Stamper.swift */; };
12 | 0C9E14281BA4B73300B6EF0E /* NSColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9E14271BA4B73300B6EF0E /* NSColor+Hex.swift */; };
13 | 0C9E142A1BA4C6A000B6EF0E /* CGRectExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9E14291BA4C6A000B6EF0E /* CGRectExtensions.swift */; settings = {ASSET_TAGS = (); }; };
14 | /* End PBXBuildFile section */
15 |
16 | /* Begin PBXCopyFilesBuildPhase section */
17 | 0C9E14191BA4790200B6EF0E /* CopyFiles */ = {
18 | isa = PBXCopyFilesBuildPhase;
19 | buildActionMask = 2147483647;
20 | dstPath = /usr/share/man/man1/;
21 | dstSubfolderSpec = 0;
22 | files = (
23 | );
24 | runOnlyForDeploymentPostprocessing = 1;
25 | };
26 | /* End PBXCopyFilesBuildPhase section */
27 |
28 | /* Begin PBXFileReference section */
29 | 0C9E141B1BA4790200B6EF0E /* stampicon */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = stampicon; sourceTree = BUILT_PRODUCTS_DIR; };
30 | 0C9E141E1BA4790200B6EF0E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
31 | 0C9E14251BA4828900B6EF0E /* Stamper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stamper.swift; sourceTree = ""; };
32 | 0C9E14271BA4B73300B6EF0E /* NSColor+Hex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSColor+Hex.swift"; sourceTree = ""; };
33 | 0C9E14291BA4C6A000B6EF0E /* CGRectExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGRectExtensions.swift; sourceTree = ""; };
34 | /* End PBXFileReference section */
35 |
36 | /* Begin PBXFrameworksBuildPhase section */
37 | 0C9E14181BA4790200B6EF0E /* Frameworks */ = {
38 | isa = PBXFrameworksBuildPhase;
39 | buildActionMask = 2147483647;
40 | files = (
41 | );
42 | runOnlyForDeploymentPostprocessing = 0;
43 | };
44 | /* End PBXFrameworksBuildPhase section */
45 |
46 | /* Begin PBXGroup section */
47 | 0C9E14121BA4790200B6EF0E = {
48 | isa = PBXGroup;
49 | children = (
50 | 0C9E141D1BA4790200B6EF0E /* StampIcon */,
51 | 0C9E141C1BA4790200B6EF0E /* Products */,
52 | );
53 | sourceTree = "";
54 | };
55 | 0C9E141C1BA4790200B6EF0E /* Products */ = {
56 | isa = PBXGroup;
57 | children = (
58 | 0C9E141B1BA4790200B6EF0E /* stampicon */,
59 | );
60 | name = Products;
61 | sourceTree = "";
62 | };
63 | 0C9E141D1BA4790200B6EF0E /* StampIcon */ = {
64 | isa = PBXGroup;
65 | children = (
66 | 0C9E141E1BA4790200B6EF0E /* main.swift */,
67 | 0C9E14251BA4828900B6EF0E /* Stamper.swift */,
68 | 0C9E14291BA4C6A000B6EF0E /* CGRectExtensions.swift */,
69 | 0C9E14271BA4B73300B6EF0E /* NSColor+Hex.swift */,
70 | );
71 | path = StampIcon;
72 | sourceTree = "";
73 | };
74 | /* End PBXGroup section */
75 |
76 | /* Begin PBXNativeTarget section */
77 | 0C9E141A1BA4790200B6EF0E /* stampicon */ = {
78 | isa = PBXNativeTarget;
79 | buildConfigurationList = 0C9E14221BA4790200B6EF0E /* Build configuration list for PBXNativeTarget "stampicon" */;
80 | buildPhases = (
81 | 0C9E14171BA4790200B6EF0E /* Sources */,
82 | 0C9E14181BA4790200B6EF0E /* Frameworks */,
83 | 0C9E14191BA4790200B6EF0E /* CopyFiles */,
84 | );
85 | buildRules = (
86 | );
87 | dependencies = (
88 | );
89 | name = stampicon;
90 | productName = StampIcon;
91 | productReference = 0C9E141B1BA4790200B6EF0E /* stampicon */;
92 | productType = "com.apple.product-type.tool";
93 | };
94 | /* End PBXNativeTarget section */
95 |
96 | /* Begin PBXProject section */
97 | 0C9E14131BA4790200B6EF0E /* Project object */ = {
98 | isa = PBXProject;
99 | attributes = {
100 | LastSwiftUpdateCheck = 0700;
101 | LastUpgradeCheck = 0700;
102 | ORGANIZATIONNAME = "Jory Stiefel";
103 | TargetAttributes = {
104 | 0C9E141A1BA4790200B6EF0E = {
105 | CreatedOnToolsVersion = 7.0;
106 | };
107 | };
108 | };
109 | buildConfigurationList = 0C9E14161BA4790200B6EF0E /* Build configuration list for PBXProject "stampicon" */;
110 | compatibilityVersion = "Xcode 3.2";
111 | developmentRegion = English;
112 | hasScannedForEncodings = 0;
113 | knownRegions = (
114 | en,
115 | );
116 | mainGroup = 0C9E14121BA4790200B6EF0E;
117 | productRefGroup = 0C9E141C1BA4790200B6EF0E /* Products */;
118 | projectDirPath = "";
119 | projectRoot = "";
120 | targets = (
121 | 0C9E141A1BA4790200B6EF0E /* stampicon */,
122 | );
123 | };
124 | /* End PBXProject section */
125 |
126 | /* Begin PBXSourcesBuildPhase section */
127 | 0C9E14171BA4790200B6EF0E /* Sources */ = {
128 | isa = PBXSourcesBuildPhase;
129 | buildActionMask = 2147483647;
130 | files = (
131 | 0C9E141F1BA4790200B6EF0E /* main.swift in Sources */,
132 | 0C9E142A1BA4C6A000B6EF0E /* CGRectExtensions.swift in Sources */,
133 | 0C9E14281BA4B73300B6EF0E /* NSColor+Hex.swift in Sources */,
134 | 0C9E14261BA4828900B6EF0E /* Stamper.swift in Sources */,
135 | );
136 | runOnlyForDeploymentPostprocessing = 0;
137 | };
138 | /* End PBXSourcesBuildPhase section */
139 |
140 | /* Begin XCBuildConfiguration section */
141 | 0C9E14201BA4790200B6EF0E /* Debug */ = {
142 | isa = XCBuildConfiguration;
143 | buildSettings = {
144 | ALWAYS_SEARCH_USER_PATHS = NO;
145 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
146 | CLANG_CXX_LIBRARY = "libc++";
147 | CLANG_ENABLE_MODULES = YES;
148 | CLANG_ENABLE_OBJC_ARC = YES;
149 | CLANG_WARN_BOOL_CONVERSION = YES;
150 | CLANG_WARN_CONSTANT_CONVERSION = YES;
151 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
152 | CLANG_WARN_EMPTY_BODY = YES;
153 | CLANG_WARN_ENUM_CONVERSION = YES;
154 | CLANG_WARN_INT_CONVERSION = YES;
155 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
156 | CLANG_WARN_UNREACHABLE_CODE = YES;
157 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
158 | COPY_PHASE_STRIP = NO;
159 | DEBUG_INFORMATION_FORMAT = dwarf;
160 | ENABLE_STRICT_OBJC_MSGSEND = YES;
161 | ENABLE_TESTABILITY = YES;
162 | GCC_C_LANGUAGE_STANDARD = gnu99;
163 | GCC_DYNAMIC_NO_PIC = NO;
164 | GCC_NO_COMMON_BLOCKS = YES;
165 | GCC_OPTIMIZATION_LEVEL = 0;
166 | GCC_PREPROCESSOR_DEFINITIONS = (
167 | "DEBUG=1",
168 | "$(inherited)",
169 | );
170 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
171 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
172 | GCC_WARN_UNDECLARED_SELECTOR = YES;
173 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
174 | GCC_WARN_UNUSED_FUNCTION = YES;
175 | GCC_WARN_UNUSED_VARIABLE = YES;
176 | MACOSX_DEPLOYMENT_TARGET = 10.10;
177 | MTL_ENABLE_DEBUG_INFO = YES;
178 | ONLY_ACTIVE_ARCH = YES;
179 | SDKROOT = macosx;
180 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
181 | };
182 | name = Debug;
183 | };
184 | 0C9E14211BA4790200B6EF0E /* Release */ = {
185 | isa = XCBuildConfiguration;
186 | buildSettings = {
187 | ALWAYS_SEARCH_USER_PATHS = NO;
188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
189 | CLANG_CXX_LIBRARY = "libc++";
190 | CLANG_ENABLE_MODULES = YES;
191 | CLANG_ENABLE_OBJC_ARC = YES;
192 | CLANG_WARN_BOOL_CONVERSION = YES;
193 | CLANG_WARN_CONSTANT_CONVERSION = YES;
194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
195 | CLANG_WARN_EMPTY_BODY = YES;
196 | CLANG_WARN_ENUM_CONVERSION = YES;
197 | CLANG_WARN_INT_CONVERSION = YES;
198 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
199 | CLANG_WARN_UNREACHABLE_CODE = YES;
200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
201 | COPY_PHASE_STRIP = NO;
202 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
203 | ENABLE_NS_ASSERTIONS = NO;
204 | ENABLE_STRICT_OBJC_MSGSEND = YES;
205 | GCC_C_LANGUAGE_STANDARD = gnu99;
206 | GCC_NO_COMMON_BLOCKS = YES;
207 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
208 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
209 | GCC_WARN_UNDECLARED_SELECTOR = YES;
210 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
211 | GCC_WARN_UNUSED_FUNCTION = YES;
212 | GCC_WARN_UNUSED_VARIABLE = YES;
213 | MACOSX_DEPLOYMENT_TARGET = 10.10;
214 | MTL_ENABLE_DEBUG_INFO = NO;
215 | SDKROOT = macosx;
216 | };
217 | name = Release;
218 | };
219 | 0C9E14231BA4790200B6EF0E /* Debug */ = {
220 | isa = XCBuildConfiguration;
221 | buildSettings = {
222 | PRODUCT_NAME = stampicon;
223 | };
224 | name = Debug;
225 | };
226 | 0C9E14241BA4790200B6EF0E /* Release */ = {
227 | isa = XCBuildConfiguration;
228 | buildSettings = {
229 | PRODUCT_MODULE_NAME = stampicon;
230 | PRODUCT_NAME = stampicon;
231 | };
232 | name = Release;
233 | };
234 | /* End XCBuildConfiguration section */
235 |
236 | /* Begin XCConfigurationList section */
237 | 0C9E14161BA4790200B6EF0E /* Build configuration list for PBXProject "stampicon" */ = {
238 | isa = XCConfigurationList;
239 | buildConfigurations = (
240 | 0C9E14201BA4790200B6EF0E /* Debug */,
241 | 0C9E14211BA4790200B6EF0E /* Release */,
242 | );
243 | defaultConfigurationIsVisible = 0;
244 | defaultConfigurationName = Release;
245 | };
246 | 0C9E14221BA4790200B6EF0E /* Build configuration list for PBXNativeTarget "stampicon" */ = {
247 | isa = XCConfigurationList;
248 | buildConfigurations = (
249 | 0C9E14231BA4790200B6EF0E /* Debug */,
250 | 0C9E14241BA4790200B6EF0E /* Release */,
251 | );
252 | defaultConfigurationIsVisible = 0;
253 | defaultConfigurationName = Release;
254 | };
255 | /* End XCConfigurationList section */
256 | };
257 | rootObject = 0C9E14131BA4790200B6EF0E /* Project object */;
258 | }
259 |
--------------------------------------------------------------------------------
/StampIcon/CGRectExtensions.swift:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014 Nikolaj Schumacher
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | // this software and associated documentation files (the "Software"), to deal in
5 | // the Software without restriction, including without limitation the rights to
6 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | // the Software, and to permit persons to whom the Software is furnished to do so,
8 | // subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in all
11 | // copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 |
20 | import Foundation
21 |
22 | // MARK: CGPoint
23 |
24 | extension CGPoint {
25 |
26 | /// Creates a point with unnamed arguments.
27 | public init(_ x: CGFloat, _ y: CGFloat) {
28 | self.x = x
29 | self.y = y
30 | }
31 |
32 | /// Returns a copy with the x value changed.
33 | public func with(x x: CGFloat) -> CGPoint {
34 | return CGPoint(x: x, y: y)
35 | }
36 | /// Returns a copy with the y value changed.
37 | public func with(y y: CGFloat) -> CGPoint {
38 | return CGPoint(x: x, y: y)
39 | }
40 | }
41 |
42 | // MARK: CGSize
43 |
44 | extension CGSize {
45 |
46 | /// Creates a size with unnamed arguments.
47 | public init(_ width: CGFloat, _ height: CGFloat) {
48 | self.width = width
49 | self.height = height
50 | }
51 |
52 | /// Returns a copy with the width value changed.
53 | public func with(width width: CGFloat) -> CGSize {
54 | return CGSize(width: width, height: height)
55 | }
56 | /// Returns a copy with the height value changed.
57 | public func with(height height: CGFloat) -> CGSize {
58 | return CGSize(width: width, height: height)
59 | }
60 | }
61 |
62 | // MARK: CGRect
63 |
64 | extension CGRect {
65 |
66 | /// Creates a rect with unnamed arguments.
67 | public init(_ origin: CGPoint, _ size: CGSize) {
68 | self.origin = origin
69 | self.size = size
70 | }
71 |
72 | /// Creates a rect with unnamed arguments.
73 | public init(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) {
74 | self.origin = CGPoint(x: x, y: y)
75 | self.size = CGSize(width: width, height: height)
76 | }
77 |
78 | // MARK: access shortcuts
79 |
80 | /// Alias for origin.x.
81 | public var x: CGFloat {
82 | get {return origin.x}
83 | set {origin.x = newValue}
84 | }
85 | /// Alias for origin.y.
86 | public var y: CGFloat {
87 | get {return origin.y}
88 | set {origin.y = newValue}
89 | }
90 |
91 | /// Accesses origin.x + 0.5 * size.width.
92 | public var centerX: CGFloat {
93 | get {return x + width * 0.5}
94 | set {x = newValue - width * 0.5}
95 | }
96 | /// Accesses origin.y + 0.5 * size.height.
97 | public var centerY: CGFloat {
98 | get {return y + height * 0.5}
99 | set {y = newValue - height * 0.5}
100 | }
101 |
102 | // MARK: edges
103 |
104 | /// Alias for origin.x.
105 | public var left: CGFloat {
106 | get {return origin.x}
107 | set {origin.x = newValue}
108 | }
109 | /// Accesses origin.x + size.width.
110 | public var right: CGFloat {
111 | get {return x + width}
112 | set {x = newValue - width}
113 | }
114 |
115 | #if os(iOS)
116 | /// Alias for origin.y.
117 | public var top: CGFloat {
118 | get {return y}
119 | set {y = newValue}
120 | }
121 | /// Accesses origin.y + size.height.
122 | public var bottom: CGFloat {
123 | get {return y + height}
124 | set {y = newValue - height}
125 | }
126 | #else
127 | /// Accesses origin.y + size.height.
128 | public var top: CGFloat {
129 | get {return y + height}
130 | set {y = newValue - height}
131 | }
132 | /// Alias for origin.y.
133 | public var bottom: CGFloat {
134 | get {return y}
135 | set {y = newValue}
136 | }
137 | #endif
138 |
139 | // MARK: points
140 |
141 | /// Accesses the point at the top left corner.
142 | public var topLeft: CGPoint {
143 | get {return CGPoint(x: left, y: top)}
144 | set {left = newValue.x; top = newValue.y}
145 | }
146 | /// Accesses the point at the middle of the top edge.
147 | public var topCenter: CGPoint {
148 | get {return CGPoint(x: centerX, y: top)}
149 | set {centerX = newValue.x; top = newValue.y}
150 | }
151 | /// Accesses the point at the top right corner.
152 | public var topRight: CGPoint {
153 | get {return CGPoint(x: right, y: top)}
154 | set {right = newValue.x; top = newValue.y}
155 | }
156 |
157 | /// Accesses the point at the middle of the left edge.
158 | public var centerLeft: CGPoint {
159 | get {return CGPoint(x: left, y: centerY)}
160 | set {left = newValue.x; centerY = newValue.y}
161 | }
162 | /// Accesses the point at the center.
163 | public var center: CGPoint {
164 | get {return CGPoint(x: centerX, y: centerY)}
165 | set {centerX = newValue.x; centerY = newValue.y}
166 | }
167 | /// Accesses the point at the middle of the right edge.
168 | public var centerRight: CGPoint {
169 | get {return CGPoint(x: right, y: centerY)}
170 | set {right = newValue.x; centerY = newValue.y}
171 | }
172 |
173 | /// Accesses the point at the bottom left corner.
174 | public var bottomLeft: CGPoint {
175 | get {return CGPoint(x: left, y: bottom)}
176 | set {left = newValue.x; bottom = newValue.y}
177 | }
178 | /// Accesses the point at the middle of the bottom edge.
179 | public var bottomCenter: CGPoint {
180 | get {return CGPoint(x: centerX, y: bottom)}
181 | set {centerX = newValue.x; bottom = newValue.y}
182 | }
183 | /// Accesses the point at the bottom right corner.
184 | public var bottomRight: CGPoint {
185 | get {return CGPoint(x: right, y: bottom)}
186 | set {right = newValue.x; bottom = newValue.y}
187 | }
188 |
189 | // MARK: with
190 |
191 | /// Returns a copy with the origin value changed.
192 | public func with(origin origin: CGPoint) -> CGRect {
193 | return CGRect(origin: origin, size: size)
194 | }
195 | /// Returns a copy with the x and y values changed.
196 | public func with(x x: CGFloat, y: CGFloat) -> CGRect {
197 | return with(origin: CGPoint(x: x, y: y))
198 | }
199 | /// Returns a copy with the x value changed.
200 | public func with(x x: CGFloat) -> CGRect {
201 | return with(x: x, y: y)
202 | }
203 | /// Returns a copy with the y value changed.
204 | public func with(y y: CGFloat) -> CGRect {
205 | return with(x: x, y: y)
206 | }
207 |
208 | /// Returns a copy with the size value changed.
209 | public func with(size size: CGSize) -> CGRect {
210 | return CGRect(origin: origin, size: size)
211 | }
212 | /// Returns a copy with the width and height values changed.
213 | public func with(width width: CGFloat, height: CGFloat) -> CGRect {
214 | return with(size: CGSize(width: width, height: height))
215 | }
216 | /// Returns a copy with the width value changed.
217 | public func with(width width: CGFloat) -> CGRect {
218 | return with(width: width, height: height)
219 | }
220 | /// Returns a copy with the height value changed.
221 | public func with(height height: CGFloat) -> CGRect {
222 | return with(width: width, height: height)
223 | }
224 |
225 | /// Returns a copy with the x and width values changed.
226 | public func with(x x: CGFloat, width: CGFloat) -> CGRect {
227 | return CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height))
228 | }
229 | /// Returns a copy with the y and height values changed.
230 | public func with(y y: CGFloat, height: CGFloat) -> CGRect {
231 | return CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height))
232 | }
233 |
234 | // MARK: offset
235 |
236 | /// Returns a copy with the x and y values offset.
237 | public func offsetBy(dx: CGFloat, _ dy: CGFloat) -> CGRect {
238 | return with(x: x + dx, y: y + dy)
239 | }
240 | /// Returns a copy with the x value values offset.
241 | public func offsetBy(dx dx: CGFloat) -> CGRect {
242 | return with(x: x + dx)
243 | }
244 | /// Returns a copy with the y value values offset.
245 | public func offsetBy(dy dy: CGFloat) -> CGRect {
246 | return with(y: y + dy)
247 | }
248 | /// Returns a copy with the x and y values offset.
249 | public func offsetBy(by: CGSize) -> CGRect {
250 | return with(x: x + by.width, y: y + by.height)
251 | }
252 |
253 | /// Modifies the x and y values by offsetting.
254 | public mutating func offsetInPlace(dx: CGFloat, _ dy: CGFloat) {
255 | offsetInPlace(dx: dx, dy: dy)
256 | }
257 | /// Modifies the x value values by offsetting.
258 | public mutating func offsetInPlace(dx dx: CGFloat = 0) {
259 | x += dx
260 | }
261 | /// Modifies the y value values by offsetting.
262 | public mutating func offsetInPlace(dy dy: CGFloat = 0) {
263 | y += dy
264 | }
265 | /// Modifies the x and y values by offsetting.
266 | public mutating func offsetInPlace(by: CGSize) {
267 | offsetInPlace(dx: by.width, dy: by.height)
268 | }
269 |
270 | // MARK: inset
271 |
272 | /// Returns a copy inset on all edges by the same value.
273 | public func insetBy(by: CGFloat) -> CGRect {
274 | return insetBy(dx: by, dy: by)
275 | }
276 |
277 | /// Returns a copy inset on the left and right edges.
278 | public func insetBy(dx dx: CGFloat) -> CGRect {
279 | return with(x: x + dx, width: width - dx * 2)
280 | }
281 | /// Returns a copy inset on the top and bottom edges.
282 | public func insetBy(dy dy: CGFloat) -> CGRect {
283 | return with(y: y + dy, height: height - dy * 2)
284 | }
285 |
286 | /// Returns a copy inset on all edges by different values.
287 | public func insetBy(minX minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect {
288 | return CGRect(x: x + minX, y: y + minY, width: width - minX - maxX, height: height - minY - maxY)
289 | }
290 |
291 | /// Returns a copy inset on all edges by different values.
292 | public func insetBy(top top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> CGRect {
293 | #if os(iOS)
294 | return CGRect(x: x + left, y: y + top, width: width - right - left, height: height - top - bottom)
295 | #else
296 | return CGRect(x: x + left, y: y + bottom, width: width - right - left, height: height - top - bottom)
297 | #endif
298 | }
299 |
300 | /// Returns a copy inset on the top and left edges.
301 | public func insetBy(topLeft topLeft: CGSize) -> CGRect {
302 | return insetBy(top: topLeft.height, left: topLeft.width)
303 | }
304 | /// Returns a copy inset on the top and right edges.
305 | public func insetBy(topRight topRight: CGSize) -> CGRect {
306 | return insetBy(top: topRight.height, right: topRight.width)
307 | }
308 | /// Returns a copy inset on the bottom and left edges.
309 | public func insetBy(bottomLeft bottomLeft: CGSize) -> CGRect {
310 | return insetBy(bottom: bottomLeft.height, left: bottomLeft.width)
311 | }
312 | /// Returns a copy inset on the bottom and right edges.
313 | public func insetBy(bottomRight bottomRight: CGSize) -> CGRect {
314 | return insetBy(bottom: bottomRight.height, right: bottomRight.width)
315 | }
316 |
317 | /// Modifies all values by insetting all edges by the same value.
318 | public mutating func insetInPlace(by: CGFloat) {
319 | insetInPlace(dx: by, dy: by)
320 | }
321 |
322 | /// Modifies all values by insetting on the left and right edges.
323 | public mutating func insetInPlace(dx dx: CGFloat) {
324 | insetInPlace(dx: dx, dy: 0)
325 | }
326 | /// Modifies all values by insetting on the top and bottom edges.
327 | public mutating func insetInPlace(dy dy: CGFloat) {
328 | insetInPlace(dx: 0, dy: dy)
329 | }
330 |
331 | /// Modifies all values by insetting all edges by different value.
332 | public mutating func insetInPlace(minX minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) {
333 | self = insetBy(minX: minX, minY: minY, maxX: maxX, maxY: maxY)
334 | }
335 |
336 | /// Modifies all values by insetting all edges by different value.
337 | public mutating func insetInPlace(top top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
338 | self = insetBy(top: top, left: left, bottom: bottom, right: right)
339 | }
340 |
341 | /// Modifies all values by insetting the top and left edges.
342 | public mutating func insetInPlace(topLeft topLeft: CGSize) {
343 | self = insetBy(topLeft: topLeft)
344 | }
345 | /// Modifies all values by insetting the top and right edges.
346 | public mutating func insetInPlace(topRight topRight: CGSize) {
347 | self = insetBy(topRight: topRight)
348 | }
349 | /// Modifies all values by insetting the bottom and left edges.
350 | public mutating func insetInPlace(bottomLeft bottomLeft: CGSize) {
351 | self = insetBy(bottomLeft: bottomLeft)
352 | }
353 | /// Modifies all values by insetting the bottom and right edges.
354 | public mutating func insetInPlace(bottomRight bottomRight: CGSize) {
355 | self = insetBy(bottomRight: bottomRight)
356 | }
357 |
358 | // MARK: extending
359 |
360 | /// Returns a copy extended on all edges by different values.
361 | public func extendBy(dx dx: CGFloat, dy: CGFloat = 0) -> CGRect {
362 | return insetBy(dx: -dx, dy: -dy)
363 | }
364 | /// Returns a copy extended on the top and bottom edges.
365 | public func extendBy(dy dy: CGFloat) -> CGRect {
366 | return insetBy(dy: -dy)
367 | }
368 |
369 | /// Returns a copy extended on all edges by the same value.
370 | public func extendBy(by: CGFloat) -> CGRect {
371 | return insetBy(dx: -by, dy: -by)
372 | }
373 |
374 | /// Returns a copy extended on all edges by different values.
375 | public func extendBy(minX minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect {
376 | return insetBy(minX: -minX, minY: -minY, maxX: -maxX, maxY: -maxY)
377 | }
378 | /// Returns a copy extended on all edges by different values.
379 | public func extendBy(top top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> CGRect {
380 | return insetBy(top: -top, left: -left, bottom: -bottom, right: -right)
381 | }
382 |
383 | /// Modifies all values by extending the top and left edges.
384 | public func extendBy(topLeft topLeft: CGSize) -> CGRect {
385 | return extendBy(top: topLeft.height, left: topLeft.width)
386 | }
387 | /// Modifies all values by extending the top and right edges.
388 | public func extendBy(topRight topRight: CGSize) -> CGRect {
389 | return insetBy(top: -topRight.height, right: -topRight.width)
390 | }
391 | /// Modifies all values by extending the bottom and left edges.
392 | public func extendBy(bottomLeft bottomLeft: CGSize) -> CGRect {
393 | return insetBy(bottom: -bottomLeft.height, left: -bottomLeft.width)
394 | }
395 | /// Modifies all values by extending the bottom and right edges.
396 | public func extendBy(bottomRight bottomRight: CGSize) -> CGRect {
397 | return insetBy(bottom: -bottomRight.height, right: -bottomRight.width)
398 | }
399 |
400 | /// Modifies all values by extending all edges by different values.
401 | public mutating func extendInPlace(dx dx: CGFloat, dy: CGFloat = 0) {
402 | self = insetBy(dx: -dx, dy: -dy)
403 | }
404 | /// Modifies all values by extending the top and bottom edges.
405 | public mutating func extendInPlace(dy dy: CGFloat) {
406 | self = insetBy(dy: -dy)
407 | }
408 |
409 | /// Modifies all values by extending all edges by the same value.
410 | public mutating func extendInPlace(by: CGFloat) {
411 | self = insetBy(dx: -by, dy: -by)
412 | }
413 |
414 | /// Modifies all values by extending all edges by different values.
415 | public mutating func extendInPlace(minX minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) {
416 | self = insetBy(minX: -minX, minY: -minY, maxX: -maxX, maxY: -maxY)
417 | }
418 | /// Modifies all values by extending all edges by different values.
419 | public mutating func extendInPlace(top top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
420 | self = insetBy(top: -top, left: -left, bottom: -bottom, right: -right)
421 | }
422 |
423 | /// Modifies all values by extending the top and left edges.
424 | public mutating func extendInPlace(topLeft topLeft: CGSize) {
425 | self = extendBy(top: topLeft.height, left: topLeft.width)
426 | }
427 | /// Modifies all values by extending the top and right edges.
428 | public mutating func extendInPlace(topRight topRight: CGSize) {
429 | self = insetBy(top: -topRight.height, right: -topRight.width)
430 | }
431 | /// Modifies all values by extending the bottom and left edges.
432 | public mutating func extendInPlace(bottomLeft bottomLeft: CGSize) {
433 | self = insetBy(bottom: -bottomLeft.height, left: -bottomLeft.width)
434 | }
435 | /// Modifies all values by extending the bottom and right edges.
436 | public mutating func extendInPlace(bottomRight bottomRight: CGSize) {
437 | self = insetBy(bottom: -bottomRight.height, right: -bottomRight.width)
438 | }
439 |
440 | // MARK: sizes
441 |
442 | /// Returns a rect of the specified size centered in this rect.
443 | public func center(size: CGSize) -> CGRect {
444 | let dx = width - size.width
445 | let dy = height - size.height
446 | return CGRect(x: x + dx * 0.5, y: y + dy * 0.5, width: size.width, height: size.height)
447 | }
448 |
449 | /// Returns a rect of the specified size centered in this rect touching the specified edge.
450 | public func center(size: CGSize, alignTo edge: CGRectEdge) -> CGRect {
451 | return CGRect(origin: alignedOrigin(size, edge: edge), size: size)
452 | }
453 |
454 | private func alignedOrigin(size: CGSize, edge: CGRectEdge) -> CGPoint {
455 | let dx = width - size.width
456 | let dy = height - size.height
457 | switch edge {
458 | case .MinXEdge:
459 | return CGPoint(x: x, y: y + dy * 0.5)
460 | case .MinYEdge:
461 | return CGPoint(x: x + dx * 0.5, y: y)
462 | case .MaxXEdge:
463 | return CGPoint(x: x + dx, y: y + dy * 0.5)
464 | case .MaxYEdge:
465 | return CGPoint(x: x + dx * 0.5, y: y + dy)
466 | }
467 | }
468 |
469 | /// Returns a rect of the specified size centered in this rect touching the specified corner.
470 | public func align(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) -> CGRect {
471 | return CGRect(origin: alignedOrigin(size, corner: e1, e2), size: size)
472 | }
473 |
474 | private func alignedOrigin(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) -> CGPoint {
475 | let dx = width - size.width
476 | let dy = height - size.height
477 | switch (e1, e2) {
478 | case (.MinXEdge, .MinYEdge), (.MinYEdge, .MinXEdge):
479 | return CGPoint(x: x, y: y)
480 | case (.MaxXEdge, .MinYEdge), (.MinYEdge, .MaxXEdge):
481 | return CGPoint(x: x + dx, y: y)
482 | case (.MinXEdge, .MaxYEdge), (.MaxYEdge, .MinXEdge):
483 | return CGPoint(x: x, y: y + dy)
484 | case (.MaxXEdge, .MaxYEdge), (.MaxYEdge, .MaxXEdge):
485 | return CGPoint(x: x + dx, y: y + dy)
486 | default:
487 | preconditionFailure("Cannot align to this combination of edges")
488 | }
489 | }
490 |
491 | /// Modifies all values by setting the size while centering the rect.
492 | public mutating func centerInPlace(size: CGSize) {
493 | self = center(size)
494 | }
495 |
496 | /// Modifies all values by setting the size while centering the rect touching the specified edge.
497 | public mutating func centerInPlace(size: CGSize, alignTo edge: CGRectEdge) {
498 | self = center(size, alignTo: edge)
499 | }
500 |
501 | /// Modifies all values by setting the size while centering the rect touching the specified corner.
502 | public mutating func alignInPlace(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) {
503 | self = align(size, corner: e1, e2)
504 | }
505 | }
506 |
507 | // MARK: transform
508 |
509 | extension CGAffineTransform: Equatable {
510 | }
511 |
512 | public func ==(t1: CGAffineTransform, t2: CGAffineTransform) -> Bool {
513 | return CGAffineTransformEqualToTransform(t1, t2)
514 | }
515 |
516 | extension CGAffineTransform: CustomDebugStringConvertible {
517 | public var debugDescription: String {
518 | return "(\(a),\(b),\(c),\(d),\(tx),\(ty))"
519 | }
520 | }
521 |
522 | // MARK: operators
523 |
524 | /// Returns a point by adding the coordinates of another point.
525 | public func +(p1: CGPoint, p2: CGPoint) -> CGPoint {
526 | return CGPoint(x: p1.x + p2.x, y: p1.y + p2.y)
527 | }
528 | /// Modifies the x and y values by adding the coordinates of another point.
529 | public func +=(inout p1: CGPoint, p2: CGPoint) {
530 | p1.x += p2.x
531 | p1.y += p2.y
532 | }
533 | /// Returns a point by subtracting the coordinates of another point.
534 | public func -(p1: CGPoint, p2: CGPoint) -> CGPoint {
535 | return CGPoint(x: p1.x - p2.x, y: p1.y - p2.y)
536 | }
537 | /// Modifies the x and y values by subtracting the coordinates of another points.
538 | public func -=(inout p1: CGPoint, p2: CGPoint) {
539 | p1.x -= p2.x
540 | p1.y -= p2.y
541 | }
542 |
543 | /// Returns a point by adding a size to the coordinates.
544 | public func +(point: CGPoint, size: CGSize) -> CGPoint {
545 | return CGPoint(x: point.x + size.width, y: point.y + size.height)
546 | }
547 | /// Modifies the x and y values by adding a size to the coordinates.
548 | public func +=(inout point: CGPoint, size: CGSize) {
549 | point.x += size.width
550 | point.y += size.height
551 | }
552 | /// Returns a point by subtracting a size from the coordinates.
553 | public func -(point: CGPoint, size: CGSize) -> CGPoint {
554 | return CGPoint(x: point.x - size.width, y: point.y - size.height)
555 | }
556 | /// Modifies the x and y values by subtracting a size from the coordinates.
557 | public func -=(inout point: CGPoint, size: CGSize) {
558 | point.x -= size.width
559 | point.y -= size.height
560 | }
561 |
562 | /// Returns a point by adding a tuple to the coordinates.
563 | public func +(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint {
564 | return CGPoint(x: point.x + tuple.0, y: point.y + tuple.1)
565 | }
566 | /// Modifies the x and y values by adding a tuple to the coordinates.
567 | public func +=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) {
568 | point.x += tuple.0
569 | point.y += tuple.1
570 | }
571 | /// Returns a point by subtracting a tuple from the coordinates.
572 | public func -(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint {
573 | return CGPoint(x: point.x - tuple.0, y: point.y - tuple.1)
574 | }
575 | /// Modifies the x and y values by subtracting a tuple from the coordinates.
576 | public func -=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) {
577 | point.x -= tuple.0
578 | point.y -= tuple.1
579 | }
580 | /// Returns a point by multiplying the coordinates with a value.
581 | public func *(point: CGPoint, factor: CGFloat) -> CGPoint {
582 | return CGPoint(x: point.x * factor, y: point.y * factor)
583 | }
584 | /// Modifies the x and y values by multiplying the coordinates with a value.
585 | public func *=(inout point: CGPoint, factor: CGFloat) {
586 | point.x *= factor
587 | point.y *= factor
588 | }
589 | /// Returns a point by multiplying the coordinates with a tuple.
590 | public func *(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint {
591 | return CGPoint(x: point.x * tuple.0, y: point.y * tuple.1)
592 | }
593 | /// Modifies the x and y values by multiplying the coordinates with a tuple.
594 | public func *=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) {
595 | point.x *= tuple.0
596 | point.y *= tuple.1
597 | }
598 | /// Returns a point by dividing the coordinates by a tuple.
599 | public func /(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint {
600 | return CGPoint(x: point.x / tuple.0, y: point.y / tuple.1)
601 | }
602 | /// Modifies the x and y values by dividing the coordinates by a tuple.
603 | public func /=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) {
604 | point.x /= tuple.0
605 | point.y /= tuple.1
606 | }
607 | /// Returns a point by dividing the coordinates by a factor.
608 | public func /(point: CGPoint, factor: CGFloat) -> CGPoint {
609 | return CGPoint(x: point.x / factor, y: point.y / factor)
610 | }
611 | /// Modifies the x and y values by dividing the coordinates by a factor.
612 | public func /=(inout point: CGPoint, factor: CGFloat) {
613 | point.x /= factor
614 | point.y /= factor
615 | }
616 |
617 | /// Returns a point by adding another size.
618 | public func +(s1: CGSize, s2: CGSize) -> CGSize {
619 | return CGSize(width: s1.width + s2.width, height: s1.height + s2.height)
620 | }
621 | /// Modifies the width and height values by adding another size.
622 | public func +=(inout s1: CGSize, s2: CGSize) {
623 | s1.width += s2.width
624 | s1.height += s2.height
625 | }
626 | /// Returns a point by subtracting another size.
627 | public func -(s1: CGSize, s2: CGSize) -> CGSize {
628 | return CGSize(width: s1.width - s2.width, height: s1.height - s2.height)
629 | }
630 | /// Modifies the width and height values by subtracting another size.
631 | public func -=(inout s1: CGSize, s2: CGSize) {
632 | s1.width -= s2.width
633 | s1.height -= s2.height
634 | }
635 |
636 | /// Returns a point by adding a tuple.
637 | public func +(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize {
638 | return CGSize(width: size.width + tuple.0, height: size.height + tuple.1)
639 | }
640 | /// Modifies the width and height values by adding a tuple.
641 | public func +=(inout size: CGSize, tuple: (CGFloat, CGFloat)) {
642 | size.width += tuple.0
643 | size.height += tuple.1
644 | }
645 | /// Returns a point by subtracting a tuple.
646 | public func -(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize {
647 | return CGSize(width: size.width - tuple.0, height: size.height - tuple.1)
648 | }
649 | /// Modifies the width and height values by subtracting a tuple.
650 | public func -=(inout size: CGSize, tuple: (CGFloat, CGFloat)) {
651 | size.width -= tuple.0
652 | size.height -= tuple.1
653 | }
654 | /// Returns a point by multiplying the size with a factor.
655 | public func *(size: CGSize, factor: CGFloat) -> CGSize {
656 | return CGSize(width: size.width * factor, height: size.height * factor)
657 | }
658 | /// Modifies the width and height values by multiplying them with a factor.
659 | public func *=(inout size: CGSize, factor: CGFloat) {
660 | size.width *= factor
661 | size.height *= factor
662 | }
663 | /// Returns a point by multiplying the size with a tuple.
664 | public func *(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize {
665 | return CGSize(width: size.width * tuple.0, height: size.height * tuple.1)
666 | }
667 | /// Modifies the width and height values by multiplying them with a tuple.
668 | public func *=(inout size: CGSize, tuple: (CGFloat, CGFloat)) {
669 | size.width *= tuple.0
670 | size.height *= tuple.1
671 | }
672 | /// Returns a point by dividing the size by a factor.
673 | public func /(size: CGSize, factor: CGFloat) -> CGSize {
674 | return CGSize(width: size.width / factor, height: size.height / factor)
675 | }
676 | /// Modifies the width and height values by dividing them by a factor.
677 | public func /=(inout size: CGSize, factor: CGFloat) {
678 | size.width /= factor
679 | size.height /= factor
680 | }
681 | /// Returns a point by dividing the size by a tuple.
682 | public func /(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize {
683 | return CGSize(width: size.width / tuple.0, height: size.height / tuple.1)
684 | }
685 | /// Modifies the width and height values by dividing them by a tuple.
686 | public func /=(inout size: CGSize, tuple: (CGFloat, CGFloat)) {
687 | size.width /= tuple.0
688 | size.height /= tuple.1
689 | }
690 |
691 | /// Returns a rect by adding the coordinates of a point to the origin.
692 | public func +(rect: CGRect, point: CGPoint) -> CGRect {
693 | return CGRect(origin: rect.origin + point, size: rect.size)
694 | }
695 | /// Modifies the x and y values by adding the coordinates of a point.
696 | public func +=(inout rect: CGRect, point: CGPoint) {
697 | rect.origin += point
698 | }
699 | /// Returns a rect by subtracting the coordinates of a point from the origin.
700 | public func -(rect: CGRect, point: CGPoint) -> CGRect {
701 | return CGRect(origin: rect.origin - point, size: rect.size)
702 | }
703 | /// Modifies the x and y values by subtracting the coordinates from a point.
704 | public func -=(inout rect: CGRect, point: CGPoint) {
705 | rect.origin -= point
706 | }
707 |
708 | /// Returns a rect by adding a size to the size.
709 | public func +(rect: CGRect, size: CGSize) -> CGRect {
710 | return CGRect(origin: rect.origin, size: rect.size + size)
711 | }
712 | /// Modifies the width and height values by adding a size.
713 | public func +=(inout rect: CGRect, size: CGSize) {
714 | rect.size += size
715 | }
716 | /// Returns a rect by subtracting a size from the size.
717 | public func -(rect: CGRect, size: CGSize) -> CGRect {
718 | return CGRect(origin: rect.origin, size: rect.size - size)
719 | }
720 | /// Modifies the width and height values by subtracting a size.
721 | public func -=(inout rect: CGRect, size: CGSize) {
722 | rect.size -= size
723 | }
724 |
725 | /// Returns a point by applying a transform.
726 | public func *(point: CGPoint, transform: CGAffineTransform) -> CGPoint {
727 | return CGPointApplyAffineTransform(point, transform)
728 | }
729 | /// Modifies all values by applying a transform.
730 | public func *=(inout point: CGPoint, transform: CGAffineTransform) {
731 | point = CGPointApplyAffineTransform(point, transform)
732 | }
733 | /// Returns a size by applying a transform.
734 | public func *(size: CGSize, transform: CGAffineTransform) -> CGSize {
735 | return CGSizeApplyAffineTransform(size, transform)
736 | }
737 | /// Modifies all values by applying a transform.
738 | public func *=(inout size: CGSize, transform: CGAffineTransform) {
739 | size = CGSizeApplyAffineTransform(size, transform)
740 | }
741 | /// Returns a rect by applying a transform.
742 | public func *(rect: CGRect, transform: CGAffineTransform) -> CGRect {
743 | return CGRectApplyAffineTransform(rect, transform)
744 | }
745 | /// Modifies all values by applying a transform.
746 | public func *=(inout rect: CGRect, transform: CGAffineTransform) {
747 | rect = CGRectApplyAffineTransform(rect, transform)
748 | }
749 |
750 | /// Returns a transform by concatenating two transforms.
751 | public func *(t1: CGAffineTransform, t2: CGAffineTransform) -> CGAffineTransform {
752 | return CGAffineTransformConcat(t1, t2)
753 | }
754 | /// Modifies all values by concatenating another transform.
755 | public func *=(inout t1: CGAffineTransform, t2: CGAffineTransform) {
756 | t1 = CGAffineTransformConcat(t1, t2)
757 | }
--------------------------------------------------------------------------------