├── .gitignore ├── Example.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── WorkspaceSettings.xcsettings ├── xcshareddata │ └── xcschemes │ │ └── Lady.xcscheme └── xcuserdata │ └── Limon.xcuserdatad │ └── xcschemes │ ├── Example.xcscheme │ └── xcschememanagement.plist ├── Example ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── SampleImage.imageset │ │ ├── Contents.json │ │ └── SampleImage.jpg ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── DefaultRenderContextViewController.swift ├── Info.plist ├── MainTableTableViewController.swift ├── MetalRenderContextViewController.swift └── OpenGLRenderContextViewController.swift ├── LICENSE ├── Lady.podspec ├── Lady ├── GreenBlueChannelOverlayBlendFilter.swift ├── HighPassFilter.swift ├── HighPassSkinSmoothingFilter.swift ├── HighPassSkinSmoothingMaskBoostFilter.swift ├── HighPassSkinSmoothingMaskGenerator.swift ├── Info.plist ├── Lady.h ├── RGBToneCurveFilter.swift └── Resources │ ├── GreenBlueChannelOverlayBlendFilter.cikernel │ ├── HighPassFilter.cikernel │ ├── HighPassSkinSmoothingMaskBoostFilter.cikernel │ └── RGBToneCurveFilter.cikernel ├── README.md └── release.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # OS X 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | ED23AC301D51BF7E00E441A9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED23AC2F1D51BF7E00E441A9 /* AppDelegate.swift */; }; 11 | ED23AC351D51BF7E00E441A9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ED23AC331D51BF7E00E441A9 /* Main.storyboard */; }; 12 | ED23AC371D51BF7E00E441A9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ED23AC361D51BF7E00E441A9 /* Assets.xcassets */; }; 13 | ED23AC3A1D51BF7E00E441A9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ED23AC381D51BF7E00E441A9 /* LaunchScreen.storyboard */; }; 14 | ED23AC491D51BFB400E441A9 /* Lady.h in Headers */ = {isa = PBXBuildFile; fileRef = ED23AC481D51BFB400E441A9 /* Lady.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | ED23AC4D1D51BFB400E441A9 /* Lady.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED23AC461D51BFB400E441A9 /* Lady.framework */; }; 16 | ED23AC4E1D51BFB400E441A9 /* Lady.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = ED23AC461D51BFB400E441A9 /* Lady.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | ED23AC561D51C17800E441A9 /* RGBToneCurveFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED23AC551D51C17800E441A9 /* RGBToneCurveFilter.swift */; }; 18 | ED3BED581D56325000007787 /* GreenBlueChannelOverlayBlendFilter.cikernel in Resources */ = {isa = PBXBuildFile; fileRef = ED3BED541D56325000007787 /* GreenBlueChannelOverlayBlendFilter.cikernel */; }; 19 | ED3BED591D56325000007787 /* HighPassFilter.cikernel in Resources */ = {isa = PBXBuildFile; fileRef = ED3BED551D56325000007787 /* HighPassFilter.cikernel */; }; 20 | ED3BED5A1D56325000007787 /* HighPassSkinSmoothingMaskBoostFilter.cikernel in Resources */ = {isa = PBXBuildFile; fileRef = ED3BED561D56325000007787 /* HighPassSkinSmoothingMaskBoostFilter.cikernel */; }; 21 | ED3BED5B1D56325000007787 /* RGBToneCurveFilter.cikernel in Resources */ = {isa = PBXBuildFile; fileRef = ED3BED571D56325000007787 /* RGBToneCurveFilter.cikernel */; }; 22 | ED6EE7CF1D52231F006D678E /* HighPassFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED6EE7CE1D52231F006D678E /* HighPassFilter.swift */; }; 23 | EDAB85CD1D5334EB0019D33A /* HighPassSkinSmoothingFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAB85CC1D5334EB0019D33A /* HighPassSkinSmoothingFilter.swift */; }; 24 | EDAB85CF1D5335FC0019D33A /* GreenBlueChannelOverlayBlendFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAB85CE1D5335FC0019D33A /* GreenBlueChannelOverlayBlendFilter.swift */; }; 25 | EDAB85D31D53373A0019D33A /* HighPassSkinSmoothingMaskBoostFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAB85D21D53373A0019D33A /* HighPassSkinSmoothingMaskBoostFilter.swift */; }; 26 | EDDA941B1D54875500C0F3B6 /* HighPassSkinSmoothingMaskGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDA941A1D54875500C0F3B6 /* HighPassSkinSmoothingMaskGenerator.swift */; }; 27 | EDDA94201D54A8A200C0F3B6 /* MainTableTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDA941C1D54A8A200C0F3B6 /* MainTableTableViewController.swift */; }; 28 | EDDA94211D54A8A200C0F3B6 /* DefaultRenderContextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDA941D1D54A8A200C0F3B6 /* DefaultRenderContextViewController.swift */; }; 29 | EDDA94221D54A8A200C0F3B6 /* OpenGLRenderContextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDA941E1D54A8A200C0F3B6 /* OpenGLRenderContextViewController.swift */; }; 30 | EDDA94231D54A8A200C0F3B6 /* MetalRenderContextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDA941F1D54A8A200C0F3B6 /* MetalRenderContextViewController.swift */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | ED23AC4B1D51BFB400E441A9 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = ED23AC241D51BF7E00E441A9 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = ED23AC451D51BFB400E441A9; 39 | remoteInfo = Lady; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXCopyFilesBuildPhase section */ 44 | ED23AC521D51BFB400E441A9 /* Embed Frameworks */ = { 45 | isa = PBXCopyFilesBuildPhase; 46 | buildActionMask = 2147483647; 47 | dstPath = ""; 48 | dstSubfolderSpec = 10; 49 | files = ( 50 | ED23AC4E1D51BFB400E441A9 /* Lady.framework in Embed Frameworks */, 51 | ); 52 | name = "Embed Frameworks"; 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXCopyFilesBuildPhase section */ 56 | 57 | /* Begin PBXFileReference section */ 58 | ED23AC2C1D51BF7E00E441A9 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | ED23AC2F1D51BF7E00E441A9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 60 | ED23AC341D51BF7E00E441A9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 61 | ED23AC361D51BF7E00E441A9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 62 | ED23AC391D51BF7E00E441A9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 63 | ED23AC3B1D51BF7E00E441A9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | ED23AC461D51BFB400E441A9 /* Lady.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Lady.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | ED23AC481D51BFB400E441A9 /* Lady.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Lady.h; sourceTree = ""; }; 66 | ED23AC4A1D51BFB400E441A9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | ED23AC551D51C17800E441A9 /* RGBToneCurveFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RGBToneCurveFilter.swift; sourceTree = ""; }; 68 | ED3BED541D56325000007787 /* GreenBlueChannelOverlayBlendFilter.cikernel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GreenBlueChannelOverlayBlendFilter.cikernel; sourceTree = ""; }; 69 | ED3BED551D56325000007787 /* HighPassFilter.cikernel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HighPassFilter.cikernel; sourceTree = ""; }; 70 | ED3BED561D56325000007787 /* HighPassSkinSmoothingMaskBoostFilter.cikernel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HighPassSkinSmoothingMaskBoostFilter.cikernel; sourceTree = ""; }; 71 | ED3BED571D56325000007787 /* RGBToneCurveFilter.cikernel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = RGBToneCurveFilter.cikernel; sourceTree = ""; }; 72 | ED6EE7CE1D52231F006D678E /* HighPassFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighPassFilter.swift; sourceTree = ""; }; 73 | EDAB85CC1D5334EB0019D33A /* HighPassSkinSmoothingFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighPassSkinSmoothingFilter.swift; sourceTree = ""; }; 74 | EDAB85CE1D5335FC0019D33A /* GreenBlueChannelOverlayBlendFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GreenBlueChannelOverlayBlendFilter.swift; sourceTree = ""; }; 75 | EDAB85D21D53373A0019D33A /* HighPassSkinSmoothingMaskBoostFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighPassSkinSmoothingMaskBoostFilter.swift; sourceTree = ""; }; 76 | EDDA941A1D54875500C0F3B6 /* HighPassSkinSmoothingMaskGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighPassSkinSmoothingMaskGenerator.swift; sourceTree = ""; }; 77 | EDDA941C1D54A8A200C0F3B6 /* MainTableTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTableTableViewController.swift; sourceTree = ""; }; 78 | EDDA941D1D54A8A200C0F3B6 /* DefaultRenderContextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultRenderContextViewController.swift; sourceTree = ""; }; 79 | EDDA941E1D54A8A200C0F3B6 /* OpenGLRenderContextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenGLRenderContextViewController.swift; sourceTree = ""; }; 80 | EDDA941F1D54A8A200C0F3B6 /* MetalRenderContextViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MetalRenderContextViewController.swift; sourceTree = ""; }; 81 | /* End PBXFileReference section */ 82 | 83 | /* Begin PBXFrameworksBuildPhase section */ 84 | ED23AC291D51BF7E00E441A9 /* Frameworks */ = { 85 | isa = PBXFrameworksBuildPhase; 86 | buildActionMask = 2147483647; 87 | files = ( 88 | ED23AC4D1D51BFB400E441A9 /* Lady.framework in Frameworks */, 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | ED23AC421D51BFB400E441A9 /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | ED23AC231D51BF7E00E441A9 = { 103 | isa = PBXGroup; 104 | children = ( 105 | ED23AC2E1D51BF7E00E441A9 /* Example */, 106 | ED23AC471D51BFB400E441A9 /* Lady */, 107 | ED23AC2D1D51BF7E00E441A9 /* Products */, 108 | ); 109 | sourceTree = ""; 110 | }; 111 | ED23AC2D1D51BF7E00E441A9 /* Products */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | ED23AC2C1D51BF7E00E441A9 /* Example.app */, 115 | ED23AC461D51BFB400E441A9 /* Lady.framework */, 116 | ); 117 | name = Products; 118 | sourceTree = ""; 119 | }; 120 | ED23AC2E1D51BF7E00E441A9 /* Example */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | ED23AC2F1D51BF7E00E441A9 /* AppDelegate.swift */, 124 | EDDA941C1D54A8A200C0F3B6 /* MainTableTableViewController.swift */, 125 | EDDA941D1D54A8A200C0F3B6 /* DefaultRenderContextViewController.swift */, 126 | EDDA941E1D54A8A200C0F3B6 /* OpenGLRenderContextViewController.swift */, 127 | EDDA941F1D54A8A200C0F3B6 /* MetalRenderContextViewController.swift */, 128 | ED23AC331D51BF7E00E441A9 /* Main.storyboard */, 129 | ED23AC361D51BF7E00E441A9 /* Assets.xcassets */, 130 | ED23AC381D51BF7E00E441A9 /* LaunchScreen.storyboard */, 131 | ED23AC3B1D51BF7E00E441A9 /* Info.plist */, 132 | ); 133 | path = Example; 134 | sourceTree = ""; 135 | }; 136 | ED23AC471D51BFB400E441A9 /* Lady */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | ED23AC481D51BFB400E441A9 /* Lady.h */, 140 | ED23AC4A1D51BFB400E441A9 /* Info.plist */, 141 | EDAB85CC1D5334EB0019D33A /* HighPassSkinSmoothingFilter.swift */, 142 | ED6EE7CE1D52231F006D678E /* HighPassFilter.swift */, 143 | ED23AC551D51C17800E441A9 /* RGBToneCurveFilter.swift */, 144 | EDAB85CE1D5335FC0019D33A /* GreenBlueChannelOverlayBlendFilter.swift */, 145 | EDAB85D21D53373A0019D33A /* HighPassSkinSmoothingMaskBoostFilter.swift */, 146 | EDDA941A1D54875500C0F3B6 /* HighPassSkinSmoothingMaskGenerator.swift */, 147 | ED3BED531D56325000007787 /* Resources */, 148 | ); 149 | path = Lady; 150 | sourceTree = ""; 151 | }; 152 | ED3BED531D56325000007787 /* Resources */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | ED3BED541D56325000007787 /* GreenBlueChannelOverlayBlendFilter.cikernel */, 156 | ED3BED551D56325000007787 /* HighPassFilter.cikernel */, 157 | ED3BED561D56325000007787 /* HighPassSkinSmoothingMaskBoostFilter.cikernel */, 158 | ED3BED571D56325000007787 /* RGBToneCurveFilter.cikernel */, 159 | ); 160 | path = Resources; 161 | sourceTree = ""; 162 | }; 163 | /* End PBXGroup section */ 164 | 165 | /* Begin PBXHeadersBuildPhase section */ 166 | ED23AC431D51BFB400E441A9 /* Headers */ = { 167 | isa = PBXHeadersBuildPhase; 168 | buildActionMask = 2147483647; 169 | files = ( 170 | ED23AC491D51BFB400E441A9 /* Lady.h in Headers */, 171 | ); 172 | runOnlyForDeploymentPostprocessing = 0; 173 | }; 174 | /* End PBXHeadersBuildPhase section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | ED23AC2B1D51BF7E00E441A9 /* Example */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = ED23AC3E1D51BF7E00E441A9 /* Build configuration list for PBXNativeTarget "Example" */; 180 | buildPhases = ( 181 | ED23AC281D51BF7E00E441A9 /* Sources */, 182 | ED23AC291D51BF7E00E441A9 /* Frameworks */, 183 | ED23AC2A1D51BF7E00E441A9 /* Resources */, 184 | ED23AC521D51BFB400E441A9 /* Embed Frameworks */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ED23AC4C1D51BFB400E441A9 /* PBXTargetDependency */, 190 | ); 191 | name = Example; 192 | productName = Example; 193 | productReference = ED23AC2C1D51BF7E00E441A9 /* Example.app */; 194 | productType = "com.apple.product-type.application"; 195 | }; 196 | ED23AC451D51BFB400E441A9 /* Lady */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = ED23AC4F1D51BFB400E441A9 /* Build configuration list for PBXNativeTarget "Lady" */; 199 | buildPhases = ( 200 | ED23AC411D51BFB400E441A9 /* Sources */, 201 | ED23AC421D51BFB400E441A9 /* Frameworks */, 202 | ED23AC431D51BFB400E441A9 /* Headers */, 203 | ED23AC441D51BFB400E441A9 /* Resources */, 204 | ); 205 | buildRules = ( 206 | ); 207 | dependencies = ( 208 | ); 209 | name = Lady; 210 | productName = Lady; 211 | productReference = ED23AC461D51BFB400E441A9 /* Lady.framework */; 212 | productType = "com.apple.product-type.framework"; 213 | }; 214 | /* End PBXNativeTarget section */ 215 | 216 | /* Begin PBXProject section */ 217 | ED23AC241D51BF7E00E441A9 /* Project object */ = { 218 | isa = PBXProject; 219 | attributes = { 220 | LastSwiftUpdateCheck = 0730; 221 | LastUpgradeCheck = 0910; 222 | ORGANIZATIONNAME = Lady; 223 | TargetAttributes = { 224 | ED23AC2B1D51BF7E00E441A9 = { 225 | CreatedOnToolsVersion = 7.3.1; 226 | LastSwiftMigration = 0910; 227 | ProvisioningStyle = Automatic; 228 | }; 229 | ED23AC451D51BFB400E441A9 = { 230 | CreatedOnToolsVersion = 7.3.1; 231 | LastSwiftMigration = 0910; 232 | }; 233 | }; 234 | }; 235 | buildConfigurationList = ED23AC271D51BF7E00E441A9 /* Build configuration list for PBXProject "Example" */; 236 | compatibilityVersion = "Xcode 3.2"; 237 | developmentRegion = English; 238 | hasScannedForEncodings = 0; 239 | knownRegions = ( 240 | en, 241 | Base, 242 | ); 243 | mainGroup = ED23AC231D51BF7E00E441A9; 244 | productRefGroup = ED23AC2D1D51BF7E00E441A9 /* Products */; 245 | projectDirPath = ""; 246 | projectRoot = ""; 247 | targets = ( 248 | ED23AC2B1D51BF7E00E441A9 /* Example */, 249 | ED23AC451D51BFB400E441A9 /* Lady */, 250 | ); 251 | }; 252 | /* End PBXProject section */ 253 | 254 | /* Begin PBXResourcesBuildPhase section */ 255 | ED23AC2A1D51BF7E00E441A9 /* Resources */ = { 256 | isa = PBXResourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | ED23AC3A1D51BF7E00E441A9 /* LaunchScreen.storyboard in Resources */, 260 | ED23AC371D51BF7E00E441A9 /* Assets.xcassets in Resources */, 261 | ED23AC351D51BF7E00E441A9 /* Main.storyboard in Resources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | ED23AC441D51BFB400E441A9 /* Resources */ = { 266 | isa = PBXResourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | ED3BED581D56325000007787 /* GreenBlueChannelOverlayBlendFilter.cikernel in Resources */, 270 | ED3BED591D56325000007787 /* HighPassFilter.cikernel in Resources */, 271 | ED3BED5B1D56325000007787 /* RGBToneCurveFilter.cikernel in Resources */, 272 | ED3BED5A1D56325000007787 /* HighPassSkinSmoothingMaskBoostFilter.cikernel in Resources */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXResourcesBuildPhase section */ 277 | 278 | /* Begin PBXSourcesBuildPhase section */ 279 | ED23AC281D51BF7E00E441A9 /* Sources */ = { 280 | isa = PBXSourcesBuildPhase; 281 | buildActionMask = 2147483647; 282 | files = ( 283 | EDDA94231D54A8A200C0F3B6 /* MetalRenderContextViewController.swift in Sources */, 284 | EDDA94211D54A8A200C0F3B6 /* DefaultRenderContextViewController.swift in Sources */, 285 | ED23AC301D51BF7E00E441A9 /* AppDelegate.swift in Sources */, 286 | EDDA94201D54A8A200C0F3B6 /* MainTableTableViewController.swift in Sources */, 287 | EDDA94221D54A8A200C0F3B6 /* OpenGLRenderContextViewController.swift in Sources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | ED23AC411D51BFB400E441A9 /* Sources */ = { 292 | isa = PBXSourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | EDAB85D31D53373A0019D33A /* HighPassSkinSmoothingMaskBoostFilter.swift in Sources */, 296 | EDDA941B1D54875500C0F3B6 /* HighPassSkinSmoothingMaskGenerator.swift in Sources */, 297 | ED6EE7CF1D52231F006D678E /* HighPassFilter.swift in Sources */, 298 | EDAB85CD1D5334EB0019D33A /* HighPassSkinSmoothingFilter.swift in Sources */, 299 | EDAB85CF1D5335FC0019D33A /* GreenBlueChannelOverlayBlendFilter.swift in Sources */, 300 | ED23AC561D51C17800E441A9 /* RGBToneCurveFilter.swift in Sources */, 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | /* End PBXSourcesBuildPhase section */ 305 | 306 | /* Begin PBXTargetDependency section */ 307 | ED23AC4C1D51BFB400E441A9 /* PBXTargetDependency */ = { 308 | isa = PBXTargetDependency; 309 | target = ED23AC451D51BFB400E441A9 /* Lady */; 310 | targetProxy = ED23AC4B1D51BFB400E441A9 /* PBXContainerItemProxy */; 311 | }; 312 | /* End PBXTargetDependency section */ 313 | 314 | /* Begin PBXVariantGroup section */ 315 | ED23AC331D51BF7E00E441A9 /* Main.storyboard */ = { 316 | isa = PBXVariantGroup; 317 | children = ( 318 | ED23AC341D51BF7E00E441A9 /* Base */, 319 | ); 320 | name = Main.storyboard; 321 | sourceTree = ""; 322 | }; 323 | ED23AC381D51BF7E00E441A9 /* LaunchScreen.storyboard */ = { 324 | isa = PBXVariantGroup; 325 | children = ( 326 | ED23AC391D51BF7E00E441A9 /* Base */, 327 | ); 328 | name = LaunchScreen.storyboard; 329 | sourceTree = ""; 330 | }; 331 | /* End PBXVariantGroup section */ 332 | 333 | /* Begin XCBuildConfiguration section */ 334 | ED23AC3C1D51BF7E00E441A9 /* Debug */ = { 335 | isa = XCBuildConfiguration; 336 | buildSettings = { 337 | ALWAYS_SEARCH_USER_PATHS = NO; 338 | CLANG_ANALYZER_NONNULL = YES; 339 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 340 | CLANG_CXX_LIBRARY = "libc++"; 341 | CLANG_ENABLE_MODULES = YES; 342 | CLANG_ENABLE_OBJC_ARC = YES; 343 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 344 | CLANG_WARN_BOOL_CONVERSION = YES; 345 | CLANG_WARN_COMMA = YES; 346 | CLANG_WARN_CONSTANT_CONVERSION = YES; 347 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 348 | CLANG_WARN_EMPTY_BODY = YES; 349 | CLANG_WARN_ENUM_CONVERSION = YES; 350 | CLANG_WARN_INFINITE_RECURSION = YES; 351 | CLANG_WARN_INT_CONVERSION = YES; 352 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 353 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 354 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 355 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 356 | CLANG_WARN_STRICT_PROTOTYPES = YES; 357 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 358 | CLANG_WARN_UNREACHABLE_CODE = YES; 359 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 360 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 361 | COPY_PHASE_STRIP = NO; 362 | DEBUG_INFORMATION_FORMAT = dwarf; 363 | ENABLE_STRICT_OBJC_MSGSEND = YES; 364 | ENABLE_TESTABILITY = YES; 365 | GCC_C_LANGUAGE_STANDARD = gnu99; 366 | GCC_DYNAMIC_NO_PIC = NO; 367 | GCC_NO_COMMON_BLOCKS = YES; 368 | GCC_OPTIMIZATION_LEVEL = 0; 369 | GCC_PREPROCESSOR_DEFINITIONS = ( 370 | "DEBUG=1", 371 | "$(inherited)", 372 | ); 373 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 374 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 375 | GCC_WARN_UNDECLARED_SELECTOR = YES; 376 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 377 | GCC_WARN_UNUSED_FUNCTION = YES; 378 | GCC_WARN_UNUSED_VARIABLE = YES; 379 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 380 | MTL_ENABLE_DEBUG_INFO = YES; 381 | ONLY_ACTIVE_ARCH = YES; 382 | SDKROOT = iphoneos; 383 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 384 | }; 385 | name = Debug; 386 | }; 387 | ED23AC3D1D51BF7E00E441A9 /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 401 | CLANG_WARN_EMPTY_BODY = YES; 402 | CLANG_WARN_ENUM_CONVERSION = YES; 403 | CLANG_WARN_INFINITE_RECURSION = YES; 404 | CLANG_WARN_INT_CONVERSION = YES; 405 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 406 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 408 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 409 | CLANG_WARN_STRICT_PROTOTYPES = YES; 410 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 411 | CLANG_WARN_UNREACHABLE_CODE = YES; 412 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 413 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 414 | COPY_PHASE_STRIP = NO; 415 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 416 | ENABLE_NS_ASSERTIONS = NO; 417 | ENABLE_STRICT_OBJC_MSGSEND = YES; 418 | GCC_C_LANGUAGE_STANDARD = gnu99; 419 | GCC_NO_COMMON_BLOCKS = YES; 420 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 421 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 422 | GCC_WARN_UNDECLARED_SELECTOR = YES; 423 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 424 | GCC_WARN_UNUSED_FUNCTION = YES; 425 | GCC_WARN_UNUSED_VARIABLE = YES; 426 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 427 | MTL_ENABLE_DEBUG_INFO = NO; 428 | SDKROOT = iphoneos; 429 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 430 | VALIDATE_PRODUCT = YES; 431 | }; 432 | name = Release; 433 | }; 434 | ED23AC3F1D51BF7E00E441A9 /* Debug */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 438 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 439 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 440 | DEVELOPMENT_TEAM = ""; 441 | INFOPLIST_FILE = Example/Info.plist; 442 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 443 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 444 | OTHER_SWIFT_FLAGS = ""; 445 | PRODUCT_BUNDLE_IDENTIFIER = top.limon.Example; 446 | PRODUCT_NAME = "$(TARGET_NAME)"; 447 | PROVISIONING_PROFILE = ""; 448 | PROVISIONING_PROFILE_SPECIFIER = ""; 449 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 450 | SWIFT_VERSION = 4.0; 451 | }; 452 | name = Debug; 453 | }; 454 | ED23AC401D51BF7E00E441A9 /* Release */ = { 455 | isa = XCBuildConfiguration; 456 | buildSettings = { 457 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 458 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 459 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 460 | DEVELOPMENT_TEAM = ""; 461 | INFOPLIST_FILE = Example/Info.plist; 462 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 463 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 464 | OTHER_SWIFT_FLAGS = ""; 465 | PRODUCT_BUNDLE_IDENTIFIER = top.limon.Example; 466 | PRODUCT_NAME = "$(TARGET_NAME)"; 467 | PROVISIONING_PROFILE = ""; 468 | PROVISIONING_PROFILE_SPECIFIER = ""; 469 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 470 | SWIFT_VERSION = 4.0; 471 | }; 472 | name = Release; 473 | }; 474 | ED23AC501D51BFB400E441A9 /* Debug */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | CLANG_ENABLE_MODULES = YES; 478 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 479 | CURRENT_PROJECT_VERSION = 1; 480 | DEFINES_MODULE = YES; 481 | DYLIB_COMPATIBILITY_VERSION = 1; 482 | DYLIB_CURRENT_VERSION = 1; 483 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 484 | INFOPLIST_FILE = Lady/Info.plist; 485 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 486 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 487 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 488 | OTHER_SWIFT_FLAGS = ""; 489 | PRODUCT_BUNDLE_IDENTIFIER = top.limon.Lady; 490 | PRODUCT_NAME = "$(TARGET_NAME)"; 491 | SKIP_INSTALL = YES; 492 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 493 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 494 | SWIFT_VERSION = 4.0; 495 | TARGETED_DEVICE_FAMILY = "1,2"; 496 | VERSIONING_SYSTEM = "apple-generic"; 497 | VERSION_INFO_PREFIX = ""; 498 | }; 499 | name = Debug; 500 | }; 501 | ED23AC511D51BFB400E441A9 /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | CLANG_ENABLE_MODULES = YES; 505 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 506 | CURRENT_PROJECT_VERSION = 1; 507 | DEFINES_MODULE = YES; 508 | DYLIB_COMPATIBILITY_VERSION = 1; 509 | DYLIB_CURRENT_VERSION = 1; 510 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 511 | INFOPLIST_FILE = Lady/Info.plist; 512 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 513 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 514 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 515 | OTHER_SWIFT_FLAGS = ""; 516 | PRODUCT_BUNDLE_IDENTIFIER = top.limon.Lady; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | SKIP_INSTALL = YES; 519 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 520 | SWIFT_VERSION = 4.0; 521 | TARGETED_DEVICE_FAMILY = "1,2"; 522 | VERSIONING_SYSTEM = "apple-generic"; 523 | VERSION_INFO_PREFIX = ""; 524 | }; 525 | name = Release; 526 | }; 527 | /* End XCBuildConfiguration section */ 528 | 529 | /* Begin XCConfigurationList section */ 530 | ED23AC271D51BF7E00E441A9 /* Build configuration list for PBXProject "Example" */ = { 531 | isa = XCConfigurationList; 532 | buildConfigurations = ( 533 | ED23AC3C1D51BF7E00E441A9 /* Debug */, 534 | ED23AC3D1D51BF7E00E441A9 /* Release */, 535 | ); 536 | defaultConfigurationIsVisible = 0; 537 | defaultConfigurationName = Release; 538 | }; 539 | ED23AC3E1D51BF7E00E441A9 /* Build configuration list for PBXNativeTarget "Example" */ = { 540 | isa = XCConfigurationList; 541 | buildConfigurations = ( 542 | ED23AC3F1D51BF7E00E441A9 /* Debug */, 543 | ED23AC401D51BF7E00E441A9 /* Release */, 544 | ); 545 | defaultConfigurationIsVisible = 0; 546 | defaultConfigurationName = Release; 547 | }; 548 | ED23AC4F1D51BFB400E441A9 /* Build configuration list for PBXNativeTarget "Lady" */ = { 549 | isa = XCConfigurationList; 550 | buildConfigurations = ( 551 | ED23AC501D51BFB400E441A9 /* Debug */, 552 | ED23AC511D51BFB400E441A9 /* Release */, 553 | ); 554 | defaultConfigurationIsVisible = 0; 555 | defaultConfigurationName = Release; 556 | }; 557 | /* End XCConfigurationList section */ 558 | }; 559 | rootObject = ED23AC241D51BF7E00E441A9 /* Project object */; 560 | } 561 | -------------------------------------------------------------------------------- /Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Latest 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example.xcodeproj/xcshareddata/xcschemes/Lady.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Example.xcodeproj/xcuserdata/Limon.xcuserdatad/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Example.xcodeproj/xcuserdata/Limon.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Example.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | Lady.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | ED23AC2B1D51BF7E00E441A9 21 | 22 | primary 23 | 24 | 25 | ED23AC451D51BFB400E441A9 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Limon on 8/3/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | private func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 17 | return true 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Example/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 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/SampleImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "SampleImage.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/SampleImage.imageset/SampleImage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Limon-O-O/Lady/1216095b93f32c9df23240ec821b81f9c6731898/Example/Assets.xcassets/SampleImage.imageset/SampleImage.jpg -------------------------------------------------------------------------------- /Example/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 | -------------------------------------------------------------------------------- /Example/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 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 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 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /Example/DefaultRenderContextViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultContextViewController.swift 3 | // Lady 4 | // 5 | // Created by Limon on 8/3/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Lady 11 | import MobileCoreServices.UTType 12 | 13 | class DefaultRenderContextViewController: UIViewController { 14 | 15 | private let context: CIContext = { 16 | let eaglContext = EAGLContext(api: .openGLES2) 17 | let options = [kCIContextWorkingColorSpace: CGColorSpaceCreateDeviceRGB()] 18 | return CIContext(eaglContext: eaglContext!, options: options) 19 | }() 20 | 21 | private let filter = HighPassSkinSmoothingFilter() 22 | 23 | @IBOutlet private weak var imageView: UIImageView! 24 | 25 | @IBOutlet fileprivate weak var amountSlider: UISlider! 26 | 27 | fileprivate var sourceImage: UIImage! { 28 | didSet { 29 | self.inputCIImage = CIImage(cgImage: self.sourceImage.cgImage!) 30 | } 31 | } 32 | 33 | private var processedImage: UIImage? 34 | 35 | private var inputCIImage: CIImage! 36 | 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | self.sourceImage = UIImage(named: "SampleImage")! 40 | self.processImage(byInputAmount: self.amountSlider.value) 41 | } 42 | 43 | @IBAction private func amountSliderTouchUp(sender: UISlider) { 44 | self.processImage(byInputAmount: sender.value) 45 | } 46 | 47 | fileprivate func processImage(byInputAmount inputAmount: Float) { 48 | 49 | filter.inputImage = inputCIImage 50 | filter.inputAmount = inputAmount 51 | filter.inputRadius = Float(7.0 * inputCIImage.extent.width/750.0) 52 | 53 | let outputCIImage = filter.outputImage! 54 | 55 | let outputCGImage = context.createCGImage(outputCIImage, from: outputCIImage.extent) 56 | let outputUIImage = UIImage(cgImage: outputCGImage!, scale: self.sourceImage.scale, orientation: sourceImage.imageOrientation) 57 | 58 | self.processedImage = outputUIImage 59 | self.imageView.image = outputUIImage 60 | } 61 | 62 | @IBAction private func handleImageViewLongPress(sender: UILongPressGestureRecognizer) { 63 | if sender.state == .began { 64 | self.imageView.image = self.sourceImage 65 | } else if (sender.state == .ended || sender.state == .cancelled) { 66 | self.imageView.image = self.processedImage 67 | } 68 | } 69 | 70 | @IBAction private func chooseImageBarButtonItemTapped(sender: AnyObject) { 71 | let imagePickerController = UIImagePickerController() 72 | imagePickerController.view.backgroundColor = UIColor.white 73 | imagePickerController.delegate = self 74 | self.present(imagePickerController, animated: true, completion: nil) 75 | } 76 | } 77 | 78 | extension DefaultRenderContextViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { 79 | 80 | private func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { 81 | 82 | if let mediaType = info[UIImagePickerControllerMediaType] as? String { 83 | 84 | switch mediaType { 85 | case String(kUTTypeImage): 86 | if let image = info[UIImagePickerControllerOriginalImage] as? UIImage { 87 | self.sourceImage = image 88 | self.processImage(byInputAmount: self.amountSlider.value) 89 | } 90 | default: 91 | break 92 | } 93 | } 94 | 95 | dismiss(animated: true, completion: nil) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Example/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/MainTableTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTableTableViewController.swift 3 | // Lady 4 | // 5 | // Created by Limon on 8/3/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MainTableTableViewController: UITableViewController { 12 | 13 | override func viewWillAppear(_ animated: Bool) { 14 | super.viewWillAppear(animated) 15 | if let indexPath = self.tableView.indexPathsForSelectedRows?.first { 16 | self.tableView.deselectRow(at: indexPath, animated: true) 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Example/MetalRenderContextViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MetalRenderContextViewController.swift 3 | // Lady 4 | // 5 | // Created by Limon on 8/3/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Lady 11 | 12 | #if !(arch(i386) || arch(x86_64)) && (os(iOS) || os(watchOS) || os(tvOS)) 13 | import MetalKit 14 | 15 | @available(iOS 9.0, *) 16 | class MetalRenderContextViewController: UIViewController, MTKViewDelegate { 17 | 18 | @IBOutlet private weak var metalView: MTKView! 19 | 20 | private var context: CIContext! 21 | private var commandQueue: MTLCommandQueue! 22 | private var inputTexture: MTLTexture! 23 | 24 | private let filter = HighPassSkinSmoothingFilter() 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | guard let device = MTLCreateSystemDefaultDevice() else { return } 30 | metalView.device = device 31 | metalView.delegate = self 32 | metalView.framebufferOnly = false 33 | metalView.enableSetNeedsDisplay = true 34 | 35 | context = CIContext(mtlDevice: device, options: [kCIContextWorkingColorSpace:CGColorSpaceCreateDeviceRGB()]) 36 | commandQueue = device.makeCommandQueue() 37 | 38 | inputTexture = try! MTKTextureLoader(device: self.metalView.device!).newTexture(with: UIImage(named: "SampleImage")!.cgImage!, options: nil) 39 | } 40 | 41 | func draw(in view: MTKView) { 42 | 43 | let commandBuffer = commandQueue.makeCommandBuffer() 44 | 45 | let inputCIImage = CIImage(mtlTexture: inputTexture, options: nil) 46 | 47 | filter.inputImage = inputCIImage 48 | filter.inputAmount = 0.7 49 | filter.inputRadius = Float(7.0 * (inputCIImage?.extent.width)!/750.0) 50 | 51 | let outputCIImage = filter.outputImage! 52 | 53 | let cs = CGColorSpaceCreateDeviceRGB() 54 | let outputTexture = view.currentDrawable?.texture 55 | 56 | context.render(outputCIImage, to: outputTexture!, 57 | commandBuffer: commandBuffer, bounds: outputCIImage.extent, colorSpace: cs) 58 | 59 | commandBuffer.present(view.currentDrawable!) 60 | commandBuffer.commit() 61 | } 62 | 63 | func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { 64 | view.draw() 65 | } 66 | } 67 | 68 | #else 69 | 70 | class MetalRenderContextViewController: UIViewController { 71 | @IBOutlet private weak var metalView: UIView! 72 | } 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /Example/OpenGLRenderContextViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OpenGLRenderContextViewController.swift 3 | // Lady 4 | // 5 | // Created by Limon on 8/3/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import GLKit 11 | import Lady 12 | import AVFoundation.AVUtilities 13 | 14 | class OpenGLRenderContextViewController: GLKViewController { 15 | 16 | private var context: CIContext! 17 | 18 | private var glkView: GLKView! { return self.view as! GLKView } 19 | 20 | private var startDate: NSDate = NSDate() 21 | 22 | private var filter = HighPassSkinSmoothingFilter() 23 | 24 | private var inputCIImage = CIImage(cgImage: UIImage(named: "SampleImage")!.cgImage!) 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | let eaglContext = EAGLContext(api: .openGLES2) 30 | 31 | context = { 32 | return CIContext(eaglContext: eaglContext!, options: [kCIContextWorkingColorSpace: CGColorSpaceCreateDeviceRGB()]) 33 | }() 34 | 35 | glkView.context = eaglContext! 36 | } 37 | 38 | override func glkView(_ view: GLKView, drawIn rect: CGRect) { 39 | 40 | let amount = abs(sin(NSDate().timeIntervalSince(self.startDate as Date)) * 0.7) 41 | 42 | title = String(format: "Input Amount: %.3f", amount) 43 | 44 | filter.inputImage = inputCIImage 45 | filter.inputAmount = Float(amount) 46 | filter.inputRadius = Float(7.0 * inputCIImage.extent.width/750.0) 47 | filter.inputSharpnessFactor = 0 48 | 49 | let outputCIImage = filter.outputImage! 50 | 51 | context.draw(outputCIImage, in: AVMakeRect(aspectRatio: outputCIImage.extent.size, insideRect: self.view.bounds.applying(CGAffineTransform(scaleX: UIScreen.main.scale, y: UIScreen.main.scale))), from: outputCIImage.extent) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Limon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Lady.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Lady" 3 | s.version = "0.4" 4 | s.summary = "High Pass Skin Smoothing." 5 | 6 | s.description = <<-DESC 7 | An implementation of High Pass Skin Smoothing using CoreImage.framework. 8 | DESC 9 | 10 | s.homepage = "https://github.com/Limon-O-O/Lady" 11 | s.license = { :type => 'MIT', :file => 'LICENSE' } 12 | s.author = { "Limon" => "fengninglong@gmail.com" } 13 | s.source = { :git => "https://github.com/Limon-O-O/Lady.git", :tag => s.version.to_s } 14 | s.social_media_url = "https://twitter.com/Limon______" 15 | 16 | s.platform = :ios, '8.0' 17 | s.requires_arc = true 18 | 19 | s.source_files = 'Lady/*.swift' 20 | s.resources = 'Lady/Resources/*.cikernel' 21 | 22 | end 23 | -------------------------------------------------------------------------------- /Lady/GreenBlueChannelOverlayBlendFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GreenBlueChannelOverlayBlendFilter.swift 3 | // Example 4 | // 5 | // Created by Limon on 8/4/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import CoreImage 10 | 11 | class GreenBlueChannelOverlayBlendFilter: CIFilter { 12 | 13 | var inputImage: CIImage? 14 | 15 | override var outputImage: CIImage? { 16 | 17 | guard let unwrappedInputImage = inputImage else { return nil } 18 | 19 | return GreenBlueChannelOverlayBlendFilter.kernel.apply(extent: unwrappedInputImage.extent, arguments: [unwrappedInputImage]) 20 | } 21 | 22 | private static let kernel: CIColorKernel = { 23 | 24 | let shaderPath = Bundle(for: GreenBlueChannelOverlayBlendFilter.self).path(forResource: "\(GreenBlueChannelOverlayBlendFilter.self)", ofType: "cikernel") 25 | 26 | guard let path = shaderPath, let kernelString = try? String(contentsOfFile: path, encoding: String.Encoding.utf8), let kernel = CIColorKernel(source: kernelString) else { 27 | 28 | fatalError("Unable to build GreenBlueChannelOverlayBlendFilter Kernel") 29 | } 30 | 31 | return kernel 32 | }() 33 | } 34 | -------------------------------------------------------------------------------- /Lady/HighPassFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HighPassFilter.swift 3 | // Example 4 | // 5 | // Created by Limon on 8/3/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import CoreImage 10 | 11 | class HighPassFilter { 12 | 13 | var inputImage: CIImage? 14 | 15 | /// A number value that controls the radius (in pixel) of the filter. The default value of this parameter is 1.0. 16 | var inputRadius: Float = 1.0 17 | 18 | private static let kernel: CIColorKernel = { 19 | 20 | let shaderPath = Bundle(for: HighPassFilter.self).path(forResource: "\(HighPassFilter.self)", ofType: "cikernel") 21 | 22 | guard let path = shaderPath, let kernelString = try? String(contentsOfFile: path, encoding: String.Encoding.utf8), let kernel = CIColorKernel(source: kernelString) else { 23 | 24 | fatalError("Unable to build HighPassFilter Kernel") 25 | } 26 | 27 | return kernel 28 | }() 29 | 30 | var outputImage: CIImage? { 31 | 32 | guard let unwrappedInputImage = inputImage, let blurFilter = blurFilter else { return nil } 33 | 34 | blurFilter.setValue(unwrappedInputImage.clampedToExtent(), forKey: kCIInputImageKey) 35 | blurFilter.setValue(inputRadius, forKey: kCIInputRadiusKey) 36 | 37 | guard let outputImage = blurFilter.outputImage else { return nil } 38 | 39 | return HighPassFilter.kernel.apply(extent: unwrappedInputImage.extent, arguments: [unwrappedInputImage, outputImage]) 40 | } 41 | 42 | private lazy var blurFilter: CIFilter? = { 43 | let filter = CIFilter(name: "CIGaussianBlur") 44 | return filter 45 | }() 46 | 47 | func setDefaults() { 48 | inputImage = nil 49 | inputRadius = 1.0 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Lady/HighPassSkinSmoothingFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HighPassSkinSmoothingFilter.swift 3 | // Example 4 | // 5 | // Created by Limon on 8/4/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import CoreImage 10 | 11 | open class HighPassSkinSmoothingFilter { 12 | 13 | /** 14 | The input image. 15 | */ 16 | open var inputImage: CIImage? 17 | 18 | /** 19 | A number value that controls the intensity of the `Curve Adjustment` step and the sharpness of the final `Sharpen` step. You use this value to control the overall filter strength. Valid from 0 to 1.0. The default value is 0.75. 20 | */ 21 | open var inputAmount: Float = 0.75 22 | 23 | /** 24 | A number value that controls the radius (in pixel) of the `High Pass` filter. The default value of this parameter is 8.0. Try adjusting this value according to the resolution of the input image and the level of detail you want to preserve. 25 | */ 26 | open var inputRadius: Float = 8.0 27 | 28 | /** 29 | A array of `CIVector` that defines the control points of the curve in `Curve Adjustment` step. The default value of this parameter is [(0,0), (120/255.0,146/255.0), (1,1)]. 30 | */ 31 | open var inputToneCurveControlPoints = HighPassSkinSmoothingFilter.defaultInputToneCurveControlPoints { 32 | didSet { 33 | skinToneCurveFilter.inputRGBCompositeControlPoints = inputToneCurveControlPoints.isEmpty ? HighPassSkinSmoothingFilter.defaultInputToneCurveControlPoints : inputToneCurveControlPoints 34 | } 35 | } 36 | 37 | open static let defaultInputToneCurveControlPoints = [CIVector(x: 0.0, y: 0.0), CIVector(x: 120/255.0, y: 146/255.0), CIVector(x: 1.0, y: 1.0)] 38 | 39 | /** 40 | A number value that controls the sharpness factor of the final `Sharpen` step. The sharpness value is calculated as `inputAmount * inputSharpnessFactor`. The default value for this parameter is 0.6. 41 | */ 42 | open var inputSharpnessFactor: Float = 0.6 43 | 44 | open var outputImage: CIImage? { 45 | 46 | guard let unwrappedInputImage = inputImage else { return nil } 47 | 48 | maskGenerator.inputImage = unwrappedInputImage 49 | maskGenerator.inputRadius = inputRadius 50 | 51 | skinToneCurveFilter.inputImage = unwrappedInputImage 52 | skinToneCurveFilter.inputIntensity = inputAmount 53 | 54 | guard let blendWithMaskFilter = blendWithMaskFilter else { return nil } 55 | 56 | blendWithMaskFilter.setValue(unwrappedInputImage, forKey: kCIInputImageKey) 57 | 58 | if let skinToneCurveFilterOutImage = skinToneCurveFilter.outputImage { 59 | blendWithMaskFilter.setValue(skinToneCurveFilterOutImage, forKey: kCIInputBackgroundImageKey) 60 | } 61 | 62 | if let skinSmoothingOutImage = maskGenerator.outputImage { 63 | blendWithMaskFilter.setValue(skinSmoothingOutImage, forKey: kCIInputMaskImageKey) 64 | } 65 | 66 | let sharpnessValue = inputSharpnessFactor * inputAmount 67 | 68 | if sharpnessValue > 0 { 69 | 70 | shapenFilter?.setValue(sharpnessValue, forKey: "inputSharpness") 71 | shapenFilter?.setValue(blendWithMaskFilter.outputImage, forKey: kCIInputImageKey) 72 | 73 | return shapenFilter?.outputImage 74 | 75 | } else { 76 | 77 | return blendWithMaskFilter.outputImage 78 | } 79 | } 80 | 81 | private lazy var skinToneCurveFilter: RGBToneCurveFilter = { 82 | let filter = RGBToneCurveFilter() 83 | filter.inputRGBCompositeControlPoints = HighPassSkinSmoothingFilter.defaultInputToneCurveControlPoints 84 | return filter 85 | }() 86 | 87 | private lazy var blendWithMaskFilter: CIFilter? = { 88 | let filter = CIFilter(name: "CIBlendWithMask") 89 | return filter 90 | }() 91 | 92 | private lazy var shapenFilter: CIFilter? = { 93 | let filter = CIFilter(name: "CISharpenLuminance") 94 | return filter 95 | }() 96 | 97 | private lazy var maskGenerator: HighPassSkinSmoothingMaskGenerator = { 98 | return HighPassSkinSmoothingMaskGenerator() 99 | }() 100 | 101 | public init() {} 102 | 103 | open func setDefaults() { 104 | inputImage = nil 105 | inputRadius = 8.0 106 | inputAmount = 0.75 107 | inputSharpnessFactor = 0.6 108 | inputToneCurveControlPoints = HighPassSkinSmoothingFilter.defaultInputToneCurveControlPoints 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Lady/HighPassSkinSmoothingMaskBoostFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HighPassSkinSmoothingMaskBoostFilter.swift 3 | // Example 4 | // 5 | // Created by Limon on 8/4/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import CoreImage 10 | 11 | class HighPassSkinSmoothingMaskBoostFilter { 12 | 13 | var inputImage: CIImage? 14 | 15 | var outputImage: CIImage? { 16 | 17 | guard let unwrappedInputImage = inputImage else { return nil } 18 | 19 | return HighPassSkinSmoothingMaskBoostFilter.kernel.apply(extent: unwrappedInputImage.extent, arguments: [unwrappedInputImage]) 20 | } 21 | 22 | private static let kernel: CIColorKernel = { 23 | 24 | let shaderPath = Bundle(for: HighPassSkinSmoothingMaskBoostFilter.self).path(forResource: "\(HighPassSkinSmoothingMaskBoostFilter.self)", ofType: "cikernel") 25 | 26 | guard let path = shaderPath, let kernelString = try? String(contentsOfFile: path, encoding: String.Encoding.utf8), let kernel = CIColorKernel(source: kernelString) else { 27 | 28 | fatalError("Unable to build HighPassSkinSmoothingMaskBoostFilter Kernel") 29 | } 30 | 31 | return kernel 32 | }() 33 | } 34 | -------------------------------------------------------------------------------- /Lady/HighPassSkinSmoothingMaskGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HighPassSkinSmoothingMaskGenerator.swift 3 | // Example 4 | // 5 | // Created by Limon on 8/5/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import CoreImage 10 | 11 | class HighPassSkinSmoothingMaskGenerator { 12 | 13 | var inputImage: CIImage? 14 | 15 | var inputRadius: Float = 0.0 16 | 17 | var outputImage: CIImage? { 18 | 19 | guard let unwrappedInputImage = inputImage, let exposureFilter = exposureFilter else { return nil } 20 | 21 | exposureFilter.setValue(unwrappedInputImage, forKey: kCIInputImageKey) 22 | exposureFilter.setValue(-1.0, forKey: kCIInputEVKey) 23 | 24 | let channelOverlayFilter = GreenBlueChannelOverlayBlendFilter() 25 | channelOverlayFilter.inputImage = exposureFilter.outputImage 26 | 27 | let highPassFilter = HighPassFilter() 28 | highPassFilter.inputImage = channelOverlayFilter.outputImage 29 | highPassFilter.inputRadius = inputRadius 30 | 31 | let hardLightFilter = HighPassSkinSmoothingMaskBoostFilter() 32 | hardLightFilter.inputImage = highPassFilter.outputImage 33 | 34 | return hardLightFilter.outputImage 35 | } 36 | 37 | private lazy var exposureFilter: CIFilter? = { 38 | let filter = CIFilter(name: "CIExposureAdjust") 39 | return filter 40 | }() 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Lady/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 | 0.4 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Lady/Lady.h: -------------------------------------------------------------------------------- 1 | // 2 | // Lady.h 3 | // Lady 4 | // 5 | // Created by Limon on 8/3/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Lady. 12 | FOUNDATION_EXPORT double LadyVersionNumber; 13 | 14 | //! Project version string for Lady. 15 | FOUNDATION_EXPORT const unsigned char LadyVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Lady/RGBToneCurveFilter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RGBToneCurveFilter.swift 3 | // Example 4 | // 5 | // Created by Limon on 8/3/16. 6 | // Copyright © 2016 Lady. All rights reserved. 7 | // 8 | 9 | import CoreImage 10 | 11 | class RGBToneCurveFilter { 12 | 13 | var inputImage: CIImage? 14 | 15 | var inputIntensity: Float = 1.0 16 | 17 | var inputRedControlPoints = RGBToneCurveFilter.defaultCurveControlPoints { 18 | didSet { 19 | redCurve = [] 20 | toneCurveTexture = nil 21 | } 22 | } 23 | 24 | var inputGreenControlPoints = RGBToneCurveFilter.defaultCurveControlPoints { 25 | didSet { 26 | greenCurve = [] 27 | toneCurveTexture = nil 28 | } 29 | } 30 | 31 | var inputBlueControlPoints = RGBToneCurveFilter.defaultCurveControlPoints { 32 | didSet { 33 | blueCurve = [] 34 | toneCurveTexture = nil 35 | } 36 | } 37 | 38 | var inputRGBCompositeControlPoints = RGBToneCurveFilter.defaultCurveControlPoints { 39 | didSet { 40 | rgbCompositeCurve = [] 41 | toneCurveTexture = nil 42 | } 43 | } 44 | 45 | private static let defaultCurveControlPoints = [CIVector(x: 0.0, y: 0.0), CIVector(x: 0.5, y: 0.5), CIVector(x: 1.0, y: 1.0)] 46 | 47 | private var toneCurveTexture: CIImage? 48 | 49 | private var redCurve = [Float](), greenCurve = [Float](), blueCurve = [Float](), rgbCompositeCurve = [Float]() 50 | 51 | private static let kernel: CIKernel = { 52 | 53 | let shaderPath = Bundle(for: RGBToneCurveFilter.self).path(forResource: "\(RGBToneCurveFilter.self)", ofType: "cikernel") 54 | 55 | guard let path = shaderPath, let kernelString = try? String(contentsOfFile: path, encoding: String.Encoding.utf8), let kernel = CIKernel(source: kernelString) else { 56 | 57 | fatalError("Unable to build RGBToneCurve Kernel") 58 | } 59 | 60 | return kernel 61 | }() 62 | 63 | fileprivate static let splineCurveCache: NSCache = { 64 | let cache = NSCache() 65 | cache.name = "RGBToneCurveSplineCurveCache" 66 | cache.totalCostLimit = 40 67 | return cache 68 | }() 69 | 70 | var outputImage: CIImage? { 71 | 72 | guard let unwrappedInputImage = inputImage else { return nil } 73 | 74 | if toneCurveTexture == nil { 75 | updateToneCurveTexture() 76 | } 77 | 78 | guard let unwrappedToneCurveTexture = toneCurveTexture else { return nil } 79 | 80 | let arguments = [ 81 | unwrappedInputImage, 82 | unwrappedToneCurveTexture, 83 | inputIntensity 84 | ] as [Any] 85 | 86 | return RGBToneCurveFilter.kernel.apply(extent: unwrappedInputImage.extent, roiCallback: { (index, destRect) -> CGRect in 87 | 88 | return index == 0 ? destRect : unwrappedToneCurveTexture.extent 89 | 90 | }, arguments: arguments) 91 | } 92 | 93 | private func updateToneCurveTexture() { 94 | 95 | if self.rgbCompositeCurve.count != 256 { 96 | self.rgbCompositeCurve = getPreparedSplineCurve(inputRGBCompositeControlPoints) 97 | } 98 | 99 | if self.redCurve.count != 256 { 100 | self.redCurve = getPreparedSplineCurve(inputRedControlPoints) 101 | } 102 | 103 | if self.greenCurve.count != 256 { 104 | self.greenCurve = getPreparedSplineCurve(inputGreenControlPoints) 105 | } 106 | 107 | if self.blueCurve.count != 256 { 108 | self.blueCurve = getPreparedSplineCurve(inputBlueControlPoints) 109 | } 110 | 111 | let length: Int = 256 * 4 112 | let toneCurveBytes = UnsafeMutablePointer.calloc(length, initialValue: UInt8(0)) 113 | 114 | let _ = (0..<256).map { currentCurveIndex in 115 | 116 | // BGRA for upload to texture 117 | let b = fmin(fmax(Float(currentCurveIndex) + blueCurve[currentCurveIndex], 0), 255) 118 | let g = fmin(fmax(Float(currentCurveIndex) + greenCurve[currentCurveIndex], 0), 255) 119 | let r = fmin(fmax(Float(currentCurveIndex) + redCurve[currentCurveIndex], 0), 255) 120 | 121 | toneCurveBytes[currentCurveIndex * 4] = UInt8(fmin(fmax(b + rgbCompositeCurve[Int(b)], 0), 255)) 122 | 123 | toneCurveBytes[currentCurveIndex * 4 + 1] = UInt8(fmin(fmax(g + rgbCompositeCurve[Int(g)], 0), 255)) 124 | 125 | toneCurveBytes[currentCurveIndex * 4 + 2] = UInt8(fmin(fmax(r + rgbCompositeCurve[Int(r)], 0), 255)) 126 | 127 | toneCurveBytes[currentCurveIndex * 4 + 3] = 255 128 | } 129 | 130 | let data = Data(bytesNoCopy: UnsafeMutablePointer(toneCurveBytes), count: length, deallocator: .free) 131 | 132 | toneCurveTexture = CIImage(bitmapData: data, bytesPerRow: length, size: CGSize(width: 256, height: 1), format: kCIFormatBGRA8, colorSpace: nil) 133 | } 134 | 135 | func setDefaults() { 136 | 137 | inputRedControlPoints = RGBToneCurveFilter.defaultCurveControlPoints 138 | inputGreenControlPoints = RGBToneCurveFilter.defaultCurveControlPoints 139 | inputBlueControlPoints = RGBToneCurveFilter.defaultCurveControlPoints 140 | inputRGBCompositeControlPoints = RGBToneCurveFilter.defaultCurveControlPoints 141 | 142 | inputIntensity = 1.0 143 | 144 | inputImage = nil 145 | 146 | toneCurveTexture = nil 147 | 148 | redCurve = [] 149 | greenCurve = [] 150 | blueCurve = [] 151 | rgbCompositeCurve = [] 152 | } 153 | 154 | } 155 | 156 | // MARK: - Curve calculation 157 | 158 | extension RGBToneCurveFilter { 159 | 160 | fileprivate func getPreparedSplineCurve(_ points: [CIVector]) -> [Float] { 161 | 162 | if let cachedCurve = RGBToneCurveFilter.splineCurveCache.object(forKey: points as AnyObject) as? [Float] { 163 | 164 | return cachedCurve 165 | } 166 | 167 | if points.isEmpty { 168 | assert(false, "Empty") 169 | return [] 170 | } 171 | 172 | // Sort the array. 173 | let sortedPoints = points.sorted { return $0.x < $1.x } 174 | 175 | // Convert from (0, 1) to (0, 255). 176 | var convertedPoints = [CIVector]() 177 | 178 | for index in 0.. 0 { 191 | 192 | for index in (0...Int(firstSplinePoint.x)).reversed() { 193 | 194 | splinePoints.insert(CIVector(x: CGFloat(index), y: 0.0), at: 0) 195 | } 196 | } 197 | 198 | // Insert points similarly at the end, if necessary. 199 | let lastSplinePoint = splinePoints.last! 200 | 201 | if lastSplinePoint.x < 255 { 202 | for index in (Int(lastSplinePoint.x) + 1)...255 { 203 | splinePoints.append(CIVector(x: CGFloat(index), y: 255)) 204 | } 205 | } 206 | 207 | // Prepare the spline points. 208 | var preparedSplinePoints = [Float]() 209 | 210 | for index in 0.. newPoint.y { 218 | distance = -distance 219 | } 220 | 221 | preparedSplinePoints.append(distance) 222 | } 223 | 224 | RGBToneCurveFilter.splineCurveCache.setObject(preparedSplinePoints as AnyObject, forKey: points as AnyObject, cost: 1) 225 | 226 | return preparedSplinePoints 227 | } 228 | 229 | private func splineCurve(_ points: [CIVector]) -> [CIVector] { 230 | 231 | let sd = secondDerivative(points) 232 | 233 | if sd.isEmpty { 234 | assert(false, "Empty") 235 | return [] 236 | } 237 | 238 | // [points count] is equal to [sdA count] 239 | let n = sd.count 240 | 241 | var output = [CIVector]() 242 | 243 | for index in 0.. [CGFloat] { 279 | 280 | let n = points.count 281 | 282 | if n <= 1 { 283 | assert(false, "") 284 | return [] 285 | } 286 | 287 | var matrix = Array(repeating: Array(repeating: CGFloat(), count: 3), count: n) 288 | 289 | var result = Array(repeating: CGFloat(), count: n) 290 | 291 | matrix[0][1] = 1 292 | 293 | // What about matrix[0][1] and matrix[0][0]? Assuming 0 for now (Brad L.) 294 | matrix[0][0] = 0 295 | matrix[0][2] = 0 296 | 297 | for index in 1..down) 319 | for index in 1..up) 330 | for index in (0...n-2).reversed() { 331 | 332 | let denominator = matrix[index+1][1] 333 | let k: CGFloat = denominator == 0.0 ? 0.0 : matrix[index][2] / denominator 334 | 335 | matrix[index][1] -= k * matrix[index+1][0] 336 | matrix[index][2] = 0.0 337 | 338 | result[index] -= k * result[index+1] 339 | } 340 | 341 | var output = [CGFloat]() 342 | 343 | for index in 0..(_ count: Int, initialValue: T) -> UnsafeMutablePointer { 355 | let ptr = UnsafeMutablePointer.allocate(capacity: count) 356 | ptr.initialize(to: initialValue, count: count) 357 | return ptr 358 | } 359 | } 360 | 361 | -------------------------------------------------------------------------------- /Lady/Resources/GreenBlueChannelOverlayBlendFilter.cikernel: -------------------------------------------------------------------------------- 1 | 2 | kernel vec4 filterKernel(__sample image) { 3 | vec4 base = vec4(image.g,image.g,image.g,1.0); 4 | vec4 overlay = vec4(image.b,image.b,image.b,1.0); 5 | float ba = 2.0 * overlay.b * base.b + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a); 6 | return vec4(ba,ba,ba,image.a); 7 | } -------------------------------------------------------------------------------- /Lady/Resources/HighPassFilter.cikernel: -------------------------------------------------------------------------------- 1 | 2 | kernel vec4 filterKernel(__sample image, __sample blurredImage) { 3 | return vec4(vec3(image.rgb - blurredImage.rgb + vec3(0.5,0.5,0.5)), image.a); 4 | } -------------------------------------------------------------------------------- /Lady/Resources/HighPassSkinSmoothingMaskBoostFilter.cikernel: -------------------------------------------------------------------------------- 1 | 2 | kernel vec4 filterKernel(__sample image) { 3 | float hardLightColor = image.b; 4 | 5 | for (int i = 0; i < 3; ++i) { 6 | if (hardLightColor < 0.5) { 7 | hardLightColor = hardLightColor * hardLightColor * 2.; 8 | } else { 9 | hardLightColor = 1. - (1. - hardLightColor) * (1. - hardLightColor) * 2.; 10 | } 11 | } 12 | 13 | const float k = 255.0 / (164.0 - 75.0); 14 | 15 | hardLightColor = (hardLightColor - 75.0 / 255.0) * k; 16 | 17 | return vec4(vec3(hardLightColor), image.a); 18 | } -------------------------------------------------------------------------------- /Lady/Resources/RGBToneCurveFilter.cikernel: -------------------------------------------------------------------------------- 1 | 2 | kernel vec4 filterKernel(sampler inputImage, sampler toneCurveTexture, float intensity) { 3 | vec4 textureColor = sample(inputImage,samplerCoord(inputImage)); 4 | vec4 toneCurveTextureExtent = samplerExtent(toneCurveTexture); 5 | 6 | vec2 redCoord = samplerTransform(toneCurveTexture,vec2(textureColor.r * 255.0 + 0.5 + toneCurveTextureExtent.x, toneCurveTextureExtent.y + 0.5)); 7 | vec2 greenCoord = samplerTransform(toneCurveTexture,vec2(textureColor.g * 255.0 + 0.5 + toneCurveTextureExtent.x, toneCurveTextureExtent.y + 0.5)); 8 | vec2 blueCoord = samplerTransform(toneCurveTexture,vec2(textureColor.b * 255.0 + 0.5 + toneCurveTextureExtent.x, toneCurveTextureExtent.y + 0.5)); 9 | 10 | float redCurveValue = sample(toneCurveTexture, redCoord).r; 11 | float greenCurveValue = sample(toneCurveTexture, greenCoord).g; 12 | float blueCurveValue = sample(toneCurveTexture, blueCoord).b; 13 | return vec4(mix(textureColor.rgb,vec3(redCurveValue, greenCurveValue, blueCurveValue),intensity),textureColor.a); 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lady 2 | 3 |

4 | 5 | 6 | 7 |

8 | 9 | Lady is [YUCIHighPassSkinSmoothing](https://github.com/YuAo/YUCIHighPassSkinSmoothing) in Swift. 10 | 11 | YUCIHighPassSkinSmoothing is an implementation of [High Pass Skin Smoothing](https://www.google.com.hk/#newwindow=1&safe=strict&q=High+Pass+Skin+Smoothing) using CoreImage.framework 12 | 13 | ## Previews 14 | ![Preview 1](http://yuao.github.io/YUCIHighPassSkinSmoothing/previews/1.jpg) 15 | 16 | ![Preview 2](http://yuao.github.io/YUCIHighPassSkinSmoothing/previews/2.jpg) 17 | 18 | ![Preview 3](http://yuao.github.io/YUCIHighPassSkinSmoothing/previews/3.jpg) 19 | 20 | ![Preview 4](http://yuao.github.io/YUCIHighPassSkinSmoothing/previews/4.jpg) 21 | 22 | ![Preview 5](http://yuao.github.io/YUCIHighPassSkinSmoothing/previews/5.jpg) 23 | 24 | ![Preview 6](http://yuao.github.io/YUCIHighPassSkinSmoothing/previews/6.jpg) 25 | 26 | ## Requirements 27 | 28 | iOS 8.0 29 | 30 | Swift 3.0 31 | 32 | ## Installation 33 | 34 | ### CocoaPods 35 | 36 | ```ruby 37 | pod 'Lady', '~> 0.4' 38 | ``` 39 | ### Carthage 40 | 41 | ```ruby 42 | github "Limon-O-O/Lady" 43 | ``` 44 | 45 | ## Contacts 46 | 47 | Contact me on [Twitter](https://twitter.com/Limon______) or [Weibo](http://weibo.com/u/1783821582) . If you find an issue, just [open a ticket](https://github.com/Limon-O-O/Lady/issues/new) on it. Pull requests are warmly welcome as well. 48 | 49 | ## License 50 | Lady is available under the MIT license. See the LICENSE file for more info. 51 | 52 | 53 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BGreen='\033[1;32m' 4 | Default='\033[0;m' 5 | 6 | podName="" 7 | version="" 8 | podspecFilePath="" 9 | homepage="" 10 | httpsRepo="" 11 | oldVersion="" 12 | confirmed="n" 13 | 14 | getPodInfo() { 15 | 16 | for file in ./* 17 | do 18 | if test -f $file 19 | then 20 | if [[ $file == *".podspec"* ]]; then 21 | filename=`basename $file` 22 | podName=${filename%.podspec*} 23 | podspecFilePath="./${podName}.podspec" 24 | fi 25 | fi 26 | done 27 | 28 | while read line; do 29 | line=${line//[[:blank:]]/} 30 | if [[ $line == *".homepage="* ]]; then 31 | homepage=${line##*='"'} 32 | homepage=${homepage%'"'} 33 | fi 34 | if [[ $line == *".source="* ]]; then 35 | httpsRepo=${line##*git=>'"'} 36 | httpsRepo=${httpsRepo%'"'*} 37 | fi 38 | # 截取旧版本号 39 | if [[ $line == *"s.version"*"="* ]]; then 40 | oldVersion=${line##*=} 41 | oldVersion=${oldVersion#*\"} 42 | oldVersion=${oldVersion%\"} 43 | oldVersion=${oldVersion#*\'} 44 | oldVersion=${oldVersion%\'} 45 | fi 46 | done < $podspecFilePath 47 | } 48 | 49 | getVersion() { 50 | read -p "Enter New Version: " version 51 | 52 | if test -z "$version"; then 53 | getVersion 54 | fi 55 | } 56 | 57 | updateVersion() { 58 | 59 | # update .podspec file 60 | while read line; do 61 | if [[ $line == *"s.version"*"="* ]]; then 62 | newLine=${line/$oldVersion/$version} 63 | sed -i '' "s#$line#$newLine#" "$podspecFilePath" 64 | fi 65 | done < $podspecFilePath 66 | 67 | # update README.md file 68 | while read line; do 69 | if [[ $line == *"pod"*"${oldVersion}"* ]]; then 70 | newLine=${line/$oldVersion/$version} 71 | sed -i '' "s#$line#$newLine#" "./README.md" 72 | fi 73 | if [[ $line == *"github"*"${podName}"*"${oldVersion}"* ]]; then 74 | newLine=${line/$oldVersion/$version} 75 | sed -i '' "s#${line}#${newLine}#" "./README.md" 76 | fi 77 | done < "./README.md" 78 | 79 | # update Xcode project 80 | updateProjectVersion --version=$version --target=$podName 81 | # ./update_version.sh --version=$version --target=$podName 82 | } 83 | 84 | getInfomation() { 85 | getVersion 86 | 87 | echo -e "\n${Default}================================================" 88 | echo -e " Pod Name : ${BGreen}${podName}${Default}" 89 | echo -e " Version : ${BGreen}${version}${Default}" 90 | echo -e " HTTPS Repo : ${BGreen}${httpsRepo}${Default}" 91 | echo -e " Home Page URL : ${BGreen}${homepage}${Default}" 92 | echo -e "================================================\n" 93 | } 94 | 95 | ########### Compare newVersion and oldVersion ############### 96 | compareVersion() { 97 | ## @file version_compare 98 | ## Compare [semantic] versions in Bash, comparable to PHP's version_compare function. 99 | # ------------------------------------------------------------------ 100 | ## @author Mark Carver 101 | ## @copyright MIT 102 | ## @version 1.0.0 103 | ## @see http://php.net/manual/en/function.version-compare.php 104 | 105 | APP_NAME=$(basename ${0}) 106 | APP_VERSION="1.0.0" 107 | 108 | # Version compare 109 | function version_compare () { 110 | # Default to a failed comparison result. 111 | local -i result=1; 112 | 113 | # Ensure there are two versions to compare. 114 | [ $# -lt 2 ] || [ -z "${1}" ] || [ -z "${2}" ] && echo "${FUNCNAME[0]} requires a minimum of two arguments to compare versions." &>/dev/stderr && return ${result} 115 | 116 | # Determine the operation to perform, if any. 117 | local op="${3}" 118 | 119 | # Convert passed versions into values for comparison. 120 | local v1=$(version_compare_convert ${1}) 121 | local v2=$(version_compare_convert ${2}) 122 | 123 | # Immediately return when comparing version equality (which doesn't require sorting). 124 | if [ -z "${op}" ]; then 125 | [ "${v1}" == "${v2}" ] && echo 0 && return; 126 | else 127 | if [ "${op}" == "!=" ] || [ "${op}" == "<>" ] || [ "${op}" == "ne" ]; then 128 | if [ "${v1}" != "${v2}" ]; then let result=0; fi; 129 | return ${result}; 130 | elif [ "${op}" == "=" ] || [ "${op}" == "==" ] || [ "${op}" == "eq" ]; then 131 | if [ "${v1}" == "${v2}" ]; then let result=0; fi; 132 | return ${result}; 133 | elif [ "${op}" == "le" ] || [ "${op}" == "<=" ] || [ "${op}" == "ge" ] || [ "${op}" == ">=" ] && [ "${v1}" == "${v2}" ]; then 134 | if [ "${v1}" == "${v2}" ]; then let result=0; fi; 135 | return ${result}; 136 | fi 137 | fi 138 | 139 | # If we get to this point, the versions should be different. 140 | # Immediately return if they're the same. 141 | [ "${v1}" == "${v2}" ] && return ${result} 142 | 143 | local sort='sort' 144 | 145 | # If only one version has a pre-release label, reverse sorting so 146 | # the version without one can take precedence. 147 | [[ "${v1}" == *"-"* ]] && [[ "${v2}" != *"-"* ]] || [[ "${v2}" == *"-"* ]] && [[ "${v1}" != *"-"* ]] && sort="${sort} -r" 148 | 149 | # Sort the versions. 150 | local -a sorted=($(printf "%s\n%s" "${v1}" "${v2}" | ${sort})) 151 | 152 | # No operator passed, indicate which direction the comparison leans. 153 | if [ -z "${op}" ]; then 154 | if [ "${v1}" == "${sorted[0]}" ]; then echo -1; else echo 1; fi 155 | return 156 | fi 157 | 158 | case "${op}" in 159 | "<" | "lt" | "<=" | "le") if [ "${v1}" == "${sorted[0]}" ]; then let result=0; fi;; 160 | ">" | "gt" | ">=" | "ge") if [ "${v1}" == "${sorted[1]}" ]; then let result=0; fi;; 161 | esac 162 | 163 | return ${result} 164 | } 165 | 166 | # Converts a version string to an integer that is used for comparison purposes. 167 | function version_compare_convert () { 168 | local version="${@}" 169 | 170 | # Remove any build meta information as it should not be used per semver spec. 171 | version="${version%+*}" 172 | 173 | # Extract any pre-release label. 174 | local prerelease 175 | [[ "${version}" = *"-"* ]] && prerelease=${version##*-} 176 | [ -n "${prerelease}" ] && prerelease="-${prerelease}" 177 | 178 | version="${version%%-*}" 179 | 180 | # Separate version (minus pre-release label) into an array using periods as the separator. 181 | local OLDIFS=${IFS} && local IFS=. && version=(${version%-*}) && IFS=${OLDIFS} 182 | 183 | # Unfortunately, we must use sed to strip of leading zeros here. 184 | local major=$(echo ${version[0]:=0} | sed 's/^0*//') 185 | local minor=$(echo ${version[1]:=0} | sed 's/^0*//') 186 | local patch=$(echo ${version[2]:=0} | sed 's/^0*//') 187 | local build=$(echo ${version[3]:=0} | sed 's/^0*//') 188 | 189 | # Combine the version parts and pad everything with zeros, except major. 190 | printf "%s%04d%04d%04d%s\n" "${major}" "${minor}" "${patch}" "${build}" "${prerelease}" 191 | } 192 | 193 | # Color Support 194 | # See: http://unix.stackexchange.com/a/10065 195 | if test -t 1; then 196 | ncolors=$(tput colors) 197 | if test -n "$ncolors" && test $ncolors -ge 8; then 198 | bold="$(tput bold)" && underline="$(tput smul)" && standout="$(tput smso)" && normal="$(tput sgr0)" 199 | black="$(tput setaf 0)" && red="$(tput setaf 1)" && green="$(tput setaf 2)" && yellow="$(tput setaf 3)" 200 | blue="$(tput setaf 4)" && magenta="$(tput setaf 5)" && cyan="$(tput setaf 6)" && white="$(tput setaf 7)" 201 | fi 202 | fi 203 | 204 | function version_compare_usage { 205 | echo "${bold}${APP_NAME} (${APP_VERSION})${normal}" 206 | echo "Compare [semantic] versions in Bash, comparable to PHP's version_compare function." 207 | echo 208 | echo "${bold}Usage:${normal}" 209 | echo " ${APP_NAME} [-hV] ${cyan} ${normal} [${cyan}${normal}]" 210 | echo 211 | echo "${bold}Required arguments:${normal}" 212 | echo " - ${cyan}${normal}: First version number to compare." 213 | echo " - ${cyan}${normal}: Second version number to compare." 214 | echo 215 | echo "${bold}Optional arguments:${normal}" 216 | echo " - ${cyan}${normal}: When this argument is provided, it will test for a particular" 217 | echo " relationship. This argument is case-sensitive, values should be lowercase." 218 | echo " Possible operators are:" 219 | echo " ${bold}=, ==, eq${normal} (equal)" 220 | echo " ${bold}>, gt${normal} (greater than)" 221 | echo " ${bold}>=, ge${normal} (greater than or equal)" 222 | echo " ${bold}<, lt${normal} (less than)" 223 | echo " ${bold}<=, le${normal} (less than or equal)" 224 | echo " ${bold}!=, <>, ne${normal} (not equal)" 225 | echo 226 | echo "${bold}Return Value:${normal}" 227 | echo " There are two distinct operation modes for ${APP_NAME}. It's solely based" 228 | echo " on whether or not the ${cyan}${normal} argument was provided:" 229 | echo 230 | echo " - When ${cyan}${normal} IS provided, ${APP_NAME} will return either a 0 or 1" 231 | echo " exit code (no output printed to /dev/stdout) based on the result of the ${cyan}${normal}" 232 | echo " relationship between the versions. This is particularly useful in cases where" 233 | echo " testing versions can, historically, be quite cumbersome:" 234 | echo 235 | echo " ${magenta}! ${APP_NAME} \${version1} \${version2} \">\" && echo \"You have not met the minimum version requirements.\" && exit 1${normal}" 236 | echo 237 | echo " You can, of course, opt for the more traditional/verbose conditional" 238 | echo " block in that suites your fancy:" 239 | echo 240 | echo " ${magenta}${APP_NAME} \${version1} \${version2}" 241 | echo " if [ \$? -gt 0 ]; then" 242 | echo " echo \"You have not met the minimum version requirements.\"" 243 | echo " exit 1" 244 | echo " fi${normal}" 245 | echo 246 | echo " - When ${cyan}${normal} is NOT provided, ${APP_NAME} will output (print to /dev/stdout):" 247 | echo " -1: ${cyan}${normal} is lower than ${cyan}${normal}" 248 | echo " 0: ${cyan}${normal} and ${cyan}${normal} are equal" 249 | echo " 1: ${cyan}${normal} is lower than ${cyan}${normal}" 250 | echo 251 | echo " This mode is primarily only ever helpful when there is a need to determine the" 252 | echo " relationship between two versions and provide logic for all three states:" 253 | echo 254 | echo " ${magenta}ret=\$(${APP_NAME} \${version1} \${version2})" 255 | echo " if [ \"\${ret}\" == \"-1\" ]; then" 256 | echo " # Do some logic here." 257 | echo " elif [ \"\${ret}\" == \"0\" ]; then" 258 | echo " # Do some logic here." 259 | echo " else" 260 | echo " # Do some logic here." 261 | echo " fi${normal}" 262 | echo 263 | echo " While there are use cases for both modes, it's recommended that you provide an" 264 | echo " ${cyan}${normal} argument to reduce any logic whenever possible." 265 | echo 266 | echo "${bold}Options:${normal}" 267 | echo " ${bold}-h${normal} Display this help and exit." 268 | echo " ${bold}-V${normal} Display version information and exit." 269 | } 270 | 271 | # Do not continue if sourced. 272 | [[ ${0} != "$BASH_SOURCE" ]] && return 273 | 274 | # Process options. 275 | while getopts ":hV" opt; do 276 | case $opt in 277 | h) version_compare_usage && exit;; 278 | V) echo "${APP_VERSION}" && exit;; 279 | \?|*) echo "${red}${APP_NAME}: illegal option: -- ${OPTARG}${normal}" >&2 && echo && version_compare_usage && exit 64;; 280 | esac 281 | done 282 | shift $((OPTIND-1)) # Remove parsed options. 283 | 284 | # Allow script to be invoked as a CLI "command" by proxying arguments to the internal function. 285 | [ $# -gt 0 ] && version_compare ${@} 286 | 287 | } 288 | 289 | ########### Update Xocde Info.plist ############### 290 | updateProjectVersion() { 291 | # Link: 292 | # ./update-version.sh --version=1.2.9 --build=95 --target=MonkeyKing 293 | 294 | # We use PlistBuddy to handle the Info.plist values. Here we define where it lives. 295 | plistBuddy="/usr/libexec/PlistBuddy" 296 | 297 | BGreen='\033[1;32m' 298 | 299 | # Parse input variables and update settings. 300 | for i in "$@"; do 301 | case $i in 302 | -h|--help) 303 | echo "usage: sh version-update.sh [options...]\n" 304 | echo "Options: (when provided via the CLI, these will override options set within the script itself)" 305 | echo " --build= Apply the given value to the build number (CFBundleVersion) for the project." 306 | echo "-p, --plist= Use the specified plist file as the source of truth for version details." 307 | echo " --version= Apply the given value to the marketing version (CFBundleShortVersionString) for the project." 308 | echo "-x, --xcodeproj= Use the specified Xcode project file to gather plist names." 309 | echo "-x, --target= Use the specified Xcode project target to gather plist names." 310 | echo "\nFor more detailed information on the use of these variables, see the script source." 311 | exit 1 312 | ;; 313 | -x=*|--xcodeproj=*) 314 | xcodeproj="${i#*=}" 315 | shift 316 | ;; 317 | -p=*|--plist=*) 318 | plist="${i#*=}" 319 | shift 320 | ;; 321 | --target=*) 322 | specified_target="${i#*=}" 323 | shift 324 | ;; 325 | --build=*) 326 | specified_build="${i#*=}" 327 | shift 328 | ;; 329 | --version=*) 330 | specified_version="${i#*=}" 331 | shift 332 | ;; 333 | *) 334 | ;; 335 | esac 336 | done 337 | 338 | # Locate the xcodeproj. 339 | # If we've specified a xcodeproj above, we'll simply use that instead. 340 | if [[ -z ${xcodeproj} ]]; then 341 | xcodeproj=$(find . -depth 1 -name "*.xcodeproj" | sed -e 's/^\.\///g') 342 | fi 343 | 344 | # Check that the xcodeproj file we've located is valid, and warn if it isn't. 345 | # This could also indicate an issue with the code used to automatically locate the xcodeproj file. 346 | # If you're encountering this and the file exists, ensure that ${xcodeproj} contains the correct 347 | # path, or use the "--xcodeproj" variable to provide an accurate location. 348 | if [[ ! -f "${xcodeproj}/project.pbxproj" ]]; then 349 | echo "${BASH_SOURCE}:${LINENO}: error: Could not locate the xcodeproj file \"${xcodeproj}\"." 350 | exit 1 351 | else 352 | echo "Xcode Project: \"${xcodeproj}\"" 353 | fi 354 | 355 | # Find unique references to Info.plist files in the project 356 | projectFile="${xcodeproj}/project.pbxproj" 357 | plists=$(grep "^\s*INFOPLIST_FILE.*$" "${projectFile}" | sed -Ee 's/^[[:space:]]+INFOPLIST_FILE[[:space:]*=[[:space:]]*["]?([^"]+)["]?;$/\1/g' | sort | uniq) 358 | 359 | # Attempt to guess the plist based on the list we have. 360 | # If we've specified a plist above, we'll simply use that instead. 361 | if [[ -z ${plist} ]]; then 362 | while read -r thisPlist; do 363 | if [[ $thisPlist == *"${specified_target}"* ]]; then 364 | plist=$thisPlist 365 | fi 366 | done <<< "${plists}" 367 | fi 368 | 369 | # Check that the plist file we've located is valid, and warn if it isn't. 370 | # This could also indicate an issue with the code used to match plist files in the xcodeproj file. 371 | # If you're encountering this and the file exists, ensure that ${plists} contains _ONLY_ filenames. 372 | if [[ ! -f ${plist} ]]; then 373 | echo "${BASH_SOURCE}:${LINENO}: error: Could not locate the plist file \"${plist}\"." 374 | exit 1 375 | else 376 | echo "Source Info.plist: \"${plist}\"" 377 | fi 378 | 379 | # Find the current build number in the main Info.plist 380 | mainBundleVersion=$("${plistBuddy}" -c "Print CFBundleVersion" "${plist}") 381 | mainBundleShortVersionString=$("${plistBuddy}" -c "Print CFBundleShortVersionString" "${plist}") 382 | echo "Current project version is ${mainBundleShortVersionString} (${mainBundleVersion})." 383 | 384 | # If the user specified a marketing version (via "--version"), we overwrite the version from the source of truth. 385 | if [[ ! -z ${specified_version} ]]; then 386 | mainBundleShortVersionString=${specified_version} 387 | echo "Applying specified marketing version (${specified_version})..." 388 | fi 389 | 390 | if [[ ! -z ${specified_build} ]]; then 391 | mainBundleVersion=${specified_build} 392 | echo "Applying specified build number (${specified_build})..." 393 | fi 394 | 395 | # Update all of the Info.plist files we discovered 396 | while read -r thisPlist; do 397 | # Find out the current version 398 | thisBundleVersion=$("${plistBuddy}" -c "Print CFBundleVersion" "${thisPlist}") 399 | thisBundleShortVersionString=$("${plistBuddy}" -c "Print CFBundleShortVersionString" "${thisPlist}") 400 | # Update the CFBundleVersion if needed 401 | if [[ ${thisBundleVersion} != ${mainBundleVersion} ]]; then 402 | echo -e "${BGreen}Updating \"${thisPlist}\" with build ${mainBundleVersion}..." 403 | "${plistBuddy}" -c "Set :CFBundleVersion ${mainBundleVersion}" "${thisPlist}" 404 | fi 405 | # Update the CFBundleShortVersionString if needed 406 | if [[ ${thisBundleShortVersionString} != ${mainBundleShortVersionString} ]]; then 407 | echo -e "${BGreen}Updating \"${thisPlist}\" with marketing version ${mainBundleShortVersionString}..." 408 | "${plistBuddy}" -c "Set :CFBundleShortVersionString ${mainBundleShortVersionString}" "${thisPlist}" 409 | git add "${thisPlist}" 410 | fi 411 | echo -e "${BGreen}Current \"${thisPlist}\" version is ${mainBundleShortVersionString} (${mainBundleVersion})." 412 | done <<< "${plist}" 413 | } 414 | 415 | ########### 开始 ############### 416 | 417 | getPodInfo 418 | 419 | echo -e "\n" 420 | 421 | echo "Current Version: ${oldVersion}" 422 | 423 | while [ "$confirmed" != "y" -a "$confirmed" != "Y" ] 424 | do 425 | if [ "$confirmed" == "n" -o "$confirmed" == "N" ]; then 426 | getInfomation 427 | fi 428 | read -p "confirm? (y/n):" confirmed 429 | done 430 | 431 | ! compareVersion $version $oldVersion ">" && echo "Invalid version. $version <= $oldVersion" && exit 1 432 | 433 | updateVersion 434 | 435 | echo "" 436 | 437 | echo "--------------------------------------------------------------------------------" 438 | 439 | echo "" 440 | 441 | git add "${podspecFilePath}" 442 | git add "./README.md" 443 | git commit -m "[$podName] update version $version" 444 | git push 445 | 446 | git tag "${version}" 447 | git push --tags 448 | 449 | echo "" 450 | 451 | echo "--------------------------------------------------------------------------------" 452 | echo "Start pod trunk push \"${podName}.podspec\" --allow-warnings" 453 | 454 | pod trunk push "${podName}.podspec" --allow-warnings 455 | 456 | echo -e "\n" 457 | 458 | say "finished" 459 | echo "finished" 460 | --------------------------------------------------------------------------------