new API for drawing
This commit is contained in:
parent
b74a516720
commit
020ea512f4
|
|
@ -10,6 +10,7 @@
|
|||
6727419D1E65B99E0075836A /* MappableUserDefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6727419C1E65B99E0075836A /* MappableUserDefaultsTests.swift */; };
|
||||
672741A01E65C1E00075836A /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6727419F1E65C1E00075836A /* Post.swift */; };
|
||||
674743941E929A5A00B47671 /* PaginationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674743931E929A5A00B47671 /* PaginationViewModelTests.swift */; };
|
||||
674E7E651EB0F2E300D13340 /* UIImage+SupportExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 674E7E641EB0F2E300D13340 /* UIImage+SupportExtensions.swift */; };
|
||||
675D24B21E9234BB00E92D1F /* PaginationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675D24B11E9234BB00E92D1F /* PaginationViewModel.swift */; };
|
||||
675FB4251EA7797C0075BF3D /* Mutex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675FB4241EA7797C0075BF3D /* Mutex.swift */; };
|
||||
676D177E1EAE0661002E19F9 /* ResizeContentMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676D177D1EAE0661002E19F9 /* ResizeContentMode.swift */; };
|
||||
|
|
@ -18,6 +19,15 @@
|
|||
678A202A1E93C1A900787562 /* PaginationTableViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 678A20291E93C1A900787562 /* PaginationTableViewWrapper.swift */; };
|
||||
679DE4901E9588B6006F25FE /* SupportProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679DE48F1E9588B6006F25FE /* SupportProtocol.swift */; };
|
||||
679DE4941E9613ED006F25FE /* UIScrollView+Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = 679DE4931E9613ED006F25FE /* UIScrollView+Support.swift */; };
|
||||
67A7B1911EAF5F4900E5BC59 /* ImageDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7B1901EAF5F4900E5BC59 /* ImageDrawingOperation.swift */; };
|
||||
67A7B1931EAF5F6A00E5BC59 /* TemplateDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7B1921EAF5F6A00E5BC59 /* TemplateDrawingOperation.swift */; };
|
||||
67A7B1951EAF5F9B00E5BC59 /* CGSize+CGContextSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7B1941EAF5F9B00E5BC59 /* CGSize+CGContextSize.swift */; };
|
||||
67A7B1971EAF5FF600E5BC59 /* DrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7B1961EAF5FF600E5BC59 /* DrawingOperation.swift */; };
|
||||
67A7B1991EAF602900E5BC59 /* RoundDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7B1981EAF602900E5BC59 /* RoundDrawingOperation.swift */; };
|
||||
67A7B19B1EAF60B100E5BC59 /* BorderDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7B19A1EAF60B100E5BC59 /* BorderDrawingOperation.swift */; };
|
||||
67A7B19F1EAF646400E5BC59 /* PaddingDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7B19E1EAF646400E5BC59 /* PaddingDrawingOperation.swift */; };
|
||||
67A7B1A11EAF67AE00E5BC59 /* SolidFillDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7B1A01EAF67AE00E5BC59 /* SolidFillDrawingOperation.swift */; };
|
||||
67A7B1A31EAF6B4600E5BC59 /* CALayerDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67A7B1A21EAF6B4600E5BC59 /* CALayerDrawingOperation.swift */; };
|
||||
67B3057B1E8A8727008169CA /* TestView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67B3057A1E8A8727008169CA /* TestView.xib */; };
|
||||
67B3057D1E8A8735008169CA /* TestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B3057C1E8A8735008169CA /* TestView.swift */; };
|
||||
67B3057F1E8A8804008169CA /* LoadFromNibTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B3057E1E8A8804008169CA /* LoadFromNibTests.swift */; };
|
||||
|
|
@ -61,10 +71,6 @@
|
|||
78A0FCC81DC366A10070B5E1 /* StoryboardProtocol+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78A0FCC61DC366A10070B5E1 /* StoryboardProtocol+Extensions.swift */; };
|
||||
78A74EA91C6B373700FE9724 /* UIView+DefaultXibName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78A74EA81C6B373700FE9724 /* UIView+DefaultXibName.swift */; };
|
||||
78B036411DA4D7060021D5CC /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B036401DA4D7060021D5CC /* UIImage+Extensions.swift */; };
|
||||
78B036431DA4FEC90021D5CC /* CGImage+Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B036421DA4FEC90021D5CC /* CGImage+Transform.swift */; };
|
||||
78B036451DA561D00021D5CC /* CGImage+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B036441DA561D00021D5CC /* CGImage+Utils.swift */; };
|
||||
78B036471DA5624D0021D5CC /* CGImage+Creation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B036461DA5624D0021D5CC /* CGImage+Creation.swift */; };
|
||||
78B036491DA562C30021D5CC /* CGImage+Template.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B036481DA562C30021D5CC /* CGImage+Template.swift */; };
|
||||
78B0364B1DA61EDE0021D5CC /* CGImage+Crop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B0364A1DA61EDE0021D5CC /* CGImage+Crop.swift */; };
|
||||
78B0FC7D1C6B2BE200358B64 /* LogFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B0FC7C1C6B2BE200358B64 /* LogFormatter.swift */; };
|
||||
78B0FC7F1C6B2C4D00358B64 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78B0FC7E1C6B2C4D00358B64 /* Log.swift */; };
|
||||
|
|
@ -115,6 +121,7 @@
|
|||
6727419C1E65B99E0075836A /* MappableUserDefaultsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MappableUserDefaultsTests.swift; sourceTree = "<group>"; };
|
||||
6727419F1E65C1E00075836A /* Post.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = "<group>"; };
|
||||
674743931E929A5A00B47671 /* PaginationViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaginationViewModelTests.swift; sourceTree = "<group>"; };
|
||||
674E7E641EB0F2E300D13340 /* UIImage+SupportExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+SupportExtensions.swift"; sourceTree = "<group>"; };
|
||||
675D24B11E9234BB00E92D1F /* PaginationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaginationViewModel.swift; sourceTree = "<group>"; };
|
||||
675FB4241EA7797C0075BF3D /* Mutex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mutex.swift; sourceTree = "<group>"; };
|
||||
676D177D1EAE0661002E19F9 /* ResizeContentMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResizeContentMode.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -123,6 +130,15 @@
|
|||
678A20291E93C1A900787562 /* PaginationTableViewWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaginationTableViewWrapper.swift; sourceTree = "<group>"; };
|
||||
679DE48F1E9588B6006F25FE /* SupportProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SupportProtocol.swift; sourceTree = "<group>"; };
|
||||
679DE4931E9613ED006F25FE /* UIScrollView+Support.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Support.swift"; sourceTree = "<group>"; };
|
||||
67A7B1901EAF5F4900E5BC59 /* ImageDrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
67A7B1921EAF5F6A00E5BC59 /* TemplateDrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
67A7B1941EAF5F9B00E5BC59 /* CGSize+CGContextSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGSize+CGContextSize.swift"; sourceTree = "<group>"; };
|
||||
67A7B1961EAF5FF600E5BC59 /* DrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawingOperation.swift; sourceTree = "<group>"; };
|
||||
67A7B1981EAF602900E5BC59 /* RoundDrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
67A7B19A1EAF60B100E5BC59 /* BorderDrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BorderDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
67A7B19E1EAF646400E5BC59 /* PaddingDrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaddingDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
67A7B1A01EAF67AE00E5BC59 /* SolidFillDrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SolidFillDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
67A7B1A21EAF6B4600E5BC59 /* CALayerDrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CALayerDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
67B3057A1E8A8727008169CA /* TestView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TestView.xib; sourceTree = "<group>"; };
|
||||
67B3057C1E8A8735008169CA /* TestView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestView.swift; sourceTree = "<group>"; };
|
||||
67B3057E1E8A8804008169CA /* LoadFromNibTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadFromNibTests.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -166,10 +182,6 @@
|
|||
78A0FCC61DC366A10070B5E1 /* StoryboardProtocol+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StoryboardProtocol+Extensions.swift"; sourceTree = "<group>"; };
|
||||
78A74EA81C6B373700FE9724 /* UIView+DefaultXibName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+DefaultXibName.swift"; path = "LeadKit/Extensions/UIView/UIView+DefaultXibName.swift"; sourceTree = SOURCE_ROOT; };
|
||||
78B036401DA4D7060021D5CC /* UIImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = "<group>"; };
|
||||
78B036421DA4FEC90021D5CC /* CGImage+Transform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGImage+Transform.swift"; sourceTree = "<group>"; };
|
||||
78B036441DA561D00021D5CC /* CGImage+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGImage+Utils.swift"; sourceTree = "<group>"; };
|
||||
78B036461DA5624D0021D5CC /* CGImage+Creation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGImage+Creation.swift"; sourceTree = "<group>"; };
|
||||
78B036481DA562C30021D5CC /* CGImage+Template.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGImage+Template.swift"; sourceTree = "<group>"; };
|
||||
78B0364A1DA61EDE0021D5CC /* CGImage+Crop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGImage+Crop.swift"; sourceTree = "<group>"; };
|
||||
78B0FC7C1C6B2BE200358B64 /* LogFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogFormatter.swift; sourceTree = "<group>"; };
|
||||
78B0FC7E1C6B2C4D00358B64 /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -260,6 +272,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
676D17801EAE137B002E19F9 /* CGSize+Resize.swift */,
|
||||
67A7B1941EAF5F9B00E5BC59 /* CGSize+CGContextSize.swift */,
|
||||
);
|
||||
path = CGSize;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -280,6 +293,20 @@
|
|||
path = Support;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
67A7B18F1EAF5F2200E5BC59 /* DrawingOperations */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
67A7B1901EAF5F4900E5BC59 /* ImageDrawingOperation.swift */,
|
||||
67A7B1921EAF5F6A00E5BC59 /* TemplateDrawingOperation.swift */,
|
||||
67A7B1981EAF602900E5BC59 /* RoundDrawingOperation.swift */,
|
||||
67A7B19A1EAF60B100E5BC59 /* BorderDrawingOperation.swift */,
|
||||
67A7B19E1EAF646400E5BC59 /* PaddingDrawingOperation.swift */,
|
||||
67A7B1A01EAF67AE00E5BC59 /* SolidFillDrawingOperation.swift */,
|
||||
67A7B1A21EAF6B4600E5BC59 /* CALayerDrawingOperation.swift */,
|
||||
);
|
||||
path = DrawingOperations;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
67B305791E8A8727008169CA /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -344,6 +371,7 @@
|
|||
children = (
|
||||
67DC650A1E979BFD002F2FFF /* Views */,
|
||||
78011AB11D48B53600EA16A2 /* Api */,
|
||||
67A7B18F1EAF5F2200E5BC59 /* DrawingOperations */,
|
||||
);
|
||||
path = Structures;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -360,10 +388,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
780D23421DA412470084620D /* CGImage+Alpha.swift */,
|
||||
78B036421DA4FEC90021D5CC /* CGImage+Transform.swift */,
|
||||
78B036441DA561D00021D5CC /* CGImage+Utils.swift */,
|
||||
78B036461DA5624D0021D5CC /* CGImage+Creation.swift */,
|
||||
78B036481DA562C30021D5CC /* CGImage+Template.swift */,
|
||||
78B0364A1DA61EDE0021D5CC /* CGImage+Crop.swift */,
|
||||
);
|
||||
path = CGImage;
|
||||
|
|
@ -616,6 +640,7 @@
|
|||
67B856E21E923BE600F54304 /* ResettableType.swift */,
|
||||
679DE48F1E9588B6006F25FE /* SupportProtocol.swift */,
|
||||
67DC65031E979B34002F2FFF /* LoadingIndicatorProtocol.swift */,
|
||||
67A7B1961EAF5FF600E5BC59 /* DrawingOperation.swift */,
|
||||
);
|
||||
path = Protocols;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -655,6 +680,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
78B036401DA4D7060021D5CC /* UIImage+Extensions.swift */,
|
||||
674E7E641EB0F2E300D13340 /* UIImage+SupportExtensions.swift */,
|
||||
);
|
||||
path = UIImage;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -903,7 +929,7 @@
|
|||
};
|
||||
782B1B3D1C7343CD003F8A95 /* Tailor */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
buildActionMask = 8;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
|
|
@ -911,13 +937,13 @@
|
|||
name = Tailor;
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if hash tailor 2>/dev/null; then\n tailor --except=trailing-whitespace,forced-type-cast\nelse\n echo \"warning: Please install Tailor from https://tailor.sh\"\nfi";
|
||||
};
|
||||
782B1B3E1C7343E0003F8A95 /* SwiftLint */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
buildActionMask = 8;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
|
|
@ -925,7 +951,7 @@
|
|||
name = SwiftLint;
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"SwiftLint does not exist, download from https://github.com/realm/SwiftLint\"\nfi";
|
||||
};
|
||||
|
|
@ -951,6 +977,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
67A7B1971EAF5FF600E5BC59 /* DrawingOperation.swift in Sources */,
|
||||
787D874A1E10E1A400D6015C /* ImmutableMappable+ObservableMappable.swift in Sources */,
|
||||
780F56CA1E0D76B8004530B6 /* Sequence+ConcurrentMap.swift in Sources */,
|
||||
7837F60F1CBCF5C0000D74C1 /* EstimatedViewHeightProtocol.swift in Sources */,
|
||||
|
|
@ -964,11 +991,11 @@
|
|||
78753E2C1DE58BF9006BC0FB /* StaticCursor.swift in Sources */,
|
||||
78D4B54A1DA64EAB005B0764 /* Any+TypeName.swift in Sources */,
|
||||
78CFEE571C5C45E500F50370 /* XibNameProtocol.swift in Sources */,
|
||||
67A7B1A11EAF67AE00E5BC59 /* SolidFillDrawingOperation.swift in Sources */,
|
||||
67DC650C1E979C0A002F2FFF /* AnyLoadingIndicator.swift in Sources */,
|
||||
788EC15A1CF64528009CFB6B /* UIStoryboard+InstantiateViewController.swift in Sources */,
|
||||
787783671CA04D4A001CDC9B /* String+SizeCalculation.swift in Sources */,
|
||||
7873D1511E112B0D001816EB /* Any+Cast.swift in Sources */,
|
||||
78B036431DA4FEC90021D5CC /* CGImage+Transform.swift in Sources */,
|
||||
78011A641D47ABC500EA16A2 /* UIView+DefaultReuseIdentifier.swift in Sources */,
|
||||
678A202A1E93C1A900787562 /* PaginationTableViewWrapper.swift in Sources */,
|
||||
679DE4901E9588B6006F25FE /* SupportProtocol.swift in Sources */,
|
||||
|
|
@ -976,13 +1003,15 @@
|
|||
CAA707D51E2E614E0022D732 /* ModuleConfigurator.swift in Sources */,
|
||||
675D24B21E9234BB00E92D1F /* PaginationViewModel.swift in Sources */,
|
||||
78B0FC811C6B2CD500358B64 /* App.swift in Sources */,
|
||||
78B036491DA562C30021D5CC /* CGImage+Template.swift in Sources */,
|
||||
676D177E1EAE0661002E19F9 /* ResizeContentMode.swift in Sources */,
|
||||
CAE698C61E96775F000394B0 /* String+Extensions.swift in Sources */,
|
||||
7873D14F1E1127BC001816EB /* LeadKitError.swift in Sources */,
|
||||
674E7E651EB0F2E300D13340 /* UIImage+SupportExtensions.swift in Sources */,
|
||||
67A7B1991EAF602900E5BC59 /* RoundDrawingOperation.swift in Sources */,
|
||||
78753E301DE594B4006BC0FB /* MapCursor.swift in Sources */,
|
||||
679DE4941E9613ED006F25FE /* UIScrollView+Support.swift in Sources */,
|
||||
780D23461DA416F80084620D /* CGContext+Initializers.swift in Sources */,
|
||||
67A7B19B1EAF60B100E5BC59 /* BorderDrawingOperation.swift in Sources */,
|
||||
95B39A861D9D51250057BD54 /* String+Localization.swift in Sources */,
|
||||
78C36F7E1D801E3E00E7EBEA /* Double+Rounding.swift in Sources */,
|
||||
67DC65061E979B70002F2FFF /* UIView+LoadingIndicator.swift in Sources */,
|
||||
|
|
@ -990,8 +1019,11 @@
|
|||
67B305841E8A92E8008169CA /* XibView.swift in Sources */,
|
||||
78C54AFD1E432EEF0051EFBA /* UIViewController+TopVisibleViewController.swift in Sources */,
|
||||
67788F9F1E69661800484DEE /* CGFloat+Pixels.swift in Sources */,
|
||||
67A7B1951EAF5F9B00E5BC59 /* CGSize+CGContextSize.swift in Sources */,
|
||||
783AF06B1E41CE6C00EC5ADE /* Observable+ToastErrorLogging.swift in Sources */,
|
||||
67A7B1A31EAF6B4600E5BC59 /* CALayerDrawingOperation.swift in Sources */,
|
||||
78CFEE561C5C45E500F50370 /* ReuseIdentifierProtocol.swift in Sources */,
|
||||
67A7B19F1EAF646400E5BC59 /* PaddingDrawingOperation.swift in Sources */,
|
||||
78A0FCC81DC366A10070B5E1 /* StoryboardProtocol+Extensions.swift in Sources */,
|
||||
78B036411DA4D7060021D5CC /* UIImage+Extensions.swift in Sources */,
|
||||
78A0FCC71DC366A10070B5E1 /* StoryboardProtocol+DefaultBundle.swift in Sources */,
|
||||
|
|
@ -1000,6 +1032,8 @@
|
|||
78C36F811D8021DD00E7EBEA /* UIColor+Hex.swift in Sources */,
|
||||
78CFEE5B1C5C45E500F50370 /* ViewModelProtocol.swift in Sources */,
|
||||
EF5FB5691E0141610030E4BE /* UIView+Rotation.swift in Sources */,
|
||||
67A7B1911EAF5F4900E5BC59 /* ImageDrawingOperation.swift in Sources */,
|
||||
67A7B1931EAF5F6A00E5BC59 /* TemplateDrawingOperation.swift in Sources */,
|
||||
783AF06D1E41CF5B00EC5ADE /* NetworkService.swift in Sources */,
|
||||
780F56CC1E0D7ACA004530B6 /* ObservableMappable.swift in Sources */,
|
||||
780D23431DA412470084620D /* CGImage+Alpha.swift in Sources */,
|
||||
|
|
@ -1018,13 +1052,11 @@
|
|||
67B856E31E923BE600F54304 /* ResettableType.swift in Sources */,
|
||||
787783631CA03CA0001CDC9B /* IndexPath+ImmutableIndexPath.swift in Sources */,
|
||||
675FB4251EA7797C0075BF3D /* Mutex.swift in Sources */,
|
||||
78B036471DA5624D0021D5CC /* CGImage+Creation.swift in Sources */,
|
||||
789CC6081DE5835600F789D3 /* CursorType.swift in Sources */,
|
||||
67DC650F1E979D0C002F2FFF /* PaginationTableViewWrapperDelegate+DefaultImplementation.swift in Sources */,
|
||||
67DC65041E979B34002F2FFF /* LoadingIndicatorProtocol.swift in Sources */,
|
||||
78B0364B1DA61EDE0021D5CC /* CGImage+Crop.swift in Sources */,
|
||||
EDF3DE3F1EA4F2E80016F729 /* UIViewController+DefaultXibName.swift in Sources */,
|
||||
78B036451DA561D00021D5CC /* CGImage+Utils.swift in Sources */,
|
||||
78CFEE591C5C45E500F50370 /* StoryboardIdentifierProtocol.swift in Sources */,
|
||||
78011AB31D48B53600EA16A2 /* ApiRequestParameters.swift in Sources */,
|
||||
676D17811EAE137B002E19F9 /* CGSize+Resize.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,15 @@
|
|||
|
||||
import CoreGraphics
|
||||
|
||||
extension CGBitmapInfo {
|
||||
|
||||
// The bitmapInfo value are hard-coded to prevent an "unsupported parameter combination" error
|
||||
|
||||
static let alphaBitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo().rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
|
||||
static let opaqueBitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo().rawValue | CGImageAlphaInfo.none.rawValue)
|
||||
|
||||
}
|
||||
|
||||
public extension CGContext {
|
||||
|
||||
/// Creates a bitmap graphics context with parameters taken from a given image.
|
||||
|
|
@ -53,7 +62,7 @@ public extension CGContext {
|
|||
/// - Returns: A new bitmap context, or NULL if a context could not be created.
|
||||
public static func create(width: Int,
|
||||
height: Int,
|
||||
bitmapInfo: CGBitmapInfo = alphaBitmapInfo,
|
||||
bitmapInfo: CGBitmapInfo = .alphaBitmapInfo,
|
||||
colorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB(),
|
||||
bitsPerComponent: Int = 8) -> CGContext? {
|
||||
|
||||
|
|
|
|||
|
|
@ -34,19 +34,4 @@ public extension CGImage {
|
|||
}
|
||||
}
|
||||
|
||||
/// Adds an alpha channel to image if it doesn't already have one.
|
||||
///
|
||||
/// - Returns: A copy of the given image, adding an alpha channel if it doesn't already have one
|
||||
/// or nil if something goes wrong.
|
||||
public func applyAlpha() -> CGImage? {
|
||||
guard !hasAlpha else {
|
||||
return self
|
||||
}
|
||||
|
||||
let ctx = CGContext.create(width: width, height: height, bitmapInfo: alphaBitmapInfo)
|
||||
ctx?.draw(self, in: bounds)
|
||||
|
||||
return ctx?.makeImage()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
public extension CGImage {
|
||||
|
||||
/// Creates new CGImage instance filled by given color.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - color: Color to fill.
|
||||
/// - width: Width of new image.
|
||||
/// - height: Height of new image.
|
||||
/// - opaque: A flag indicating whether the bitmap is opaque (default: false).
|
||||
/// - Returns: A new instanse of UIImage with given size and color or nil if something goes wrong.
|
||||
public static func create(color: CGColor,
|
||||
width: Int,
|
||||
height: Int,
|
||||
opaque: Bool = false) -> CGImage? {
|
||||
|
||||
let context = CGContext.create(width: width,
|
||||
height: height,
|
||||
bitmapInfo: opaque ? opaqueBitmapInfo : alphaBitmapInfo)
|
||||
|
||||
guard let ctx = context else {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.setFillColor(color)
|
||||
ctx.fill(CGRect(origin: CGPoint.zero, size: CGSize(width: width, height: height)))
|
||||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
/// Creates an image from a given UIView.
|
||||
///
|
||||
/// - Parameter view: The source view.
|
||||
/// - Returns: A new image created from the given view or nil if something goes wrong.
|
||||
public static func create(fromView view: UIView) -> CGImage? {
|
||||
let size = view.bounds.size
|
||||
|
||||
let ctxWidth = Int(ceil(size.width))
|
||||
let ctxHeight = Int(ceil(size.height))
|
||||
|
||||
guard let ctx = CGContext.create(width: ctxWidth, height: ctxHeight) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.translateBy(x: 0, y: size.height)
|
||||
ctx.scaleBy(x: 1.0, y: -1.0)
|
||||
|
||||
view.layer.render(in: ctx)
|
||||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
public extension CGImage {
|
||||
|
||||
/// Creates a new image with rounded corners.
|
||||
///
|
||||
/// - Parameter radius: The corner radius.
|
||||
/// - Returns: New image with rounded corners or nil if something goes wrong.
|
||||
public func round(withRadius radius: CGFloat) -> CGImage? {
|
||||
guard let ctx = CGContext.create(forCGImage: self) ?? CGContext.create(width: width, height: height) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.addPath(UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath)
|
||||
ctx.clip()
|
||||
ctx.draw(self, in: bounds)
|
||||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
/// Creates a new image with a border.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - border: The size of the border.
|
||||
/// - color: The color of the border.
|
||||
/// - radius: The corner radius.
|
||||
/// - extendSize: Extend result image size and don't overlap source image by border.
|
||||
/// - Returns: A new image with border or nil if something goes wrong..
|
||||
public func applyBorder(width border: CGFloat,
|
||||
color: CGColor,
|
||||
radius: CGFloat = 0,
|
||||
extendSize: Bool = false) -> CGImage? {
|
||||
|
||||
let offset = extendSize ? border : 0
|
||||
|
||||
let newWidth = CGFloat(width) + offset * 2
|
||||
let newHeight = CGFloat(height) + offset * 2
|
||||
|
||||
let ctxWidth = Int(ceil(newWidth))
|
||||
let ctxHeight = Int(ceil(newHeight))
|
||||
|
||||
let ctxRect = CGRect(origin: CGPoint.zero, size: CGSize(width: newWidth, height: newHeight))
|
||||
|
||||
let context = CGContext.create(width: ctxWidth,
|
||||
height: ctxHeight,
|
||||
bitmapInfo: bitmapInfo,
|
||||
colorSpace: colorSpace ?? CGColorSpaceCreateDeviceRGB(),
|
||||
bitsPerComponent: bitsPerComponent)
|
||||
|
||||
guard let ctx = context ?? CGContext.create(width: width, height: height) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.draw(self, in: CGRect(x: offset, y: offset, width: CGFloat(width), height: CGFloat(height)))
|
||||
|
||||
ctx.setStrokeColor(color)
|
||||
|
||||
let widthDiff = CGFloat(ctxWidth) - newWidth // difference between context width and real width
|
||||
let heightDiff = CGFloat(ctxWidth) - newWidth // difference between context height and real height
|
||||
|
||||
let inset = ctxRect.insetBy(dx: border / 2 + widthDiff, dy: border / 2 + heightDiff)
|
||||
|
||||
if radius != 0 {
|
||||
ctx.setLineWidth(border)
|
||||
ctx.addPath(UIBezierPath(roundedRect: inset, cornerRadius: radius).cgPath)
|
||||
ctx.strokePath()
|
||||
} else {
|
||||
ctx.stroke(inset, width: border)
|
||||
}
|
||||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
/// Creates a resized copy of an image.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - newSize: The new size of the image.
|
||||
/// - origin: The point where to place resized image
|
||||
/// - Returns: A new resized image or nil if something goes wrong.
|
||||
public func resize(to newSize: CGSize, usingOrigin origin: CGPoint = .zero) -> CGImage? {
|
||||
let ctxWidth = Int(ceil(newSize.width))
|
||||
let ctxHeight = Int(ceil(newSize.height))
|
||||
|
||||
let context = CGContext.create(width: ctxWidth,
|
||||
height: ctxHeight,
|
||||
bitmapInfo: bitmapInfo,
|
||||
colorSpace: colorSpace ?? CGColorSpaceCreateDeviceRGB(),
|
||||
bitsPerComponent: bitsPerComponent)
|
||||
|
||||
guard let ctx = context ?? CGContext.create(width: ctxWidth, height: ctxHeight) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let resizeRect = CGRect(origin: origin,
|
||||
size: CGSize(width: ctxWidth, height: ctxHeight))
|
||||
|
||||
ctx.interpolationQuality = .high
|
||||
ctx.draw(self, in: resizeRect)
|
||||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
/// Creates a copy of the image with border of the given size added around its edges.
|
||||
///
|
||||
/// - Parameter padding: The padding amount.
|
||||
/// - Returns: A new padded image or nil if something goes wrong..
|
||||
public func applyPadding(_ padding: CGFloat) -> CGImage? {
|
||||
let ctxWidth = Int(ceil(CGFloat(width) + padding * 2))
|
||||
let ctxHeight = Int(ceil(CGFloat(height) + padding * 2))
|
||||
|
||||
let context = CGContext.create(width: ctxWidth,
|
||||
height: ctxHeight,
|
||||
bitmapInfo: bitmapInfo,
|
||||
colorSpace: colorSpace ?? CGColorSpaceCreateDeviceRGB(),
|
||||
bitsPerComponent: bitsPerComponent)
|
||||
|
||||
guard let ctx = context ?? CGContext.create(width: ctxWidth, height: ctxHeight) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Draw the image in the center of the context, leaving a gap around the edges
|
||||
let imageLocation = CGRect(x: padding,
|
||||
y: padding,
|
||||
width: CGFloat(width),
|
||||
height: CGFloat(height))
|
||||
|
||||
ctx.addRect(imageLocation)
|
||||
ctx.clip()
|
||||
|
||||
ctx.draw(self, in: imageLocation)
|
||||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics.CGGeometry
|
||||
|
||||
public extension CGSize {
|
||||
|
||||
var ceiledContextSize: CGContextSize {
|
||||
return (width: Int(ceil(width)), height: Int(ceil(height)))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
public extension UIImage {
|
||||
|
||||
/// Creates an image filled by given color.
|
||||
|
|
@ -29,37 +30,41 @@ public extension UIImage {
|
|||
/// - Parameters:
|
||||
/// - color: The color to fill
|
||||
/// - size: The size of an new image.
|
||||
/// - Returns: A new instanse of UIImage with given size and color or nil if something goes wrong.
|
||||
public convenience init?(color: UIColor, size: CGSize) {
|
||||
let cgImage = CGImage.create(color: color.cgColor,
|
||||
width: Int(ceil(size.width)),
|
||||
height: Int(ceil(size.height)))
|
||||
/// - scale: The scale of image.
|
||||
/// - Returns: A new instanse of UIImage with given size and color.
|
||||
public static func imageWith(color: UIColor, size: CGSize) -> UIImage {
|
||||
let width = Int(ceil(size.width))
|
||||
let height = Int(ceil(size.height))
|
||||
|
||||
guard let image = cgImage else {
|
||||
return nil
|
||||
}
|
||||
let operation = SolidFillDrawingOperation(color: color.cgColor, width: width, height: height)
|
||||
|
||||
self.init(cgImage: image)
|
||||
return operation.imageFromNewRenderer(scale: UIScreen.main.scale)
|
||||
}
|
||||
|
||||
/// Creates an image from a UIView.
|
||||
///
|
||||
/// - Parameter fromView: The source view.
|
||||
/// - Returns: A new instance of UIImage or nil if something goes wrong.
|
||||
public convenience init?(fromView view: UIView) {
|
||||
guard let cgImage = CGImage.create(fromView: view) else {
|
||||
return nil
|
||||
}
|
||||
public static func imageFrom(view: UIView) -> UIImage {
|
||||
let operation = CALayerDrawingOperation(layer: view.layer, size: view.bounds.size)
|
||||
|
||||
self.init(cgImage: cgImage)
|
||||
return operation.imageFromNewRenderer(scale: UIScreen.main.scale)
|
||||
}
|
||||
|
||||
/// Render current template UIImage into new image using given color.
|
||||
///
|
||||
/// - Parameter color: Color to fill template image.
|
||||
/// - Returns: A new UIImage rendered with given color or nil if something goes wrong.
|
||||
public func renderTemplate(withColor color: UIColor) -> UIImage? {
|
||||
return cgImage?.renderTemplate(withColor: color.cgColor)?.uiImage
|
||||
/// - Returns: A new UIImage rendered with given color.
|
||||
public func renderTemplate(withColor color: UIColor) -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let operation = TemplateDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
color: color.cgColor)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a new image with rounded corners and border.
|
||||
|
|
@ -69,25 +74,49 @@ public extension UIImage {
|
|||
/// - borderWidth: The size of the border.
|
||||
/// - color: The color of the border.
|
||||
/// - extendSize: Extend result image size and don't overlap source image by border.
|
||||
/// - Returns: A new image or nil if something goes wrong.
|
||||
/// - Returns: A new image with rounded corners.
|
||||
public func roundCorners(cornerRadius: CGFloat,
|
||||
borderWidth: CGFloat,
|
||||
color: UIColor,
|
||||
extendSize: Bool = false) -> UIImage? {
|
||||
extendSize: Bool = false) -> UIImage {
|
||||
|
||||
let rounded = cgImage?.round(withRadius: cornerRadius)
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
return rounded?.applyBorder(width: borderWidth,
|
||||
color: color.cgColor,
|
||||
radius: cornerRadius,
|
||||
extendSize: extendSize)?.uiImage
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: cornerRadius)
|
||||
|
||||
guard let roundImage = roundOperation.imageFromNewRenderer(scale: scale).cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: size,
|
||||
border: borderWidth,
|
||||
color: color.cgColor,
|
||||
radius: cornerRadius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a new circle image.
|
||||
///
|
||||
/// - Returns: A new circled image or nil if something goes wrong.
|
||||
public func roundCornersToCircle() -> UIImage? {
|
||||
return cgImage?.round(withRadius: CGFloat(min(size.width, size.height) / 2))?.uiImage
|
||||
/// - Returns: A new circled image.
|
||||
public func roundCornersToCircle() -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(size.width, size.height) / 2)
|
||||
|
||||
let operation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: radius)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a new circle image with a border.
|
||||
|
|
@ -99,15 +128,30 @@ public extension UIImage {
|
|||
/// - Returns: A new image with rounded corners or nil if something goes wrong.
|
||||
public func roundCornersToCircle(borderWidth: CGFloat,
|
||||
borderColor: UIColor,
|
||||
extendSize: Bool = false) -> UIImage? {
|
||||
extendSize: Bool = false) -> UIImage {
|
||||
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(size.width, size.height) / 2)
|
||||
let rounded = cgImage?.round(withRadius: radius)
|
||||
|
||||
return rounded?.applyBorder(width: borderWidth,
|
||||
color: borderColor.cgColor,
|
||||
radius: radius,
|
||||
extendSize: extendSize)?.uiImage
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: radius)
|
||||
|
||||
guard let roundImage = roundOperation.imageFromNewRenderer(scale: scale).cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: size,
|
||||
border: borderWidth,
|
||||
color: borderColor.cgColor,
|
||||
radius: radius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a resized copy of an image.
|
||||
|
|
@ -116,71 +160,84 @@ public extension UIImage {
|
|||
/// - newSize: The new size of the image.
|
||||
/// - contentMode: The way to handle the content in the new size.
|
||||
/// - Returns: A new image scaled to new size.
|
||||
public func resize(newSize: CGSize, contentMode: ResizeMode = .scaleToFill) -> UIImage? {
|
||||
public func resize(newSize: CGSize, contentMode: ResizeMode = .scaleToFill) -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let resizedRect = size.resizeRect(forNewSize: newSize, resizeMode: contentMode)
|
||||
|
||||
return cgImage?.resize(to: resizedRect.size, usingOrigin: resizedRect.origin)?.uiImage
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: resizedRect.size,
|
||||
origin: resizedRect.origin)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a resized copy of image using scaleAspectFit strategy.
|
||||
///
|
||||
/// - Parameter preferredNewSize: The preferred new size of image.
|
||||
/// - Returns: A new image which size may be less then or equals to given preferredSize.
|
||||
public func resizeAspectFit(preferredNewSize: CGSize) -> UIImage? {
|
||||
let resizeRect = size.resizeRect(forNewSize: preferredNewSize, resizeMode: .scaleAspectFit)
|
||||
public func resizeAspectFit(preferredNewSize: CGSize) -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
return cgImage?.resize(to: resizeRect.size)?.uiImage
|
||||
}
|
||||
let resizedRect = size.resizeRect(forNewSize: preferredNewSize, resizeMode: .scaleAspectFit)
|
||||
|
||||
/// Creates a cropped copy of image using the data contained within a subregion of an existing image.
|
||||
///
|
||||
/// - Parameter bounds: A rectangle whose coordinates specify the area to create an image from.
|
||||
/// - Returns: A new image that specifies a subimage of the image.
|
||||
/// If the rect parameter defines an area that is not in the image, returns NULL.
|
||||
public func crop(to bounds: CGRect) -> UIImage? {
|
||||
return cgImage?.cropping(to: bounds)?.uiImage
|
||||
}
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: resizedRect.size,
|
||||
origin: .zero)
|
||||
|
||||
/// Crop image with given margin values.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - top: Top margin.
|
||||
/// - left: Left margin.
|
||||
/// - bottom: Bottom margin.
|
||||
/// - right: Right margin.
|
||||
/// - Returns: A new image cropped with given paddings or nil if something goes wrong.
|
||||
public func crop(top: CGFloat = 0,
|
||||
left: CGFloat = 0,
|
||||
bottom: CGFloat = 0,
|
||||
right: CGFloat = 0) -> UIImage? {
|
||||
|
||||
return cgImage?.crop(top: top,
|
||||
left: left,
|
||||
bottom: bottom,
|
||||
right: right)?.uiImage
|
||||
}
|
||||
|
||||
/// Crop image to square from center.
|
||||
///
|
||||
/// - Returns: A new cropped image.
|
||||
public func cropFromCenterToSquare() -> UIImage? {
|
||||
return cgImage?.cropFromCenterToSquare()?.uiImage
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Adds an alpha channel if UIImage doesn't already have one.
|
||||
///
|
||||
/// - Returns: A copy of the given image, adding an alpha channel if it doesn't already have one.
|
||||
public func applyAlpha() -> UIImage? {
|
||||
return cgImage?.applyAlpha()?.uiImage
|
||||
public func applyAlpha() -> UIImage {
|
||||
guard let image = cgImage, !image.hasAlpha else {
|
||||
return self
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/// Creates a copy of the image with border of the given size added around its edges.
|
||||
///
|
||||
/// - Parameter padding: The padding amount.
|
||||
/// - Returns: A new padded image or nil if something goes wrong.
|
||||
public func applyPadding(_ padding: CGFloat) -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let operation = PaddingDrawingOperation(image: image, imageSize: size, padding: padding)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension CGImage {
|
||||
@available(iOS 10.0, *)
|
||||
private extension DrawingOperation {
|
||||
|
||||
/// Creates new UIImage instance from CGImage.
|
||||
public var uiImage: UIImage {
|
||||
return UIImage(cgImage: self)
|
||||
func imageFromNewRenderer(scale: CGFloat) -> UIImage {
|
||||
let ctxSize = contextSize
|
||||
let size = CGSize(width: ctxSize.width, height: ctxSize.height)
|
||||
let format = UIGraphicsImageRendererFormat()
|
||||
format.opaque = opaque
|
||||
format.scale = scale
|
||||
|
||||
let renderer = UIGraphicsImageRenderer(size: size, format: format)
|
||||
|
||||
return renderer.image { context in
|
||||
self.apply(in: context.cgContext)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,256 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public extension Support where Base: UIImage {
|
||||
|
||||
/// Creates an image filled by given color.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - color: The color to fill
|
||||
/// - size: The size of an new image.
|
||||
/// - Returns: A new instanse of UIImage with given size and color or nil if something goes wrong.
|
||||
public static func imageWith(color: UIColor, size: CGSize) -> UIImage? {
|
||||
let width = Int(ceil(size.width))
|
||||
let height = Int(ceil(size.height))
|
||||
|
||||
let operation = SolidFillDrawingOperation(color: color.cgColor, width: width, height: height)
|
||||
|
||||
return operation.imageFromNewContext(scale: UIScreen.main.scale)
|
||||
}
|
||||
|
||||
/// Creates an image from a UIView.
|
||||
///
|
||||
/// - Parameter fromView: The source view.
|
||||
/// - Returns: A new instance of UIImage or nil if something goes wrong.
|
||||
public static func imageFrom(view: UIView) -> UIImage? {
|
||||
let operation = CALayerDrawingOperation(layer: view.layer, size: view.bounds.size)
|
||||
|
||||
return operation.imageFromNewContext(scale: UIScreen.main.scale)
|
||||
}
|
||||
|
||||
/// Render current template UIImage into new image using given color.
|
||||
///
|
||||
/// - Parameter color: Color to fill template image.
|
||||
/// - Returns: A new UIImage rendered with given color or nil if something goes wrong.
|
||||
public func renderTemplate(withColor color: UIColor) -> UIImage? {
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
}
|
||||
|
||||
let operation = TemplateDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
color: color.cgColor)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
/// Creates a new image with rounded corners and border.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - cornerRadius: The corner radius.
|
||||
/// - borderWidth: The size of the border.
|
||||
/// - color: The color of the border.
|
||||
/// - extendSize: Extend result image size and don't overlap source image by border.
|
||||
/// - Returns: A new image with rounded corners or nil if something goes wrong.
|
||||
public func roundCorners(cornerRadius: CGFloat,
|
||||
borderWidth: CGFloat,
|
||||
color: UIColor,
|
||||
extendSize: Bool = false) -> UIImage? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
}
|
||||
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radius: cornerRadius)
|
||||
|
||||
guard let roundImage = roundOperation.cgImageFromNewContext(scale: base.scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: base.size,
|
||||
border: borderWidth,
|
||||
color: color.cgColor,
|
||||
radius: cornerRadius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
/// Creates a new circle image.
|
||||
///
|
||||
/// - Returns: A new circled image or nil if something goes wrong.
|
||||
public func roundCornersToCircle() -> UIImage? {
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(base.size.width, base.size.height) / 2)
|
||||
|
||||
let operation = RoundDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radius: radius)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
/// Creates a new circle image with a border.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - borderWidth: The size of the border.
|
||||
/// - borderColor: The color of the border.
|
||||
/// - extendSize: Extend result image size and don't overlap source image by border (default = false).
|
||||
/// - Returns: A new image with rounded corners or nil if something goes wrong.
|
||||
public func roundCornersToCircle(borderWidth: CGFloat,
|
||||
borderColor: UIColor,
|
||||
extendSize: Bool = false) -> UIImage? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
}
|
||||
|
||||
let radius = CGFloat(min(base.size.width, base.size.height) / 2)
|
||||
|
||||
let roundOperation = RoundDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
radius: radius)
|
||||
|
||||
guard let roundImage = roundOperation.cgImageFromNewContext(scale: base.scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let borderOperation = BorderDrawingOperation(image: roundImage,
|
||||
imageSize: base.size,
|
||||
border: borderWidth,
|
||||
color: borderColor.cgColor,
|
||||
radius: radius,
|
||||
extendSize: extendSize)
|
||||
|
||||
return borderOperation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
/// Creates a resized copy of an image.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - newSize: The new size of the image.
|
||||
/// - contentMode: The way to handle the content in the new size.
|
||||
/// - Returns: A new image scaled to new size.
|
||||
public func resize(newSize: CGSize, contentMode: ResizeMode = .scaleToFill) -> UIImage? {
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
}
|
||||
|
||||
let resizedRect = base.size.resizeRect(forNewSize: newSize, resizeMode: contentMode)
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: resizedRect.size,
|
||||
origin: resizedRect.origin)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
/// Creates a resized copy of image using scaleAspectFit strategy.
|
||||
///
|
||||
/// - Parameter preferredNewSize: The preferred new size of image.
|
||||
/// - Returns: A new image which size may be less then or equals to given preferredSize.
|
||||
public func resizeAspectFit(preferredNewSize: CGSize) -> UIImage? {
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
}
|
||||
|
||||
let resizedRect = base.size.resizeRect(forNewSize: preferredNewSize, resizeMode: .scaleAspectFit)
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: resizedRect.size,
|
||||
origin: .zero)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
/// Adds an alpha channel if UIImage doesn't already have one.
|
||||
///
|
||||
/// - Returns: A copy of the given image, adding an alpha channel if it doesn't already have one.
|
||||
public func applyAlpha() -> UIImage? {
|
||||
guard let image = base.cgImage, !image.hasAlpha else {
|
||||
return base
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: base.size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
/// Creates a copy of the image with border of the given size added around its edges.
|
||||
///
|
||||
/// - Parameter padding: The padding amount.
|
||||
/// - Returns: A new padded image or nil if something goes wrong.
|
||||
public func applyPadding(_ padding: CGFloat) -> UIImage? {
|
||||
guard let image = base.cgImage else {
|
||||
return base
|
||||
}
|
||||
|
||||
let operation = PaddingDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
padding: padding)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension DrawingOperation {
|
||||
|
||||
func cgImageFromNewContext(scale: CGFloat) -> CGImage? {
|
||||
let ctxSize = contextSize
|
||||
|
||||
let intScale = Int(scale)
|
||||
|
||||
let context = CGContext.create(width: ctxSize.width * intScale,
|
||||
height: ctxSize.height * intScale,
|
||||
bitmapInfo: opaque ? .opaqueBitmapInfo : .alphaBitmapInfo)
|
||||
|
||||
guard let ctx = context else {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.scaleBy(x: scale, y: scale)
|
||||
|
||||
apply(in: ctx)
|
||||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
func imageFromNewContext(scale: CGFloat) -> UIImage? {
|
||||
guard let image = cgImageFromNewContext(scale: scale) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return UIImage(cgImage: image, scale: scale, orientation: .up)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
|
|
@ -22,29 +22,29 @@
|
|||
|
||||
import CoreGraphics
|
||||
|
||||
public extension CGImage {
|
||||
public typealias CGContextSize = (width: Int, height: Int)
|
||||
|
||||
/// Renders current template CGImage into new image using given color.
|
||||
/// Abstract drawing operation that can can be applied in CGContext.
|
||||
/// Reports required size and opaque attibute for context.
|
||||
public protocol DrawingOperation {
|
||||
|
||||
/// Required context size.
|
||||
var contextSize: CGContextSize { get }
|
||||
|
||||
/// Reports: should context be opaque.
|
||||
var opaque: Bool { get }
|
||||
|
||||
/// Applies drawing operation in given CGContext instance.
|
||||
///
|
||||
/// - Parameter color: Color used to fill template image
|
||||
/// - Returns: A new CGImage instance rendered with given color or nil if something goes wrong.
|
||||
public func renderTemplate(withColor color: CGColor) -> CGImage? {
|
||||
guard let ctx = CGContext.create(forCGImage: self) ?? CGContext.create(width: width, height: height) else {
|
||||
return nil
|
||||
}
|
||||
/// - Parameter context: CGContext to perform drawing manipulations.
|
||||
func apply(in context: CGContext)
|
||||
|
||||
let imageRect = bounds
|
||||
}
|
||||
|
||||
ctx.setFillColor(color)
|
||||
extension DrawingOperation {
|
||||
|
||||
ctx.translateBy(x: 0, y: CGFloat(height))
|
||||
ctx.scaleBy(x: 1.0, y: -1.0)
|
||||
ctx.clip(to: imageRect, mask: self)
|
||||
ctx.fill(imageRect)
|
||||
|
||||
ctx.setBlendMode(.multiply)
|
||||
|
||||
return ctx.makeImage()
|
||||
var opaque: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
struct BorderDrawingOperation: DrawingOperation {
|
||||
|
||||
private let image: CGImage
|
||||
private let imageSize: CGSize
|
||||
private let border: CGFloat
|
||||
private let color: CGColor
|
||||
private let radius: CGFloat
|
||||
private let extendSize: Bool
|
||||
|
||||
public init(image: CGImage,
|
||||
imageSize: CGSize,
|
||||
border: CGFloat,
|
||||
color: CGColor,
|
||||
radius: CGFloat,
|
||||
extendSize: Bool) {
|
||||
|
||||
self.image = image
|
||||
self.imageSize = imageSize
|
||||
self.border = border
|
||||
self.color = color
|
||||
self.radius = radius
|
||||
self.extendSize = extendSize
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
let offset = extendSize ? border : 0
|
||||
|
||||
let width = imageSize.width + offset * 2
|
||||
let height = imageSize.height + offset * 2
|
||||
|
||||
return (width: Int(ceil(width)), height: Int(ceil(height)))
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
let offset = extendSize ? border : 0
|
||||
|
||||
let newWidth = imageSize.width + offset * 2
|
||||
let newHeight = imageSize.height + offset * 2
|
||||
|
||||
let ctxSize = contextSize
|
||||
|
||||
let ctxRect = CGRect(origin: .zero, size: CGSize(width: ctxSize.width, height: ctxSize.height))
|
||||
|
||||
let imageRect = CGRect(x: offset, y: offset, width: imageSize.width, height: imageSize.height)
|
||||
|
||||
context.draw(image, in: imageRect)
|
||||
|
||||
context.setStrokeColor(color)
|
||||
|
||||
let widthDiff = CGFloat(ctxSize.width) - newWidth // difference between context width and real width
|
||||
let heightDiff = CGFloat(ctxSize.height) - newHeight // difference between context height and real height
|
||||
|
||||
let inset = ctxRect.insetBy(dx: border / 2 + widthDiff, dy: border / 2 + heightDiff)
|
||||
|
||||
if radius != 0 {
|
||||
context.setLineWidth(border)
|
||||
context.addPath(UIBezierPath(roundedRect: inset, cornerRadius: radius).cgPath)
|
||||
context.strokePath()
|
||||
} else {
|
||||
context.stroke(inset, width: border)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
|
|
@ -22,21 +22,25 @@
|
|||
|
||||
import CoreGraphics
|
||||
|
||||
// The bitmapInfo value are hard-coded to prevent an "unsupported parameter combination" error
|
||||
struct CALayerDrawingOperation: DrawingOperation {
|
||||
|
||||
public let alphaBitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo().rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
|
||||
public let opaqueBitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo().rawValue | CGImageAlphaInfo.none.rawValue)
|
||||
private let layer: CALayer
|
||||
private let size: CGSize
|
||||
|
||||
public extension CGImage {
|
||||
|
||||
/// Size of image.
|
||||
var size: CGSize {
|
||||
return CGSize(width: width, height: height)
|
||||
public init(layer: CALayer, size: CGSize) {
|
||||
self.layer = layer
|
||||
self.size = size
|
||||
}
|
||||
|
||||
/// Bounds of image.
|
||||
var bounds: CGRect {
|
||||
return CGRect(origin: .zero, size: size)
|
||||
public var contextSize: CGContextSize {
|
||||
return size.ceiledContextSize
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
context.translateBy(x: 0, y: size.height)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
|
||||
layer.render(in: context)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
struct ImageDrawingOperation: DrawingOperation {
|
||||
|
||||
private let image: CGImage
|
||||
private let newSize: CGSize
|
||||
private let origin: CGPoint
|
||||
public let opaque: Bool
|
||||
|
||||
public init(image: CGImage, newSize: CGSize, origin: CGPoint = .zero, opaque: Bool = true) {
|
||||
self.image = image
|
||||
self.newSize = newSize
|
||||
self.origin = origin
|
||||
self.opaque = opaque
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
return newSize.ceiledContextSize
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
context.interpolationQuality = .high
|
||||
context.draw(image, in: CGRect(origin: origin, size: newSize))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
struct PaddingDrawingOperation: DrawingOperation {
|
||||
|
||||
private let image: CGImage
|
||||
private let imageSize: CGSize
|
||||
private let padding: CGFloat
|
||||
|
||||
public init(image: CGImage, imageSize: CGSize, padding: CGFloat) {
|
||||
self.image = image
|
||||
self.imageSize = imageSize
|
||||
self.padding = padding
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
let width = Int(ceil(imageSize.width + padding * 2))
|
||||
let height = Int(ceil(imageSize.height + padding * 2))
|
||||
|
||||
return (width: width, height: height)
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
// Draw the image in the center of the context, leaving a gap around the edges
|
||||
let imageLocation = CGRect(x: padding,
|
||||
y: padding,
|
||||
width: imageSize.width,
|
||||
height: imageSize.height)
|
||||
|
||||
context.addRect(imageLocation)
|
||||
context.clip()
|
||||
|
||||
context.draw(image, in: imageLocation)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
struct RoundDrawingOperation: DrawingOperation {
|
||||
|
||||
private let image: CGImage
|
||||
private let imageSize: CGSize
|
||||
private let radius: CGFloat
|
||||
|
||||
public init(image: CGImage, imageSize: CGSize, radius: CGFloat) {
|
||||
self.image = image
|
||||
self.imageSize = imageSize
|
||||
self.radius = radius
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
return imageSize.ceiledContextSize
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
let imageLocation = CGRect(origin: .zero, size: imageSize)
|
||||
|
||||
context.addPath(UIBezierPath(roundedRect: imageLocation, cornerRadius: radius).cgPath)
|
||||
context.clip()
|
||||
context.draw(image, in: imageLocation)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
struct SolidFillDrawingOperation: DrawingOperation {
|
||||
|
||||
private let color: CGColor
|
||||
private let width: Int
|
||||
private let height: Int
|
||||
|
||||
public init(color: CGColor, width: Int, height: Int) {
|
||||
self.color = color
|
||||
self.width = width
|
||||
self.height = height
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
return (width: width, height: height)
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
context.setFillColor(color)
|
||||
context.fill(CGRect(origin: .zero, size: CGSize(width: width, height: height)))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
struct TemplateDrawingOperation: DrawingOperation {
|
||||
|
||||
private let image: CGImage
|
||||
private let imageSize: CGSize
|
||||
private let color: CGColor
|
||||
|
||||
public init(image: CGImage, imageSize: CGSize, color: CGColor) {
|
||||
self.image = image
|
||||
self.imageSize = imageSize
|
||||
self.color = color
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
return imageSize.ceiledContextSize
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
let imageRect = CGRect(origin: .zero, size: imageSize)
|
||||
|
||||
context.setFillColor(color)
|
||||
|
||||
context.translateBy(x: 0, y: imageSize.height)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
context.clip(to: imageRect, mask: image)
|
||||
context.fill(imageRect)
|
||||
|
||||
context.setBlendMode(.multiply)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue