Merge pull request #61 from TouchInstinct/core-graphics-edit-new-api
Core graphics edit new api
This commit is contained in:
commit
fc0bf2f36e
|
|
@ -7,15 +7,28 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
67186B181EB1DC0500CFAFFB /* ResizeDrawingOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67186B171EB1DC0500CFAFFB /* ResizeDrawingOperation.swift */; };
|
||||
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 */; };
|
||||
676D17811EAE137B002E19F9 /* CGSize+Resize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 676D17801EAE137B002E19F9 /* CGSize+Resize.swift */; };
|
||||
67788F9F1E69661800484DEE /* CGFloat+Pixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67788F9E1E69661800484DEE /* CGFloat+Pixels.swift */; };
|
||||
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 */; };
|
||||
|
|
@ -59,10 +72,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 */; };
|
||||
|
|
@ -109,16 +118,29 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
12F36034A5278991B658B53E /* Pods_LeadKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
67186B171EB1DC0500CFAFFB /* ResizeDrawingOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResizeDrawingOperation.swift; sourceTree = "<group>"; };
|
||||
671FF1611EAA264B001B882C /* iOS.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = iOS.playground; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
676D17801EAE137B002E19F9 /* CGSize+Resize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGSize+Resize.swift"; sourceTree = "<group>"; };
|
||||
67788F9E1E69661800484DEE /* CGFloat+Pixels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGFloat+Pixels.swift"; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
|
|
@ -162,10 +184,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>"; };
|
||||
|
|
@ -252,6 +270,15 @@
|
|||
path = Concurrency;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
676D177F1EAE1364002E19F9 /* CGSize */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
676D17801EAE137B002E19F9 /* CGSize+Resize.swift */,
|
||||
67A7B1941EAF5F9B00E5BC59 /* CGSize+CGContextSize.swift */,
|
||||
);
|
||||
path = CGSize;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
67788F9D1E6965F800484DEE /* CGFloat */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -268,6 +295,21 @@
|
|||
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 */,
|
||||
67186B171EB1DC0500CFAFFB /* ResizeDrawingOperation.swift */,
|
||||
);
|
||||
path = DrawingOperations;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
67B305791E8A8727008169CA /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -322,6 +364,7 @@
|
|||
children = (
|
||||
78753E231DE58A5D006BC0FB /* CursorError.swift */,
|
||||
7873D14E1E1127BC001816EB /* LeadKitError.swift */,
|
||||
676D177D1EAE0661002E19F9 /* ResizeContentMode.swift */,
|
||||
);
|
||||
path = Enums;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -331,6 +374,7 @@
|
|||
children = (
|
||||
67DC650A1E979BFD002F2FFF /* Views */,
|
||||
78011AB11D48B53600EA16A2 /* Api */,
|
||||
67A7B18F1EAF5F2200E5BC59 /* DrawingOperations */,
|
||||
);
|
||||
path = Structures;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -347,10 +391,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;
|
||||
|
|
@ -556,6 +596,7 @@
|
|||
780D23441DA416E80084620D /* CGContext */,
|
||||
67788F9D1E6965F800484DEE /* CGFloat */,
|
||||
780D23411DA412330084620D /* CGImage */,
|
||||
676D177F1EAE1364002E19F9 /* CGSize */,
|
||||
789CC6091DE584C000F789D3 /* CursorType */,
|
||||
78C36F7C1D801E2F00E7EBEA /* Double */,
|
||||
787783611CA03C84001CDC9B /* IndexPath */,
|
||||
|
|
@ -565,8 +606,8 @@
|
|||
780F56C81E0D76A5004530B6 /* Sequence */,
|
||||
78A0FCC41DC366A10070B5E1 /* StoryboardProtocol */,
|
||||
787783651CA04D14001CDC9B /* String */,
|
||||
CAE698BF1E965AE9000394B0 /* TableDirector */,
|
||||
679DE4921E9613ED006F25FE /* Support */,
|
||||
CAE698BF1E965AE9000394B0 /* TableDirector */,
|
||||
EF2921A41E16595100E8F43B /* TimeInterval */,
|
||||
67DC65071E979BA9002F2FFF /* UIActivityIndicator */,
|
||||
E126CBB11DB68D9A00E1B2F8 /* UICollectionView */,
|
||||
|
|
@ -602,6 +643,7 @@
|
|||
67B856E21E923BE600F54304 /* ResettableType.swift */,
|
||||
679DE48F1E9588B6006F25FE /* SupportProtocol.swift */,
|
||||
67DC65031E979B34002F2FFF /* LoadingIndicatorProtocol.swift */,
|
||||
67A7B1961EAF5FF600E5BC59 /* DrawingOperation.swift */,
|
||||
);
|
||||
path = Protocols;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -641,6 +683,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
78B036401DA4D7060021D5CC /* UIImage+Extensions.swift */,
|
||||
674E7E641EB0F2E300D13340 /* UIImage+SupportExtensions.swift */,
|
||||
);
|
||||
path = UIImage;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -937,6 +980,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 */,
|
||||
|
|
@ -950,11 +994,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 */,
|
||||
|
|
@ -962,21 +1006,28 @@
|
|||
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 */,
|
||||
67186B181EB1DC0500CFAFFB /* ResizeDrawingOperation.swift in Sources */,
|
||||
787609221E1403830093CE36 /* Observable+DeferredJust.swift in Sources */,
|
||||
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 */,
|
||||
|
|
@ -985,6 +1036,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 */,
|
||||
|
|
@ -1003,15 +1056,14 @@
|
|||
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 */,
|
||||
78B0FC7D1C6B2BE200358B64 /* LogFormatter.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -20,24 +20,14 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreGraphics
|
||||
|
||||
// The bitmapInfo value are hard-coded to prevent an "unsupported parameter combination" error
|
||||
|
||||
public let alphaBitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo().rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
|
||||
public let opaqueBitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo().rawValue | CGImageAlphaInfo.none.rawValue)
|
||||
|
||||
public enum ImageContentMode {
|
||||
/// A type representing an possible strategies which can be used to resize content.
|
||||
///
|
||||
/// - scaleToFill: The option to scale the content to fit the size of itself
|
||||
/// by changing the aspect ratio of the content if necessary.
|
||||
/// - scaleAspectFit: The option to scale the content to fit the size of the view
|
||||
/// by maintaining the aspect ratio. Any remaining area of the view’s bounds is transparent.
|
||||
/// - scaleAspectFill: The option to scale the content to fill the size of the view.
|
||||
/// Some portion of the content may be clipped to fill the view’s bounds.
|
||||
public enum ResizeMode {
|
||||
case scaleToFill, scaleAspectFit, scaleAspectFill
|
||||
}
|
||||
|
||||
public extension CGImage {
|
||||
|
||||
/**
|
||||
- returns: bounds of image.
|
||||
*/
|
||||
var bounds: CGRect {
|
||||
return CGRect(origin: CGPoint.zero, size: CGSize(width: width, height: height))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,14 +22,23 @@
|
|||
|
||||
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 {
|
||||
|
||||
/**
|
||||
method which creates an instance of CGContext with parameters taken from a given image
|
||||
|
||||
- parameter forCGImage: CGImage instance from which the parameters will be taken
|
||||
- parameter fallbackColorSpace: fallback color space if image doesn't have it
|
||||
*/
|
||||
/// Creates a bitmap graphics context with parameters taken from a given image.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - cgImage: CGImage instance from which the parameters will be taken.
|
||||
/// - fallbackColorSpace: Fallback color space if image doesn't have it.
|
||||
/// - Returns: A new bitmap context, or NULL if a context could not be created.
|
||||
public static func create(forCGImage cgImage: CGImage,
|
||||
fallbackColorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB()) -> CGContext? {
|
||||
|
||||
|
|
@ -40,20 +49,20 @@ public extension CGContext {
|
|||
bitsPerComponent: cgImage.bitsPerComponent)
|
||||
}
|
||||
|
||||
/**
|
||||
method which creates an instance of CGContext
|
||||
|
||||
- parameter width: The width, in pixels, of the required bitmap.
|
||||
- parameter height: The height, in pixels, of the required bitmap.
|
||||
- parameter bitmapInfo: Constants that specify whether the bitmap should contain an alpha channel,
|
||||
the alpha channel’s relative location in a pixel,
|
||||
and information about whether the pixel components are floating-point or integer values.
|
||||
- parameter colorSpace: The color space to use for the bitmap context.
|
||||
- parameter bitsPerComponent: The number of bits to use for each component of a pixel in memory.
|
||||
*/
|
||||
/// Creates a bitmap graphics context.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - width: The width, in pixels, of the required bitmap.
|
||||
/// - height: The height, in pixels, of the required bitmap.
|
||||
/// - bitmapInfo: Constants that specify whether the bitmap should contain an alpha channel,
|
||||
/// the alpha channel’s relative location in a pixel,
|
||||
/// and information about whether the pixel components are floating-point or integer values.
|
||||
/// - colorSpace: The color space to use for the bitmap context.
|
||||
/// - bitsPerComponent: The number of bits to use for each component of a pixel in memory.
|
||||
/// - 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? {
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ import CoreGraphics
|
|||
|
||||
public extension CGImage {
|
||||
|
||||
/**
|
||||
- returns: true if the image has an alpha layer.
|
||||
*/
|
||||
/// A Boolean value indicating whether the image data has an alpha channel.
|
||||
public var hasAlpha: Bool {
|
||||
switch alphaInfo {
|
||||
case .first, .last, .premultipliedFirst, .premultipliedLast:
|
||||
|
|
@ -36,18 +34,4 @@ public extension CGImage {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
- returns: a copy of the given image, adding an alpha channel if it doesn't already have one.
|
||||
*/
|
||||
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,81 +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 {
|
||||
|
||||
/**
|
||||
method which creates new CGImage instance filled by given color
|
||||
|
||||
- parameter color: color to fill
|
||||
- parameter width: width of new image
|
||||
- parameter height: height of new image
|
||||
- parameter opaque: a flag indicating whether the bitmap is opaque (default: False)
|
||||
|
||||
- returns: new instanse of UIImage with given size and color
|
||||
*/
|
||||
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 UIView.
|
||||
|
||||
- parameter fromView: The source view.
|
||||
|
||||
- returns A new image
|
||||
*/
|
||||
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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,11 +24,9 @@ import CoreGraphics
|
|||
|
||||
public extension CGImage {
|
||||
|
||||
/**
|
||||
crop image to square from center
|
||||
|
||||
- returns: cropped image
|
||||
*/
|
||||
/// Crop image to square from center.
|
||||
///
|
||||
/// - Returns: A new cropped image or nil if something goes wrong.
|
||||
public func cropFromCenterToSquare() -> CGImage? {
|
||||
let shortest = min(width, height)
|
||||
|
||||
|
|
@ -46,4 +44,25 @@ public extension CGImage {
|
|||
return cropping(to: cropRect)
|
||||
}
|
||||
|
||||
/// Crop image with given margin values.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - top: Top margin.
|
||||
/// - left: Left margin.
|
||||
/// - bottom: Bottom margin.
|
||||
/// - right: Right margin.
|
||||
/// - Returns: A new CGImage 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) -> CGImage? {
|
||||
|
||||
let rect = CGRect(x: left,
|
||||
y: top,
|
||||
width: CGFloat(width) - left - right,
|
||||
height: CGFloat(height) - top - bottom)
|
||||
|
||||
return cropping(to: rect)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,203 +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 withRadius: The corner radius.
|
||||
|
||||
- returns: A new image
|
||||
*/
|
||||
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.
|
||||
|
||||
- parameter width: The size of the border.
|
||||
- parameter color: The color of the border.
|
||||
- parameter radius: The corner radius.
|
||||
- parameter extendSize: Extend result image size and don't overlap source image by border.
|
||||
|
||||
- returns: A new image
|
||||
*/
|
||||
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 = 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.
|
||||
|
||||
- parameter newSize: the new size of the image.
|
||||
- parameter contentMode: the way to handle the content in the new size.
|
||||
|
||||
- returns: a new image
|
||||
*/
|
||||
public func resize(newSize: CGSize, contentMode: ImageContentMode = .scaleToFill) -> 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 horizontalRatio = newSize.width / CGFloat(width)
|
||||
let verticalRatio = newSize.height / CGFloat(height)
|
||||
|
||||
let ratio: CGFloat
|
||||
|
||||
switch contentMode {
|
||||
case .scaleToFill:
|
||||
ratio = 1
|
||||
case .scaleAspectFill:
|
||||
ratio = max(horizontalRatio, verticalRatio)
|
||||
case .scaleAspectFit:
|
||||
ratio = min(horizontalRatio, verticalRatio)
|
||||
}
|
||||
|
||||
let newImageWidth = contentMode == .scaleToFill ? newSize.width : CGFloat(width) * ratio
|
||||
let newImageHeight = contentMode == .scaleToFill ? newSize.height : CGFloat(height) * ratio
|
||||
|
||||
let originX: CGFloat
|
||||
let originY: CGFloat
|
||||
|
||||
if newImageWidth > newSize.width {
|
||||
originX = (newSize.width - newImageWidth) / 2
|
||||
} else if newImageWidth < newSize.width {
|
||||
originX = newSize.width / 2 - newImageWidth / 2
|
||||
} else {
|
||||
originX = 0
|
||||
}
|
||||
|
||||
if newImageHeight > newSize.height {
|
||||
originY = (newSize.height - newImageHeight) / 2
|
||||
} else if newImageHeight < newSize.height {
|
||||
originY = newSize.height / 2 - newImageHeight / 2
|
||||
} else {
|
||||
originY = 0
|
||||
}
|
||||
|
||||
let rect = CGRect(origin: CGPoint(x: originX, y: originY),
|
||||
size: CGSize(width: newImageWidth, height: newImageHeight))
|
||||
|
||||
ctx.interpolationQuality = .high
|
||||
ctx.draw(self, in: rect)
|
||||
|
||||
return ctx.makeImage()
|
||||
}
|
||||
|
||||
/**
|
||||
returns a copy of the image with border of the given size added around its edges.
|
||||
|
||||
- parameter padding: The padding amount.
|
||||
|
||||
- returns: A new image.
|
||||
*/
|
||||
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)))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// 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 {
|
||||
|
||||
/// Calculates rect "inside" CGSize to match requested size with accordance to resize mode.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - newSize: Requested new size.
|
||||
/// - resizeMode: Resize mode to use.
|
||||
/// - Returns: A new CGRect instance matching request parameters.
|
||||
public func resizeRect(forNewSize newSize: CGSize, resizeMode: ResizeMode) -> CGRect {
|
||||
let horizontalRatio = newSize.width / width
|
||||
let verticalRatio = newSize.height / height
|
||||
|
||||
let ratio: CGFloat
|
||||
|
||||
switch resizeMode {
|
||||
case .scaleToFill:
|
||||
ratio = 1
|
||||
case .scaleAspectFill:
|
||||
ratio = max(horizontalRatio, verticalRatio)
|
||||
case .scaleAspectFit:
|
||||
ratio = min(horizontalRatio, verticalRatio)
|
||||
}
|
||||
|
||||
let newWidth = resizeMode == .scaleToFill ? newSize.width : width * ratio
|
||||
let newHeight = resizeMode == .scaleToFill ? newSize.height : height * ratio
|
||||
|
||||
let originX: CGFloat
|
||||
let originY: CGFloat
|
||||
|
||||
if newWidth > newSize.width {
|
||||
originX = (newSize.width - newWidth) / 2
|
||||
} else if newWidth < newSize.width {
|
||||
originX = newSize.width / 2 - newWidth / 2
|
||||
} else {
|
||||
originX = 0
|
||||
}
|
||||
|
||||
if newHeight > newSize.height {
|
||||
originY = (newSize.height - newHeight) / 2
|
||||
} else if newHeight < newSize.height {
|
||||
originY = newSize.height / 2 - newHeight / 2
|
||||
} else {
|
||||
originY = 0
|
||||
}
|
||||
|
||||
return CGRect(origin: CGPoint(x: originX, y: originY),
|
||||
size: CGSize(width: newWidth, height: newHeight))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,148 +22,218 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
public extension UIImage {
|
||||
|
||||
/**
|
||||
method which creates new UIImage instance filled by given color
|
||||
/// Creates an image filled by given color.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - color: The color to fill
|
||||
/// - size: The size of an new image.
|
||||
/// - 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))
|
||||
|
||||
- parameter color: color to fill
|
||||
- parameter size: size of new image
|
||||
let operation = SolidFillDrawingOperation(color: color.cgColor, width: width, height: height)
|
||||
|
||||
- returns: new instanse of UIImage with given size and color
|
||||
*/
|
||||
return operation.imageFromNewRenderer(scale: UIScreen.main.scale)
|
||||
}
|
||||
|
||||
public convenience init?(color: UIColor, size: CGSize) {
|
||||
let cgImage = CGImage.create(color: color.cgColor,
|
||||
width: Int(ceil(size.width)),
|
||||
height: Int(ceil(size.height)))
|
||||
/// 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.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.
|
||||
public func renderTemplate(withColor color: UIColor) -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return nil
|
||||
return self
|
||||
}
|
||||
|
||||
self.init(cgImage: image)
|
||||
let operation = TemplateDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
color: color.cgColor)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
/**
|
||||
creates an image from a UIView.
|
||||
|
||||
- parameter fromView: The source view.
|
||||
|
||||
- returns A new image or nil if something goes wrong.
|
||||
*/
|
||||
|
||||
public convenience init?(fromView view: UIView) {
|
||||
guard let cgImage = CGImage.create(fromView: view) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(cgImage: cgImage)
|
||||
}
|
||||
|
||||
/**
|
||||
method which render current template CGImage into new image using given color
|
||||
|
||||
- parameter withColor: color which used to fill template image
|
||||
|
||||
- returns: new CGImage rendered with given color or nil if something goes wrong
|
||||
*/
|
||||
public func renderTemplate(withColor color: UIColor) -> UIImage? {
|
||||
return cgImage?.renderTemplate(withColor: color.cgColor)?.uiImage
|
||||
}
|
||||
|
||||
/**
|
||||
creates a new image with rounded corners and border.
|
||||
|
||||
- parameter cornerRadius: The corner radius.
|
||||
- parameter border: The size of the border.
|
||||
- parameter color: The color of the border.
|
||||
- parameter extendSize: Extend result image size and don't overlap source image by border.
|
||||
|
||||
- returns: A new image
|
||||
*/
|
||||
/// 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.
|
||||
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 image
|
||||
*/
|
||||
public func roundCornersToCircle() -> UIImage? {
|
||||
return cgImage?.round(withRadius: CGFloat(min(size.width, size.height) / 2))?.uiImage
|
||||
}
|
||||
|
||||
/**
|
||||
creates a new circle image with a border.
|
||||
|
||||
- parameter border: CGFloat The size of the border.
|
||||
- parameter color: UIColor The color of the border.
|
||||
- parameter extendSize: Extend result image size and don't overlap source image by border.
|
||||
|
||||
- returns: UIImage?
|
||||
*/
|
||||
public func roundCornersToCircle(borderWidth: CGFloat,
|
||||
borderColor: UIColor,
|
||||
extendSize: Bool = false) -> UIImage? {
|
||||
/// Creates a new circle image.
|
||||
///
|
||||
/// - 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 rounded = cgImage?.round(withRadius: radius)
|
||||
|
||||
return rounded?.applyBorder(width: borderWidth,
|
||||
color: borderColor.cgColor,
|
||||
radius: radius,
|
||||
extendSize: extendSize)?.uiImage
|
||||
let operation = RoundDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
radius: radius)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/**
|
||||
creates a resized copy of an image.
|
||||
/// 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 {
|
||||
|
||||
- parameter newSize: the new size of the image.
|
||||
- parameter contentMode: the way to handle the content in the new size.
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
- returns: a new image
|
||||
*/
|
||||
public func resize(newSize: CGSize, contentMode: ImageContentMode = .scaleToFill) -> UIImage? {
|
||||
return cgImage?.resize(newSize: newSize, contentMode: contentMode)?.uiImage
|
||||
let radius = CGFloat(min(size.width, size.height) / 2)
|
||||
|
||||
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 cropped copy of an image.
|
||||
/// 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.
|
||||
/// - cropToImageBounds: Should output image size match resized image size.
|
||||
/// Note: If passed true with ResizeMode.scaleAspectFit content mode it will give the original image.
|
||||
/// - Returns: A new image scaled to new size.
|
||||
public func resize(newSize: CGSize,
|
||||
contentMode: ResizeMode = .scaleToFill,
|
||||
cropToImageBounds: Bool = false) -> UIImage {
|
||||
|
||||
- parameter to: The bounds of the rectangle inside the image.
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
- returns: A new image
|
||||
*/
|
||||
public func crop(to bounds: CGRect) -> UIImage? {
|
||||
return cgImage?.cropping(to: bounds)?.uiImage
|
||||
let operation = ResizeDrawingOperation(image: image,
|
||||
imageSize: size,
|
||||
preferredNewSize: newSize,
|
||||
resizeMode: contentMode,
|
||||
cropToImageBounds: cropToImageBounds)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/**
|
||||
crop image to square from center
|
||||
/// 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 = cgImage, !image.hasAlpha else {
|
||||
return self
|
||||
}
|
||||
|
||||
- returns: cropped image
|
||||
*/
|
||||
public func cropFromCenterToSquare() -> UIImage? {
|
||||
return cgImage?.cropFromCenterToSquare()?.uiImage
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale).redraw()
|
||||
}
|
||||
|
||||
/// 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).redraw()
|
||||
}
|
||||
|
||||
/// Workaround to fix flipped image rendering (by Y)
|
||||
private func redraw() -> UIImage {
|
||||
guard let image = cgImage else {
|
||||
return self
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image, newSize: size)
|
||||
|
||||
return operation.imageFromNewRenderer(scale: scale)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension CGImage {
|
||||
@available(iOS 10.0, *)
|
||||
private extension DrawingOperation {
|
||||
|
||||
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
|
||||
|
||||
return UIGraphicsImageRenderer(size: size, format: format).image {
|
||||
self.apply(in: $0.cgContext)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,257 @@
|
|||
//
|
||||
// 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) -> Support<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)?.support
|
||||
}
|
||||
|
||||
/// 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) -> Support<UIImage>? {
|
||||
let layerDrawingOperation = CALayerDrawingOperation(layer: view.layer, size: view.bounds.size)
|
||||
|
||||
return layerDrawingOperation.imageFromNewContext(scale: UIScreen.main.scale)?.support.flipY()
|
||||
}
|
||||
|
||||
/// 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) -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let operation = TemplateDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
color: color.cgColor)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// 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) -> Support<UIImage>? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(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)?.support
|
||||
}
|
||||
|
||||
/// Creates a new circle image.
|
||||
///
|
||||
/// - Returns: A new circled image or nil if something goes wrong.
|
||||
public func roundCornersToCircle() -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(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)?.support
|
||||
}
|
||||
|
||||
/// 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) -> Support<UIImage>? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(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)?.support
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// - cropToImageBounds: Should output image size match resized image size.
|
||||
/// Note: If passed true with ResizeMode.scaleAspectFit content mode it will give the original image.
|
||||
/// - Returns: A new image scaled to new size.
|
||||
public func resize(newSize: CGSize,
|
||||
contentMode: ResizeMode = .scaleToFill,
|
||||
cropToImageBounds: Bool = false) -> Support<UIImage>? {
|
||||
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let operation = ResizeDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
preferredNewSize: newSize,
|
||||
resizeMode: contentMode,
|
||||
cropToImageBounds: cropToImageBounds)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// 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() -> Support<UIImage>? {
|
||||
guard let image = base.cgImage, !image.hasAlpha else {
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let operation = ImageDrawingOperation(image: image,
|
||||
newSize: base.size,
|
||||
opaque: false)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
/// 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) -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let operation = PaddingDrawingOperation(image: image,
|
||||
imageSize: base.size,
|
||||
padding: padding)
|
||||
|
||||
return operation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
private func flipY() -> Support<UIImage>? {
|
||||
guard let image = base.cgImage else {
|
||||
return Support<UIImage>(base)
|
||||
}
|
||||
|
||||
let flipOperation = ImageDrawingOperation(image: image,
|
||||
newSize: base.size,
|
||||
origin: .zero,
|
||||
opaque: false,
|
||||
flipY: true)
|
||||
|
||||
return flipOperation.imageFromNewContext(scale: base.scale)?.support
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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,32 +22,29 @@
|
|||
|
||||
import CoreGraphics
|
||||
|
||||
public extension CGImage {
|
||||
public typealias CGContextSize = (width: Int, height: Int)
|
||||
|
||||
/**
|
||||
method which render 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 {
|
||||
|
||||
- parameter withColor: color which used to fill template image
|
||||
/// Required context size.
|
||||
var contextSize: CGContextSize { get }
|
||||
|
||||
- returns: new CGImage 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
|
||||
}
|
||||
/// Reports: should context be opaque.
|
||||
var opaque: Bool { get }
|
||||
|
||||
let imageRect = bounds
|
||||
/// Applies drawing operation in given CGContext instance.
|
||||
///
|
||||
/// - Parameter context: CGContext to perform drawing manipulations.
|
||||
func apply(in context: CGContext)
|
||||
|
||||
ctx.setFillColor(color)
|
||||
}
|
||||
|
||||
ctx.translateBy(x: 0, y: CGFloat(height))
|
||||
ctx.scaleBy(x: 1.0, y: -1.0)
|
||||
ctx.clip(to: imageRect, mask: self)
|
||||
ctx.fill(imageRect)
|
||||
extension DrawingOperation {
|
||||
|
||||
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 CGSize(width: width, height: height).ceiledContextSize
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// 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 CALayerDrawingOperation: DrawingOperation {
|
||||
|
||||
private let layer: CALayer
|
||||
private let size: CGSize
|
||||
|
||||
public init(layer: CALayer, size: CGSize) {
|
||||
self.layer = layer
|
||||
self.size = size
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
return size.ceiledContextSize
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
layer.render(in: context)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// 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
|
||||
private let flipY: Bool
|
||||
|
||||
public init(image: CGImage,
|
||||
newSize: CGSize,
|
||||
origin: CGPoint = .zero,
|
||||
opaque: Bool = false,
|
||||
flipY: Bool = false) {
|
||||
|
||||
self.image = image
|
||||
self.newSize = newSize
|
||||
self.origin = origin
|
||||
self.opaque = opaque
|
||||
self.flipY = flipY
|
||||
}
|
||||
|
||||
public var contextSize: CGContextSize {
|
||||
return newSize.ceiledContextSize
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
if flipY {
|
||||
context.translateBy(x: 0, y: newSize.height)
|
||||
context.scaleBy(x: 1.0, y: -1.0)
|
||||
}
|
||||
|
||||
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,55 @@
|
|||
//
|
||||
// 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 ResizeDrawingOperation: DrawingOperation {
|
||||
|
||||
private let image: CGImage
|
||||
private let drawRect: CGRect
|
||||
public let contextSize: CGContextSize
|
||||
|
||||
public init(image: CGImage,
|
||||
imageSize: CGSize,
|
||||
preferredNewSize: CGSize,
|
||||
resizeMode: ResizeMode,
|
||||
cropToImageBounds: Bool = false) {
|
||||
|
||||
self.image = image
|
||||
|
||||
let resizedRect = imageSize.resizeRect(forNewSize: preferredNewSize, resizeMode: resizeMode)
|
||||
|
||||
if cropToImageBounds {
|
||||
drawRect = CGRect(origin: .zero, size: resizedRect.size)
|
||||
contextSize = resizedRect.size.ceiledContextSize
|
||||
} else {
|
||||
drawRect = resizedRect
|
||||
contextSize = preferredNewSize.ceiledContextSize
|
||||
}
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
context.interpolationQuality = .high
|
||||
context.draw(image, in: drawRect)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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