Merge pull request #61 from TouchInstinct/core-graphics-edit-new-api

Core graphics edit new api
This commit is contained in:
Nikolai Ashanin 2017-05-04 14:45:28 +03:00 committed by GitHub
commit fc0bf2f36e
20 changed files with 1139 additions and 488 deletions

View File

@ -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;

View File

@ -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 views 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 views 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))
}
}

View File

@ -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 channels 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 channels 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? {

View File

@ -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()
}
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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)))
}
}

View File

@ -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))
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}

View File

@ -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))
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)))
}
}

View File

@ -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)
}
}