├── .gitignore ├── .swift-version ├── Cmg.podspec ├── Images ├── CmgImage.png └── Screenshot.png ├── LICENSE ├── Proj ├── Cmg.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Cmg.xcscheme ├── Cmg.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── CmgTests │ ├── CmgTests.swift │ └── Info.plist ├── Demo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── Demo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Benchmark.swift │ ├── FilterCell.swift │ ├── FilterCell.xib │ ├── Filters.swift │ ├── Info.plist │ ├── PhotoRequest │ ├── ActionSheet.swift │ ├── Authorization.swift │ ├── PhotoRequester.swift │ └── UIImage+Orientation.swift │ ├── SliderCell.swift │ ├── SliderCell.xib │ ├── SliderTableView.swift │ ├── StretchImageView.swift │ ├── ViewController.swift │ ├── blendImage.jpg │ ├── maskImage.png │ └── sample.jpg ├── README.md └── Sources ├── AffineTransformInput.swift ├── AffineTransformInputProtocols.swift ├── Blur.swift ├── BooleanInput.swift ├── BooleanInputProtocols.swift ├── CIImage+Cmg.swift ├── Cmg.h ├── Cmg.swift ├── ColorAdjustment.swift ├── ColorEffect.swift ├── ColorInput.swift ├── ColorInputProtocols.swift ├── CompositeOperation.swift ├── Context.swift ├── DistortionEffect.swift ├── Enhancement.swift ├── FaceDetection.swift ├── FilterGroup.swift ├── Generator.swift ├── GeometryAdjustment.swift ├── Gradient.swift ├── HalftoneEffect.swift ├── ImageInput.swift ├── ImageInputProtocols.swift ├── Info.plist ├── Range.swift ├── Reduction.swift ├── ScalarInput.swift ├── ScalarInputProtocols.swift ├── Sharpen.swift ├── Slider.swift ├── StringInput.swift ├── StringInputProtocols.swift ├── Stylize.swift ├── TileEffect.swift ├── UIImage+CmgFilterChain.swift ├── UIImage+CmgShorthand.swift ├── Vector.swift ├── VectorInput.swift └── VectorInputProtocols.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | *.moved-aside 17 | DerivedData 18 | *.hmap 19 | *.ipa 20 | *.xcuserstate 21 | 22 | # SPM 23 | .build/ 24 | 25 | # Carthage 26 | Carthage/Build 27 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /Cmg.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Cmg" 3 | s.version = "1.5.0" 4 | s.summary = "Easy image filtering library of using Core Image." 5 | s.homepage = "https://github.com/xxxAIRINxxx/Cmg" 6 | s.license = 'MIT' 7 | s.author = { "Airin" => "xl1138@gmail.com" } 8 | s.source = { :git => "https://github.com/xxxAIRINxxx/Cmg.git", :tag => s.version.to_s } 9 | 10 | s.requires_arc = true 11 | s.platform = :ios, '10.0' 12 | 13 | s.source_files = 'Sources/*.swift' 14 | end 15 | -------------------------------------------------------------------------------- /Images/CmgImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xxxAIRINxxx/Cmg/586be7e8862c8d4d83bc5b900a86181d4f517ac5/Images/CmgImage.png -------------------------------------------------------------------------------- /Images/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xxxAIRINxxx/Cmg/586be7e8862c8d4d83bc5b900a86181d4f517ac5/Images/Screenshot.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 xxxAIRINxxx 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Proj/Cmg.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Proj/Cmg.xcodeproj/xcshareddata/xcschemes/Cmg.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Proj/Cmg.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Proj/Cmg.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Proj/CmgTests/CmgTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CmgTests.swift 3 | // CmgTests 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class CmgTests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | super.tearDown() 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | // Use XCTAssert and related functions to verify your tests produce the correct results. 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measureBlock { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Proj/CmgTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Proj/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Proj/Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Demo 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | let color = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0) 18 | UINavigationBar.appearance().setBackgroundImage(color.image, for: .default) 19 | 20 | return true 21 | } 22 | } 23 | 24 | extension UIColor { 25 | 26 | var image: UIImage { 27 | let rect = CGRect(x: 0, y: 0, width: 1, height: 1) 28 | 29 | UIGraphicsBeginImageContext(rect.size) 30 | 31 | let context = UIGraphicsGetCurrentContext() 32 | 33 | context?.setFillColor(self.cgColor) 34 | context?.fill(rect) 35 | 36 | let image = UIGraphicsGetImageFromCurrentImageContext() 37 | UIGraphicsEndImageContext() 38 | 39 | return image! 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Proj/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Proj/Demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Proj/Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /Proj/Demo/Benchmark.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Benchmark.swift 3 | // Demo 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public final class Benchmarker { 12 | 13 | public static let shared = Benchmarker() 14 | 15 | fileprivate var benchmarks : [Benchmark] = [] 16 | 17 | public func start(_ key: String = #file + #function) { 18 | let filterd = self.benchmarks.filter({ return $0.key == key }).first 19 | if filterd == nil { 20 | self.benchmarks.append(Benchmark(key: key)) 21 | } 22 | } 23 | 24 | public func cancel(_ key: String = "") { 25 | self.benchmarks = self.benchmarks.filter() { return $0.key != key } 26 | } 27 | 28 | public func finish(_ key: String = "") -> String { 29 | guard let _benchmark = self.benchmarks.filter({ return $0.key == key }).first else { 30 | return "Benchmarker Error - Not Found" 31 | } 32 | 33 | let string = _benchmark.finish() 34 | self.cancel(key) 35 | return string 36 | } 37 | } 38 | 39 | public final class Benchmark { 40 | 41 | public let startTime : CFAbsoluteTime 42 | public let key : String 43 | 44 | public convenience init(fileName: String = #file , funcName: String = #function) { 45 | let _fileName = fileName.components(separatedBy: "/").last ?? "" 46 | let key = _fileName + " " + funcName 47 | self.init(key: key) 48 | } 49 | 50 | public init(key: String) { 51 | self.startTime = CFAbsoluteTimeGetCurrent() 52 | self.key = key 53 | } 54 | 55 | public func finish() -> String { 56 | let elapsed = (CFAbsoluteTimeGetCurrent() - startTime) 57 | let formatedElapsed = String(format: "%.5f", elapsed) 58 | let string = "\(self.key) time : \(formatedElapsed) (second)" 59 | print(string) 60 | return self.key 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Proj/Demo/FilterCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilterCell.swift 3 | // Demo 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | final class FilterCell: UICollectionViewCell { 13 | 14 | @IBOutlet fileprivate weak var imageView: UIImageView! 15 | @IBOutlet fileprivate weak var titleLabel: UILabel! 16 | 17 | var isSelectedFilter: Bool = false { 18 | didSet { self.setNeedsDisplay() } 19 | } 20 | 21 | override func awakeFromNib() { 22 | super.awakeFromNib() 23 | 24 | self.imageView.layer.cornerRadius = 5.0 25 | } 26 | 27 | override func prepareForReuse() { 28 | super.prepareForReuse() 29 | 30 | self.isSelectedFilter = false 31 | } 32 | 33 | func setFilter(_ filter: PhotoProcessable) { 34 | self.titleLabel.text = filter.name 35 | self.imageView.image = filter.thumbnailImage 36 | } 37 | 38 | override func draw(_ rect: CGRect) { 39 | super.draw(rect) 40 | 41 | if self.isSelectedFilter == false { return } 42 | 43 | let lineWidth: CGFloat = 2.0 44 | 45 | let bottomLine = UIBezierPath(rect: CGRect(x: 0, y: self.frame.size.height - lineWidth, width: self.frame.size.width, height: lineWidth)) 46 | UIColor.blue.setStroke() 47 | bottomLine.lineWidth = lineWidth 48 | bottomLine.stroke() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Proj/Demo/FilterCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Proj/Demo/Filters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Filters.swift 3 | // Demo 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import Cmg 12 | 13 | protocol PhotoProcessable: class { 14 | 15 | var name: String { get } 16 | var sliders: [Slider] { get } 17 | var thumbnailImage: UIImage? { get set } 18 | 19 | func processing(_ uiImage: UIImage) -> UIImage 20 | func resetSliderValues() 21 | } 22 | 23 | final class Filter: PhotoProcessable { 24 | 25 | typealias FilterClass = T 26 | 27 | let name: String 28 | let sliders: [Slider] 29 | let internalFilter: FilterClass 30 | 31 | var thumbnailImage: UIImage? 32 | 33 | init(_ filter: FilterClass) { 34 | self.name = filter.name 35 | self.sliders = filter.sliders() 36 | self.internalFilter = filter 37 | } 38 | 39 | func processing(_ uiImage: UIImage) -> UIImage { 40 | let b = Benchmark(key: self.name); defer { _ = b.finish() } 41 | 42 | let filteredImage: UIImage? = self.internalFilter.processing(uiImage) 43 | if filteredImage == nil { 44 | print("filteredImage is nil !!!") 45 | if let _filter = self.internalFilter as? Filterable { print(_filter.filter.attributes) } 46 | } 47 | return filteredImage ?? uiImage 48 | } 49 | 50 | func resetSliderValues() { 51 | self.sliders.forEach() { $0.resetValue() } 52 | } 53 | } 54 | 55 | struct FilterGenerator {} 56 | 57 | enum Image { 58 | case original 59 | case thumbnail 60 | 61 | var size : CGSize { 62 | switch self { 63 | case .original: 64 | return CGSize(width: 1200, height: 1200) 65 | case .thumbnail: 66 | return CGSize(width: 600, height: 600) 67 | } 68 | } 69 | } 70 | 71 | extension FilterGenerator { 72 | 73 | static var originalImage: UIImage = UIImage(named: "sample.jpg")! 74 | static var thumbnailImage: UIImage = UIImage(named: "sample.jpg")!.cmg_resizeAtAspectFill(Image.thumbnail.size)! 75 | 76 | static func generate() -> [PhotoProcessable] { 77 | var filters: [PhotoProcessable] = [] 78 | let size = FilterGenerator.originalImage.size 79 | let blendImage = UIImage(named: "blendImage.jpg")! 80 | let maskImage = UIImage(named: "maskImage.png")! 81 | 82 | // Blur 83 | 84 | if #available(iOS 9.0, *) { 85 | filters.append(Filter(BoxBlur())) 86 | filters.append(Filter(DiscBlur())) 87 | filters.append(Filter(GaussianBlur())) 88 | filters.append(Filter(Median())) 89 | filters.append(Filter(MotionBlur())) 90 | filters.append(Filter(NoiseReduction())) 91 | filters.append(Filter(ZoomBlur(imageSize: size))) 92 | } 93 | 94 | // Color Adjustment 95 | 96 | filters.append(Filter(ColorClamp())) 97 | filters.append(Filter(ColorControls())) 98 | filters.append(Filter(ColorMatrix())) 99 | filters.append(Filter(ColorPolynomial())) 100 | filters.append(Filter(ExposureAdjust())) 101 | filters.append(Filter(GammaAdjust())) 102 | filters.append(Filter(HueAdjust())) 103 | filters.append(Filter(LinearToSRGBToneCurve())) 104 | filters.append(Filter(SRGBToneCurveToLinear())) 105 | filters.append(Filter(TemperatureAndTint())) 106 | filters.append(Filter(ToneCurve())) 107 | filters.append(Filter(Vibrance())) 108 | filters.append(Filter(WhitePointAdjust())) 109 | 110 | // Color Effect 111 | 112 | filters.append(Filter(ColorCrossPolynomial())) 113 | 114 | let cubeArray: [Double] = [ 115 | 0.7, 0.5, 1.0, 0.6, 116 | 0.0, 1.0, 0.0, 1.0, 117 | 0.9, 0.6, 0.8, 0.2, 118 | 1.0, 0.4, 0.0, 1.0] 119 | let bytes = UnsafeMutablePointer.allocate(capacity: cubeArray.count) 120 | let count = MemoryLayout.size * cubeArray.count 121 | let cubeData = Data(bytes: bytes, count: count) 122 | defer { 123 | bytes.deinitialize(count: cubeArray.count) 124 | bytes.deallocate() 125 | } 126 | 127 | filters.append(Filter(ColorCube(inputCubeData: cubeData))) 128 | filters.append(Filter(ColorCubeWithColorSpace(inputCubeData: cubeData, inputColorSpace: CGColorSpaceCreateDeviceRGB()))) 129 | filters.append(Filter(ColorInvert())) 130 | filters.append(Filter(ColorMap(uiImage: blendImage))) 131 | filters.append(Filter(ColorMonochrome())) 132 | filters.append(Filter(ColorPosterize())) 133 | filters.append(Filter(FalseColor())) 134 | filters.append(Filter(MaskToAlpha())) 135 | filters.append(Filter(MaximumComponent())) 136 | filters.append(Filter(MinimumComponent())) 137 | filters.append(Filter(PhotoEffectChrome())) 138 | filters.append(Filter(PhotoEffectFade())) 139 | filters.append(Filter(PhotoEffectInstant())) 140 | filters.append(Filter(PhotoEffectMono())) 141 | filters.append(Filter(PhotoEffectNoir())) 142 | filters.append(Filter(PhotoEffectProcess())) 143 | filters.append(Filter(PhotoEffectTonal())) 144 | filters.append(Filter(PhotoEffectTransfer())) 145 | filters.append(Filter(SepiaTone())) 146 | filters.append(Filter(Vignette())) 147 | filters.append(Filter(VignetteEffect(imageSize: size))) 148 | 149 | // Composite Operation 150 | 151 | filters.append(Filter(AdditionCompositing(uiImage: blendImage))) 152 | filters.append(Filter(ColorBlendMode(uiImage: blendImage))) 153 | filters.append(Filter(ColorBurnBlendMode(uiImage: blendImage))) 154 | filters.append(Filter(ColorDodgeBlendMode(uiImage: blendImage))) 155 | filters.append(Filter(DarkenBlendMode(uiImage: blendImage))) 156 | filters.append(Filter(DifferenceBlendMode(uiImage: blendImage))) 157 | filters.append(Filter(DivideBlendMode(uiImage: blendImage))) 158 | filters.append(Filter(ExclusionBlendMode(uiImage: blendImage))) 159 | filters.append(Filter(HardLightBlendMode(uiImage: blendImage))) 160 | filters.append(Filter(HueBlendMode(uiImage: blendImage))) 161 | filters.append(Filter(LightenBlendMode(uiImage: blendImage))) 162 | filters.append(Filter(LinearBurnBlendMode(uiImage: blendImage))) 163 | filters.append(Filter(OverlayBlendMode(uiImage: blendImage))) 164 | filters.append(Filter(ScreenBlendMode(uiImage: blendImage))) 165 | filters.append(Filter(SourceAtopCompositing(uiImage: blendImage))) 166 | filters.append(Filter(SourceOverCompositing(uiImage: blendImage))) 167 | 168 | // Distortion Effect 169 | 170 | filters.append(Filter(BumpDistortion(imageSize: size))) 171 | filters.append(Filter(CircleSplashDistortion(imageSize: size))) 172 | if #available(iOS 9.0, *) { 173 | filters.append(Filter(CircularWrap(imageSize: size))) 174 | filters.append(Filter(Droste(imageSize: size))) 175 | filters.append(Filter(DisplacementDistortion(image: blendImage))) 176 | filters.append(Filter(GlassDistortion(image: blendImage, imageSize: size))) 177 | filters.append(Filter(GlassLozenge(imageSize: size))) 178 | filters.append(Filter(StretchCrop(imageSize: size))) 179 | filters.append(Filter(TorusLensDistortion(imageSize: size))) 180 | } 181 | filters.append(Filter(HoleDistortion(imageSize: size))) 182 | filters.append(Filter(LightTunnel(imageSize: size))) 183 | filters.append(Filter(PinchDistortion(imageSize: size))) 184 | filters.append(Filter(TwirlDistortion(imageSize: size))) 185 | filters.append(Filter(VortexDistortion(imageSize: size))) 186 | 187 | // Generator 188 | 189 | filters.append(Filter(AztecCodeGenerator(message: "Cmg"))) 190 | filters.append(Filter(CheckerboardGenerator(imageSize: size))) 191 | filters.append(Filter(Code128BarcodeGenerator(message: "Cmg"))) 192 | filters.append(Filter(ConstantColorGenerator())) 193 | filters.append(Filter(QRCodeGenerator(message: "Cmg"))) 194 | filters.append(Filter(RandomGenerator())) 195 | filters.append(Filter(StarShineGenerator(imageSize: size))) 196 | filters.append(Filter(StripesGenerator(imageSize: size))) 197 | if #available(iOS 9.0, *) { 198 | filters.append(Filter(LenticularHaloGenerator(imageSize: size))) 199 | filters.append(Filter(PDF417BarcodeGenerator(inputMessage: "Cmg", imageSize: size))) 200 | filters.append(Filter(SunbeamsGenerator(imageSize: size))) 201 | } 202 | 203 | // Geometry Adjustment 204 | 205 | filters.append(Filter(AffineTransform())) 206 | filters.append(Filter(Crop(imageSize: size))) 207 | filters.append(Filter(LanczosScaleTransform())) 208 | filters.append(Filter(PerspectiveCorrection(imageSize: size))) 209 | filters.append(Filter(PerspectiveTransform(imageSize: size))) 210 | filters.append(Filter(PerspectiveTransformWithExtent(imageSize: size))) 211 | filters.append(Filter(StraightenFilter())) 212 | 213 | // Gradient 214 | 215 | filters.append(Filter(GaussianGradient(imageSize: size))) 216 | filters.append(Filter(LinearGradient(imageSize: size))) 217 | filters.append(Filter(RadialGradient(imageSize: size))) 218 | filters.append(Filter(SmoothLinearGradient(imageSize: size))) 219 | 220 | // Halftone Effect 221 | 222 | filters.append(Filter(CircularScreen(imageSize: size))) 223 | if #available(iOS 9.0, *) { 224 | filters.append(Filter(CMYKHalftone(imageSize: size))) 225 | } 226 | filters.append(Filter(DotScreen(imageSize: size))) 227 | filters.append(Filter(HatchedScreen(imageSize: size))) 228 | filters.append(Filter(LineScreen(imageSize: size))) 229 | 230 | // Reduction 231 | 232 | if #available(iOS 9.0, *) { 233 | filters.append(Filter(AreaAverage(imageSize: size))) 234 | filters.append(Filter(RowAverage(imageSize: size))) 235 | filters.append(Filter(ColumnAverage(imageSize: size))) 236 | filters.append(Filter(HistogramDisplayFilter())) 237 | filters.append(Filter(AreaMaximum(imageSize: size))) 238 | filters.append(Filter(AreaMinimum(imageSize: size))) 239 | filters.append(Filter(AreaMaximumAlpha(imageSize: size))) 240 | filters.append(Filter(AreaMinimumAlpha(imageSize: size))) 241 | } 242 | 243 | // Sharpen 244 | 245 | filters.append(Filter(SharpenLuminance())) 246 | filters.append(Filter(UnsharpMask())) 247 | 248 | // Stylize 249 | 250 | filters.append(Filter(BlendWithAlphaMask(backgroundImage: blendImage, maskImage: maskImage))) 251 | filters.append(Filter(BlendWithMask(backgroundImage: maskImage, maskImage: blendImage))) 252 | filters.append(Filter(Convolution3X3())) 253 | filters.append(Filter(Convolution5X5())) 254 | filters.append(Filter(Convolution9Horizontal())) 255 | filters.append(Filter(Convolution9Vertical())) 256 | filters.append(Filter(Gloom())) 257 | filters.append(Filter(HighlightShadowAdjust())) 258 | filters.append(Filter(Pixellate(imageSize: size))) 259 | if #available(iOS 9.0, *) { 260 | filters.append(Filter(ComicEffect())) 261 | filters.append(Filter(Convolution7X7())) 262 | filters.append(Filter(Crystallize(imageSize: size))) 263 | filters.append(Filter(DepthOfField(imageSize: size))) 264 | filters.append(Filter(Edges())) 265 | filters.append(Filter(EdgeWork())) 266 | filters.append(Filter(HeightFieldFromMask())) 267 | filters.append(Filter(HexagonalPixellate(imageSize: size))) 268 | filters.append(Filter(LineOverlay())) 269 | filters.append(Filter(Pointillize(imageSize: size))) 270 | filters.append(Filter(ShadedMaterial(uiImage: blendImage))) 271 | } 272 | 273 | // Tile Effect 274 | 275 | filters.append(Filter(AffineClamp())) 276 | filters.append(Filter(AffineTile())) 277 | filters.append(Filter(EightfoldReflectedTile(imageSize: size))) 278 | filters.append(Filter(FourfoldReflectedTile(imageSize: size))) 279 | filters.append(Filter(FourfoldRotatedTile(imageSize: size))) 280 | filters.append(Filter(FourfoldTranslatedTile(imageSize: size))) 281 | filters.append(Filter(GlideReflectedTile(imageSize: size))) 282 | if #available(iOS 9.0, *) { 283 | filters.append(Filter(Kaleidoscope(imageSize: size))) 284 | filters.append(Filter(OpTile(imageSize: size))) 285 | filters.append(Filter(ParallelogramTile(imageSize: size))) 286 | filters.append(Filter(TriangleTile(imageSize: size))) 287 | } 288 | filters.append(Filter(PerspectiveTile(imageSize: size))) 289 | filters.append(Filter(SixfoldReflectedTile(imageSize: size))) 290 | filters.append(Filter(SixfoldRotatedTile(imageSize: size))) 291 | filters.append(Filter(TriangleKaleidoscope(imageSize: size))) 292 | filters.append(Filter(TwelvefoldReflectedTile(imageSize: size))) 293 | 294 | // Enhancement 295 | 296 | filters.append(Filter(AutoAdjustEnhance())) 297 | filters.append(Filter(AutoAdjustRedEye())) 298 | 299 | // Filter Group 300 | 301 | filters.append( 302 | Filter(FilterGroup(name: "FilterGroup1", [ 303 | SepiaTone(), 304 | ColorControls(), 305 | PhotoEffectProcess(), 306 | ] 307 | ))) 308 | filters.append( 309 | Filter(FilterGroup(name: "FilterGroup2", [ 310 | ColorMonochrome(), 311 | HueAdjust(), 312 | LightenBlendMode(uiImage: blendImage), 313 | ] 314 | ))) 315 | if #available(iOS 9.0, *) { 316 | filters.append( 317 | Filter(FilterGroup(name: "FilterGroup3", [ 318 | BoxBlur(), 319 | Vignette(), 320 | PhotoEffectMono(), 321 | ] 322 | ))) 323 | } 324 | 325 | return filters 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /Proj/Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Cmg 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSCameraUsageDescription 28 | use demo 29 | NSPhotoLibraryUsageDescription 30 | use demo 31 | UILaunchStoryboardName 32 | LaunchScreen 33 | UIMainStoryboardFile 34 | Main 35 | UIRequiredDeviceCapabilities 36 | 37 | armv7 38 | 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationPortrait 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Proj/Demo/PhotoRequest/ActionSheet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionSheet.swift 3 | // PhotoRequest 4 | // 5 | // Created by xxxAIRINxxx on 2016/04/17. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public typealias ActionSheetCompletion = (() -> Void) 12 | 13 | private struct ActionInfo { 14 | 15 | let type : UIAlertAction.Style 16 | let title : String 17 | let completion : ActionSheetCompletion? 18 | 19 | init(_ type: UIAlertAction.Style, _ title: String, _ completion: ActionSheetCompletion?) { 20 | self.type = type 21 | self.title = title 22 | self.completion = completion 23 | } 24 | } 25 | 26 | public final class ActionSheet { 27 | 28 | let title : String 29 | let message : String 30 | 31 | fileprivate var actionInfo : [ActionInfo] = [] 32 | fileprivate var cancelInfo : ActionInfo? 33 | 34 | // MARK: - Initializer 35 | 36 | public init(title: String, message: String? = nil) { 37 | self.title = title 38 | self.message = message ?? "" 39 | } 40 | 41 | // MARK: - Instance Functions 42 | 43 | public func setCancelAction(_ title: String, completion: ActionSheetCompletion? = nil) -> ActionSheet { 44 | self.cancelInfo = ActionInfo(.cancel, title, completion) 45 | return self 46 | } 47 | 48 | public func addAction(_ title: String, completion: ActionSheetCompletion?) -> ActionSheet { 49 | let a = ActionInfo(.default, title, completion) 50 | self.actionInfo.append(a) 51 | return self 52 | } 53 | 54 | public func addDestructiveAction(_ title: String, completion: ActionSheetCompletion?) -> ActionSheet { 55 | let a = ActionInfo(.destructive, title, completion) 56 | self.actionInfo.append(a) 57 | return self 58 | } 59 | 60 | public func show(_ owner: UIViewController) { 61 | let alertController = UIAlertController(title: self.title, message: self.message, preferredStyle: .actionSheet) 62 | 63 | if let _cancelInfo = self.cancelInfo { 64 | self.actionInfo.append(_cancelInfo) 65 | self.cancelInfo = nil 66 | } 67 | 68 | self.actionInfo.forEach() { info in 69 | let action = UIAlertAction(title: info.title, style: info.type) { _ in 70 | info.completion?() 71 | } 72 | alertController.addAction(action) 73 | } 74 | 75 | if self.actionInfo.count == 0 { 76 | let action = UIAlertAction(title: "Cancel", style: .default) { _ in } 77 | alertController.addAction(action) 78 | } 79 | 80 | owner.present(alertController, animated: true, completion: nil) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Proj/Demo/PhotoRequest/Authorization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Authorization.swift 3 | // PhotoRequest 4 | // 5 | // Created by xxxAIRINxxx on 2016/04/17. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import AVFoundation 12 | import Photos 13 | 14 | public enum AuthorizedErrorType { 15 | 16 | case restricted 17 | case denied 18 | } 19 | 20 | public enum AuthorizedResult { 21 | 22 | case success 23 | case error(AuthorizedErrorType) 24 | } 25 | 26 | public typealias AuthorizedCompletion = ((AuthorizedResult) -> Void) 27 | 28 | public final class Authorization { 29 | 30 | fileprivate init() {} 31 | 32 | public static func camera(_ completion: AuthorizedCompletion?) { 33 | let status = AVCaptureDevice.authorizationStatus(for: AVMediaType.video) 34 | switch status { 35 | case .authorized: 36 | completion?(AuthorizedResult.success) 37 | case .restricted: 38 | completion?(AuthorizedResult.error(.restricted)) 39 | case .denied: 40 | completion?(AuthorizedResult.error(.denied)) 41 | case .notDetermined: 42 | AVCaptureDevice.requestAccess(for: AVMediaType.video) { granted in 43 | DispatchQueue.main.async { 44 | if granted { 45 | completion?(AuthorizedResult.success) 46 | } else { 47 | completion?(AuthorizedResult.error(.denied)) 48 | } 49 | } 50 | } 51 | @unknown default: 52 | fatalError() 53 | } 54 | } 55 | 56 | public static func photo(_ completion: AuthorizedCompletion?) { 57 | switch PHPhotoLibrary.authorizationStatus() { 58 | case .authorized: 59 | completion?(AuthorizedResult.success) 60 | case .restricted: 61 | completion?(AuthorizedResult.error(.restricted)) 62 | case .denied: 63 | completion?(AuthorizedResult.error(.denied)) 64 | case .notDetermined: 65 | PHPhotoLibrary.requestAuthorization() { status in 66 | DispatchQueue.main.async { 67 | if status == PHAuthorizationStatus.authorized { 68 | completion?(AuthorizedResult.success) 69 | } else { 70 | completion?(AuthorizedResult.error(.denied)) 71 | } 72 | } 73 | } 74 | @unknown default: 75 | fatalError() 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Proj/Demo/PhotoRequest/PhotoRequester.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoRequester.swift 3 | // PhotoRequest 4 | // 5 | // Created by xxxAIRINxxx on 2016/04/17. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public enum PhotoResquestResult { 13 | case success(image: UIImage) 14 | case faild 15 | case cancel 16 | 17 | var image: UIImage? { 18 | if case .success(let image) = self { return image } 19 | return nil 20 | } 21 | } 22 | 23 | public typealias PhotoResquestCompletion = ((PhotoResquestResult) -> Void) 24 | 25 | public final class PhotoRequester: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { 26 | 27 | fileprivate static let shared : PhotoRequester = PhotoRequester() 28 | 29 | fileprivate var completionHandler : (PhotoResquestCompletion)? 30 | 31 | public static func showActionSheet(_ parentViewController: UIViewController, completion: PhotoResquestCompletion?) { 32 | ActionSheet(title: "Select SourceType", message: "") 33 | .addAction("PhotoLibrary") { 34 | PhotoRequester.requestPhotoLibrary(parentViewController, completion: completion) 35 | } 36 | .addAction("Camera") { 37 | PhotoRequester.requestPhotoFromCamera(parentViewController, completion: completion) 38 | } 39 | .addAction("SavedPhotosAlbum") { 40 | PhotoRequester.requestPhotoFromSavedPhotosAlbum(parentViewController, completion: completion) 41 | } 42 | .setCancelAction("Cancel") { } 43 | .show(parentViewController) 44 | } 45 | 46 | public static func requestPhotoLibrary(_ parentViewController: UIViewController, completion: PhotoResquestCompletion?) { 47 | self.shared.requestPhoto(parentViewController, .photoLibrary, completion) 48 | } 49 | 50 | public static func requestPhotoFromCamera(_ parentViewController: UIViewController, completion: PhotoResquestCompletion?) { 51 | self.shared.requestPhoto(parentViewController, .camera, completion) 52 | } 53 | 54 | public static func requestPhotoFromSavedPhotosAlbum(_ parentViewController: UIViewController, completion: PhotoResquestCompletion?) { 55 | self.shared.requestPhoto(parentViewController, .savedPhotosAlbum, completion) 56 | } 57 | 58 | fileprivate func requestPhoto(_ parentViewController: UIViewController, _ sourceType: UIImagePickerController.SourceType , _ completion: PhotoResquestCompletion?) { 59 | 60 | if !UIImagePickerController.isSourceTypeAvailable(sourceType) { 61 | completion?(PhotoResquestResult.faild) 62 | return 63 | } 64 | 65 | let resultBlock: AuthorizedCompletion = { [unowned self] result in 66 | switch result { 67 | case .success: 68 | let imagePickerController : UIImagePickerController = UIImagePickerController() 69 | imagePickerController.sourceType = sourceType 70 | imagePickerController.allowsEditing = false 71 | imagePickerController.delegate = self 72 | 73 | self.completionHandler = completion 74 | 75 | parentViewController.present(imagePickerController, animated: true, completion: nil) 76 | case .error(_): 77 | completion?(PhotoResquestResult.faild) 78 | } 79 | } 80 | 81 | switch sourceType { 82 | case .camera: 83 | Authorization.camera(resultBlock) 84 | case .photoLibrary, .savedPhotosAlbum: 85 | Authorization.photo(resultBlock) 86 | @unknown default: 87 | fatalError() 88 | } 89 | } 90 | } 91 | 92 | // MARK: - UIImagePickerControllerDelegate 93 | 94 | extension PhotoRequester { 95 | 96 | public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 97 | let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage 98 | picker.dismiss(animated: true) { [unowned self] in 99 | self.completionHandler?(PhotoResquestResult.success(image: image)) 100 | self.completionHandler = nil 101 | } 102 | } 103 | 104 | public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { 105 | picker.dismiss(animated: true) { [unowned self] in 106 | self.completionHandler?(PhotoResquestResult.cancel) 107 | self.completionHandler = nil 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Proj/Demo/PhotoRequest/UIImage+Orientation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Orientation.swift 3 | // PhotoRequest 4 | // 5 | // Created by xxxAIRINxxx on 2016/04/17. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIImage { 13 | 14 | public func fixOrientation() -> UIImage { 15 | if self.imageOrientation == .up { return self } 16 | 17 | var transform = CGAffineTransform.identity 18 | 19 | switch (self.imageOrientation) { 20 | case .down, .downMirrored: 21 | transform = transform.translatedBy(x: self.size.width, y: self.size.height) 22 | transform = transform.rotated(by: CGFloat(Double.pi)) 23 | case .left, .leftMirrored: 24 | transform = transform.translatedBy(x: self.size.width, y: 0) 25 | transform = transform.rotated(by: CGFloat(Double.pi / 2)) 26 | case .right, .rightMirrored: 27 | transform = transform.translatedBy(x: 0, y: self.size.height) 28 | transform = transform.rotated(by: CGFloat(-Double.pi / 2)) 29 | case .up, .upMirrored: 30 | break 31 | @unknown default: 32 | fatalError() 33 | } 34 | 35 | switch (self.imageOrientation) { 36 | case .upMirrored, .downMirrored: 37 | transform = transform.translatedBy(x: self.size.width, y: 0) 38 | transform = transform.scaledBy(x: -1, y: 1) 39 | case .leftMirrored, .rightMirrored: 40 | transform = transform.translatedBy(x: self.size.height, y: 0) 41 | transform = transform.scaledBy(x: -1, y: 1) 42 | case .up, .down, .left, .right: 43 | break 44 | @unknown default: 45 | fatalError() 46 | } 47 | 48 | let width = Int(self.size.width) 49 | let height = Int(self.size.height) 50 | let ctx = CGContext( 51 | data: nil, 52 | width: width, 53 | height: height, 54 | bitsPerComponent: (self.cgImage?.bitsPerComponent)!, 55 | bytesPerRow: 0, 56 | space: (self.cgImage?.colorSpace!)!, 57 | bitmapInfo: (self.cgImage?.bitmapInfo.rawValue)! 58 | ) 59 | 60 | ctx?.concatenate(transform) 61 | switch (self.imageOrientation) { 62 | case .left, .leftMirrored, .right, .rightMirrored: 63 | ctx?.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: self.size.height, height: self.size.width)) 64 | default: 65 | ctx?.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)) 66 | } 67 | 68 | let cgimg = ctx?.makeImage() 69 | let image = UIImage(cgImage: cgimg!) 70 | 71 | return image 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Proj/Demo/SliderCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SliderCell.swift 3 | // Demo 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import Cmg 12 | 13 | final class SliderCell: UITableViewCell { 14 | 15 | var upsdateSliderHandler: ((Float) -> Void)? 16 | 17 | @IBOutlet weak var label: UILabel! 18 | @IBOutlet weak var slider: UISlider! 19 | @IBOutlet weak var valueLabel: UILabel! 20 | 21 | override func awakeFromNib() { 22 | super.awakeFromNib() 23 | self.label.adjustsFontSizeToFitWidth = true 24 | } 25 | } 26 | 27 | // MARK: - User Interaction 28 | 29 | extension SliderCell { 30 | 31 | @IBAction fileprivate func changeSliderValue(_ sender: UISlider) { 32 | self.valueLabel.text = sender.value.description 33 | self.upsdateSliderHandler?(sender.value) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Proj/Demo/SliderCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 26 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Proj/Demo/SliderTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SliderTableView.swift 3 | // Demo 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import Cmg 12 | 13 | final class SliderTableView: UITableView { 14 | 15 | var upsdateSliderHandler: (() -> Void)? 16 | 17 | fileprivate var sliders: [Slider] = [] 18 | 19 | override init(frame: CGRect, style: UITableView.Style) { 20 | super.init(frame: frame, style: style) 21 | self.commonInit() 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | super.init(coder: aDecoder) 26 | self.commonInit() 27 | } 28 | 29 | fileprivate func commonInit() { 30 | self.register(UINib(nibName: "SliderCell", bundle: nil), forCellReuseIdentifier: "SliderCell") 31 | self.dataSource = self 32 | self.delegate = self 33 | } 34 | 35 | func updateSliders(_ filter: PhotoProcessable) { 36 | self.sliders = filter.sliders 37 | self.reloadData() 38 | } 39 | } 40 | 41 | extension SliderTableView: UITableViewDataSource { 42 | 43 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 44 | return self.sliders.count 45 | } 46 | 47 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 48 | let cell = tableView.dequeueReusableCell(withIdentifier: "SliderCell", for: indexPath) as! SliderCell 49 | 50 | let slider = self.sliders[(indexPath as NSIndexPath).row] 51 | 52 | cell.label.text = slider.name 53 | cell.valueLabel.text = slider.currentValue.description 54 | cell.slider.maximumValue = slider.range.maximumValue 55 | cell.slider.minimumValue = slider.range.minimumValue 56 | cell.slider.value = slider.currentValue 57 | cell.upsdateSliderHandler = { [weak self] value in 58 | slider.currentValue = value 59 | self?.upsdateSliderHandler?() 60 | } 61 | 62 | return cell 63 | } 64 | } 65 | 66 | extension SliderTableView: UITableViewDelegate { 67 | 68 | func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { 69 | tableView.deselectRow(at: indexPath, animated: true) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Proj/Demo/StretchImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StretchImageView.swift 3 | // Demo 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | final class StretchImageView: UIView, UIScrollViewDelegate { 13 | 14 | fileprivate static let maxZoomScale: CGFloat = 3 15 | 16 | var image : UIImage? { 17 | get { return self.imageView.image } 18 | set { 19 | let isFirst = self.imageView.image == nil 20 | self.imageView.image = newValue 21 | if isFirst == true { 22 | self.resetZoomScale(false) 23 | } 24 | } 25 | } 26 | 27 | fileprivate var imageView : UIImageView! 28 | fileprivate var scrollView : UIScrollView! 29 | 30 | override init(frame: CGRect) { 31 | super.init(frame: frame) 32 | self.setup() 33 | } 34 | 35 | required init?(coder aDecoder: NSCoder) { 36 | super.init(coder: aDecoder) 37 | self.setup() 38 | } 39 | 40 | override func layoutSubviews() { 41 | super.layoutSubviews() 42 | 43 | self.scrollView.contentSize = self.imageView.frame.size 44 | } 45 | 46 | func addToParentView(_ parentView: UIView) { 47 | self.frame = parentView.bounds 48 | self.autoresizingMask = [.flexibleWidth, .flexibleHeight] 49 | parentView.addSubview(self) 50 | } 51 | 52 | fileprivate func setup() { 53 | self.scrollView = UIScrollView(frame: self.bounds) 54 | self.scrollView.showsHorizontalScrollIndicator = false 55 | self.scrollView.showsVerticalScrollIndicator = false 56 | self.scrollView.delegate = self 57 | self.scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 58 | self.scrollView.panGestureRecognizer.delaysTouchesBegan = false 59 | self.scrollView.panGestureRecognizer.minimumNumberOfTouches = 1 60 | self.scrollView.bounces = false 61 | self.addSubview(self.scrollView) 62 | 63 | self.imageView = UIImageView(frame: self.bounds) 64 | self.imageView.isUserInteractionEnabled = true 65 | self.imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 66 | self.scrollView.addSubview(self.imageView) 67 | } 68 | 69 | func resetImageViewFrame() { 70 | let size = (self.imageView.image != nil) ? self.imageView.image!.size : self.imageView.frame.size 71 | if size.width > 0 && size.height > 0 { 72 | let ratio = min(self.scrollView.frame.size.width / size.width, self.scrollView.frame.size.height / size.height) 73 | let W = ratio * size.width * self.scrollView.zoomScale 74 | let H = ratio * size.height * self.scrollView.zoomScale 75 | 76 | self.imageView.frame = CGRect( 77 | x: max(0, (self.scrollView.frame.size.width - W) / 2), 78 | y: max(0, (self.scrollView.frame.size.height - H) / 2), width: W, height: H) 79 | } 80 | } 81 | 82 | func resetZoomScale(_ animated: Bool) { 83 | var Rw = self.scrollView.frame.size.width / self.imageView.frame.size.width 84 | var Rh = self.scrollView.frame.size.height / self.imageView.frame.size.height 85 | 86 | let scale: CGFloat = UIScreen.main.scale 87 | Rw = max(Rw, self.imageView.image!.size.width / (scale * self.scrollView.frame.size.width)) 88 | Rh = max(Rh, self.imageView.image!.size.height / (scale * self.scrollView.frame.size.height)) 89 | 90 | self.scrollView.contentSize = self.imageView.frame.size 91 | self.scrollView.minimumZoomScale = 1 92 | self.scrollView.maximumZoomScale = max(max(Rw, Rh), StretchImageView.maxZoomScale) 93 | 94 | self.scrollView.setZoomScale(self.scrollView.minimumZoomScale, animated: animated) 95 | } 96 | 97 | func reset(_ animated: Bool) { 98 | self.resetImageViewFrame() 99 | self.resetZoomScale(animated) 100 | } 101 | } 102 | 103 | // MARK: - UIScrollViewDelegate 104 | 105 | extension StretchImageView { 106 | 107 | @objc(viewForZoomingInScrollView:) func viewForZooming(in scrollView: UIScrollView) -> UIView? { 108 | return self.imageView 109 | } 110 | 111 | func scrollViewDidZoom(_ scrollView: UIScrollView) { 112 | let Ws = self.scrollView.frame.size.width - self.scrollView.contentInset.left - self.scrollView.contentInset.right 113 | let Hs = self.scrollView.frame.size.height - self.scrollView.contentInset.top - self.scrollView.contentInset.bottom 114 | let W = self.imageView.frame.size.width 115 | let H = self.imageView.frame.size.height 116 | 117 | var rct = self.imageView.frame 118 | rct.origin.x = max((Ws-W) / 2, 0) 119 | rct.origin.y = max((Hs-H) / 2, 0) 120 | self.imageView.frame = rct 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /Proj/Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Demo 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Cmg 11 | 12 | final class ViewController: UIViewController { 13 | 14 | @IBOutlet weak var imageView: StretchImageView! 15 | @IBOutlet weak var collectionView: UICollectionView! 16 | @IBOutlet weak var sliderTableView: SliderTableView! 17 | 18 | fileprivate weak var selectedFilter: PhotoProcessable? 19 | 20 | fileprivate var filters: [PhotoProcessable] = FilterGenerator.generate() 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | self.setup() 26 | } 27 | 28 | fileprivate func setup() { 29 | self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Photo", 30 | style: .plain, 31 | target: self, 32 | action: #selector(ViewController.requestPhoto)) 33 | 34 | self.imageView.image = FilterGenerator.originalImage 35 | 36 | self.collectionView.register(UINib(nibName: "FilterCell", bundle: nil), forCellWithReuseIdentifier: "FilterCell") 37 | 38 | let flowLayout = UICollectionViewFlowLayout() 39 | flowLayout.scrollDirection = .horizontal 40 | flowLayout.headerReferenceSize = CGSize.zero 41 | flowLayout.sectionInset = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 0.0, right: 10.0) 42 | flowLayout.itemSize = CGSize(width: 80, height: 110) 43 | flowLayout.minimumLineSpacing = 10 44 | flowLayout.minimumInteritemSpacing = 0 45 | self.collectionView.collectionViewLayout = flowLayout 46 | 47 | self.sliderTableView.upsdateSliderHandler = { [unowned self] in 48 | let image = self.selectedFilter!.processing(FilterGenerator.originalImage) 49 | self.imageView.image = image 50 | } 51 | } 52 | 53 | @objc fileprivate func requestPhoto() { 54 | PhotoRequester.showActionSheet(self) { [unowned self] result in 55 | switch result { 56 | case .success(let image): 57 | let _image = image.fixOrientation() 58 | FilterGenerator.originalImage = _image.cmg_resizeAtAspectFit(Image.original.size)! 59 | FilterGenerator.thumbnailImage = _image.cmg_resizeAtAspectFit(Image.thumbnail.size)! 60 | self.imageView.image = FilterGenerator.originalImage 61 | self.filters = FilterGenerator.generate() 62 | self.selectedFilter = nil 63 | self.collectionView.reloadData() 64 | case .faild: 65 | break 66 | case .cancel: 67 | break 68 | } 69 | } 70 | } 71 | } 72 | 73 | // MARK: - UICollectionViewDataSource 74 | 75 | extension ViewController : UICollectionViewDataSource { 76 | 77 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 78 | return self.filters.count 79 | } 80 | 81 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 82 | return collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCell", for: indexPath) 83 | } 84 | 85 | func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { 86 | let filter = self.filters[(indexPath as NSIndexPath).row] 87 | 88 | switch cell { 89 | case let _cell as FilterCell: 90 | if filter.thumbnailImage == nil { 91 | filter.thumbnailImage = filter.processing(FilterGenerator.thumbnailImage) 92 | } 93 | if let _selectedFilter = self.selectedFilter { 94 | _cell.isSelectedFilter = _selectedFilter.name == filter.name 95 | } else { 96 | _cell.isSelectedFilter = false 97 | } 98 | _cell.setFilter(filter) 99 | default: 100 | break 101 | } 102 | } 103 | } 104 | 105 | // MARK: - UICollectionViewDelegate 106 | 107 | extension ViewController : UICollectionViewDelegate { 108 | 109 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 110 | let filter = self.filters[(indexPath as NSIndexPath).row] 111 | 112 | if (self.selectedFilter.flatMap() { $0 === filter }) == true { return } 113 | 114 | self.selectedFilter = filter 115 | filter.resetSliderValues() 116 | self.sliderTableView.updateSliders(filter) 117 | 118 | self.imageView.image = filter.processing(FilterGenerator.originalImage) 119 | 120 | collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Proj/Demo/blendImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xxxAIRINxxx/Cmg/586be7e8862c8d4d83bc5b900a86181d4f517ac5/Proj/Demo/blendImage.jpg -------------------------------------------------------------------------------- /Proj/Demo/maskImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xxxAIRINxxx/Cmg/586be7e8862c8d4d83bc5b900a86181d4f517ac5/Proj/Demo/maskImage.png -------------------------------------------------------------------------------- /Proj/Demo/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xxxAIRINxxx/Cmg/586be7e8862c8d4d83bc5b900a86181d4f517ac5/Proj/Demo/sample.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cmg 2 | 3 | [![Version](https://img.shields.io/cocoapods/v/Cmg.svg?style=flat)](http://cocoadocs.org/docsets/Cmg) 4 | [![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg?style=flat)](https://developer.apple.com/swift/) 5 | [![Platforms iOS](https://img.shields.io/badge/Platforms-iOS-lightgray.svg?style=flat)](https://developer.apple.com/swift/) 6 | [![Xcode 10.2](https://img.shields.io/badge/Xcode-10.2-blue.svg?style=flat)](https://developer.apple.com/swift/) 7 | 8 | ![CmgImage](Images/CmgImage.png "CmgImage") 9 | 10 | Easy image filtering library of using Core Image. (CIFilter) 11 | 12 | Cmg is inspired by [Filterpedia](https://github.com/FlexMonkey/Filterpedia). 13 | 14 | ## Requirements 15 | 16 | * Xcode 10+ 17 | 18 | | | OS | Swift | 19 | |------------|------------------|--------------| 20 | | **v1.1.x** | iOS 8+ | 3.0 | 21 | | **v1.2.x** | iOS 9+ | 3.2 | 22 | | **v1.3.x** | iOS 9+ | 4.1 | 23 | | **v1.4.x** | iOS 9+ | 4.2 | 24 | | **v1.5.x** | iOS 10+ | 5.0 | 25 | 26 | ## Features 27 | 28 | - Image filtering (use CIFilter) 29 | - Filter grouping 30 | - Filter chaining 31 | - Slider support 32 | - Face detection (bonus) 33 | 34 | ## Usage 35 | 36 | ### Basics 37 | 38 | Example for GaussianBlur filter 39 | 40 | Default filter 41 | ```swift 42 | let filteredImage = GaussianBlur().processing(image) 43 | ``` 44 | 45 | Edit for filter parameter 46 | 47 | ```swift 48 | var filter = GaussianBlur() 49 | filter.radius = 15.0 50 | let filteredImage = filter.processing(image) 51 | ``` 52 | 53 | Edit for filter parameter (using closure) 54 | 55 | ```swift 56 | let filteredImage = GaussianBlur() 57 | .configuration(){ filter in filter.inputRadius.setValue(15) } 58 | .processing(image) 59 | ``` 60 | 61 | ### Filter Group 62 | 63 | ```swift 64 | let filterGroup = FilterGroup(name: "FilterGroupExample", [ 65 | BoxBlur().configuration({ filter in filter.inputRadius.setValue(15) }), 66 | Vignette(), 67 | PhotoEffectMono(), 68 | ]) 69 | 70 | let filteredImage = filterGroup.processing(image) 71 | ``` 72 | 73 | ### Filter Chaining 74 | 75 | ```swift 76 | let filteredImage = image.cmg_chain([ 77 | ComicEffect(), 78 | GaussianBlur().configuration({ filter in 79 | filter.inputRadius.setValue(15) 80 | }), 81 | PerspectiveTile(imageSize: image.size).configuration({ filter in 82 | filter.inputTopLeft.setVector(Vector2(x: 118.0, y: 490.0).ciVector) 83 | }), 84 | ]) 85 | ``` 86 | 87 | ## Demo 88 | 89 | Please see Demo project. (Support Filter ShowCase) 90 | Please try on iOS Devices. 91 | Simulator is very slow... 92 | 93 | ![Screenshot](Images/Screenshot.png "Screenshot") 94 | 95 | ## Installation 96 | 97 | ### CocoaPods 98 | 99 | Cmg is available through [CocoaPods](http://cocoapods.org). To install 100 | it, simply add the following line to your Podfile: 101 | 102 | ```ruby 103 | use_frameworks! 104 | 105 | pod "Cmg" 106 | ``` 107 | 108 | ### Carthage 109 | 110 | To integrate Cmg into your Xcode project using Carthage, specify it in your Cartfile: 111 | 112 | ```ruby 113 | github "xxxAIRINxxx/Cmg" 114 | ``` 115 | 116 | ## Use Image 117 | 118 | Thanks [pixabay](https://pixabay.com/) 119 | 120 | ## License 121 | 122 | MIT license. See the LICENSE file for more info. 123 | -------------------------------------------------------------------------------- /Sources/AffineTransformInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AffineTransformInput.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/22. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public final class AffineTransformInput: FilterInputable { 14 | 15 | public var value: CGAffineTransform 16 | 17 | fileprivate let initialValue: CGAffineTransform 18 | 19 | public init() { 20 | self.value = CGAffineTransform.identity 21 | self.value.a = 1.0 22 | self.value.d = 1.0 23 | 24 | self.initialValue = self.value 25 | } 26 | 27 | public func sliders() -> [Slider] { 28 | return [] 29 | } 30 | 31 | public func setInput(_ filter: CIFilter) { 32 | filter.setValue(NSValue(cgAffineTransform: self.value), forKey: kCIInputTransformKey) 33 | } 34 | 35 | public func resetValue() { 36 | self.value = self.initialValue 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/AffineTransformInputProtocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AffineTransformInputProtocols.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/24. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public protocol InputAffineTransformAvailable { 14 | 15 | var inputTransform: AffineTransformInput { get } 16 | } 17 | 18 | extension InputAffineTransformAvailable { 19 | 20 | public var transform: CGAffineTransform { 21 | get { return inputTransform.value } 22 | set { inputTransform.value = newValue } 23 | } 24 | 25 | public func setTranslate(_ x: CGFloat, _ y: CGFloat) { 26 | inputTransform.value = inputTransform.value.translatedBy(x: x, y: y) 27 | } 28 | 29 | public func setScale(_ x: CGFloat, _ y: CGFloat) { 30 | inputTransform.value = inputTransform.value.scaledBy(x: x, y: y) 31 | } 32 | 33 | public func setRotate(_ angle: CGFloat) { 34 | inputTransform.value = inputTransform.value.rotated(by: angle) 35 | } 36 | 37 | public func invert() { 38 | inputTransform.value = inputTransform.value.inverted() 39 | } 40 | 41 | public func concat(_ transform: CGAffineTransform) { 42 | inputTransform.value = inputTransform.value.concatenating(transform) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Blur.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Blur.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | @available(iOS 9.0, *) 14 | public struct BoxBlur: Filterable, FilterInputCollectionType, 15 | InputRadiusAvailable { 16 | 17 | public let filter: CIFilter = CIFilter(name: "CIBoxBlur")! 18 | 19 | public let inputRadius: ScalarInput 20 | 21 | public init() { 22 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 23 | } 24 | 25 | public func inputs() -> [FilterInputable] { 26 | return [ 27 | self.inputRadius 28 | ] 29 | } 30 | } 31 | 32 | @available(iOS 9.0, *) 33 | public struct DiscBlur: Filterable, FilterInputCollectionType, 34 | InputRadiusAvailable { 35 | 36 | public let filter: CIFilter = CIFilter(name: "CIDiscBlur")! 37 | 38 | public let inputRadius: ScalarInput 39 | 40 | public init() { 41 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 42 | } 43 | 44 | public func inputs() -> [FilterInputable] { 45 | return [ 46 | self.inputRadius 47 | ] 48 | } 49 | } 50 | 51 | public struct GaussianBlur: Filterable, FilterInputCollectionType, 52 | InputRadiusAvailable { 53 | 54 | public let filter: CIFilter = CIFilter(name: "CIGaussianBlur")! 55 | 56 | public let inputRadius: ScalarInput 57 | 58 | public init() { 59 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 60 | } 61 | 62 | public func inputs() -> [FilterInputable] { 63 | return [ 64 | self.inputRadius 65 | ] 66 | } 67 | } 68 | 69 | @available(iOS 9.0, *) 70 | public struct Median: Filterable { 71 | 72 | public let filter: CIFilter = CIFilter(name: "CIMedianFilter")! 73 | 74 | public init() {} 75 | } 76 | 77 | @available(iOS 9.0, *) 78 | public struct MotionBlur: Filterable, FilterInputCollectionType, 79 | InputRadiusAvailable, InputAngleAvailable { 80 | 81 | public let filter: CIFilter = CIFilter(name: "CIMotionBlur")! 82 | 83 | public let inputRadius: ScalarInput 84 | public let inputAngle: ScalarInput 85 | 86 | public init() { 87 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 88 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 89 | } 90 | 91 | public func inputs() -> [FilterInputable] { 92 | return [ 93 | self.inputRadius, 94 | self.inputAngle 95 | ] 96 | } 97 | } 98 | 99 | @available(iOS 9.0, *) 100 | public struct NoiseReduction: Filterable, FilterInputCollectionType, 101 | InputNoiseLevelAvailable, InputSharpnessAvailable { 102 | 103 | public let filter: CIFilter = CIFilter(name: "CINoiseReduction")! 104 | 105 | public let inputNoiseLevel: ScalarInput 106 | public let inputSharpness: ScalarInput 107 | 108 | public init() { 109 | self.inputNoiseLevel = ScalarInput(filter: self.filter, key: "inputNoiseLevel") 110 | self.inputSharpness = ScalarInput(filter: self.filter, key: kCIInputSharpnessKey) 111 | } 112 | 113 | public func inputs() -> [FilterInputable] { 114 | return [ 115 | self.inputNoiseLevel, 116 | self.inputSharpness 117 | ] 118 | } 119 | } 120 | 121 | @available(iOS 9.0, *) 122 | public struct ZoomBlur: Filterable, FilterInputCollectionType, 123 | InputAmountAvailable, InputCenterAvailable { 124 | 125 | public let filter: CIFilter = CIFilter(name: "CIZoomBlur")! 126 | 127 | public let inputAmount: ScalarInput 128 | public let inputCenter: VectorInput 129 | 130 | public init(imageSize: CGSize) { 131 | self.inputAmount = ScalarInput(filter: self.filter, key: "inputAmount") 132 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 133 | } 134 | 135 | public func inputs() -> [FilterInputable] { 136 | return [ 137 | self.inputAmount, 138 | self.inputCenter 139 | ] 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Sources/BooleanInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BooleanInput.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/24. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public final class BooleanInput: FilterInputable { 14 | 15 | public let key: String 16 | public var value: Bool 17 | 18 | fileprivate let initialValue: Bool 19 | 20 | public init(filter: CIFilter, key: String) { 21 | self.key = key 22 | let attributes = filter.attributes[key] as? [String : AnyObject] 23 | let _value = attributes?["CIAttributeDefault"] as? NSNumber ?? NSNumber(value: true as Bool) 24 | self.value = _value.boolValue 25 | self.initialValue = self.value 26 | } 27 | 28 | public func sliders() -> [Slider] { 29 | return [] 30 | } 31 | 32 | public func setInput(_ filter: CIFilter) { 33 | filter.setValue(self.value, forKey: self.key) 34 | } 35 | 36 | public func resetValue() { 37 | self.value = self.initialValue 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/BooleanInputProtocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BooleanInputProtocols.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/24. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | // MARK: - CompactStyle 14 | 15 | public protocol InputCompactStyleAvailable { 16 | 17 | var inputCompactStyle: BooleanInput { get } 18 | } 19 | 20 | extension InputCompactStyleAvailable { 21 | 22 | public var compactStyle: Bool { 23 | get { return inputCompactStyle.value } 24 | set { inputCompactStyle.value = newValue } 25 | } 26 | } -------------------------------------------------------------------------------- /Sources/CIImage+Cmg.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CIImage+Convert.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | extension CIImage { 14 | 15 | static func generate(_ uiImage: UIImage) -> CIImage? { 16 | var ciImage: CIImage? = uiImage.ciImage 17 | if ciImage == nil { 18 | guard let _cgImage = uiImage.cgImage else { return nil } 19 | ciImage = CIImage(cgImage: _cgImage) 20 | } 21 | return ciImage 22 | } 23 | 24 | func generateUIImage() -> UIImage { 25 | let cgImage = Context.ciContext.createCGImage(self, from: self.extent) 26 | 27 | return UIImage(cgImage: cgImage!, scale: UIScreen.main.scale, orientation: .up) 28 | } 29 | 30 | func generateUIImage(_ originalImage: UIImage) -> UIImage { 31 | var extent = self.extent 32 | if extent.origin.x < 0.0 || extent.origin.y < 0.0 { 33 | extent = CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.height) 34 | } 35 | let cgImage = Context.ciContext.createCGImage(self, from: extent) 36 | 37 | return UIImage(cgImage: cgImage!, scale: originalImage.scale, orientation: originalImage.imageOrientation) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Cmg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Cmg.h 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Cmg. 12 | FOUNDATION_EXPORT double CmgVersionNumber; 13 | 14 | //! Project version string for Cmg. 15 | FOUNDATION_EXPORT const unsigned char CmgVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/Cmg.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Cmg.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | // MARK: - Processable 14 | 15 | public protocol Processable { 16 | 17 | var name: String { get } 18 | 19 | func processing(_ uiImage: UIImage) -> UIImage? 20 | func processing(_ ciImage: CIImage?) -> CIImage? 21 | func processingIntoCIImage(_ uiImage: UIImage) -> CIImage? 22 | 23 | // slider support 24 | func sliders() -> [Slider] 25 | } 26 | 27 | extension Processable { 28 | 29 | public func processing(_ uiImage: UIImage) -> UIImage? { 30 | let ciImage = CIImage.generate(uiImage) 31 | return self.processing(ciImage)?.generateUIImage(uiImage) 32 | } 33 | 34 | public func processingIntoCIImage(_ uiImage: UIImage) -> CIImage? { 35 | let ciImage = CIImage.generate(uiImage) 36 | return self.processing(ciImage) 37 | } 38 | 39 | public func sliders() -> [Slider] { return [] } 40 | 41 | public func resetSilderValues() { 42 | self.sliders().forEach() { $0.resetValue() } 43 | } 44 | } 45 | 46 | // MARK: - Filterable 47 | 48 | public protocol Filterable: Processable { 49 | 50 | var filter: CIFilter { get } 51 | 52 | func setupFilter() 53 | } 54 | 55 | extension Filterable { 56 | 57 | public var name: String { return self.filter.name } 58 | 59 | public func processing(_ ciImage: CIImage?) -> CIImage? { 60 | guard let _ciImage = ciImage else { return nil } 61 | 62 | self.filter.setDefaults() 63 | self.filter.setValue(_ciImage, forKey: kCIInputImageKey) 64 | self.setupFilter() 65 | let outputImage = self.filter.outputImage 66 | 67 | return outputImage 68 | } 69 | 70 | public func setupFilter() {} 71 | } 72 | 73 | // MARK: - FilterInputable 74 | 75 | public protocol FilterInputable { 76 | 77 | func sliders() -> [Slider] 78 | 79 | func setInput(_ filter: CIFilter) 80 | 81 | func resetValue() 82 | } 83 | 84 | // MARK: - FilterInputCollectionType 85 | 86 | public protocol FilterInputCollectionType { 87 | 88 | func inputs() -> [FilterInputable] 89 | } 90 | 91 | extension FilterInputCollectionType { 92 | 93 | public func resetSilderValues() { 94 | self.inputs().forEach() { $0.resetValue() } 95 | } 96 | } 97 | 98 | extension Filterable where Self: FilterInputCollectionType { 99 | 100 | public func sliders() -> [Slider] { 101 | let value: [Slider] = [] 102 | return self.inputs().reduce(value) { $0 + $1.sliders() } 103 | } 104 | 105 | public func setupFilter() { 106 | self.inputs().forEach() { $0.setInput(self.filter) } 107 | } 108 | } 109 | 110 | // MARK: - InputImageUnusable 111 | 112 | public protocol InputImageUnusable: Filterable {} 113 | 114 | extension InputImageUnusable { 115 | 116 | public func processing() -> UIImage? { 117 | return self.processing(ciImage: nil)?.generateUIImage() 118 | } 119 | 120 | public func processing(_ uiImage: UIImage) -> UIImage? { 121 | return self.processing(ciImage: nil)?.generateUIImage(uiImage) 122 | } 123 | 124 | public func processing(ciImage: CIImage?) -> CIImage? { 125 | self.filter.setDefaults() 126 | self.setupFilter() 127 | let outputImage = self.filter.outputImage 128 | 129 | return outputImage 130 | } 131 | } 132 | 133 | -------------------------------------------------------------------------------- /Sources/ColorAdjustment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorAdjustment.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public struct ColorClamp: Filterable, FilterInputCollectionType, 14 | InputMinComponentsAvailable, InputMaxComponentsAvailable { 15 | 16 | public let filter: CIFilter = CIFilter(name: "CIColorClamp")! 17 | 18 | public let inputMinComponents: VectorInput 19 | public let inputMaxComponents: VectorInput 20 | 21 | public init() { 22 | self.inputMinComponents = VectorInput(.color, self.filter, "inputMinComponents") 23 | self.inputMaxComponents = VectorInput(.color, self.filter, "inputMaxComponents") 24 | } 25 | 26 | public func inputs() -> [FilterInputable] { 27 | return [ 28 | self.inputMinComponents, 29 | self.inputMaxComponents 30 | ] 31 | } 32 | } 33 | 34 | public struct ColorControls: Filterable, FilterInputCollectionType, 35 | InputSaturationAvailable, InputBrightnessAvailable, InputContrastAvailable { 36 | 37 | public let filter: CIFilter = CIFilter(name: "CIColorControls")! 38 | 39 | public let inputSaturation: ScalarInput 40 | public let inputBrightness: ScalarInput 41 | public let inputContrast: ScalarInput 42 | 43 | public init() { 44 | self.inputSaturation = ScalarInput(filter: self.filter, key: kCIInputSaturationKey) 45 | self.inputBrightness = ScalarInput(filter: self.filter, key: kCIInputBrightnessKey) 46 | self.inputContrast = ScalarInput(filter: self.filter, key: kCIInputContrastKey) 47 | } 48 | 49 | public func inputs() -> [FilterInputable] { 50 | return [ 51 | self.inputSaturation, 52 | self.inputBrightness, 53 | self.inputContrast 54 | ] 55 | } 56 | } 57 | 58 | public struct ColorMatrix: Filterable, FilterInputCollectionType, 59 | InputRVectorAvailable, InputGVectorAvailable, InputBVectorAvailable, InputAVectorAvailable, InputBiasVectorAvailable { 60 | 61 | public let filter: CIFilter = CIFilter(name: "CIColorMatrix")! 62 | 63 | public let inputRVector: VectorInput 64 | public let inputGVector: VectorInput 65 | public let inputBVector: VectorInput 66 | public let inputAVector: VectorInput 67 | public let inputBiasVector: VectorInput 68 | 69 | public init() { 70 | self.inputRVector = VectorInput(.color, self.filter, "inputRVector") 71 | self.inputGVector = VectorInput(.color, self.filter, "inputGVector") 72 | self.inputBVector = VectorInput(.color, self.filter, "inputBVector") 73 | self.inputAVector = VectorInput(.color, self.filter, "inputAVector") 74 | self.inputBiasVector = VectorInput(.color, self.filter, "inputBiasVector") 75 | } 76 | 77 | public func inputs() -> [FilterInputable] { 78 | return [ 79 | self.inputRVector, 80 | self.inputGVector, 81 | self.inputBVector, 82 | self.inputAVector, 83 | self.inputBiasVector 84 | ] 85 | } 86 | } 87 | 88 | public struct ColorPolynomial: Filterable, FilterInputCollectionType, 89 | InputRedCoefficientsAvailable, InputGreenCoefficientsAvailable, InputBlueCoefficientsAvailable, InputAlphaCoefficientsAvailable { 90 | 91 | public let filter: CIFilter = CIFilter(name: "CIColorPolynomial")! 92 | 93 | public let inputRedCoefficients: VectorInput 94 | public let inputGreenCoefficients: VectorInput 95 | public let inputBlueCoefficients: VectorInput 96 | public let inputAlphaCoefficients: VectorInput 97 | 98 | public init() { 99 | self.inputRedCoefficients = VectorInput(.color, self.filter, "inputRedCoefficients") 100 | self.inputGreenCoefficients = VectorInput(.color, self.filter, "inputGreenCoefficients") 101 | self.inputBlueCoefficients = VectorInput(.color, self.filter, "inputBlueCoefficients") 102 | self.inputAlphaCoefficients = VectorInput(.color, self.filter, "inputAlphaCoefficients") 103 | } 104 | 105 | public func inputs() -> [FilterInputable] { 106 | return [ 107 | self.inputRedCoefficients, 108 | self.inputGreenCoefficients, 109 | self.inputBlueCoefficients, 110 | self.inputAlphaCoefficients 111 | ] 112 | } 113 | } 114 | 115 | public struct ExposureAdjust: Filterable, FilterInputCollectionType, 116 | InputEVAvailable { 117 | 118 | public let filter: CIFilter = CIFilter(name: "CIExposureAdjust")! 119 | 120 | public let inputEV: ScalarInput 121 | 122 | public init() { 123 | self.inputEV = ScalarInput(filter: self.filter, key: kCIInputEVKey) 124 | } 125 | 126 | public func inputs() -> [FilterInputable] { 127 | return [ 128 | self.inputEV 129 | ] 130 | } 131 | } 132 | 133 | public struct GammaAdjust: Filterable, FilterInputCollectionType, 134 | InputPowerAvailable { 135 | 136 | public let filter: CIFilter = CIFilter(name: "CIGammaAdjust")! 137 | 138 | public let inputPower: ScalarInput 139 | 140 | public init() { 141 | self.inputPower = ScalarInput(filter: self.filter, key: "inputPower") 142 | } 143 | 144 | public func inputs() -> [FilterInputable] { 145 | return [ 146 | self.inputPower 147 | ] 148 | } 149 | } 150 | 151 | public struct HueAdjust: Filterable, FilterInputCollectionType, 152 | InputAngleAvailable { 153 | 154 | public let filter: CIFilter = CIFilter(name: "CIHueAdjust")! 155 | 156 | public let inputAngle: ScalarInput 157 | 158 | public init() { 159 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 160 | } 161 | 162 | public func inputs() -> [FilterInputable] { 163 | return [ 164 | self.inputAngle 165 | ] 166 | } 167 | } 168 | 169 | public struct LinearToSRGBToneCurve: Filterable { 170 | 171 | public let filter: CIFilter = CIFilter(name: "CILinearToSRGBToneCurve")! 172 | 173 | public init() {} 174 | } 175 | 176 | public struct SRGBToneCurveToLinear: Filterable { 177 | 178 | public let filter: CIFilter = CIFilter(name: "CISRGBToneCurveToLinear")! 179 | 180 | public init() {} 181 | } 182 | 183 | public struct TemperatureAndTint: Filterable, 184 | InputNeutralAvailable, InputTargetNeutralAvailable { 185 | 186 | public let filter: CIFilter = CIFilter(name: "CITemperatureAndTint")! 187 | 188 | public var inputNeutral: VectorInput 189 | public var inputTargetNeutral: VectorInput 190 | 191 | public init() { 192 | self.inputNeutral = VectorInput(.other(count: 2), self.filter, "inputNeutral") 193 | self.inputTargetNeutral = VectorInput(.other(count: 2), self.filter, "inputTargetNeutral") 194 | } 195 | 196 | public func inputs() -> [FilterInputable] { 197 | return [ 198 | self.inputNeutral, 199 | self.inputTargetNeutral 200 | ] 201 | } 202 | } 203 | 204 | public struct ToneCurve: Filterable, FilterInputCollectionType, 205 | InputPoint0Available, InputPoint1Available, InputPoint2Available, InputPoint3Available, InputPoint4Available { 206 | 207 | public let filter: CIFilter = CIFilter(name: "CIToneCurve")! 208 | 209 | public let inputPoint0: VectorInput 210 | public let inputPoint1: VectorInput 211 | public let inputPoint2: VectorInput 212 | public let inputPoint3: VectorInput 213 | public let inputPoint4: VectorInput 214 | 215 | public init() { 216 | self.inputPoint0 = VectorInput(.colorOffset, self.filter, "inputPoint0") 217 | self.inputPoint1 = VectorInput(.colorOffset, self.filter, "inputPoint1") 218 | self.inputPoint2 = VectorInput(.colorOffset, self.filter, "inputPoint2") 219 | self.inputPoint3 = VectorInput(.colorOffset, self.filter, "inputPoint3") 220 | self.inputPoint4 = VectorInput(.colorOffset, self.filter, "inputPoint4") 221 | } 222 | 223 | public func inputs() -> [FilterInputable] { 224 | return [ 225 | self.inputPoint0, 226 | self.inputPoint1, 227 | self.inputPoint2, 228 | self.inputPoint3, 229 | self.inputPoint4 230 | ] 231 | } 232 | } 233 | 234 | public struct Vibrance: Filterable, FilterInputCollectionType, 235 | InputAmountAvailable { 236 | 237 | public let filter: CIFilter = CIFilter(name: "CIVibrance")! 238 | 239 | public let inputAmount: ScalarInput 240 | 241 | public init() { 242 | self.inputAmount = ScalarInput(filter: self.filter, key: "inputAmount") 243 | } 244 | 245 | public func inputs() -> [FilterInputable] { 246 | return [ 247 | self.inputAmount 248 | ] 249 | } 250 | } 251 | 252 | public struct WhitePointAdjust: Filterable, FilterInputCollectionType, 253 | InputColorAvailable { 254 | 255 | public let filter: CIFilter = CIFilter(name: "CIWhitePointAdjust")! 256 | 257 | public let inputColor: ColorInput 258 | 259 | public init() { 260 | self.inputColor = ColorInput(filter: self.filter, key: kCIInputColorKey) 261 | } 262 | 263 | public func inputs() -> [FilterInputable] { 264 | return [ 265 | self.inputColor 266 | ] 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /Sources/ColorEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorEffect.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public struct ColorCrossPolynomial: Filterable, FilterInputCollectionType, 14 | InputRedCoefficientsAvailable, InputGreenCoefficientsAvailable, InputBlueCoefficientsAvailable { 15 | 16 | public let filter: CIFilter = CIFilter(name: "CIColorCrossPolynomial")! 17 | 18 | public let inputRedCoefficients: VectorInput 19 | public let inputGreenCoefficients: VectorInput 20 | public let inputBlueCoefficients: VectorInput 21 | 22 | public init() { 23 | self.inputRedCoefficients = VectorInput(.other(count: 10), self.filter, "inputRedCoefficients") 24 | self.inputGreenCoefficients = VectorInput(.other(count: 10), self.filter, "inputGreenCoefficients") 25 | self.inputBlueCoefficients = VectorInput(.other(count: 10), self.filter, "inputBlueCoefficients") 26 | } 27 | 28 | public func inputs() -> [FilterInputable] { 29 | return [ 30 | self.inputRedCoefficients, 31 | self.inputGreenCoefficients, 32 | self.inputBlueCoefficients 33 | ] 34 | } 35 | } 36 | 37 | public struct ColorCube: Filterable, InputCubeDimensionAvailable { 38 | 39 | public let filter: CIFilter = CIFilter(name: "CIColorCube")! 40 | 41 | public let inputCubeDimension: ScalarInput 42 | public var cubeData: Data 43 | 44 | public init(inputCubeData: Data) { 45 | self.inputCubeDimension = ScalarInput(filter: self.filter, key: "inputCubeDimension") 46 | self.cubeData = inputCubeData 47 | } 48 | 49 | public func sliders() -> [Slider] { 50 | return self.inputCubeDimension.sliders() 51 | } 52 | 53 | public func setupFilter() { 54 | self.inputCubeDimension.setInput(self.filter) 55 | self.filter.setValue(self.cubeData, forKey: "inputCubeData") 56 | } 57 | } 58 | 59 | public struct ColorCubeWithColorSpace: Filterable, InputCubeDimensionAvailable { 60 | 61 | public let filter: CIFilter = CIFilter(name: "CIColorCubeWithColorSpace")! 62 | 63 | public let inputCubeDimension: ScalarInput 64 | public var cubeData: Data 65 | public var colorSpace: CGColorSpace 66 | 67 | public init(inputCubeData: Data, inputColorSpace: CGColorSpace) { 68 | self.inputCubeDimension = ScalarInput(filter: self.filter, key: "inputCubeDimension") 69 | self.cubeData = inputCubeData 70 | self.colorSpace = inputColorSpace 71 | } 72 | 73 | public func sliders() -> [Slider] { 74 | return self.inputCubeDimension.sliders() 75 | } 76 | 77 | public func setupFilter() { 78 | self.inputCubeDimension.setInput(self.filter) 79 | self.filter.setValue(self.cubeData, forKey: "inputCubeData") 80 | self.filter.setValue(self.colorSpace, forKey: "inputColorSpace") 81 | } 82 | } 83 | 84 | public struct ColorInvert: Filterable { 85 | 86 | public let filter: CIFilter = CIFilter(name: "CIColorInvert")! 87 | 88 | public init() {} 89 | } 90 | 91 | public struct ColorMap: Filterable, FilterInputCollectionType, 92 | InputImageAvailable { 93 | 94 | public let filter: CIFilter = CIFilter(name: "CIColorMap")! 95 | 96 | public let inputImage: ImageInput 97 | 98 | public init(uiImage: UIImage) { 99 | self.inputImage = ImageInput(image: CIImage(cgImage: uiImage.cgImage!), key: "inputGradientImage") 100 | } 101 | 102 | public init(ciImage: CIImage) { 103 | self.inputImage = ImageInput(image: ciImage, key: "inputGradientImage") 104 | } 105 | 106 | public func inputs() -> [FilterInputable] { 107 | return [ 108 | self.inputImage 109 | ] 110 | } 111 | } 112 | 113 | public struct ColorMonochrome: Filterable, FilterInputCollectionType, 114 | InputIntensityAvailable, InputColorAvailable { 115 | 116 | public let filter: CIFilter = CIFilter(name: "CIColorMonochrome")! 117 | 118 | public let inputIntensity: ScalarInput 119 | public let inputColor: ColorInput 120 | 121 | public init() { 122 | self.inputIntensity = ScalarInput(filter: self.filter, key: kCIInputIntensityKey) 123 | self.inputColor = ColorInput(filter: self.filter, key: kCIInputColorKey) 124 | } 125 | 126 | public func inputs() -> [FilterInputable] { 127 | return [ 128 | self.inputIntensity, 129 | self.inputColor 130 | ] 131 | } 132 | } 133 | 134 | public struct ColorPosterize: Filterable, FilterInputCollectionType, 135 | InputLevelsAvailable { 136 | 137 | public let filter: CIFilter = CIFilter(name: "CIColorPosterize")! 138 | 139 | public let inputLevels: ScalarInput 140 | 141 | public init() { 142 | self.inputLevels = ScalarInput(filter: self.filter, key: "inputLevels") 143 | } 144 | 145 | public func inputs() -> [FilterInputable] { 146 | return [ 147 | self.inputLevels 148 | ] 149 | } 150 | } 151 | 152 | public struct FalseColor: Filterable, FilterInputCollectionType, 153 | InputColor0Available, InputColor1Available { 154 | 155 | public let filter: CIFilter = CIFilter(name: "CIFalseColor")! 156 | 157 | public let inputColor0: ColorInput 158 | public let inputColor1: ColorInput 159 | 160 | public init() { 161 | self.inputColor0 = ColorInput(filter: self.filter, key: "inputColor0") 162 | self.inputColor1 = ColorInput(filter: self.filter, key: "inputColor1") 163 | } 164 | 165 | public func inputs() -> [FilterInputable] { 166 | return [ 167 | self.inputColor0, 168 | self.inputColor1 169 | ] 170 | } 171 | } 172 | 173 | public struct MaskToAlpha: Filterable { 174 | 175 | public let filter: CIFilter = CIFilter(name: "CIMaskToAlpha")! 176 | 177 | public init() {} 178 | } 179 | 180 | public struct MaximumComponent: Filterable { 181 | 182 | public let filter: CIFilter = CIFilter(name: "CIMaximumComponent")! 183 | 184 | public init() {} 185 | } 186 | 187 | public struct MinimumComponent: Filterable { 188 | 189 | public let filter: CIFilter = CIFilter(name: "CIMinimumComponent")! 190 | 191 | public init() {} 192 | } 193 | 194 | public struct PhotoEffectChrome: Filterable { 195 | 196 | public let filter: CIFilter = CIFilter(name: "CIPhotoEffectChrome")! 197 | 198 | public init() {} 199 | } 200 | 201 | public struct PhotoEffectFade: Filterable { 202 | 203 | public let filter: CIFilter = CIFilter(name: "CIPhotoEffectFade")! 204 | 205 | public init() {} 206 | } 207 | 208 | public struct PhotoEffectInstant: Filterable { 209 | 210 | public let filter: CIFilter = CIFilter(name: "CIPhotoEffectInstant")! 211 | 212 | public init() {} 213 | } 214 | 215 | public struct PhotoEffectMono: Filterable { 216 | 217 | public let filter: CIFilter = CIFilter(name: "CIPhotoEffectMono")! 218 | 219 | public init() {} 220 | } 221 | 222 | public struct PhotoEffectNoir: Filterable { 223 | 224 | public let filter: CIFilter = CIFilter(name: "CIPhotoEffectNoir")! 225 | 226 | public init() {} 227 | } 228 | 229 | public struct PhotoEffectProcess: Filterable { 230 | 231 | public let filter: CIFilter = CIFilter(name: "CIPhotoEffectProcess")! 232 | 233 | public init() {} 234 | } 235 | 236 | public struct PhotoEffectTonal: Filterable { 237 | 238 | public let filter: CIFilter = CIFilter(name: "CIPhotoEffectTonal")! 239 | 240 | public init() {} 241 | } 242 | 243 | public struct PhotoEffectTransfer: Filterable { 244 | 245 | public let filter: CIFilter = CIFilter(name: "CIPhotoEffectTransfer")! 246 | 247 | public init() {} 248 | } 249 | 250 | public struct SepiaTone: Filterable, FilterInputCollectionType, 251 | InputIntensityAvailable { 252 | 253 | public let filter: CIFilter = CIFilter(name: "CISepiaTone")! 254 | 255 | public let inputIntensity: ScalarInput 256 | 257 | public init() { 258 | self.inputIntensity = ScalarInput(filter: self.filter, key: kCIInputIntensityKey) 259 | } 260 | 261 | public func inputs() -> [FilterInputable] { 262 | return [ 263 | self.inputIntensity 264 | ] 265 | } 266 | } 267 | 268 | public struct Vignette: Filterable, FilterInputCollectionType, 269 | InputRadiusAvailable, InputIntensityAvailable { 270 | 271 | public let filter: CIFilter = CIFilter(name: "CIVignette")! 272 | 273 | public let inputRadius: ScalarInput 274 | public let inputIntensity: ScalarInput 275 | 276 | public init() { 277 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 278 | self.inputIntensity = ScalarInput(filter: self.filter, key: kCIInputIntensityKey) 279 | } 280 | 281 | public func inputs() -> [FilterInputable] { 282 | return [ 283 | self.inputRadius, 284 | self.inputIntensity 285 | ] 286 | } 287 | } 288 | 289 | public struct VignetteEffect: Filterable, FilterInputCollectionType, 290 | InputIntensityAvailable, InputCenterAvailable { 291 | 292 | public let filter: CIFilter = CIFilter(name: "CIVignetteEffect")! 293 | 294 | public let inputIntensity: ScalarInput 295 | public let inputCenter: VectorInput 296 | 297 | public init(imageSize: CGSize) { 298 | self.inputIntensity = ScalarInput(filter: self.filter, key: kCIInputIntensityKey) 299 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 300 | } 301 | 302 | public func inputs() -> [FilterInputable] { 303 | return [ 304 | self.inputIntensity, 305 | self.inputCenter 306 | ] 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /Sources/ColorInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorInput.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public final class ColorInput: FilterInputable { 14 | 15 | public let key: String 16 | public fileprivate(set) var red: CGFloat = 0.0 17 | public fileprivate(set) var green: CGFloat = 0.0 18 | public fileprivate(set) var blue: CGFloat = 0.0 19 | public fileprivate(set) var alpha: CGFloat = 0.0 20 | 21 | fileprivate let initialValue: UIColor 22 | 23 | public init(filter: CIFilter, key: String) { 24 | self.key = key 25 | 26 | let attributes = filter.attributes[key] as? [String : AnyObject] 27 | let color = attributes?[kCIAttributeDefault] as? CIColor 28 | if let _color = color { 29 | self.red = _color.red * 255.0 30 | self.green = _color.green * 255.0 31 | self.blue = _color.blue * 255.0 32 | self.alpha = _color.alpha * 255 33 | } 34 | 35 | self.initialValue = UIColor(red: self.red, green: self.green, blue: self.blue, alpha: self.alpha) 36 | } 37 | 38 | public func sliders() -> [Slider] { 39 | return [ 40 | Slider("red", Range(0.0, 255.0, Float(self.red))) { [weak self] value in 41 | self?.red = CGFloat(value) 42 | }, 43 | Slider("green", Range(0.0, 255.0, Float(self.green))) { [weak self] value in 44 | self?.green = CGFloat(value) 45 | }, 46 | Slider("blue", Range(0.0, 255.0, Float(self.blue))) { [weak self] value in 47 | self?.blue = CGFloat(value) 48 | }, 49 | Slider("alpha", Range(0.0, 255.0, Float(self.alpha))) { [weak self] value in 50 | self?.alpha = CGFloat(value) 51 | }, 52 | ] 53 | } 54 | 55 | public func setInput(_ filter: CIFilter) { 56 | filter.setValue(CIColor(cgColor: self.rgbaCcolor.cgColor), forKey: self.key) 57 | } 58 | 59 | public func resetValue() { 60 | self.setColor(self.initialValue) 61 | } 62 | } 63 | 64 | extension ColorInput { 65 | 66 | public func setRed(_ red: CGFloat) { 67 | var v: CGFloat = red 68 | if v > 255.0 { v = 255.0 } 69 | if v < 0.0 { v = 0.0 } 70 | self.red = v 71 | } 72 | 73 | public func setGreen(_ green: CGFloat) { 74 | var v: CGFloat = green 75 | if v > 255.0 { v = 255.0 } 76 | if v < 0.0 { v = 0.0 } 77 | self.green = v 78 | } 79 | 80 | public func setBlue(_ blue: CGFloat) { 81 | var v: CGFloat = blue 82 | if v > 255.0 { v = 255.0 } 83 | if v < 0.0 { v = 0.0 } 84 | self.blue = v 85 | } 86 | 87 | public func setAlpha(_ alpha: CGFloat) { 88 | var v: CGFloat = alpha 89 | if v > 255.0 { v = 255.0 } 90 | if v < 0.0 { v = 0.0 } 91 | self.alpha = v 92 | } 93 | 94 | public func setColor(_ color: UIColor) { 95 | func zeroIfDodgy(_ value: CGFloat) -> CGFloat { 96 | return value.isNaN || value.isInfinite ? 0.0 : value 97 | } 98 | 99 | let colorRef = color.cgColor.components 100 | if color.cgColor.numberOfComponents == 4 { 101 | self.red = 255 * zeroIfDodgy((colorRef?[0])!) 102 | self.green = 255 * zeroIfDodgy((colorRef?[1])!) 103 | self.blue = 255 * zeroIfDodgy((colorRef?[2])!) 104 | self.alpha = 255 * zeroIfDodgy((colorRef?[3])!) 105 | } else if color.cgColor.numberOfComponents == 2 { 106 | let greyComponent = 255 * zeroIfDodgy((colorRef?[0])!) 107 | self.red = greyComponent 108 | self.green = greyComponent 109 | self.blue = greyComponent 110 | self.alpha = 255 * zeroIfDodgy((colorRef?[1])!) 111 | } 112 | } 113 | 114 | public var rgbaCcolor: UIColor { 115 | return UIColor(red: self.red / 255.0, 116 | green: self.green / 255.0, 117 | blue: self.blue / 255.0, 118 | alpha: self.alpha / 255.0 119 | ) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Sources/ColorInputProtocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // inputColorProtocols.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/19. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | // MARK: - Color 14 | 15 | public protocol InputColorAvailable { 16 | 17 | var inputColor: ColorInput { get } 18 | } 19 | 20 | extension InputColorAvailable { 21 | 22 | public var red: CGFloat { 23 | get { return inputColor.red } 24 | set { inputColor.setRed(newValue) } 25 | } 26 | public var green: CGFloat { 27 | get { return inputColor.green } 28 | set { inputColor.setGreen(newValue) } 29 | } 30 | public var blue: CGFloat { 31 | get { return inputColor.blue } 32 | set { inputColor.setBlue(newValue) } 33 | } 34 | public var alpha: CGFloat { 35 | get { return inputColor.alpha } 36 | set { inputColor.setAlpha(newValue) } 37 | } 38 | 39 | public var color: UIColor { 40 | get { return inputColor.rgbaCcolor } 41 | set { inputColor.setColor(newValue) } 42 | } 43 | } 44 | 45 | // MARK: - Color0 46 | 47 | public protocol InputColor0Available { 48 | 49 | var inputColor0: ColorInput { get } 50 | } 51 | 52 | extension InputColor0Available { 53 | 54 | public var red_0: CGFloat { 55 | get { return inputColor0.red } 56 | set { inputColor0.setRed(newValue) } 57 | } 58 | public var green_0: CGFloat { 59 | get { return inputColor0.green } 60 | set { inputColor0.setGreen(newValue) } 61 | } 62 | public var blue_0: CGFloat { 63 | get { return inputColor0.blue } 64 | set { inputColor0.setBlue(newValue) } 65 | } 66 | public var alpha_0: CGFloat { 67 | get { return inputColor0.alpha } 68 | set { inputColor0.setAlpha(newValue) } 69 | } 70 | 71 | public var color_0: UIColor { 72 | get { return inputColor0.rgbaCcolor } 73 | set { inputColor0.setColor(newValue) } 74 | } 75 | } 76 | 77 | // MARK: - Color1 78 | 79 | public protocol InputColor1Available { 80 | 81 | var inputColor1: ColorInput { get } 82 | } 83 | 84 | extension InputColor1Available { 85 | 86 | public var red_1: CGFloat { 87 | get { return inputColor1.red } 88 | set { inputColor1.setRed(newValue) } 89 | } 90 | public var green_1: CGFloat { 91 | get { return inputColor1.green } 92 | set { inputColor1.setGreen(newValue) } 93 | } 94 | public var blue_1: CGFloat { 95 | get { return inputColor1.blue } 96 | set { inputColor1.setBlue(newValue) } 97 | } 98 | public var alpha_1: CGFloat { 99 | get { return inputColor1.alpha } 100 | set { inputColor1.setAlpha(newValue) } 101 | } 102 | 103 | public var color_1: UIColor { 104 | get { return inputColor1.rgbaCcolor } 105 | set { inputColor1.setColor(newValue) } 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /Sources/CompositeOperation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CompositeOperation.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public protocol CompositeFilterable: Filterable { 14 | 15 | var compositeOperator: CompositeOperator { get } 16 | 17 | init(backgroundImage: CIImage) 18 | } 19 | 20 | extension CompositeFilterable { 21 | 22 | public var filter: CIFilter { return self.compositeOperator.filter } 23 | 24 | public var alpha: CGFloat { 25 | get { return self.compositeOperator.inputBackgroundImage.alpha } 26 | set { self.compositeOperator.inputBackgroundImage.updateAlpha(newValue) } 27 | } 28 | 29 | public func sliders() -> [Slider] { 30 | return self.compositeOperator.inputBackgroundImage.sliders() 31 | } 32 | 33 | public func setupFilter() { 34 | self.compositeOperator.inputBackgroundImage.setInput(self.filter) 35 | } 36 | 37 | public init(uiImage: UIImage) { 38 | self.init(backgroundImage: CIImage(cgImage: uiImage.cgImage!)) 39 | } 40 | } 41 | 42 | public final class CompositeOperator { 43 | 44 | public let filter: CIFilter 45 | public let inputBackgroundImage: ImageInput 46 | 47 | public init(filter: CIFilter, _ uiImage: UIImage) { 48 | self.filter = filter 49 | self.inputBackgroundImage = ImageInput(image: CIImage(cgImage: uiImage.cgImage!), key: kCIInputBackgroundImageKey) 50 | } 51 | 52 | public init(filter: CIFilter, _ ciImage: CIImage) { 53 | self.filter = filter 54 | self.inputBackgroundImage = ImageInput(image: ciImage, key: kCIInputBackgroundImageKey) 55 | } 56 | } 57 | 58 | public struct AdditionCompositing: CompositeFilterable { 59 | 60 | public let compositeOperator: CompositeOperator 61 | 62 | public init(backgroundImage: CIImage) { 63 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIAdditionCompositing")!, backgroundImage) 64 | } 65 | } 66 | 67 | public struct ColorBlendMode: CompositeFilterable { 68 | 69 | public let compositeOperator: CompositeOperator 70 | 71 | public init(backgroundImage: CIImage) { 72 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIColorBlendMode")!, backgroundImage) 73 | } 74 | } 75 | 76 | public struct ColorBurnBlendMode: CompositeFilterable { 77 | 78 | public let compositeOperator: CompositeOperator 79 | 80 | public init(backgroundImage: CIImage) { 81 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIColorBurnBlendMode")!, backgroundImage) 82 | } 83 | } 84 | 85 | public struct ColorDodgeBlendMode: CompositeFilterable { 86 | 87 | public let compositeOperator: CompositeOperator 88 | 89 | public init(backgroundImage: CIImage) { 90 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIColorDodgeBlendMode")!, backgroundImage) 91 | } 92 | } 93 | 94 | public struct DarkenBlendMode: CompositeFilterable { 95 | 96 | public let compositeOperator: CompositeOperator 97 | 98 | public init(backgroundImage: CIImage) { 99 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIDarkenBlendMode")!, backgroundImage) 100 | } 101 | } 102 | 103 | public struct DifferenceBlendMode: CompositeFilterable { 104 | 105 | public let compositeOperator: CompositeOperator 106 | 107 | public init(backgroundImage: CIImage) { 108 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIDifferenceBlendMode")!, backgroundImage) 109 | } 110 | } 111 | 112 | public struct DivideBlendMode: CompositeFilterable { 113 | 114 | public let compositeOperator: CompositeOperator 115 | 116 | public init(backgroundImage: CIImage) { 117 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIDivideBlendMode")!, backgroundImage) 118 | } 119 | } 120 | 121 | public struct ExclusionBlendMode: CompositeFilterable { 122 | 123 | public let compositeOperator: CompositeOperator 124 | 125 | public init(backgroundImage: CIImage) { 126 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIExclusionBlendMode")!, backgroundImage) 127 | } 128 | } 129 | 130 | public struct HardLightBlendMode: CompositeFilterable { 131 | 132 | public let compositeOperator: CompositeOperator 133 | 134 | public init(backgroundImage: CIImage) { 135 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIHardLightBlendMode")!, backgroundImage) 136 | } 137 | } 138 | 139 | public struct HueBlendMode: CompositeFilterable { 140 | 141 | public let compositeOperator: CompositeOperator 142 | 143 | public init(backgroundImage: CIImage) { 144 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIHueBlendMode")!, backgroundImage) 145 | } 146 | } 147 | 148 | public struct LightenBlendMode: CompositeFilterable { 149 | 150 | public let compositeOperator: CompositeOperator 151 | 152 | public init(backgroundImage: CIImage) { 153 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CILightenBlendMode")!, backgroundImage) 154 | } 155 | } 156 | 157 | public struct LinearBurnBlendMode: CompositeFilterable { 158 | 159 | public let compositeOperator: CompositeOperator 160 | 161 | public init(backgroundImage: CIImage) { 162 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CILinearBurnBlendMode")!, backgroundImage) 163 | } 164 | } 165 | 166 | public struct LinearDodgeBlendMode: CompositeFilterable { 167 | 168 | public let compositeOperator: CompositeOperator 169 | 170 | public init(backgroundImage: CIImage) { 171 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CILinearDodgeBlendMode")!, backgroundImage) 172 | } 173 | } 174 | 175 | public struct OverlayBlendMode: CompositeFilterable { 176 | 177 | public let compositeOperator: CompositeOperator 178 | 179 | public init(backgroundImage: CIImage) { 180 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIOverlayBlendMode")!, backgroundImage) 181 | } 182 | } 183 | 184 | public struct ScreenBlendMode: CompositeFilterable { 185 | 186 | public let compositeOperator: CompositeOperator 187 | 188 | public init(backgroundImage: CIImage) { 189 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CIScreenBlendMode")!, backgroundImage) 190 | } 191 | } 192 | 193 | public struct SourceAtopCompositing: CompositeFilterable { 194 | 195 | public let compositeOperator: CompositeOperator 196 | 197 | public init(backgroundImage: CIImage) { 198 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CISourceAtopCompositing")!, backgroundImage) 199 | } 200 | } 201 | 202 | public struct SourceOverCompositing: CompositeFilterable { 203 | 204 | public let compositeOperator: CompositeOperator 205 | 206 | public init(backgroundImage: CIImage) { 207 | self.compositeOperator = CompositeOperator.init(filter: CIFilter(name: "CISourceOverCompositing")!, backgroundImage) 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Sources/Context.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Context.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public final class Context { 14 | 15 | public static let shared = Context() 16 | 17 | public static var egleContext : EAGLContext { return Context.shared.egleContext ?? Context.shared.defaultEgleContext } 18 | public static var ciContext : CIContext { return Context.shared.ciContext ?? Context.shared.defaultCIContext } 19 | 20 | public static var options: [CIContextOption : Any] { 21 | #if targetEnvironment(simulator) 22 | return [ 23 | CIContextOption.priorityRequestLow : true, 24 | CIContextOption.workingColorSpace : NSNull(), 25 | CIContextOption.priorityRequestLow: true 26 | ] 27 | #else 28 | return [ 29 | CIContextOption.useSoftwareRenderer : false, 30 | CIContextOption.workingColorSpace : NSNull() 31 | ] 32 | #endif 33 | } 34 | 35 | public let defaultEgleContext : EAGLContext 36 | public let defaultCIContext : CIContext 37 | 38 | public var egleContext : EAGLContext? 39 | public var ciContext : CIContext? 40 | 41 | fileprivate init() { 42 | self.defaultEgleContext = EAGLContext(api: .openGLES2)! 43 | self.defaultCIContext = CIContext(eaglContext: self.defaultEgleContext, options: Context.options) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/DistortionEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DistortionEffect.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public struct BumpDistortion: Filterable, FilterInputCollectionType, 14 | InputCenterAvailable, InputRadiusAvailable, InputScaleAvailable { 15 | 16 | public let filter: CIFilter = CIFilter(name: "CIBumpDistortion")! 17 | 18 | public let inputCenter: VectorInput 19 | public let inputRadius: ScalarInput 20 | public let inputScale: ScalarInput 21 | 22 | public init(imageSize: CGSize) { 23 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 24 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 25 | self.inputScale = ScalarInput(filter: self.filter, key: kCIInputScaleKey) 26 | } 27 | 28 | public func inputs() -> [FilterInputable] { 29 | return [ 30 | self.inputCenter, 31 | self.inputRadius, 32 | self.inputScale 33 | ] 34 | } 35 | } 36 | 37 | public struct CircleSplashDistortion: Filterable, FilterInputCollectionType, 38 | InputCenterAvailable, InputRadiusAvailable { 39 | 40 | public let filter: CIFilter = CIFilter(name: "CICircleSplashDistortion")! 41 | 42 | public let inputCenter: VectorInput 43 | public let inputRadius: ScalarInput 44 | 45 | public init(imageSize: CGSize) { 46 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 47 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 48 | } 49 | 50 | public func inputs() -> [FilterInputable] { 51 | return [ 52 | self.inputCenter, 53 | self.inputRadius 54 | ] 55 | } 56 | } 57 | 58 | @available(iOS 9.0, *) 59 | public struct CircularWrap: Filterable, FilterInputCollectionType, 60 | InputCenterAvailable, InputRadiusAvailable, InputAngleAvailable { 61 | 62 | public let filter: CIFilter = CIFilter(name: "CICircularWrap")! 63 | 64 | public let inputCenter: VectorInput 65 | public let inputRadius: ScalarInput 66 | public let inputAngle: ScalarInput 67 | 68 | public init(imageSize: CGSize) { 69 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 70 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 71 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 72 | } 73 | 74 | public func inputs() -> [FilterInputable] { 75 | return [ 76 | self.inputCenter, 77 | self.inputRadius, 78 | self.inputAngle 79 | ] 80 | } 81 | } 82 | 83 | @available(iOS 9.0, *) 84 | public struct Droste: Filterable, FilterInputCollectionType, 85 | InputInsetPoint0Available, InputInsetPoint1Available, InputStrandsAvailable, 86 | InputPeriodicityAvailable, InputRotationAvailable, InputZoomAvailable { 87 | 88 | public let filter: CIFilter = CIFilter(name: "CIDroste")! 89 | 90 | public let inputInsetPoint0: VectorInput 91 | public let inputInsetPoint1: VectorInput 92 | public let inputStrands: ScalarInput 93 | public let inputPeriodicity: ScalarInput 94 | public let inputRotation: ScalarInput 95 | public let inputZoom: ScalarInput 96 | 97 | public init(imageSize: CGSize) { 98 | self.inputInsetPoint0 = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputInsetPoint0") 99 | self.inputInsetPoint1 = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputInsetPoint1") 100 | self.inputStrands = ScalarInput(filter: self.filter, key: "inputStrands") 101 | self.inputPeriodicity = ScalarInput(filter: self.filter, key: "inputPeriodicity") 102 | self.inputRotation = ScalarInput(filter: self.filter, key: "inputRotation") 103 | self.inputZoom = ScalarInput(filter: self.filter, key: "inputZoom") 104 | } 105 | 106 | public func inputs() -> [FilterInputable] { 107 | return [ 108 | self.inputInsetPoint0, 109 | self.inputInsetPoint1, 110 | self.inputStrands, 111 | self.inputPeriodicity, 112 | self.inputRotation, 113 | self.inputZoom 114 | ] 115 | } 116 | } 117 | 118 | @available(iOS 9.0, *) 119 | public struct DisplacementDistortion: Filterable, FilterInputCollectionType, 120 | InputDisplacementImageAvailable, InputScaleAvailable { 121 | 122 | public let filter: CIFilter = CIFilter(name: "CIDisplacementDistortion")! 123 | 124 | public let inputDisplacementImage: ImageInput 125 | public let inputScale: ScalarInput 126 | 127 | public init(image: UIImage) { 128 | self.inputDisplacementImage = ImageInput(image: CIImage(cgImage: image.cgImage!), key: "inputDisplacementImage") 129 | self.inputScale = ScalarInput(filter: self.filter, key: kCIInputScaleKey) 130 | } 131 | 132 | public init(image: CIImage) { 133 | self.inputDisplacementImage = ImageInput(image: image, key: "inputDisplacementImage") 134 | self.inputScale = ScalarInput(filter: self.filter, key: kCIInputScaleKey) 135 | } 136 | 137 | public func inputs() -> [FilterInputable] { 138 | return [ 139 | self.inputDisplacementImage, 140 | self.inputScale 141 | ] 142 | } 143 | } 144 | 145 | @available(iOS 9.0, *) 146 | public struct GlassDistortion: Filterable, FilterInputCollectionType, 147 | InputTextureAvailable, InputCenterAvailable, InputScaleAvailable { 148 | 149 | public let filter: CIFilter = CIFilter(name: "CIGlassDistortion")! 150 | 151 | public let inputTexture: ImageInput 152 | public let inputCenter: VectorInput 153 | public let inputScale: ScalarInput 154 | 155 | public init(image: UIImage, imageSize: CGSize) { 156 | self.inputTexture = ImageInput(image: CIImage(cgImage: image.cgImage!), key: "inputTexture") 157 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 158 | self.inputScale = ScalarInput(filter: self.filter, key: kCIInputScaleKey) 159 | } 160 | 161 | public init(image: CIImage, imageSize: CGSize) { 162 | self.inputTexture = ImageInput(image: image, key: "inputTexture") 163 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 164 | self.inputScale = ScalarInput(filter: self.filter, key: kCIInputScaleKey) 165 | } 166 | 167 | public func inputs() -> [FilterInputable] { 168 | return [ 169 | self.inputTexture, 170 | self.inputCenter, 171 | self.inputScale 172 | ] 173 | } 174 | } 175 | 176 | @available(iOS 9.0, *) 177 | public struct GlassLozenge: Filterable, FilterInputCollectionType, 178 | InputPoint0Available, InputPoint1Available, InputRadiusAvailable, 179 | InputRefractionAvailable { 180 | 181 | public let filter: CIFilter = CIFilter(name: "CIGlassLozenge")! 182 | 183 | public let inputPoint0: VectorInput 184 | public let inputPoint1: VectorInput 185 | public let inputRadius: ScalarInput 186 | public let inputRefraction: ScalarInput 187 | 188 | public init(imageSize: CGSize) { 189 | self.inputPoint0 = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputPoint0") 190 | self.inputPoint1 = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputPoint1") 191 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 192 | self.inputRefraction = ScalarInput(filter: self.filter, key: "inputRefraction") 193 | } 194 | 195 | public func inputs() -> [FilterInputable] { 196 | return [ 197 | self.inputPoint0, 198 | self.inputPoint1, 199 | self.inputRadius, 200 | self.inputRefraction 201 | ] 202 | } 203 | } 204 | 205 | public struct HoleDistortion: Filterable, FilterInputCollectionType, 206 | InputCenterAvailable, InputRadiusAvailable { 207 | 208 | public let filter: CIFilter = CIFilter(name: "CIHoleDistortion")! 209 | 210 | public let inputCenter: VectorInput 211 | public let inputRadius: ScalarInput 212 | 213 | public init(imageSize: CGSize) { 214 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 215 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 216 | } 217 | 218 | public func inputs() -> [FilterInputable] { 219 | return [ 220 | self.inputCenter, 221 | self.inputRadius, 222 | ] 223 | } 224 | } 225 | 226 | public struct LightTunnel: Filterable, FilterInputCollectionType, 227 | InputCenterAvailable, InputRotationAvailable, InputRadiusAvailable { 228 | 229 | public let filter: CIFilter = CIFilter(name: "CILightTunnel")! 230 | 231 | public let inputCenter: VectorInput 232 | public let inputRotation: ScalarInput 233 | public let inputRadius: ScalarInput 234 | 235 | public init(imageSize: CGSize) { 236 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 237 | self.inputRotation = ScalarInput(filter: self.filter, key: "inputRotation") 238 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 239 | } 240 | 241 | public func inputs() -> [FilterInputable] { 242 | return [ 243 | self.inputCenter, 244 | self.inputRotation, 245 | self.inputRadius, 246 | ] 247 | } 248 | } 249 | 250 | public struct PinchDistortion: Filterable, FilterInputCollectionType, 251 | InputCenterAvailable, InputRadiusAvailable, InputScaleAvailable { 252 | 253 | public let filter: CIFilter = CIFilter(name: "CIPinchDistortion")! 254 | 255 | public let inputCenter: VectorInput 256 | public let inputRadius: ScalarInput 257 | public let inputScale: ScalarInput 258 | 259 | public init(imageSize: CGSize) { 260 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 261 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 262 | self.inputScale = ScalarInput(filter: self.filter, key: kCIInputScaleKey) 263 | } 264 | 265 | public func inputs() -> [FilterInputable] { 266 | return [ 267 | self.inputCenter, 268 | self.inputRadius, 269 | self.inputScale 270 | ] 271 | } 272 | } 273 | 274 | @available(iOS 9.0, *) 275 | public struct StretchCrop: Filterable, FilterInputCollectionType, 276 | InputVectorSizeAvailable, InputCropAmountAvailable, InputCenterStretchAmountAvailable { 277 | 278 | public let filter: CIFilter = CIFilter(name: "CIStretchCrop")! 279 | 280 | public let inputVectorSize: VectorInput 281 | public let inputCropAmount: ScalarInput 282 | public let inputCenterStretchAmount: ScalarInput 283 | 284 | public init(imageSize: CGSize) { 285 | self.inputVectorSize = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputSize") 286 | self.inputCropAmount = ScalarInput(filter: self.filter, key: "inputCropAmount") 287 | self.inputCenterStretchAmount = ScalarInput(filter: self.filter, key: "inputCenterStretchAmount") 288 | } 289 | 290 | public func inputs() -> [FilterInputable] { 291 | return [ 292 | self.inputVectorSize, 293 | self.inputCropAmount, 294 | self.inputCenterStretchAmount 295 | ] 296 | } 297 | } 298 | 299 | @available(iOS 9.0, *) 300 | public struct TorusLensDistortion: Filterable, FilterInputCollectionType, 301 | InputCenterAvailable, InputRadiusAvailable, InputWidthAvailable, 302 | InputRefractionAvailable { 303 | 304 | public let filter: CIFilter = CIFilter(name: "CITorusLensDistortion")! 305 | 306 | public let inputCenter: VectorInput 307 | public let inputRadius: ScalarInput 308 | public let inputWidth: ScalarInput 309 | public let inputRefraction: ScalarInput 310 | 311 | public init(imageSize: CGSize) { 312 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 313 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 314 | self.inputWidth = ScalarInput(filter: self.filter, key: kCIInputWidthKey) 315 | self.inputRefraction = ScalarInput(filter: self.filter, key: "inputRefraction") 316 | } 317 | 318 | public func inputs() -> [FilterInputable] { 319 | return [ 320 | self.inputCenter, 321 | self.inputRadius, 322 | self.inputWidth, 323 | self.inputRefraction 324 | ] 325 | } 326 | } 327 | 328 | public struct TwirlDistortion: Filterable, FilterInputCollectionType, 329 | InputCenterAvailable, InputRadiusAvailable, InputAngleAvailable { 330 | 331 | public let filter: CIFilter = CIFilter(name: "CITwirlDistortion")! 332 | 333 | public let inputCenter: VectorInput 334 | public let inputRadius: ScalarInput 335 | public let inputAngle: ScalarInput 336 | 337 | public init(imageSize: CGSize) { 338 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 339 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 340 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 341 | } 342 | 343 | public func inputs() -> [FilterInputable] { 344 | return [ 345 | self.inputCenter, 346 | self.inputRadius, 347 | self.inputAngle 348 | ] 349 | } 350 | } 351 | 352 | public struct VortexDistortion: Filterable, FilterInputCollectionType, 353 | InputCenterAvailable, InputRadiusAvailable, InputAngleAvailable { 354 | 355 | public let filter: CIFilter = CIFilter(name: "CIVortexDistortion")! 356 | 357 | public let inputCenter: VectorInput 358 | public let inputRadius: ScalarInput 359 | public let inputAngle: ScalarInput 360 | 361 | public init(imageSize: CGSize) { 362 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 363 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 364 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 365 | } 366 | 367 | public func inputs() -> [FilterInputable] { 368 | return [ 369 | self.inputCenter, 370 | self.inputRadius, 371 | self.inputAngle 372 | ] 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /Sources/Enhancement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Enhancement.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public struct AutoAdjustEnhance: Processable { 14 | 15 | public var name: String = "AutoAdjustEnhance" 16 | 17 | public init() {} 18 | 19 | public func processing(_ ciImage: CIImage?) -> CIImage? { 20 | guard let _ciImage = ciImage else { return nil } 21 | 22 | let filters = _ciImage.autoAdjustmentFilters(options: [CIImageAutoAdjustmentOption.enhance : true, CIImageAutoAdjustmentOption.redEye : false]) 23 | 24 | return filters.reduce(ciImage) { result, filter in 25 | filter.setValue(result, forKey: kCIInputImageKey) 26 | return filter.outputImage 27 | } 28 | } 29 | } 30 | 31 | public struct AutoAdjustRedEye: Processable { 32 | 33 | public var name: String = "AutoAdjustRedEye" 34 | 35 | public init() {} 36 | 37 | public func processing(_ ciImage: CIImage?) -> CIImage? { 38 | guard let _ciImage = ciImage else { return nil } 39 | 40 | let filters = _ciImage.autoAdjustmentFilters(options: [CIImageAutoAdjustmentOption.enhance : false, CIImageAutoAdjustmentOption.redEye : true]) 41 | 42 | return filters.reduce(ciImage) { result, filter in 43 | filter.setValue(result, forKey: kCIInputImageKey) 44 | return filter.outputImage 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/FaceDetection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FaceDetection.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/24. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public enum Accuracy { 14 | case low 15 | case high 16 | 17 | var options: [String : AnyObject] { 18 | switch self { 19 | case .low: 20 | return [CIDetectorAccuracy: CIDetectorAccuracyLow as AnyObject] 21 | case .high: 22 | return [CIDetectorAccuracy: CIDetectorAccuracyHigh as AnyObject] 23 | } 24 | } 25 | } 26 | 27 | public enum Option { 28 | case smile 29 | case eyeBlink 30 | 31 | var options: [String : AnyObject] { 32 | switch self { 33 | case .smile: 34 | return [CIDetectorSmile: true as AnyObject] 35 | case .eyeBlink: 36 | return [CIDetectorEyeBlink: true as AnyObject] 37 | } 38 | } 39 | } 40 | 41 | public struct FaceDetection { 42 | 43 | public let accuracy: Accuracy 44 | 45 | public init(accuracy: Accuracy = .high) { 46 | self.accuracy = accuracy 47 | 48 | // let options: [String : AnyObject] = [CIDetectorSmile: true, CIDetectorEyeBlink: true] 49 | // let detector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyLow]) 50 | } 51 | 52 | public func processing(_ uiImage: UIImage) -> [CIFaceFeature] { 53 | guard let ciImage = CIImage.generate(uiImage) else { return [] } 54 | 55 | let detector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: self.accuracy.options) 56 | return detector!.features(in: ciImage, options: [:]).compactMap() { $0 as? CIFaceFeature } 57 | } 58 | 59 | public func detectionOfFaceBounds(_ image: UIImage) -> [CGRect] { 60 | let features = self.processing(image) 61 | 62 | return features.map() { 63 | var faceRect = $0.bounds 64 | faceRect.origin.y = image.size.height - faceRect.origin.y - faceRect.size.height 65 | return faceRect 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/FilterGroup.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilterGroup.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | /** 14 | example 15 | 16 | let filterGroup = FilterGroup(name: "FilterGroupExample", [ 17 | BoxBlur().configuration({ filter in 18 | filter.inputRadius.setValue(15) 19 | }), 20 | Vignette(), 21 | PhotoEffectMono(), 22 | ] 23 | ) 24 | 25 | let filteredImage = filterGroup.processing(image) 26 | */ 27 | 28 | public struct FilterGroup: Processable { 29 | 30 | public let name: String 31 | fileprivate var filters: [Filterable] 32 | 33 | public init(name: String? = nil, _ filters : [Filterable] = []) { 34 | self.name = name ?? "FilterGroup" 35 | self.filters = filters 36 | } 37 | 38 | mutating func append(_ filter: Filterable) { 39 | self.filters.append(filter) 40 | } 41 | 42 | public func sliders() -> [Slider] { 43 | let sliderRanges: [Slider] = [] 44 | 45 | return self.filters.reduce(sliderRanges) { result, slider in 46 | return result + slider.sliders() 47 | } 48 | } 49 | 50 | public func processing(_ ciImage: CIImage?) -> CIImage? { 51 | return self.filters.reduce(ciImage) { return $1.processing($0) } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/GeometryAdjustment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeometryAdjustment.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public struct AffineTransform: Filterable, FilterInputCollectionType, 14 | InputAffineTransformAvailable { 15 | 16 | public let filter: CIFilter = CIFilter(name: "CIAffineTransform")! 17 | 18 | public var inputTransform: AffineTransformInput 19 | 20 | public init() { 21 | self.inputTransform = AffineTransformInput() 22 | } 23 | 24 | public func inputs() -> [FilterInputable] { 25 | return [ 26 | self.inputTransform 27 | ] 28 | } 29 | } 30 | 31 | public struct Crop: Filterable, FilterInputCollectionType, 32 | InputRectangleAvailable { 33 | 34 | public let filter: CIFilter = CIFilter(name: "CICrop")! 35 | 36 | public let inputRectangle: VectorInput 37 | 38 | public init(imageSize: CGSize) { 39 | self.inputRectangle = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, "inputRectangle") 40 | } 41 | 42 | public func inputs() -> [FilterInputable] { 43 | return [ 44 | self.inputRectangle 45 | ] 46 | } 47 | } 48 | 49 | public struct LanczosScaleTransform: Filterable, FilterInputCollectionType, 50 | InputScaleAvailable, InputAspectRatioAvailable { 51 | 52 | public let filter: CIFilter = CIFilter(name: "CILanczosScaleTransform")! 53 | 54 | public let inputScale: ScalarInput 55 | public let inputAspectRatio: ScalarInput 56 | 57 | public init() { 58 | self.inputScale = ScalarInput(filter: self.filter, key: kCIInputScaleKey) 59 | self.inputAspectRatio = ScalarInput(filter: self.filter, key: kCIInputAspectRatioKey) 60 | } 61 | 62 | public func inputs() -> [FilterInputable] { 63 | return [ 64 | self.inputScale, 65 | self.inputAspectRatio 66 | ] 67 | } 68 | } 69 | 70 | public struct PerspectiveCorrection: Filterable, FilterInputCollectionType, 71 | InputTopLeftAvailable, InputTopRightAvailable, InputBottomRightAvailable, 72 | InputBottomLeftAvailable { 73 | 74 | public let filter: CIFilter = CIFilter(name: "CIPerspectiveCorrection")! 75 | 76 | public let inputTopLeft: VectorInput 77 | public let inputTopRight: VectorInput 78 | public let inputBottomLeft: VectorInput 79 | public let inputBottomRight: VectorInput 80 | 81 | public init(imageSize: CGSize) { 82 | self.inputTopLeft = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputTopLeft") 83 | self.inputTopRight = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputTopRight") 84 | self.inputBottomLeft = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputBottomLeft") 85 | self.inputBottomRight = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputBottomRight") 86 | } 87 | 88 | public func inputs() -> [FilterInputable] { 89 | return [ 90 | self.inputTopLeft, 91 | self.inputTopRight, 92 | self.inputBottomLeft, 93 | self.inputBottomRight 94 | ] 95 | } 96 | } 97 | 98 | public struct PerspectiveTransform: Filterable, FilterInputCollectionType, 99 | InputTopLeftAvailable, InputTopRightAvailable, InputBottomRightAvailable, 100 | InputBottomLeftAvailable { 101 | 102 | public let filter: CIFilter = CIFilter(name: "CIPerspectiveTransform")! 103 | 104 | public let inputTopLeft: VectorInput 105 | public let inputTopRight: VectorInput 106 | public let inputBottomLeft: VectorInput 107 | public let inputBottomRight: VectorInput 108 | 109 | public init(imageSize: CGSize) { 110 | self.inputTopLeft = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputTopLeft") 111 | self.inputTopRight = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputTopRight") 112 | self.inputBottomLeft = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputBottomLeft") 113 | self.inputBottomRight = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputBottomRight") 114 | } 115 | 116 | public func inputs() -> [FilterInputable] { 117 | return [ 118 | self.inputTopLeft, 119 | self.inputTopRight, 120 | self.inputBottomLeft, 121 | self.inputBottomRight 122 | ] 123 | } 124 | } 125 | 126 | public struct PerspectiveTransformWithExtent: Filterable, FilterInputCollectionType, 127 | InputExtentAvailable, InputTopLeftAvailable, InputTopRightAvailable, 128 | InputBottomRightAvailable, InputBottomLeftAvailable { 129 | 130 | public let filter: CIFilter = CIFilter(name: "CIPerspectiveTransformWithExtent")! 131 | 132 | public let inputExtent: VectorInput 133 | public let inputTopLeft: VectorInput 134 | public let inputTopRight: VectorInput 135 | public let inputBottomLeft: VectorInput 136 | public let inputBottomRight: VectorInput 137 | 138 | public init(imageSize: CGSize) { 139 | self.inputExtent = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, kCIInputExtentKey) 140 | self.inputTopLeft = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputTopLeft") 141 | self.inputTopRight = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputTopRight") 142 | self.inputBottomLeft = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputBottomLeft") 143 | self.inputBottomRight = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputBottomRight") 144 | } 145 | 146 | public func inputs() -> [FilterInputable] { 147 | return [ 148 | self.inputExtent, 149 | self.inputTopLeft, 150 | self.inputTopRight, 151 | self.inputBottomLeft, 152 | self.inputBottomRight 153 | ] 154 | } 155 | } 156 | 157 | public struct StraightenFilter: Filterable, FilterInputCollectionType, 158 | InputAngleAvailable { 159 | 160 | public let filter: CIFilter = CIFilter(name: "CIStraightenFilter")! 161 | 162 | public let inputAngle: ScalarInput 163 | 164 | public init() { 165 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 166 | } 167 | 168 | public func inputs() -> [FilterInputable] { 169 | return [ 170 | self.inputAngle 171 | ] 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /Sources/Gradient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Gradient.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public struct GaussianGradient: InputImageUnusable, FilterInputCollectionType, 14 | InputCenterAvailable, InputRadiusAvailable, InputColor0Available, 15 | InputColor1Available { 16 | 17 | public let filter: CIFilter = CIFilter(name: "CIGaussianGradient")! 18 | 19 | public let inputCenter: VectorInput 20 | public let inputColor0: ColorInput 21 | public let inputColor1: ColorInput 22 | public let inputRadius: ScalarInput 23 | 24 | public init(imageSize: CGSize) { 25 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 26 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 27 | self.inputColor0 = ColorInput(filter: self.filter, key: "inputColor0") 28 | self.inputColor1 = ColorInput(filter: self.filter, key: "inputColor1") 29 | } 30 | 31 | public func inputs() -> [FilterInputable] { 32 | return [ 33 | self.inputCenter, 34 | self.inputColor0, 35 | self.inputColor1, 36 | self.inputRadius 37 | ] 38 | } 39 | } 40 | 41 | public struct LinearGradient: InputImageUnusable, FilterInputCollectionType, 42 | InputPoint0Available, InputPoint1Available, InputColor0Available, 43 | InputColor1Available { 44 | 45 | public let filter: CIFilter = CIFilter(name: "CILinearGradient")! 46 | 47 | public var inputPoint0: VectorInput 48 | public var inputPoint1: VectorInput 49 | public let inputColor0: ColorInput 50 | public let inputColor1: ColorInput 51 | 52 | public init(imageSize: CGSize) { 53 | self.inputPoint0 = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputPoint0") 54 | self.inputPoint1 = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputPoint1") 55 | self.inputColor0 = ColorInput(filter: self.filter, key: "inputColor0") 56 | self.inputColor1 = ColorInput(filter: self.filter, key: "inputColor1") 57 | } 58 | 59 | public func inputs() -> [FilterInputable] { 60 | return [ 61 | self.inputPoint0, 62 | self.inputPoint1, 63 | self.inputColor0, 64 | self.inputColor1 65 | ] 66 | } 67 | } 68 | 69 | public struct RadialGradient: InputImageUnusable, FilterInputCollectionType, 70 | InputCenterAvailable, InputRadius0Available, InputRadius1Available, 71 | InputColor0Available, InputColor1Available { 72 | 73 | public let filter: CIFilter = CIFilter(name: "CIRadialGradient")! 74 | 75 | public let inputCenter: VectorInput 76 | public let inputColor0: ColorInput 77 | public let inputColor1: ColorInput 78 | public let inputRadius0: ScalarInput 79 | public let inputRadius1: ScalarInput 80 | 81 | public init(imageSize: CGSize) { 82 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 83 | self.inputColor0 = ColorInput(filter: self.filter, key: "inputColor0") 84 | self.inputColor1 = ColorInput(filter: self.filter, key: "inputColor1") 85 | self.inputRadius0 = ScalarInput(filter: self.filter, key: "inputRadius0") 86 | self.inputRadius1 = ScalarInput(filter: self.filter, key: "inputRadius1") 87 | } 88 | 89 | public func inputs() -> [FilterInputable] { 90 | return [ 91 | self.inputCenter, 92 | self.inputColor0, 93 | self.inputColor1, 94 | self.inputRadius0, 95 | self.inputRadius1 96 | ] 97 | } 98 | } 99 | 100 | public struct SmoothLinearGradient: InputImageUnusable, FilterInputCollectionType, 101 | InputPoint0Available, InputPoint1Available, InputColor0Available, 102 | InputColor1Available { 103 | 104 | public let filter: CIFilter = CIFilter(name: "CISmoothLinearGradient")! 105 | 106 | public var inputPoint0: VectorInput 107 | public var inputPoint1: VectorInput 108 | public let inputColor0: ColorInput 109 | public let inputColor1: ColorInput 110 | 111 | public init(imageSize: CGSize) { 112 | self.inputPoint0 = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputPoint0") 113 | self.inputPoint1 = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, "inputPoint1") 114 | self.inputColor0 = ColorInput(filter: self.filter, key: "inputColor0") 115 | self.inputColor1 = ColorInput(filter: self.filter, key: "inputColor1") 116 | } 117 | 118 | public func inputs() -> [FilterInputable] { 119 | return [ 120 | self.inputPoint0, 121 | self.inputPoint1, 122 | self.inputColor0, 123 | self.inputColor1 124 | ] 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Sources/HalftoneEffect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HalftoneEffect.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public struct CircularScreen: Filterable, FilterInputCollectionType, 14 | InputCenterAvailable, InputWidthAvailable, InputSharpnessAvailable { 15 | 16 | public let filter: CIFilter = CIFilter(name: "CICircularScreen")! 17 | 18 | public let inputCenter: VectorInput 19 | public let inputWidth: ScalarInput 20 | public let inputSharpness: ScalarInput 21 | 22 | public init(imageSize: CGSize) { 23 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 24 | self.inputWidth = ScalarInput(filter: self.filter, key: kCIInputWidthKey) 25 | self.inputSharpness = ScalarInput(filter: self.filter, key: kCIInputSharpnessKey) 26 | } 27 | 28 | public func inputs() -> [FilterInputable] { 29 | return [ 30 | self.inputCenter, 31 | self.inputWidth, 32 | self.inputSharpness 33 | ] 34 | } 35 | } 36 | 37 | @available(iOS 9.0, *) 38 | public struct CMYKHalftone: Filterable, FilterInputCollectionType, 39 | InputCenterAvailable, InputWidthAvailable, InputSharpnessAvailable, 40 | InputAngleAvailable, InputGCRAvailable, InputUCRAvailable { 41 | 42 | public let filter: CIFilter = CIFilter(name: "CICMYKHalftone")! 43 | 44 | public let inputCenter: VectorInput 45 | public let inputWidth: ScalarInput 46 | public let inputSharpness: ScalarInput 47 | public let inputAngle: ScalarInput 48 | public let inputGCR: ScalarInput 49 | public let inputUCR: ScalarInput 50 | 51 | public init(imageSize: CGSize) { 52 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 53 | self.inputWidth = ScalarInput(filter: self.filter, key: kCIInputWidthKey) 54 | self.inputSharpness = ScalarInput(filter: self.filter, key: kCIInputSharpnessKey) 55 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 56 | self.inputGCR = ScalarInput(filter: self.filter, key: "inputGCR") 57 | self.inputUCR = ScalarInput(filter: self.filter, key: "inputUCR") 58 | } 59 | 60 | public func inputs() -> [FilterInputable] { 61 | return [ 62 | self.inputCenter, 63 | self.inputWidth, 64 | self.inputSharpness, 65 | self.inputAngle, 66 | self.inputGCR, 67 | self.inputUCR 68 | ] 69 | } 70 | } 71 | 72 | public struct DotScreen: Filterable, FilterInputCollectionType, 73 | InputCenterAvailable, InputWidthAvailable, InputSharpnessAvailable, 74 | InputAngleAvailable { 75 | 76 | public let filter: CIFilter = CIFilter(name: "CIDotScreen")! 77 | 78 | public let inputCenter: VectorInput 79 | public let inputWidth: ScalarInput 80 | public let inputSharpness: ScalarInput 81 | public let inputAngle: ScalarInput 82 | 83 | public init(imageSize: CGSize) { 84 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 85 | self.inputWidth = ScalarInput(filter: self.filter, key: kCIInputWidthKey) 86 | self.inputSharpness = ScalarInput(filter: self.filter, key: kCIInputSharpnessKey) 87 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 88 | } 89 | 90 | public func inputs() -> [FilterInputable] { 91 | return [ 92 | self.inputCenter, 93 | self.inputWidth, 94 | self.inputSharpness, 95 | self.inputAngle, 96 | ] 97 | } 98 | } 99 | 100 | public struct HatchedScreen: Filterable, FilterInputCollectionType, 101 | InputCenterAvailable, InputWidthAvailable, InputSharpnessAvailable, 102 | InputAngleAvailable { 103 | 104 | public let filter: CIFilter = CIFilter(name: "CIHatchedScreen")! 105 | 106 | public let inputCenter: VectorInput 107 | public let inputWidth: ScalarInput 108 | public let inputSharpness: ScalarInput 109 | public let inputAngle: ScalarInput 110 | 111 | public init(imageSize: CGSize) { 112 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 113 | self.inputWidth = ScalarInput(filter: self.filter, key: kCIInputWidthKey) 114 | self.inputSharpness = ScalarInput(filter: self.filter, key: kCIInputSharpnessKey) 115 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 116 | } 117 | 118 | public func inputs() -> [FilterInputable] { 119 | return [ 120 | self.inputCenter, 121 | self.inputWidth, 122 | self.inputSharpness, 123 | self.inputAngle, 124 | ] 125 | } 126 | } 127 | 128 | public struct LineScreen: Filterable, FilterInputCollectionType, 129 | InputCenterAvailable, InputWidthAvailable, InputSharpnessAvailable, 130 | InputAngleAvailable { 131 | 132 | public let filter: CIFilter = CIFilter(name: "CILineScreen")! 133 | 134 | public let inputCenter: VectorInput 135 | public let inputWidth: ScalarInput 136 | public let inputSharpness: ScalarInput 137 | public let inputAngle: ScalarInput 138 | 139 | public init(imageSize: CGSize) { 140 | self.inputCenter = VectorInput(.position(maximumSize: Vector2(size: imageSize)), self.filter, kCIInputCenterKey) 141 | self.inputWidth = ScalarInput(filter: self.filter, key: kCIInputWidthKey) 142 | self.inputSharpness = ScalarInput(filter: self.filter, key: kCIInputSharpnessKey) 143 | self.inputAngle = ScalarInput(filter: self.filter, key: kCIInputAngleKey) 144 | } 145 | 146 | public func inputs() -> [FilterInputable] { 147 | return [ 148 | self.inputCenter, 149 | self.inputWidth, 150 | self.inputSharpness, 151 | self.inputAngle, 152 | ] 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /Sources/ImageInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageInput.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public final class ImageInput: FilterInputable { 14 | 15 | public let key: String 16 | public let range: Range 17 | public let image: CIImage 18 | 19 | public fileprivate(set) var alpha: CGFloat = 1.0 20 | 21 | public init(image: CIImage, key: String) { 22 | self.key = key 23 | self.image = image 24 | self.range = Range(0.0, 1.0, 1.0) 25 | } 26 | 27 | public func sliders() -> [Slider] { 28 | let value: [Slider] = [Slider(self.key, self.range) { [weak self] value in 29 | self?.alpha = CGFloat(value) 30 | }] 31 | return value 32 | } 33 | 34 | public func setInput(_ filter: CIFilter) { 35 | var _image = self.image 36 | 37 | if self.alpha < 1.0 { 38 | let alphaFilter = CIFilter(name: "CIColorMatrix")! 39 | alphaFilter.setValue(self.image, forKey: kCIInputImageKey) 40 | alphaFilter.setValue(CIVector(x: 0.0, y: 0.0, z: 0.0, w: self.alpha), forKey: "inputAVector") 41 | _image = alphaFilter.outputImage! 42 | } 43 | 44 | filter.setValue(_image, forKey: self.key) 45 | } 46 | 47 | public func updateAlpha(_ value: CGFloat) { 48 | self.alpha = CGFloat(self.range.convertValue(Float(value))) 49 | } 50 | 51 | public func resetValue() { 52 | self.alpha = 1.0 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/ImageInputProtocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageInputProtocols.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/19. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | // MARK: - Image 14 | 15 | public protocol InputImageAvailable { 16 | 17 | var inputImage: ImageInput { get } 18 | } 19 | 20 | extension InputImageAvailable { 21 | 22 | public var alpha_image: CGFloat { 23 | get { return inputImage.alpha } 24 | set { inputImage.updateAlpha(newValue) } 25 | } 26 | } 27 | 28 | // MARK: - BackgroundImage 29 | 30 | public protocol InputBackgroundImageAvailable { 31 | 32 | var inputBackgroundImage: ImageInput { get } 33 | } 34 | 35 | extension InputBackgroundImageAvailable { 36 | 37 | public var alpha_background: CGFloat { 38 | get { return inputBackgroundImage.alpha } 39 | set { inputBackgroundImage.updateAlpha(newValue) } 40 | } 41 | } 42 | 43 | // MARK: - MaskImage 44 | 45 | public protocol InputMaskImageAvailable { 46 | 47 | var inputMaskImage: ImageInput { get } 48 | } 49 | 50 | extension InputMaskImageAvailable { 51 | 52 | public var alpha_mask: CGFloat { 53 | get { return inputMaskImage.alpha } 54 | set { inputMaskImage.updateAlpha(newValue) } 55 | } 56 | } 57 | 58 | // MARK: - DisplacementImage 59 | 60 | public protocol InputDisplacementImageAvailable { 61 | 62 | var inputDisplacementImage: ImageInput { get } 63 | } 64 | 65 | extension InputDisplacementImageAvailable { 66 | 67 | public var alpha_displacement: CGFloat { 68 | get { return inputDisplacementImage.alpha } 69 | set { inputDisplacementImage.updateAlpha(newValue) } 70 | } 71 | } 72 | 73 | 74 | // MARK: - Texture 75 | 76 | public protocol InputTextureAvailable { 77 | 78 | var inputTexture: ImageInput { get } 79 | } 80 | 81 | extension InputTextureAvailable { 82 | 83 | public var alpha_texture: CGFloat { 84 | get { return inputTexture.alpha } 85 | set { inputTexture.updateAlpha(newValue) } 86 | } 87 | } 88 | 89 | // MARK: - Shading Image 90 | 91 | public protocol InputShadingImageAvailable { 92 | 93 | var inputShadingImage: ImageInput { get } 94 | } 95 | 96 | extension InputShadingImageAvailable { 97 | 98 | public var alpha_shadingImage: CGFloat { 99 | get { return inputShadingImage.alpha } 100 | set { inputShadingImage.updateAlpha(newValue) } 101 | } 102 | } -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.5.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Range.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Range.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public struct Range { 14 | 15 | public let minimumValue: Float 16 | public let maximumValue: Float 17 | public let initialValue: Float 18 | 19 | public init(_ minimumValue: Float, _ maximumValue: Float, _ initialValue: Float) { 20 | self.minimumValue = minimumValue 21 | self.maximumValue = maximumValue 22 | self.initialValue = initialValue 23 | } 24 | 25 | public func convertValue(_ currentValue: Float) -> Float { 26 | if currentValue > self.maximumValue { return self.maximumValue } 27 | if currentValue < self.minimumValue { return self.minimumValue } 28 | return currentValue 29 | } 30 | 31 | static func generateFromFilterAttributes(_ key: String, filter: CIFilter) -> Range { 32 | let attributes = filter.attributes[key] as? [String : AnyObject] 33 | 34 | let min = attributes?["CIAttributeSliderMin"] as? NSNumber ?? attributes?["CIAttributeMin"] as? NSNumber 35 | let max = attributes?["CIAttributeSliderMax"] as? NSNumber ?? attributes?["CIAttributeMax"] as? NSNumber 36 | let def = attributes?["CIAttributeDefault"] as? NSNumber ?? min 37 | 38 | return Range(min?.floatValue ?? Float(0), max?.floatValue ?? Float(0), def?.floatValue ?? Float(0)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Reduction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Reduction.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/22. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | @available(iOS 9.0, *) 14 | public struct AreaAverage: Filterable, FilterInputCollectionType, 15 | InputExtentAvailable { 16 | 17 | public let filter: CIFilter = CIFilter(name: "CIAreaAverage")! 18 | 19 | public let inputExtent: VectorInput 20 | 21 | public init(imageSize: CGSize) { 22 | self.inputExtent = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, kCIInputExtentKey) 23 | } 24 | 25 | public func inputs() -> [FilterInputable] { 26 | return [ 27 | self.inputExtent, 28 | ] 29 | } 30 | } 31 | 32 | public struct AreaHistogram: Filterable, FilterInputCollectionType, 33 | InputExtentAvailable, InputScaleAvailable, InputCountAvailable { 34 | 35 | public let filter: CIFilter = CIFilter(name: "CIAreaHistogram")! 36 | 37 | public let inputExtent: VectorInput 38 | public let inputCount: ScalarInput 39 | public let inputScale: ScalarInput 40 | 41 | public init(imageSize: CGSize) { 42 | self.inputExtent = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, kCIInputExtentKey) 43 | self.inputCount = ScalarInput(filter: self.filter, key: "inputCount") 44 | self.inputScale = ScalarInput(filter: self.filter, key: kCIInputScaleKey) 45 | } 46 | 47 | public func inputs() -> [FilterInputable] { 48 | return [ 49 | self.inputExtent, 50 | self.inputCount, 51 | self.inputScale, 52 | ] 53 | } 54 | } 55 | 56 | @available(iOS 9.0, *) 57 | public struct RowAverage: Filterable, FilterInputCollectionType, 58 | InputExtentAvailable { 59 | 60 | public let filter: CIFilter = CIFilter(name: "CIRowAverage")! 61 | 62 | public let inputExtent: VectorInput 63 | 64 | public init(imageSize: CGSize) { 65 | self.inputExtent = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, kCIInputExtentKey) 66 | } 67 | 68 | public func inputs() -> [FilterInputable] { 69 | return [ 70 | self.inputExtent, 71 | ] 72 | } 73 | } 74 | 75 | @available(iOS 9.0, *) 76 | public struct ColumnAverage: Filterable, FilterInputCollectionType, 77 | InputExtentAvailable { 78 | 79 | public let filter: CIFilter = CIFilter(name: "CIColumnAverage")! 80 | 81 | public let inputExtent: VectorInput 82 | 83 | public init(imageSize: CGSize) { 84 | self.inputExtent = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, kCIInputExtentKey) 85 | } 86 | 87 | public func inputs() -> [FilterInputable] { 88 | return [ 89 | self.inputExtent, 90 | ] 91 | } 92 | } 93 | 94 | @available(iOS 9.0, *) 95 | public struct HistogramDisplayFilter: Filterable, FilterInputCollectionType, 96 | InputHeightAvailable, InputHighLimitAvailable, InputLowLimitAvailable { 97 | 98 | public let filter: CIFilter = CIFilter(name: "CIHistogramDisplayFilter")! 99 | 100 | public let inputHeight: ScalarInput 101 | public let inputHighLimit: ScalarInput 102 | public let inputLowLimit: ScalarInput 103 | 104 | public init() { 105 | self.inputHeight = ScalarInput(filter: self.filter, key: "inputHeight") 106 | self.inputHighLimit = ScalarInput(filter: self.filter, key: "inputHighLimit") 107 | self.inputLowLimit = ScalarInput(filter: self.filter, key: "inputLowLimit") 108 | } 109 | 110 | public func inputs() -> [FilterInputable] { 111 | return [ 112 | self.inputHeight, 113 | self.inputHighLimit, 114 | self.inputLowLimit, 115 | ] 116 | } 117 | } 118 | 119 | @available(iOS 9.0, *) 120 | public struct AreaMaximum: Filterable, FilterInputCollectionType, 121 | InputExtentAvailable { 122 | 123 | public let filter: CIFilter = CIFilter(name: "CIAreaMaximum")! 124 | 125 | public let inputExtent: VectorInput 126 | 127 | public init(imageSize: CGSize) { 128 | self.inputExtent = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, kCIInputExtentKey) 129 | } 130 | 131 | public func inputs() -> [FilterInputable] { 132 | return [ 133 | self.inputExtent, 134 | ] 135 | } 136 | } 137 | 138 | @available(iOS 9.0, *) 139 | public struct AreaMinimum: Filterable, FilterInputCollectionType, 140 | InputExtentAvailable { 141 | 142 | public let filter: CIFilter = CIFilter(name: "CIAreaMinimum")! 143 | 144 | public let inputExtent: VectorInput 145 | 146 | public init(imageSize: CGSize) { 147 | self.inputExtent = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, kCIInputExtentKey) 148 | } 149 | 150 | public func inputs() -> [FilterInputable] { 151 | return [ 152 | self.inputExtent, 153 | ] 154 | } 155 | } 156 | 157 | @available(iOS 9.0, *) 158 | public struct AreaMaximumAlpha: Filterable, FilterInputCollectionType, 159 | InputExtentAvailable { 160 | 161 | public let filter: CIFilter = CIFilter(name: "CIAreaMaximumAlpha")! 162 | 163 | public let inputExtent: VectorInput 164 | 165 | public init(imageSize: CGSize) { 166 | self.inputExtent = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, kCIInputExtentKey) 167 | } 168 | 169 | public func inputs() -> [FilterInputable] { 170 | return [ 171 | self.inputExtent, 172 | ] 173 | } 174 | } 175 | 176 | @available(iOS 9.0, *) 177 | public struct AreaMinimumAlpha: Filterable, FilterInputCollectionType, 178 | InputExtentAvailable { 179 | 180 | public let filter: CIFilter = CIFilter(name: "CIAreaMinimumAlpha")! 181 | 182 | public let inputExtent: VectorInput 183 | 184 | public init(imageSize: CGSize) { 185 | self.inputExtent = VectorInput(.extent(extent: Vector4(size: imageSize)), self.filter, kCIInputExtentKey) 186 | } 187 | 188 | public func inputs() -> [FilterInputable] { 189 | return [ 190 | self.inputExtent, 191 | ] 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Sources/ScalarInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScalarInput.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public final class ScalarInput: FilterInputable { 14 | 15 | public let key: String 16 | public let range: Range 17 | public fileprivate(set) var value: Float 18 | 19 | public init(filter: CIFilter, key: String, _ defaultValue: Float? = nil) { 20 | self.key = key 21 | let range = Range.generateFromFilterAttributes(key, filter: filter) 22 | self.range = range 23 | self.value = defaultValue ?? range.initialValue 24 | } 25 | 26 | public func sliders() -> [Slider] { 27 | return [ 28 | Slider(self.key, self.range) { [weak self] value in 29 | self?.setValue(value) 30 | } 31 | ] 32 | } 33 | 34 | public func setInput(_ filter: CIFilter) { 35 | filter.setValue(self.value, forKey: self.key) 36 | } 37 | 38 | public func setValue(_ value: Float) { 39 | self.value = self.range.convertValue(value) 40 | } 41 | 42 | public func resetValue() { 43 | self.value = self.range.initialValue 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Sharpen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sharpen.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public struct SharpenLuminance: Filterable, FilterInputCollectionType, 14 | InputSharpnessAvailable { 15 | 16 | public let filter: CIFilter = CIFilter(name: "CISharpenLuminance")! 17 | 18 | public let inputSharpness: ScalarInput 19 | 20 | public init() { 21 | self.inputSharpness = ScalarInput(filter: self.filter, key: kCIInputSharpnessKey) 22 | } 23 | 24 | public func inputs() -> [FilterInputable] { 25 | return [ 26 | self.inputSharpness 27 | ] 28 | } 29 | } 30 | 31 | public struct UnsharpMask: Filterable, FilterInputCollectionType, 32 | InputRadiusAvailable, InputIntensityAvailable { 33 | 34 | public let filter: CIFilter = CIFilter(name: "CIUnsharpMask")! 35 | 36 | public let inputRadius: ScalarInput 37 | public let inputIntensity: ScalarInput 38 | 39 | public init() { 40 | self.inputRadius = ScalarInput(filter: self.filter, key: kCIInputRadiusKey) 41 | self.inputIntensity = ScalarInput(filter: self.filter, key: kCIInputIntensityKey) 42 | } 43 | 44 | public func inputs() -> [FilterInputable] { 45 | return [ 46 | self.inputRadius, 47 | self.inputIntensity 48 | ] 49 | } 50 | } -------------------------------------------------------------------------------- /Sources/Slider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Slider.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | open class Slider { 12 | 13 | open var name: String 14 | public let range: Range 15 | 16 | fileprivate let bindHandler: ((Float) -> Void) 17 | 18 | open var currentValue: Float { 19 | didSet { 20 | self.bindHandler(self.range.convertValue(self.currentValue)) 21 | } 22 | } 23 | 24 | public init(_ name: String, _ range: Range, _ handler: @escaping ((Float) -> Void)) { 25 | self.name = name 26 | self.range = range 27 | self.bindHandler = handler 28 | self.currentValue = range.initialValue 29 | } 30 | 31 | open func resetValue() { 32 | self.currentValue = self.range.initialValue 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/StringInput.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringInput.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public final class StringInput: FilterInputable { 14 | 15 | public let key: String 16 | public var value: String 17 | public let needDataEncode: Bool 18 | public let encoding: UInt 19 | 20 | fileprivate let initialValue: String 21 | 22 | public init(filter: CIFilter, 23 | key: String, 24 | _ value: String? = nil, 25 | _ needDataEncode: Bool = true, 26 | _ encoding: UInt = String.Encoding.unicode.rawValue) { 27 | self.key = key 28 | if let _value = value { 29 | self.value = _value 30 | } else { 31 | let attributes = filter.attributes[key] as? [String : AnyObject] 32 | self.value = attributes?["CIAttributeDefault"] as? String ?? "" 33 | } 34 | self.needDataEncode = needDataEncode 35 | self.encoding = encoding 36 | self.initialValue = self.value 37 | } 38 | 39 | public func sliders() -> [Slider] { 40 | return [] 41 | } 42 | 43 | public func setInput(_ filter: CIFilter) { 44 | if self.needDataEncode { 45 | filter.setValue(self.value.data(using: String.Encoding(rawValue: self.encoding)), forKey: self.key) 46 | } else { 47 | filter.setValue(self.value, forKey: self.key) 48 | } 49 | } 50 | 51 | public func resetValue() { 52 | self.value = self.initialValue 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/StringInputProtocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringInputProtocols.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/24. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | // MARK: - Message 14 | 15 | public protocol InputMessageAvailable { 16 | 17 | var inputMessage: StringInput { get } 18 | } 19 | 20 | extension InputMessageAvailable { 21 | 22 | public var message: String { 23 | get { return inputMessage.value } 24 | set { inputMessage.value = newValue } 25 | } 26 | } 27 | 28 | // MARK: - Correction Level 29 | 30 | public protocol InputStringCorrectionLevelAvailable { 31 | 32 | var inputStringCorrectionLevel: StringInput { get } 33 | } 34 | 35 | extension InputStringCorrectionLevelAvailable { 36 | 37 | public var correctionLevel: String { 38 | get { return inputStringCorrectionLevel.value } 39 | set { inputStringCorrectionLevel.value = newValue } 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Sources/UIImage+CmgFilterChain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+CmgFilterChain.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/24. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | // MARK: - Filter Chain Support 14 | 15 | /** 16 | example 17 | 18 | let filteredImage = image.cmg_chain([ 19 | ComicEffect(), 20 | GaussianBlur().configuration({ filter in 21 | filter.inputRadius.setValue(15) 22 | }), 23 | PerspectiveTile(imageSize: FilterGenerator.originalImage.size).configuration({ filter in 24 | filter.inputTopLeft.setVector(Vector2(x: 118.0, y: 490.0).ciVector) 25 | }), 26 | ]) 27 | */ 28 | 29 | extension Filterable { 30 | 31 | public func configuration(_ configurationBlock: ((Self) -> Void)) -> Self { 32 | configurationBlock(self) 33 | return self 34 | } 35 | } 36 | 37 | extension UIImage { 38 | 39 | public func cmg_chain(_ filters: [Filterable]) -> UIImage? { 40 | return filters.reduce(self) { return $1.processing($0) ?? $0 } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/UIImage+CmgShorthand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+CmgShorthand.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/24. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | extension UIImage { 14 | 15 | // MARK: - Blur 16 | 17 | @available(iOS 9.0, *) 18 | public func cmg_boxBlur(_ radius: Float = 10) -> UIImage? { 19 | var filter = BoxBlur() 20 | filter.radius = radius 21 | return filter.processing(self) 22 | } 23 | 24 | public func cmg_gaussianBlur(_ radius: Float = 10) -> UIImage? { 25 | var filter = GaussianBlur() 26 | filter.radius = radius 27 | return filter.processing(self) 28 | } 29 | 30 | @available(iOS 9.0, *) 31 | public func cmg_median() -> UIImage? { 32 | return Median().processing(self) 33 | } 34 | 35 | // Geometry Adjustment 36 | 37 | public func cmg_resize(_ newSize: CGSize) -> UIImage? { 38 | let scale = CGPoint(x: newSize.width / self.size.width, y: newSize.height / self.size.height) 39 | let bounds = CGRect(origin: CGPoint.zero, size: newSize) 40 | 41 | let filter = AffineTransform() 42 | filter.setScale(scale.x, scale.y) 43 | return filter.processingIntoCIImage(self)?.cropped(to: bounds).generateUIImage(self) 44 | } 45 | 46 | public func cmg_resizeAtAspectFit(_ newSize: CGSize) -> UIImage? { 47 | var destWidth: CGFloat = newSize.width 48 | var destHeight: CGFloat = newSize.height 49 | 50 | if self.size.width > self.size.height { 51 | destHeight = (self.size.height * newSize.width / self.size.width) 52 | } else { 53 | destWidth = (self.size.width * newSize.height / self.size.height) 54 | } 55 | 56 | if destWidth > newSize.width { 57 | destWidth = newSize.width 58 | destHeight = (self.size.height * newSize.width / self.size.width) 59 | } 60 | if (destHeight > newSize.height) { 61 | destHeight = newSize.height 62 | destWidth = (self.size.width * newSize.height / self.size.height) 63 | } 64 | return self.cmg_resize(CGSize(width: destWidth, height: destHeight)) 65 | } 66 | 67 | public func cmg_resizeAtAspectFill(_ newSize: CGSize) -> UIImage? { 68 | var destWidth: CGFloat = newSize.width 69 | var destHeight: CGFloat = newSize.height 70 | let widthRatio = newSize.width / self.size.width 71 | let heightRatio = newSize.height / self.size.height 72 | 73 | if heightRatio > widthRatio { 74 | destHeight = newSize.height 75 | destWidth = (self.size.width * newSize.height / self.size.height) 76 | } else { 77 | destWidth = newSize.width; 78 | destHeight = (self.size.height * newSize.width / self.size.width) 79 | } 80 | return self.cmg_resize(CGSize(width: destWidth, height: destHeight)) 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /Sources/Vector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Vector.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/20. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | public protocol Vectorable { 14 | 15 | var ciVector: CIVector { get } 16 | } 17 | 18 | public struct Vector2: Vectorable { 19 | public var x: CGFloat 20 | public var y: CGFloat 21 | 22 | public init(size: CGSize) { 23 | self.x = size.width 24 | self.y = size.height 25 | } 26 | 27 | public init(x: CGFloat, y: CGFloat) { 28 | self.x = x 29 | self.y = y 30 | } 31 | 32 | public init(values: [CGFloat]) { 33 | self.x = values.count >= 2 ? values[0] : 0.0 34 | self.y = values.count >= 2 ? values[1] : 0.0 35 | } 36 | 37 | public var ciVector: CIVector { return CIVector(x: x, y: y) } 38 | } 39 | 40 | public struct Vector4: Vectorable { 41 | public var x: CGFloat 42 | public var y: CGFloat 43 | public var z: CGFloat 44 | public var w: CGFloat 45 | 46 | public init(x: CGFloat, y: CGFloat, z: CGFloat, w: CGFloat) { 47 | self.x = x 48 | self.y = y 49 | self.z = z 50 | self.w = z 51 | } 52 | 53 | public init(size: CGSize) { 54 | self.x = 0.0 55 | self.y = 0.0 56 | self.z = size.width 57 | self.w = size.height 58 | } 59 | 60 | public init(rect: CGRect) { 61 | self.x = rect.origin.x 62 | self.y = rect.origin.y 63 | self.z = rect.size.width 64 | self.w = rect.size.height 65 | } 66 | 67 | public init(values: [CGFloat]) { 68 | self.x = values.count >= 4 ? values[0] : 0.0 69 | self.y = values.count >= 4 ? values[1] : 0.0 70 | self.z = values.count >= 4 ? values[2] : 0.0 71 | self.w = values.count >= 4 ? values[3] : 0.0 72 | } 73 | 74 | public var ciVector: CIVector { return CIVector(x: x, y: y, z: z, w: w) } 75 | } 76 | 77 | public struct VectorAny: Vectorable { 78 | public let count: Int 79 | public fileprivate(set) var values: [CGFloat] = [] 80 | 81 | public init(count: Int, values: [CGFloat]) { 82 | self.count = count 83 | 84 | for index in 0.. [Slider] { 81 | var names: [String] 82 | switch type { 83 | case .position(_): 84 | names = ["\(self.key) x", "\(self.key) y"] 85 | case .extent(_): 86 | names = ["\(self.key) x", "\(self.key) y", "\(self.key) z" , "\(self.key) w"] 87 | case .color: 88 | names = ["\(self.key) red", "\(self.key) green", "\(self.key) blue", "\(self.key) alpha"] 89 | case .colorOffset: 90 | names = ["\(self.key) x", "\(self.key) y"] 91 | case .other: 92 | names = [] 93 | self.values.enumerated().forEach() { 94 | names.append("\(self.key) \($0.0)") 95 | } 96 | } 97 | return [] 98 | // return self.values.enumerated().map { i in 99 | // return Slider(names[i.1], self.ranges[i.0]) { [weak self] value in 100 | // self?.setValue(i.index, value: CGFloat(value)) 101 | // } 102 | // } 103 | } 104 | 105 | public func setInput(_ filter: CIFilter) { 106 | filter.setValue(CIVector(values: self.values, count: self.values.count), forKey: self.key) 107 | } 108 | 109 | public func resetValue() { 110 | guard let _initialValue = self.initialValue else { return } 111 | self.setVector(_initialValue) 112 | } 113 | } 114 | 115 | extension VectorInput { 116 | 117 | public var vector2: Vector2 { 118 | get { return Vector2(values: self.values) } 119 | } 120 | 121 | public var vector4: Vector4 { 122 | get { return Vector4(values: self.values) } 123 | } 124 | 125 | public var vectorAny: VectorAny { 126 | get { return VectorAny(count: self.values.count, values: self.values) } 127 | } 128 | } 129 | 130 | extension CGSize { 131 | 132 | public var vector2 : Vector2 { 133 | get { return Vector2(size: self) } 134 | } 135 | 136 | public var vector4 : Vector4 { 137 | get { return Vector4(x: 0, y: 0, z: self.width, w: self.height) } 138 | } 139 | } 140 | 141 | extension CGRect { 142 | 143 | public var vector2 : Vector2 { 144 | get { return Vector2(size: self.size) } 145 | } 146 | 147 | public var vector4 : Vector4 { 148 | get { return Vector4(rect: self) } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /Sources/VectorInputProtocols.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VectorInputProtocols.swift 3 | // Cmg 4 | // 5 | // Created by xxxAIRINxxx on 2016/02/19. 6 | // Copyright © 2016 xxxAIRINxxx. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CoreImage 12 | 13 | // MARK: - Center 14 | 15 | public protocol InputCenterAvailable { 16 | 17 | var inputCenter: VectorInput { get } 18 | } 19 | 20 | extension InputCenterAvailable { 21 | 22 | public var center: Vector2 { 23 | get { return inputCenter.vector2 } 24 | set { inputCenter.setVector(newValue.ciVector) } 25 | } 26 | } 27 | 28 | // MARK: - Point 29 | 30 | public protocol InputPointAvailable { 31 | 32 | var inputPoint: VectorInput { get } 33 | } 34 | 35 | extension InputPointAvailable { 36 | 37 | public var point: Vector2 { 38 | get { return inputPoint.vector2 } 39 | set { inputPoint.setVector(newValue.ciVector) } 40 | } 41 | } 42 | 43 | public protocol InputPoint0Available { 44 | 45 | var inputPoint0: VectorInput { get } 46 | } 47 | 48 | extension InputPoint0Available { 49 | 50 | public var point0: Vector2 { 51 | get { return inputPoint0.vector2 } 52 | set { inputPoint0.setVector(newValue.ciVector) } 53 | } 54 | } 55 | 56 | public protocol InputPoint1Available { 57 | 58 | var inputPoint1: VectorInput { get } 59 | } 60 | 61 | extension InputPoint1Available { 62 | 63 | public var point1: Vector2 { 64 | get { return inputPoint1.vector2 } 65 | set { inputPoint1.setVector(newValue.ciVector) } 66 | } 67 | } 68 | 69 | public protocol InputPoint2Available { 70 | 71 | var inputPoint2: VectorInput { get } 72 | } 73 | 74 | extension InputPoint2Available { 75 | 76 | public var point2: Vector2 { 77 | get { return inputPoint2.vector2 } 78 | set { inputPoint2.setVector(newValue.ciVector) } 79 | } 80 | } 81 | 82 | public protocol InputPoint3Available { 83 | 84 | var inputPoint3: VectorInput { get } 85 | } 86 | 87 | extension InputPoint3Available { 88 | 89 | public var point3: Vector2 { 90 | get { return inputPoint3.vector2 } 91 | set { inputPoint3.setVector(newValue.ciVector) } 92 | } 93 | } 94 | 95 | public protocol InputPoint4Available { 96 | 97 | var inputPoint4: VectorInput { get } 98 | } 99 | 100 | extension InputPoint4Available { 101 | 102 | public var point4: Vector2 { 103 | get { return inputPoint4.vector2 } 104 | set { inputPoint4.setVector(newValue.ciVector) } 105 | } 106 | } 107 | 108 | // MARK: - Inset 109 | 110 | public protocol InputInsetPoint0Available { 111 | 112 | var inputInsetPoint0: VectorInput { get } 113 | } 114 | 115 | extension InputInsetPoint0Available { 116 | 117 | public var insetPoint0: Vector2 { 118 | get { return inputInsetPoint0.vector2 } 119 | set { inputInsetPoint0.setVector(newValue.ciVector) } 120 | } 121 | } 122 | 123 | public protocol InputInsetPoint1Available { 124 | 125 | var inputInsetPoint1: VectorInput { get } 126 | } 127 | 128 | extension InputInsetPoint1Available { 129 | 130 | public var insetPoint1: Vector2 { 131 | get { return inputInsetPoint1.vector2 } 132 | set { inputInsetPoint1.setVector(newValue.ciVector) } 133 | } 134 | } 135 | 136 | // MARK: - Size 137 | 138 | public protocol InputVectorSizeAvailable { 139 | 140 | var inputVectorSize: VectorInput { get } 141 | } 142 | 143 | extension InputVectorSizeAvailable { 144 | 145 | public var vector_size: Vector2 { 146 | get { return inputVectorSize.vector2 } 147 | set { inputVectorSize.setVector(newValue.ciVector) } 148 | } 149 | } 150 | 151 | // MARK: - Extent 152 | 153 | public protocol InputExtentAvailable { 154 | 155 | var inputExtent: VectorInput { get } 156 | } 157 | 158 | extension InputExtentAvailable { 159 | 160 | public var extent: Vector4 { 161 | get { return inputExtent.vector4 } 162 | set { inputExtent.setVector(newValue.ciVector) } 163 | } 164 | } 165 | 166 | // MARK: - Rect 167 | 168 | public protocol InputTopLeftAvailable { 169 | 170 | var inputTopLeft: VectorInput { get } 171 | } 172 | 173 | extension InputTopLeftAvailable { 174 | 175 | public var topLeft: Vector2 { 176 | get { return inputTopLeft.vector2 } 177 | set { inputTopLeft.setVector(newValue.ciVector) } 178 | } 179 | } 180 | 181 | public protocol InputTopRightAvailable { 182 | 183 | var inputTopRight: VectorInput { get } 184 | } 185 | 186 | extension InputTopRightAvailable { 187 | 188 | public var topRight: Vector2 { 189 | get { return inputTopRight.vector2 } 190 | set { inputTopRight.setVector(newValue.ciVector) } 191 | } 192 | } 193 | 194 | public protocol InputBottomRightAvailable { 195 | 196 | var inputBottomRight: VectorInput { get } 197 | } 198 | 199 | extension InputBottomRightAvailable { 200 | 201 | public var bottomRight: Vector2 { 202 | get { return inputBottomRight.vector2 } 203 | set { inputBottomRight.setVector(newValue.ciVector) } 204 | } 205 | } 206 | 207 | public protocol InputBottomLeftAvailable { 208 | 209 | var inputBottomLeft: VectorInput { get } 210 | } 211 | 212 | extension InputBottomLeftAvailable { 213 | 214 | public var bottomLeft: Vector2 { 215 | get { return inputBottomLeft.vector2 } 216 | set { inputBottomLeft.setVector(newValue.ciVector) } 217 | } 218 | } 219 | 220 | // MARK: - Components Input 221 | 222 | public protocol InputMaxComponentsAvailable { 223 | 224 | var inputMaxComponents: VectorInput { get } 225 | } 226 | 227 | extension InputMaxComponentsAvailable { 228 | 229 | public var maxComponents: Vector4 { 230 | get { return inputMaxComponents.vector4 } 231 | set { inputMaxComponents.setVector(newValue.ciVector) } 232 | } 233 | } 234 | 235 | public protocol InputMinComponentsAvailable { 236 | 237 | var inputMinComponents: VectorInput { get } 238 | } 239 | 240 | extension InputMinComponentsAvailable { 241 | 242 | public var minComponents: Vector4 { 243 | get { return inputMinComponents.vector4 } 244 | set { inputMinComponents.setVector(newValue.ciVector) } 245 | } 246 | } 247 | 248 | // MARK: - RGBA 249 | 250 | public protocol InputRVectorAvailable { 251 | 252 | var inputRVector: VectorInput { get } 253 | } 254 | 255 | extension InputRVectorAvailable { 256 | 257 | public var rVector: Vector4 { 258 | get { return inputRVector.vector4 } 259 | set { inputRVector.setVector(newValue.ciVector) } 260 | } 261 | } 262 | 263 | public protocol InputGVectorAvailable { 264 | 265 | var inputGVector: VectorInput { get } 266 | } 267 | 268 | extension InputGVectorAvailable { 269 | 270 | public var gVector: Vector4 { 271 | get { return inputGVector.vector4 } 272 | set { inputGVector.setVector(newValue.ciVector) } 273 | } 274 | } 275 | 276 | public protocol InputBVectorAvailable { 277 | 278 | var inputBVector: VectorInput { get } 279 | } 280 | 281 | extension InputBVectorAvailable { 282 | 283 | public var bVector: Vector4 { 284 | get { return inputBVector.vector4 } 285 | set { inputBVector.setVector(newValue.ciVector) } 286 | } 287 | } 288 | 289 | public protocol InputAVectorAvailable { 290 | 291 | var inputAVector: VectorInput { get } 292 | } 293 | 294 | extension InputAVectorAvailable { 295 | 296 | public var aVector: Vector4 { 297 | get { return inputAVector.vector4 } 298 | set { inputAVector.setVector(newValue.ciVector) } 299 | } 300 | } 301 | 302 | public protocol InputBiasVectorAvailable { 303 | 304 | var inputBiasVector: VectorInput { get } 305 | } 306 | 307 | extension InputBiasVectorAvailable { 308 | 309 | public var biasVector: Vector4 { 310 | get { return inputBiasVector.vector4 } 311 | set { inputBiasVector.setVector(newValue.ciVector) } 312 | } 313 | } 314 | 315 | // MARK: - Coefficients 316 | 317 | public protocol InputRedCoefficientsAvailable { 318 | 319 | var inputRedCoefficients: VectorInput { get } 320 | } 321 | 322 | extension InputRedCoefficientsAvailable { 323 | 324 | public var redCoefficients: VectorAny { 325 | get { return inputRedCoefficients.vectorAny } 326 | set { inputRedCoefficients.setVector(newValue.ciVector) } 327 | } 328 | } 329 | 330 | public protocol InputGreenCoefficientsAvailable { 331 | 332 | var inputGreenCoefficients: VectorInput { get } 333 | } 334 | 335 | extension InputGreenCoefficientsAvailable { 336 | 337 | public var greenCoefficients: VectorAny { 338 | get { return inputGreenCoefficients.vectorAny } 339 | set { inputGreenCoefficients.setVector(newValue.ciVector) } 340 | } 341 | } 342 | 343 | public protocol InputBlueCoefficientsAvailable { 344 | 345 | var inputBlueCoefficients: VectorInput { get } 346 | } 347 | 348 | extension InputBlueCoefficientsAvailable { 349 | 350 | public var blueCoefficients: VectorAny { 351 | get { return inputBlueCoefficients.vectorAny } 352 | set { inputBlueCoefficients.setVector(newValue.ciVector) } 353 | } 354 | } 355 | 356 | public protocol InputAlphaCoefficientsAvailable { 357 | 358 | var inputAlphaCoefficients: VectorInput { get } 359 | } 360 | 361 | extension InputAlphaCoefficientsAvailable { 362 | 363 | public var alphaCoefficients: VectorAny { 364 | get { return inputAlphaCoefficients.vectorAny } 365 | set { inputAlphaCoefficients.setVector(newValue.ciVector) } 366 | } 367 | } 368 | 369 | // MARK: - Neutral 370 | 371 | public protocol InputNeutralAvailable { 372 | 373 | var inputNeutral: VectorInput { get } 374 | } 375 | 376 | extension InputNeutralAvailable { 377 | 378 | public var neutral: Vector2 { 379 | get { return inputNeutral.vector2 } 380 | set { inputNeutral.setVector(newValue.ciVector) } 381 | } 382 | } 383 | 384 | // MARK: - Target Neutral 385 | 386 | public protocol InputTargetNeutralAvailable { 387 | 388 | var inputTargetNeutral: VectorInput { get } 389 | } 390 | 391 | extension InputTargetNeutralAvailable { 392 | 393 | public var targetNeutral: Vector2 { 394 | get { return inputTargetNeutral.vector2 } 395 | set { inputTargetNeutral.setVector(newValue.ciVector) } 396 | } 397 | } 398 | 399 | // MARK: - Min Width 400 | 401 | public protocol InputMinWidthAvailable { 402 | 403 | var inputMinWidth: VectorInput { get } 404 | } 405 | 406 | extension InputMinWidthAvailable { 407 | 408 | public var minWidth: Vector2 { 409 | get { return inputMinWidth.vector2 } 410 | set { inputMinWidth.setVector(newValue.ciVector) } 411 | } 412 | } 413 | 414 | // MARK: - Max Width 415 | 416 | public protocol InputMaxWidthAvailable { 417 | 418 | var inputMaxWidth: VectorInput { get } 419 | } 420 | 421 | extension InputMaxWidthAvailable { 422 | 423 | public var maxWidth: Vector2 { 424 | get { return inputMaxWidth.vector2 } 425 | set { inputMaxWidth.setVector(newValue.ciVector) } 426 | } 427 | } 428 | 429 | // MARK: - Min Height 430 | 431 | public protocol InputMinHeightAvailable { 432 | 433 | var inputMinHeight: VectorInput { get } 434 | } 435 | 436 | extension InputMinHeightAvailable { 437 | 438 | public var minHeight: Vector2 { 439 | get { return inputMinHeight.vector2 } 440 | set { inputMinHeight.setVector(newValue.ciVector) } 441 | } 442 | } 443 | 444 | // MARK: - Max Height 445 | 446 | public protocol InputMaxHeightAvailable { 447 | 448 | var inputMaxHeight: VectorInput { get } 449 | } 450 | 451 | extension InputMaxHeightAvailable { 452 | 453 | public var maxHeight: Vector2 { 454 | get { return inputMaxHeight.vector2 } 455 | set { inputMaxHeight.setVector(newValue.ciVector) } 456 | } 457 | } 458 | 459 | // MARK: - Weights 460 | 461 | public protocol InputWeightsAvailable { 462 | 463 | var inputWeights: VectorInput { get } 464 | } 465 | 466 | extension InputWeightsAvailable { 467 | 468 | public var weights: VectorAny { 469 | get { return inputWeights.vectorAny } 470 | set { inputWeights.setVector(newValue.ciVector) } 471 | } 472 | } 473 | 474 | // MARK: - Rectangle 475 | 476 | public protocol InputRectangleAvailable { 477 | 478 | var inputRectangle: VectorInput { get } 479 | } 480 | 481 | extension InputRectangleAvailable { 482 | 483 | public var rectangle: Vector4 { 484 | get { return inputRectangle.vector4 } 485 | set { inputRectangle.setVector(newValue.ciVector) } 486 | } 487 | } 488 | --------------------------------------------------------------------------------