├── .gitignore ├── LICENSE ├── MFExtractColor.podspec ├── MFExtractColorDemo ├── MFExtractColorDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── MFExtractColorDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── MFExtractColor │ ├── MFExtractColor.h │ └── MFExtractColor.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── README.md └── SnapShot ├── 1.jpg ├── 2.png ├── 3.png ├── 4.png └── 5.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/macos,objective-c 3 | 4 | ### macOS ### 5 | *.DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | 12 | # Thumbnails 13 | ._* 14 | 15 | # Files that might appear in the root of a volume 16 | .DocumentRevisions-V100 17 | .fseventsd 18 | .Spotlight-V100 19 | .TemporaryItems 20 | .Trashes 21 | .VolumeIcon.icns 22 | .com.apple.timemachine.donotpresent 23 | 24 | # Directories potentially created on remote AFP share 25 | .AppleDB 26 | .AppleDesktop 27 | Network Trash Folder 28 | Temporary Items 29 | .apdisk 30 | 31 | ### Objective-C ### 32 | # Xcode 33 | # 34 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 35 | 36 | ## Build generated 37 | build/ 38 | DerivedData/ 39 | 40 | ## Various settings 41 | *.pbxuser 42 | !default.pbxuser 43 | *.mode1v3 44 | !default.mode1v3 45 | *.mode2v3 46 | !default.mode2v3 47 | *.perspectivev3 48 | !default.perspectivev3 49 | xcuserdata/ 50 | 51 | ## Other 52 | *.moved-aside 53 | *.xccheckout 54 | *.xcscmblueprint 55 | 56 | ## Obj-C/Swift specific 57 | *.hmap 58 | *.ipa 59 | *.dSYM.zip 60 | *.dSYM 61 | 62 | # CocoaPods - Refactored to standalone file 63 | 64 | 65 | # Carthage - Refactored to standalone file 66 | 67 | # fastlane 68 | # 69 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 70 | # screenshots whenever they are needed. 71 | # For more information about the recommended setup visit: 72 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 73 | 74 | fastlane/report.xml 75 | fastlane/Preview.html 76 | fastlane/screenshots 77 | fastlane/test_output 78 | 79 | # Code Injection 80 | # 81 | # After new code Injection tools there's a generated folder /iOSInjectionProject 82 | # https://github.com/johnno1962/injectionforxcode 83 | 84 | iOSInjectionProject/ 85 | 86 | ### Objective-C Patch ### 87 | 88 | 89 | # End of https://www.gitignore.io/api/macos,objective-c -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 清风 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 | -------------------------------------------------------------------------------- /MFExtractColor.podspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | Pod::Spec.new do |s| 4 | s.name = 'MFExtractColor' 5 | s.version = '1.0.1' 6 | s.summary = 'Fetches the most dominant and prominent colors from an image.' 7 | 8 | 9 | 10 | s.homepage = 'https://github.com/GodzzZZZ/MFExtractColor' 11 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 12 | s.license = { :type => 'MIT', :file => 'LICENSE' } 13 | s.author = { 'GodzzZZZ' => 'GodzzZZZ@qq.com' } 14 | s.source = { :git => 'https://github.com/GodzzZZZ/MFExtractColor.git', :tag => s.version.to_s } 15 | # s.social_media_url = 'https://twitter.com/' 16 | 17 | s.ios.deployment_target = '8.0' 18 | 19 | s.source_files = 'MFExtractColorDemo/MFExtractColorDemo/MFExtractColor/*.{h,m}' 20 | 21 | 22 | s.frameworks = "UIKit", "Foundation" 23 | end 24 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5E0AD2BE204A493A0008F2BA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E0AD2BD204A493A0008F2BA /* AppDelegate.m */; }; 11 | 5E0AD2C1204A493A0008F2BA /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E0AD2C0204A493A0008F2BA /* ViewController.m */; }; 12 | 5E0AD2C4204A493A0008F2BA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E0AD2C2204A493A0008F2BA /* Main.storyboard */; }; 13 | 5E0AD2C6204A493A0008F2BA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E0AD2C5204A493A0008F2BA /* Assets.xcassets */; }; 14 | 5E0AD2C9204A493A0008F2BA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E0AD2C7204A493A0008F2BA /* LaunchScreen.storyboard */; }; 15 | 5E0AD2CC204A493A0008F2BA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E0AD2CB204A493A0008F2BA /* main.m */; }; 16 | 5E0AD2D5204A499F0008F2BA /* MFExtractColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E0AD2D4204A499F0008F2BA /* MFExtractColor.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 5E0AD2B9204A493A0008F2BA /* MFExtractColorDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MFExtractColorDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 5E0AD2BC204A493A0008F2BA /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 22 | 5E0AD2BD204A493A0008F2BA /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 23 | 5E0AD2BF204A493A0008F2BA /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 24 | 5E0AD2C0204A493A0008F2BA /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 25 | 5E0AD2C3204A493A0008F2BA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 5E0AD2C5204A493A0008F2BA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 27 | 5E0AD2C8204A493A0008F2BA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | 5E0AD2CA204A493A0008F2BA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 5E0AD2CB204A493A0008F2BA /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 30 | 5E0AD2D3204A499F0008F2BA /* MFExtractColor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFExtractColor.h; sourceTree = ""; }; 31 | 5E0AD2D4204A499F0008F2BA /* MFExtractColor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFExtractColor.m; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | 5E0AD2B6204A493A0008F2BA /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 5E0AD2B0204A493A0008F2BA = { 46 | isa = PBXGroup; 47 | children = ( 48 | 5E0AD2BB204A493A0008F2BA /* MFExtractColorDemo */, 49 | 5E0AD2BA204A493A0008F2BA /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | 5E0AD2BA204A493A0008F2BA /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | 5E0AD2B9204A493A0008F2BA /* MFExtractColorDemo.app */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | 5E0AD2BB204A493A0008F2BA /* MFExtractColorDemo */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 5E0AD2D2204A49860008F2BA /* MFExtractColor */, 65 | 5E0AD2BC204A493A0008F2BA /* AppDelegate.h */, 66 | 5E0AD2BD204A493A0008F2BA /* AppDelegate.m */, 67 | 5E0AD2BF204A493A0008F2BA /* ViewController.h */, 68 | 5E0AD2C0204A493A0008F2BA /* ViewController.m */, 69 | 5E0AD2C2204A493A0008F2BA /* Main.storyboard */, 70 | 5E0AD2C5204A493A0008F2BA /* Assets.xcassets */, 71 | 5E0AD2C7204A493A0008F2BA /* LaunchScreen.storyboard */, 72 | 5E0AD2CA204A493A0008F2BA /* Info.plist */, 73 | 5E0AD2CB204A493A0008F2BA /* main.m */, 74 | ); 75 | path = MFExtractColorDemo; 76 | sourceTree = ""; 77 | }; 78 | 5E0AD2D2204A49860008F2BA /* MFExtractColor */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 5E0AD2D3204A499F0008F2BA /* MFExtractColor.h */, 82 | 5E0AD2D4204A499F0008F2BA /* MFExtractColor.m */, 83 | ); 84 | path = MFExtractColor; 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXNativeTarget section */ 90 | 5E0AD2B8204A493A0008F2BA /* MFExtractColorDemo */ = { 91 | isa = PBXNativeTarget; 92 | buildConfigurationList = 5E0AD2CF204A493A0008F2BA /* Build configuration list for PBXNativeTarget "MFExtractColorDemo" */; 93 | buildPhases = ( 94 | 5E0AD2B5204A493A0008F2BA /* Sources */, 95 | 5E0AD2B6204A493A0008F2BA /* Frameworks */, 96 | 5E0AD2B7204A493A0008F2BA /* Resources */, 97 | ); 98 | buildRules = ( 99 | ); 100 | dependencies = ( 101 | ); 102 | name = MFExtractColorDemo; 103 | productName = MFExtractColorDemo; 104 | productReference = 5E0AD2B9204A493A0008F2BA /* MFExtractColorDemo.app */; 105 | productType = "com.apple.product-type.application"; 106 | }; 107 | /* End PBXNativeTarget section */ 108 | 109 | /* Begin PBXProject section */ 110 | 5E0AD2B1204A493A0008F2BA /* Project object */ = { 111 | isa = PBXProject; 112 | attributes = { 113 | LastUpgradeCheck = 0920; 114 | ORGANIZATIONNAME = GodzzZZZ; 115 | TargetAttributes = { 116 | 5E0AD2B8204A493A0008F2BA = { 117 | CreatedOnToolsVersion = 9.2; 118 | ProvisioningStyle = Automatic; 119 | }; 120 | }; 121 | }; 122 | buildConfigurationList = 5E0AD2B4204A493A0008F2BA /* Build configuration list for PBXProject "MFExtractColorDemo" */; 123 | compatibilityVersion = "Xcode 8.0"; 124 | developmentRegion = en; 125 | hasScannedForEncodings = 0; 126 | knownRegions = ( 127 | en, 128 | Base, 129 | ); 130 | mainGroup = 5E0AD2B0204A493A0008F2BA; 131 | productRefGroup = 5E0AD2BA204A493A0008F2BA /* Products */; 132 | projectDirPath = ""; 133 | projectRoot = ""; 134 | targets = ( 135 | 5E0AD2B8204A493A0008F2BA /* MFExtractColorDemo */, 136 | ); 137 | }; 138 | /* End PBXProject section */ 139 | 140 | /* Begin PBXResourcesBuildPhase section */ 141 | 5E0AD2B7204A493A0008F2BA /* Resources */ = { 142 | isa = PBXResourcesBuildPhase; 143 | buildActionMask = 2147483647; 144 | files = ( 145 | 5E0AD2C9204A493A0008F2BA /* LaunchScreen.storyboard in Resources */, 146 | 5E0AD2C6204A493A0008F2BA /* Assets.xcassets in Resources */, 147 | 5E0AD2C4204A493A0008F2BA /* Main.storyboard in Resources */, 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | /* End PBXResourcesBuildPhase section */ 152 | 153 | /* Begin PBXSourcesBuildPhase section */ 154 | 5E0AD2B5204A493A0008F2BA /* Sources */ = { 155 | isa = PBXSourcesBuildPhase; 156 | buildActionMask = 2147483647; 157 | files = ( 158 | 5E0AD2D5204A499F0008F2BA /* MFExtractColor.m in Sources */, 159 | 5E0AD2C1204A493A0008F2BA /* ViewController.m in Sources */, 160 | 5E0AD2CC204A493A0008F2BA /* main.m in Sources */, 161 | 5E0AD2BE204A493A0008F2BA /* AppDelegate.m in Sources */, 162 | ); 163 | runOnlyForDeploymentPostprocessing = 0; 164 | }; 165 | /* End PBXSourcesBuildPhase section */ 166 | 167 | /* Begin PBXVariantGroup section */ 168 | 5E0AD2C2204A493A0008F2BA /* Main.storyboard */ = { 169 | isa = PBXVariantGroup; 170 | children = ( 171 | 5E0AD2C3204A493A0008F2BA /* Base */, 172 | ); 173 | name = Main.storyboard; 174 | sourceTree = ""; 175 | }; 176 | 5E0AD2C7204A493A0008F2BA /* LaunchScreen.storyboard */ = { 177 | isa = PBXVariantGroup; 178 | children = ( 179 | 5E0AD2C8204A493A0008F2BA /* Base */, 180 | ); 181 | name = LaunchScreen.storyboard; 182 | sourceTree = ""; 183 | }; 184 | /* End PBXVariantGroup section */ 185 | 186 | /* Begin XCBuildConfiguration section */ 187 | 5E0AD2CD204A493A0008F2BA /* Debug */ = { 188 | isa = XCBuildConfiguration; 189 | buildSettings = { 190 | ALWAYS_SEARCH_USER_PATHS = NO; 191 | CLANG_ANALYZER_NONNULL = YES; 192 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 193 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 194 | CLANG_CXX_LIBRARY = "libc++"; 195 | CLANG_ENABLE_MODULES = YES; 196 | CLANG_ENABLE_OBJC_ARC = YES; 197 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 198 | CLANG_WARN_BOOL_CONVERSION = YES; 199 | CLANG_WARN_COMMA = YES; 200 | CLANG_WARN_CONSTANT_CONVERSION = YES; 201 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 202 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 203 | CLANG_WARN_EMPTY_BODY = YES; 204 | CLANG_WARN_ENUM_CONVERSION = YES; 205 | CLANG_WARN_INFINITE_RECURSION = YES; 206 | CLANG_WARN_INT_CONVERSION = YES; 207 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 208 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 210 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 211 | CLANG_WARN_STRICT_PROTOTYPES = YES; 212 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 213 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 214 | CLANG_WARN_UNREACHABLE_CODE = YES; 215 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 216 | CODE_SIGN_IDENTITY = "iPhone Developer"; 217 | COPY_PHASE_STRIP = NO; 218 | DEBUG_INFORMATION_FORMAT = dwarf; 219 | ENABLE_STRICT_OBJC_MSGSEND = YES; 220 | ENABLE_TESTABILITY = YES; 221 | GCC_C_LANGUAGE_STANDARD = gnu11; 222 | GCC_DYNAMIC_NO_PIC = NO; 223 | GCC_NO_COMMON_BLOCKS = YES; 224 | GCC_OPTIMIZATION_LEVEL = 0; 225 | GCC_PREPROCESSOR_DEFINITIONS = ( 226 | "DEBUG=1", 227 | "$(inherited)", 228 | ); 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 236 | MTL_ENABLE_DEBUG_INFO = YES; 237 | ONLY_ACTIVE_ARCH = YES; 238 | SDKROOT = iphoneos; 239 | }; 240 | name = Debug; 241 | }; 242 | 5E0AD2CE204A493A0008F2BA /* Release */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 249 | CLANG_CXX_LIBRARY = "libc++"; 250 | CLANG_ENABLE_MODULES = YES; 251 | CLANG_ENABLE_OBJC_ARC = YES; 252 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 253 | CLANG_WARN_BOOL_CONVERSION = YES; 254 | CLANG_WARN_COMMA = YES; 255 | CLANG_WARN_CONSTANT_CONVERSION = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 258 | CLANG_WARN_EMPTY_BODY = YES; 259 | CLANG_WARN_ENUM_CONVERSION = YES; 260 | CLANG_WARN_INFINITE_RECURSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 265 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 266 | CLANG_WARN_STRICT_PROTOTYPES = YES; 267 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 268 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 269 | CLANG_WARN_UNREACHABLE_CODE = YES; 270 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 271 | CODE_SIGN_IDENTITY = "iPhone Developer"; 272 | COPY_PHASE_STRIP = NO; 273 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 274 | ENABLE_NS_ASSERTIONS = NO; 275 | ENABLE_STRICT_OBJC_MSGSEND = YES; 276 | GCC_C_LANGUAGE_STANDARD = gnu11; 277 | GCC_NO_COMMON_BLOCKS = YES; 278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 280 | GCC_WARN_UNDECLARED_SELECTOR = YES; 281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 282 | GCC_WARN_UNUSED_FUNCTION = YES; 283 | GCC_WARN_UNUSED_VARIABLE = YES; 284 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 285 | MTL_ENABLE_DEBUG_INFO = NO; 286 | SDKROOT = iphoneos; 287 | VALIDATE_PRODUCT = YES; 288 | }; 289 | name = Release; 290 | }; 291 | 5E0AD2D0204A493A0008F2BA /* Debug */ = { 292 | isa = XCBuildConfiguration; 293 | buildSettings = { 294 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 295 | CODE_SIGN_STYLE = Automatic; 296 | DEVELOPMENT_TEAM = 5D33D27VC6; 297 | INFOPLIST_FILE = MFExtractColorDemo/Info.plist; 298 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 299 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 300 | PRODUCT_BUNDLE_IDENTIFIER = net.microfeel.MFExtractColorDemo; 301 | PRODUCT_NAME = "$(TARGET_NAME)"; 302 | TARGETED_DEVICE_FAMILY = "1,2"; 303 | }; 304 | name = Debug; 305 | }; 306 | 5E0AD2D1204A493A0008F2BA /* Release */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 310 | CODE_SIGN_STYLE = Automatic; 311 | DEVELOPMENT_TEAM = 5D33D27VC6; 312 | INFOPLIST_FILE = MFExtractColorDemo/Info.plist; 313 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 314 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 315 | PRODUCT_BUNDLE_IDENTIFIER = net.microfeel.MFExtractColorDemo; 316 | PRODUCT_NAME = "$(TARGET_NAME)"; 317 | TARGETED_DEVICE_FAMILY = "1,2"; 318 | }; 319 | name = Release; 320 | }; 321 | /* End XCBuildConfiguration section */ 322 | 323 | /* Begin XCConfigurationList section */ 324 | 5E0AD2B4204A493A0008F2BA /* Build configuration list for PBXProject "MFExtractColorDemo" */ = { 325 | isa = XCConfigurationList; 326 | buildConfigurations = ( 327 | 5E0AD2CD204A493A0008F2BA /* Debug */, 328 | 5E0AD2CE204A493A0008F2BA /* Release */, 329 | ); 330 | defaultConfigurationIsVisible = 0; 331 | defaultConfigurationName = Release; 332 | }; 333 | 5E0AD2CF204A493A0008F2BA /* Build configuration list for PBXNativeTarget "MFExtractColorDemo" */ = { 334 | isa = XCConfigurationList; 335 | buildConfigurations = ( 336 | 5E0AD2D0204A493A0008F2BA /* Debug */, 337 | 5E0AD2D1204A493A0008F2BA /* Release */, 338 | ); 339 | defaultConfigurationIsVisible = 0; 340 | defaultConfigurationName = Release; 341 | }; 342 | /* End XCConfigurationList section */ 343 | }; 344 | rootObject = 5E0AD2B1204A493A0008F2BA /* Project object */; 345 | } 346 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MFExtractColorDemo 4 | // 5 | // Copyright © 2018年 GodzzZZZ. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow *window; 13 | 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MFExtractColorDemo 4 | // 5 | // Copyright © 2018年 GodzzZZZ. All rights reserved. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 26 | } 27 | 28 | 29 | - (void)applicationDidEnterBackground:(UIApplication *)application { 30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | 35 | - (void)applicationWillEnterForeground:(UIApplication *)application { 36 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | 40 | - (void)applicationDidBecomeActive:(UIApplication *)application { 41 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | } 43 | 44 | 45 | - (void)applicationWillTerminate:(UIApplication *)application { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/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 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/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 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | NSPhotoLibraryUsageDescription 45 | . 46 | 47 | 48 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/MFExtractColor/MFExtractColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // MFExtractColor.h 3 | // MFExtractColorDemo 4 | // 5 | // Copyright © 2018年 GodzzZZZ. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | @class MFExtractColor; 11 | typedef void (^completionHandler) (MFExtractColor *); 12 | @interface MFExtractColor : NSObject 13 | 14 | @property (nonatomic, strong, readonly) UIColor *backgroundColor; 15 | @property (nonatomic, strong, readonly) UIColor *primaryColor; 16 | @property (nonatomic, strong, readonly) UIColor *secondaryColor; 17 | @property (nonatomic, strong, readonly) UIColor *detailColor; 18 | 19 | + (void)extractColorFromImage:(UIImage *)image 20 | scaled:(CGSize)scaledSize 21 | completionHandler:(completionHandler)completionHandler; 22 | @end 23 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/MFExtractColor/MFExtractColor.m: -------------------------------------------------------------------------------- 1 | // 2 | // MFExtractColor.m 3 | // MFExtractColorDemo 4 | // 5 | // Copyright © 2018年 GodzzZZZ. All rights reserved. 6 | // 7 | 8 | #import "MFExtractColor.h" 9 | 10 | @interface UIColor (MFColor) 11 | 12 | - (BOOL)mf_isDarkColor; 13 | - (BOOL)mf_isDistinct:(UIColor *)compareColor; 14 | - (UIColor *)mf_colorWithMinimumSaturation:(CGFloat)minSaturation; 15 | - (BOOL)mf_isBlackOrWhite; 16 | - (BOOL)mf_isContrastingColor:(UIColor *)color; 17 | 18 | @end 19 | 20 | @interface MFCountedColor : NSObject 21 | 22 | @property (nonatomic, assign) NSUInteger count; 23 | @property (nonatomic, strong) UIColor *color; 24 | 25 | - (instancetype)initWithColor:(UIColor *)color count:(NSUInteger)count; 26 | 27 | @end 28 | 29 | @interface UIImage (Scale) 30 | 31 | - (UIImage *)scaledToSize:(CGSize)newSize; 32 | 33 | @end 34 | 35 | @interface MFExtractColor() 36 | @property (nonatomic, strong, readwrite) UIColor *backgroundColor; 37 | @property (nonatomic, strong, readwrite) UIColor *primaryColor; 38 | @property (nonatomic, strong, readwrite) UIColor *secondaryColor; 39 | @property (nonatomic, strong, readwrite) UIColor *detailColor; 40 | 41 | @end 42 | 43 | @implementation MFExtractColor 44 | 45 | - (instancetype)initWithImage:(UIImage *)image { 46 | self = [super init]; 47 | if (self) { 48 | [self analyzeImage:image]; 49 | } 50 | return self; 51 | } 52 | 53 | + (void)extractColorFromImage:(UIImage *)image scaled:(CGSize)scaledSize completionHandler:(completionHandler)completionHandler { 54 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 55 | UIImage *newImage = [image scaledToSize:scaledSize]; 56 | MFExtractColor *extractColor = [[MFExtractColor alloc] initWithImage:newImage]; 57 | dispatch_async(dispatch_get_main_queue(), ^{ 58 | completionHandler(extractColor); 59 | }); 60 | }); 61 | } 62 | 63 | - (void)analyzeImage:(UIImage *)anImage { 64 | NSCountedSet *imageColors = nil; 65 | UIColor *backgroundColor = [self findEdgeColor:anImage imageColors:&imageColors]; 66 | UIColor *primaryColor = nil; 67 | UIColor *secondaryColor = nil; 68 | UIColor *detailColor = nil; 69 | if (!backgroundColor) { 70 | backgroundColor = [UIColor whiteColor]; 71 | } 72 | BOOL darkBackground = [backgroundColor mf_isDarkColor]; 73 | [self findTextColors:imageColors primaryColor:&primaryColor secondaryColor:&secondaryColor detailColor:&detailColor backgroundColor:backgroundColor]; 74 | 75 | if (!primaryColor) { 76 | if (darkBackground) 77 | primaryColor = [UIColor whiteColor]; 78 | else 79 | primaryColor = [UIColor blackColor]; 80 | } 81 | 82 | if (!secondaryColor) { 83 | if (darkBackground) 84 | secondaryColor = [UIColor whiteColor]; 85 | else 86 | secondaryColor = [UIColor blackColor]; 87 | } 88 | 89 | if (!detailColor) { 90 | if (darkBackground) 91 | detailColor = [UIColor whiteColor]; 92 | else 93 | detailColor = [UIColor blackColor]; 94 | } 95 | self.backgroundColor = backgroundColor; 96 | self.primaryColor = primaryColor; 97 | self.secondaryColor = secondaryColor; 98 | self.detailColor = detailColor; 99 | } 100 | 101 | typedef struct RGBAPixel { 102 | Byte red; 103 | Byte green; 104 | Byte blue; 105 | Byte alpha; 106 | 107 | } RGBAPixel; 108 | 109 | - (UIColor *)findEdgeColor:(UIImage *)image imageColors:(NSCountedSet**)colors { 110 | 111 | CGImageRef imageRep = image.CGImage; 112 | NSInteger width = CGImageGetWidth(imageRep); 113 | NSInteger height = CGImageGetHeight(imageRep); 114 | CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); 115 | 116 | CGContextRef bitmapContext = CGBitmapContextCreate(NULL, width, height, 8, 4 * width, cs, kCGImageAlphaNoneSkipLast); 117 | CGContextDrawImage(bitmapContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = height}, image.CGImage); 118 | CGColorSpaceRelease(cs); 119 | NSCountedSet* imageColors = [[NSCountedSet alloc] initWithCapacity:width * height]; 120 | NSCountedSet* edgeColors = [[NSCountedSet alloc] initWithCapacity:height]; 121 | //优化:将bitmap取出作为变量传入,而不是每次循环重新创建。 122 | const RGBAPixel* pixels = (const RGBAPixel*)CGBitmapContextGetData(bitmapContext); 123 | for (NSUInteger y = 0; y < height; y++) { 124 | for (NSUInteger x = 0; x < width; x++) { 125 | const NSUInteger index = x + y * width; 126 | RGBAPixel pixel = pixels[index]; 127 | UIColor* color = [[UIColor alloc] initWithRed:((CGFloat)pixel.red / 255.0f) green:((CGFloat)pixel.green / 255.0f) blue:((CGFloat)pixel.blue / 255.0f) alpha:1.0f]; 128 | if (0 == x) 129 | [edgeColors addObject:color]; 130 | [imageColors addObject:color]; 131 | } 132 | } 133 | CGContextRelease(bitmapContext); 134 | *colors = imageColors; 135 | 136 | NSEnumerator *enumerator = [edgeColors objectEnumerator]; 137 | UIColor *curColor = nil; 138 | NSMutableArray *sortedColors = [NSMutableArray arrayWithCapacity:[edgeColors count]]; 139 | 140 | while (curColor = [enumerator nextObject]) { 141 | NSUInteger colorCount = [edgeColors countForObject:curColor]; 142 | if (colorCount <= 2) // prevent using random colors, threshold should be based on input image size 143 | continue; 144 | MFCountedColor *container = [[MFCountedColor alloc] initWithColor:curColor count:colorCount]; 145 | [sortedColors addObject:container]; 146 | } 147 | [sortedColors sortUsingSelector:@selector(compare:)]; 148 | MFCountedColor *proposedEdgeColor = nil; 149 | 150 | if ([sortedColors count] > 0) { 151 | proposedEdgeColor = [sortedColors objectAtIndex:0]; 152 | 153 | if ([proposedEdgeColor.color mf_isBlackOrWhite] ) // want to choose color over black/white so we keep looking 154 | { 155 | for ( NSInteger i = 1; i < [sortedColors count]; i++ ) { 156 | MFCountedColor *nextProposedColor = [sortedColors objectAtIndex:i]; 157 | 158 | if (((double)nextProposedColor.count / (double)proposedEdgeColor.count) > .4 ) // make sure the second choice color is 40% as common as the first choice 159 | { 160 | if (![nextProposedColor.color mf_isBlackOrWhite]) { 161 | proposedEdgeColor = nextProposedColor; 162 | break; 163 | } 164 | }else { 165 | // reached color threshold less than 40% of the original proposed edge color so bail 166 | break; 167 | } 168 | } 169 | } 170 | } 171 | 172 | return proposedEdgeColor.color; 173 | } 174 | 175 | 176 | - (void)findTextColors:(NSCountedSet*)colors primaryColor:(UIColor **)primaryColor secondaryColor:(UIColor **)secondaryColor detailColor:(UIColor **)detailColor backgroundColor:(UIColor *)backgroundColor { 177 | NSEnumerator *enumerator = [colors objectEnumerator]; 178 | UIColor *curColor = nil; 179 | NSMutableArray *sortedColors = [NSMutableArray arrayWithCapacity:[colors count]]; 180 | BOOL findDarkTextColor = ![backgroundColor mf_isDarkColor]; 181 | while ((curColor = [enumerator nextObject])) { 182 | curColor = [curColor mf_colorWithMinimumSaturation:.15]; 183 | 184 | if ([curColor mf_isDarkColor] == findDarkTextColor) { 185 | NSUInteger colorCount = [colors countForObject:curColor]; 186 | 187 | //if ( colorCount <= 2 ) // prevent using random colors, threshold should be based on input image size 188 | // continue; 189 | 190 | MFCountedColor *container = [[MFCountedColor alloc] initWithColor:curColor count:colorCount]; 191 | 192 | [sortedColors addObject:container]; 193 | } 194 | } 195 | 196 | [sortedColors sortUsingSelector:@selector(compare:)]; 197 | 198 | for (MFCountedColor *curContainer in sortedColors) { 199 | curColor = curContainer.color; 200 | if (!*primaryColor) { 201 | if ([curColor mf_isContrastingColor:backgroundColor]) 202 | *primaryColor = curColor; 203 | }else if (!*secondaryColor) { 204 | if (![*primaryColor mf_isDistinct:curColor] || ![curColor mf_isContrastingColor:backgroundColor]) 205 | continue; 206 | *secondaryColor = curColor; 207 | }else if (!*detailColor) { 208 | if (![*secondaryColor mf_isDistinct:curColor] || ![*primaryColor mf_isDistinct:curColor] || ![curColor mf_isContrastingColor:backgroundColor] ) 209 | continue; 210 | *detailColor = curColor; 211 | break; 212 | } 213 | } 214 | } 215 | 216 | @end 217 | 218 | @implementation UIColor(MFColor) 219 | - (BOOL)mf_isDarkColor { 220 | CGFloat r, g, b, a; 221 | [self getRed:&r green:&g blue:&b alpha:&a]; 222 | CGFloat lum = 0.2126 * r + 0.7152 * g + 0.0722 * b; 223 | if (lum < .5) { 224 | return YES; 225 | } 226 | return NO; 227 | } 228 | 229 | - (BOOL)mf_isDistinct:(UIColor *)compareColor { 230 | CGFloat r, g, b, a; 231 | CGFloat r1, g1, b1, a1; 232 | [self getRed:&r green:&g blue:&b alpha:&a]; 233 | [compareColor getRed:&r1 green:&g1 blue:&b1 alpha:&a1]; 234 | CGFloat threshold = .25; //.15 235 | 236 | if (fabs(r - r1) > threshold || 237 | fabs(g - g1) > threshold || 238 | fabs(b - b1) > threshold || 239 | fabs(a - a1) > threshold) { 240 | // check for grays, prevent multiple gray colors 241 | if (fabs(r - g) < .03 && fabs(r - b) < .03) { 242 | if (fabs(r1 - g1) < .03 && fabs(r1 - b1) < .03) 243 | return NO; 244 | } 245 | return YES; 246 | } 247 | return NO; 248 | } 249 | 250 | - (UIColor *)mf_colorWithMinimumSaturation:(CGFloat)minSaturation { 251 | if (self) { 252 | CGFloat hue = 0.0; 253 | CGFloat saturation = 0.0; 254 | CGFloat brightness = 0.0; 255 | CGFloat alpha = 0.0; 256 | [self getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha]; 257 | if (saturation < minSaturation) { 258 | return [UIColor colorWithHue:hue saturation:minSaturation brightness:brightness alpha:alpha]; 259 | } 260 | } 261 | return self; 262 | } 263 | 264 | - (BOOL)mf_isBlackOrWhite { 265 | if (self) { 266 | CGFloat r, g, b, a; 267 | [self getRed:&r green:&g blue:&b alpha:&a]; 268 | if (r > .91 && g > .91 && b > .91) 269 | return YES; // white 270 | 271 | if (r < .09 && g < .09 && b < .09) 272 | return YES; // black 273 | } 274 | return NO; 275 | } 276 | 277 | - (BOOL)mf_isContrastingColor:(UIColor *)color { 278 | if (self && color) { 279 | CGFloat br, bg, bb, ba; 280 | CGFloat fr, fg, fb, fa; 281 | [self getRed:&br green:&bg blue:&bb alpha:&ba]; 282 | [color getRed:&fr green:&fg blue:&fb alpha:&fa]; 283 | CGFloat bLum = 0.2126 * br + 0.7152 * bg + 0.0722 * bb; 284 | CGFloat fLum = 0.2126 * fr + 0.7152 * fg + 0.0722 * fb; 285 | CGFloat contrast = 0.; 286 | if ( bLum > fLum ) 287 | contrast = (bLum + 0.05) / (fLum + 0.05); 288 | else 289 | contrast = (fLum + 0.05) / (bLum + 0.05); 290 | //return contrast > 3.0; //3-4.5 W3C recommends 3:1 ratio, but that filters too many colors 291 | return contrast > 1.6; 292 | } 293 | return YES; 294 | } 295 | @end 296 | 297 | @implementation MFCountedColor 298 | 299 | - (instancetype)initWithColor:(UIColor *)color count:(NSUInteger)count { 300 | self = [super init]; 301 | if (self) { 302 | self.color = color; 303 | self.count = count; 304 | } 305 | return self; 306 | } 307 | 308 | - (NSComparisonResult)compare:(MFCountedColor *)object { 309 | if ( [object isKindOfClass:[MFCountedColor class]]) { 310 | if ( self.count < object.count ) { 311 | return NSOrderedDescending; 312 | }else if (self.count == object.count) { 313 | return NSOrderedSame; 314 | } 315 | } 316 | return NSOrderedAscending; 317 | } 318 | 319 | 320 | @end 321 | 322 | @implementation UIImage (Scale) 323 | 324 | - (UIImage *)scaledToSize:(CGSize)newSize { 325 | UIGraphicsBeginImageContext(newSize); 326 | [self drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; 327 | UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); 328 | UIGraphicsEndImageContext(); 329 | return newImage; 330 | } 331 | 332 | @end 333 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MFExtractColorDemo 4 | // 5 | // Copyright © 2018年 GodzzZZZ. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MFExtractColorDemo 4 | // 5 | // Copyright © 2018年 GodzzZZZ. All rights reserved. 6 | // 7 | 8 | #import "ViewController.h" 9 | #import "MFExtractColor.h" 10 | #import 11 | @interface ViewController () 12 | < 13 | UIImagePickerControllerDelegate, 14 | UINavigationControllerDelegate 15 | > 16 | @property (nonatomic, strong) UIView *containerView; 17 | @property (nonatomic, strong) UIImageView *imageView; 18 | @property (nonatomic, strong) MFExtractColor *extractColor; 19 | @property (nonatomic, strong) UIView *backgroundColorView; 20 | @property (nonatomic, strong) UILabel *primaryColorLabel; 21 | @property (nonatomic, strong) UILabel *secondaryColorLabel; 22 | @property (nonatomic, strong) UILabel *detailColorLabel; 23 | @end 24 | 25 | @implementation ViewController 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | 30 | self.containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 100 + CGRectGetWidth(self.view.frame) - 20)]; 31 | self.containerView.backgroundColor = [UIColor whiteColor]; 32 | self.containerView.alpha = 0; 33 | [self.view addSubview:self.containerView]; 34 | 35 | self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, CGRectGetWidth(self.view.frame) - 100, CGRectGetWidth(self.view.frame) - 20)]; 36 | self.imageView.contentMode = UIViewContentModeScaleAspectFill; 37 | self.imageView.layer.masksToBounds = YES; 38 | [self.containerView addSubview:self.imageView]; 39 | 40 | self.backgroundColorView = [[UIView alloc] initWithFrame:CGRectMake(0, 100, 100, CGRectGetWidth(self.view.frame) - 20)]; 41 | self.backgroundColorView.backgroundColor = self.extractColor.backgroundColor; 42 | [self.containerView addSubview:self.backgroundColorView]; 43 | 44 | 45 | self.primaryColorLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, 80, 50)]; 46 | self.primaryColorLabel.text = @"primary color text"; 47 | self.primaryColorLabel.font = [UIFont systemFontOfSize:15]; 48 | self.primaryColorLabel.numberOfLines = 0; 49 | self.primaryColorLabel.textColor = self.extractColor.primaryColor; 50 | [self.backgroundColorView addSubview:self.primaryColorLabel]; 51 | 52 | self.secondaryColorLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, CGRectGetMaxY(self.primaryColorLabel.frame), 80, 50)]; 53 | self.secondaryColorLabel.text = @"secondary color text"; 54 | self.secondaryColorLabel.font = [UIFont systemFontOfSize:15]; 55 | self.secondaryColorLabel.numberOfLines = 0; 56 | self.secondaryColorLabel.textColor = self.extractColor.secondaryColor; 57 | [self.backgroundColorView addSubview:self.secondaryColorLabel]; 58 | 59 | self.detailColorLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, CGRectGetMaxY(self.secondaryColorLabel.frame), 80, 50)]; 60 | self.detailColorLabel.text = @"detail color text"; 61 | self.detailColorLabel.font = [UIFont systemFontOfSize:15]; 62 | self.detailColorLabel.numberOfLines = 0; 63 | self.detailColorLabel.textColor = self.extractColor.detailColor; 64 | [self.backgroundColorView addSubview:self.detailColorLabel]; 65 | 66 | 67 | 68 | UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(10, CGRectGetMaxY(self.imageView.frame) + 50, CGRectGetWidth(self.view.frame) - 20, 50)]; 69 | [btn setTitle:@"choose" forState:UIControlStateNormal]; 70 | btn.backgroundColor = [UIColor colorWithRed:52/255.0 green:152/255.0 blue:219/255.0 alpha:1]; 71 | [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 72 | [self.view addSubview:btn]; 73 | 74 | [btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside]; 75 | } 76 | 77 | - (void)setExtractColor:(MFExtractColor *)extractColor { 78 | _extractColor = extractColor; 79 | self.backgroundColorView.backgroundColor = extractColor.backgroundColor; 80 | self.primaryColorLabel.textColor = extractColor.primaryColor; 81 | self.secondaryColorLabel.textColor = extractColor.secondaryColor; 82 | self.detailColorLabel.textColor = extractColor.detailColor; 83 | [UIView animateWithDuration:0.2 animations:^{ 84 | self.containerView.alpha = 1; 85 | }]; 86 | } 87 | 88 | - (void)click { 89 | 90 | UIImagePickerController *picker = [[UIImagePickerController alloc] init]; 91 | picker.delegate = self; 92 | picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; 93 | [self presentViewController:picker animated:YES completion:^{ 94 | self.containerView.alpha = 0; 95 | }]; 96 | } 97 | 98 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { 99 | [self dismissViewControllerAnimated:YES completion:^{ 100 | PHAsset *asset = nil; 101 | if (@available(iOS 11.0, *)) { 102 | asset = [info objectForKey:UIImagePickerControllerPHAsset]; 103 | } else { 104 | NSURL *imageAssetUrl = [info objectForKey:UIImagePickerControllerReferenceURL]; 105 | PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[imageAssetUrl] options:nil]; 106 | asset = [result firstObject]; 107 | 108 | } 109 | [self getPhotoWithAsset:asset progress:^(double progress, NSError * _Nullable error, BOOL * _Nonnull stop, NSDictionary * _Nullable info) { 110 | //不走这个callback,不知道什么bug 111 | } completion:^(UIImage *image, NSDictionary *info) { 112 | self.imageView.image = image; 113 | [MFExtractColor extractColorFromImage:image scaled:CGSizeMake(512, 512) completionHandler:^(MFExtractColor *extractColor) { 114 | self.extractColor = extractColor; 115 | }]; 116 | }]; 117 | }]; 118 | } 119 | 120 | 121 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { 122 | [self dismissViewControllerAnimated:YES completion:^{ 123 | 124 | }]; 125 | } 126 | 127 | - (void)getPhotoWithAsset:(PHAsset *)asset progress:(PHAssetImageProgressHandler)progressHandler completion:(void (^)(UIImage *, NSDictionary *))completion { 128 | 129 | PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init]; 130 | option.networkAccessAllowed = YES; 131 | option.progressHandler = progressHandler; 132 | 133 | [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:option resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) { 134 | if (result) { 135 | if (completion) dispatch_async(dispatch_get_main_queue(), ^{ 136 | completion(result, info); 137 | }); 138 | } 139 | }]; 140 | } 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /MFExtractColorDemo/MFExtractColorDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MFExtractColorDemo 4 | // 5 | // Created by pipelining on 2018/3/3. 6 | // Copyright © 2018年 GodzzZZZ. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MFExtractColor 2 | 3 | [Port of ColorArt code from OS X to iOS.](https://github.com/panicinc/ColorArt) 4 | Fetches the most dominant and prominent colors from an image. 5 | 6 | 自动从图片中提取主要颜色,参见ZEEEN (Dribbble client for iOS). 7 | 8 | 9 | ![DUB](https://img.shields.io/dub/l/vibe-d.svg) 10 | ![Total-downloads](https://img.shields.io/cocoapods/dt/MFExtractColor.svg) 11 | ![Version](https://img.shields.io/cocoapods/v/MFExtractColor.svg?style=flat) 12 | ![Platform](https://img.shields.io/cocoapods/p/MFExtractColor.svg?style=flat) 13 | ![Language](https://img.shields.io/badge/language-objectivec-blue.svg) 14 | 15 | ## 集成方式 16 | - cocoapod 17 | ``` 18 | pod 'MFExtractColor' 19 | ``` 20 | 21 | ## 效果 22 | 23 | 24 | 25 | ## about 26 | (From the Panic blog: http://www.panic.com/blog/2012/12/itunes-11-and-colors/) 27 | 28 | iTunes 11 is a radical departure from previous versions and nothing illustrates this more than the new album display mode. The headlining feature of this display is the new view style that visually matches the track listing to the album’s cover art. The result is an attractive display of textual information that seamlessly integrates with the album’s artwork. 29 | 30 | After using iTunes for a day I wondered just how hard it would be to mimic this functionality — use a source image to create a themed image/text display. 31 | 32 | Once I started filtering black and white backgrounds my results started to get a bit closer to iTunes. After doing some more analysis I saw that iTunes also looks for borders around the artwork. So lets say you have a solid white border around the artwork picture, iTunes will remove the border and base its theming colors off the remaining interior content. I didn’t add this functionality as it was outside the scope of my simple demo application. 33 | 34 | After the background color was determined, the next step is to find contrasting text colors. Again, the first thing I tried was simple color counting, this provides surprisingly good results but iTunes does better. If we relied only on color frequency you’d get variants of the same color for the different types of text (EG: primary, secondary, detail). So the next thing I did to improve the results were to make sure the text colors were distinct enough from each other to be considered a separate color. At this point things were really starting to look good. But what other aspects would need to be considered to ensure the text always looked good on the chosen background color? To ensure colorful text I also added a bit of code to make sure the color used for the text had a minimum saturation level. This prevents washed out colors or very light pastel colors from being used that might not give the best appearance. Now that the text had unique colors that looked good with the background, the only remaining problem was that the resulting text colors could end up lacking enough contrast with the background to be readable. So the last thing I added was a check to make sure any text color would provide enough contrast with the background to be readable. Unfortunately this requirement does cause a rare “miss” when finding text colors which then cause the default black/white colors to be used. 35 | 36 | ## License 37 | based on Panic's OS X ColorArt. 38 | -------------------------------------------------------------------------------- /SnapShot/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ess/MFExtractColor/4668a5f87cc20298eaabb13ee855ad15eb1c0ae7/SnapShot/1.jpg -------------------------------------------------------------------------------- /SnapShot/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ess/MFExtractColor/4668a5f87cc20298eaabb13ee855ad15eb1c0ae7/SnapShot/2.png -------------------------------------------------------------------------------- /SnapShot/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ess/MFExtractColor/4668a5f87cc20298eaabb13ee855ad15eb1c0ae7/SnapShot/3.png -------------------------------------------------------------------------------- /SnapShot/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ess/MFExtractColor/4668a5f87cc20298eaabb13ee855ad15eb1c0ae7/SnapShot/4.png -------------------------------------------------------------------------------- /SnapShot/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/1ess/MFExtractColor/4668a5f87cc20298eaabb13ee855ad15eb1c0ae7/SnapShot/5.png --------------------------------------------------------------------------------