├── ios.png
├── mac.png
├── DominantColor
├── .gitignore
├── Mac
│ ├── ExampleMac-Bridging-Header.h
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── DragAndDropImageView.swift
│ ├── AppDelegate.swift
│ └── Base.lproj
│ │ └── MainMenu.xib
├── iOS
│ ├── ExampleiOS-Bridging-Header.h
│ ├── AppDelegate.swift
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── ViewController.swift
│ └── Base.lproj
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
└── Shared
│ ├── DominantColor.h
│ ├── Memoization.swift
│ ├── INVector3SwiftExtensions.swift
│ ├── Info.plist
│ ├── KMeans.swift
│ ├── PlatformExtensions.swift
│ ├── ColorDifference.swift
│ ├── ColorSpaceConversion.swift
│ └── DominantColors.swift
├── DominantColor.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ ├── DominantColor-Mac.xcscheme
│ │ ├── DominantColor-iOS.xcscheme
│ │ ├── ExampleMac.xcscheme
│ │ └── ExampleiOS.xcscheme
└── project.pbxproj
├── .gitignore
├── Package.swift
├── DominantColor.podspec
├── LICENSE
└── README.md
/ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/DominantColor/HEAD/ios.png
--------------------------------------------------------------------------------
/mac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/DominantColor/HEAD/mac.png
--------------------------------------------------------------------------------
/DominantColor/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.so
3 | _test
4 | *.swp
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/DominantColor/Mac/ExampleMac-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #include
6 | extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));
7 |
--------------------------------------------------------------------------------
/DominantColor/iOS/ExampleiOS-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #include
6 | extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));
7 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.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 | .DS_Store
20 | contents.xcworkspacedata
21 |
--------------------------------------------------------------------------------
/DominantColor/iOS/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DominantColor-iOS
4 | //
5 | // Created by Jamal E. Kharrat on 12/22/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/DominantColor/Shared/DominantColor.h:
--------------------------------------------------------------------------------
1 | //
2 | // DominantColor.h
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/22/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for DominantColor.
12 | FOUNDATION_EXPORT double DominantColorVersionNumber;
13 |
14 | //! Project version string for DominantColor.
15 | FOUNDATION_EXPORT const unsigned char DominantColorVersionString[];
16 |
--------------------------------------------------------------------------------
/DominantColor/Shared/Memoization.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Memoization.swift
3 | // DominantColor
4 | //
5 | // Created by Emmanuel Odeke on 2014-12-25.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | func memoize(_ f: @escaping (T) -> U) -> (T) -> U {
10 | var cache = [T : U]()
11 |
12 | return { key in
13 | var value = cache[key]
14 | if value == nil {
15 | value = f(key)
16 | cache[key] = value
17 | }
18 | return value!
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 |
3 | /**
4 | * DominantColor
5 | * Copyright (c) Indragie Karunaratne 2021
6 | * See LICENSE file.
7 | */
8 |
9 | import PackageDescription
10 |
11 | let package = Package(
12 | name: "DominantColor",
13 | platforms: [
14 | .macOS(.v10_13),
15 | .iOS(.v11),
16 | .tvOS(.v11)
17 | ],
18 | products: [
19 | .library(name: "DominantColor", targets: ["DominantColor"]),
20 | .library(name: "DominantColor_Dynamic", type: .dynamic, targets: ["DominantColor"]),
21 | ],
22 | targets: [
23 | .target(
24 | name: "DominantColor",
25 | path: "DominantColor/Shared"
26 | ),
27 | ]
28 | )
29 |
--------------------------------------------------------------------------------
/DominantColor.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'DominantColor'
3 | spec.version = '0.2.2'
4 | spec.summary = 'Finding dominant colors of an image using k-means clustering.'
5 | spec.homepage = 'https://github.com/indragiek/DominantColor'
6 | spec.license = 'MIT'
7 | spec.author = { 'Indragie Karunaratne' => 'i@indragie.com' }
8 | spec.source = { :git => 'https://github.com/indragiek/DominantColor.git', :tag => spec.version.to_s }
9 | spec.source_files = 'DominantColor/Shared/*.{swift,h,m}'
10 | spec.requires_arc = true
11 | spec.frameworks = ['GameKit']
12 | spec.ios.deployment_target = '11.0'
13 | spec.osx.deployment_target = '10.13'
14 | spec.ios.frameworks = 'UIKit'
15 | spec.osx.frameworks = 'Cocoa'
16 | spec.swift_versions = ['5.0']
17 | end
18 |
--------------------------------------------------------------------------------
/DominantColor/iOS/Images.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 | }
--------------------------------------------------------------------------------
/DominantColor/Shared/INVector3SwiftExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // INVector3SwiftExtensions.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/24/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import simd
10 |
11 | extension simd_float3 {
12 | func unpack() -> (Float, Float, Float) {
13 | return (x, y, z)
14 | }
15 |
16 | static var identity: simd_float3 {
17 | return simd_float3(0, 0, 0)
18 | }
19 |
20 | static func +(lhs: simd_float3, rhs: simd_float3) -> simd_float3 {
21 | return simd_float3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z)
22 | }
23 |
24 | static func /(lhs: simd_float3, rhs: Float) -> simd_float3 {
25 | return simd_float3(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs)
26 | }
27 |
28 | static func /(lhs: simd_float3, rhs: Int) -> simd_float3 {
29 | return lhs / Float(rhs)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/DominantColor/Shared/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 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSHumanReadableCopyright
24 | Copyright © 2014 Indragie Karunaratne. All rights reserved.
25 | NSPrincipalClass
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Indragie Karunaratne
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/DominantColor/Mac/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/DominantColor/Mac/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | FMWK
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Copyright © 2014 Indragie Karunaratne. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/DominantColor/iOS/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 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | LSRequiresIPhoneOS
24 |
25 | NSPhotoLibraryUsageDescription
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UIRequiredDeviceCapabilities
32 |
33 | armv7
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/DominantColor/Mac/DragAndDropImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DragAndDropImageView.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/19/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @objc protocol DragAndDropImageViewDelegate {
12 | func dragAndDropImageView(imageView: DragAndDropImageView, droppedImage image: NSImage?)
13 | }
14 |
15 | class DragAndDropImageView: NSImageView {
16 | @IBOutlet weak var delegate: DragAndDropImageViewDelegate?
17 |
18 | private let filenameType = NSPasteboard.PasteboardType(rawValue: "NSFilenamesPboardType")
19 |
20 | required init?(coder: NSCoder) {
21 | super.init(coder: coder)
22 | registerForDraggedTypes([filenameType])
23 | }
24 |
25 | private func fileURL(for pasteboard: NSPasteboard) -> URL? {
26 | if let files = pasteboard.propertyList(forType: filenameType) as? [String] {
27 | guard let filename = files.first else { return nil }
28 |
29 | return URL(fileURLWithPath: filename)
30 | }
31 | return nil
32 | }
33 |
34 | // MARK: NSDraggingDestination
35 |
36 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
37 | let pasteboard = sender.draggingPasteboard
38 | if let fileURL = fileURL(for: pasteboard) {
39 | let pathExtension = fileURL.pathExtension as CFString
40 | guard let UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, nil)?.takeRetainedValue() else { return [] }
41 | return (UTTypeConformsTo(UTI, kUTTypeImage) == true) ? .copy : []
42 | }
43 | return []
44 | }
45 |
46 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
47 | let pasteboard = sender.draggingPasteboard
48 | if let fileURL = fileURL(for: pasteboard) {
49 | self.delegate?.dragAndDropImageView(imageView: self, droppedImage: NSImage(contentsOf: fileURL))
50 | return true
51 | }
52 | return false
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/DominantColor/Mac/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/18/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import DominantColor
11 |
12 | @NSApplicationMain
13 | class AppDelegate: NSObject, NSApplicationDelegate, DragAndDropImageViewDelegate {
14 |
15 | @IBOutlet weak var window: NSWindow!
16 | @IBOutlet weak var box1: NSBox!
17 | @IBOutlet weak var box2: NSBox!
18 | @IBOutlet weak var box3: NSBox!
19 | @IBOutlet weak var box4: NSBox!
20 | @IBOutlet weak var box5: NSBox!
21 | @IBOutlet weak var box6: NSBox!
22 | @IBOutlet weak var imageView: DragAndDropImageView!
23 |
24 | var image: NSImage?
25 |
26 | func applicationDidFinishLaunching(aNotification: NSNotification) {
27 | }
28 |
29 | // MARK: DragAndDropImageViewDelegate
30 |
31 | @IBAction func runBenchmark(_ sender: NSButton) {
32 | if let image = image {
33 | let nValues: [Int] = [100, 1000, 2000, 5000, 10000]
34 | let CGImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil)!
35 | for n in nValues {
36 | let ns = dispatch_benchmark(5) {
37 | _ = dominantColorsInImage(CGImage, maxSampledPixels: n)
38 | return
39 | }
40 | print("n = \(n) averaged \(ns/1000000) ms")
41 | }
42 | }
43 | }
44 |
45 | func dragAndDropImageView(imageView: DragAndDropImageView, droppedImage image: NSImage?) {
46 | if let image = image {
47 | imageView.image = image
48 |
49 | self.image = image
50 | let colors = image.dominantColors()
51 | let boxes = [box1, box2, box3, box4, box5, box6]
52 |
53 | for box in boxes {
54 | box?.fillColor = .clear
55 | }
56 | for i in 0.. [String: Any] {
70 | return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)})
71 | }
72 |
73 | // Helper function inserted by Swift 4.2 migrator.
74 | fileprivate func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String {
75 | return input.rawValue
76 | }
77 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/xcshareddata/xcschemes/DominantColor-Mac.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
66 |
67 |
68 |
69 |
71 |
72 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/xcshareddata/xcschemes/DominantColor-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
66 |
67 |
68 |
69 |
71 |
72 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/xcshareddata/xcschemes/ExampleMac.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/xcshareddata/xcschemes/ExampleiOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/DominantColor/iOS/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/DominantColor/Shared/KMeans.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KMeans.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/20/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Darwin
10 | import GameKit
11 |
12 | // Represents a type that can be clustered using the k-means clustering
13 | // algorithm.
14 | protocol ClusteredType {
15 | // Used to compute average values to determine the cluster centroids.
16 | static func +(lhs: Self, rhs: Self) -> Self
17 | static func /(lhs: Self, rhs: Int) -> Self
18 |
19 | // Identity value such that x + identity = x. Typically the 0 vector.
20 | static var identity: Self { get }
21 | }
22 |
23 | struct Cluster {
24 | let centroid: T
25 | let size: Int
26 | }
27 |
28 | // k-means clustering algorithm from
29 | // http://users.eecs.northwestern.edu/~wkliao/Kmeans/
30 |
31 | func kmeans(
32 | _ points: [T],
33 | k: Int,
34 | seed: UInt64,
35 | distance: ((T, T) -> Float),
36 | threshold: Float = 0.0001
37 | ) -> [Cluster] {
38 |
39 | let n = points.count
40 | assert(k <= n, "k cannot be larger than the total number of points")
41 |
42 | var centroids = points.randomValues(k, seed: seed)
43 | var memberships = [Int](repeating: -1, count: n)
44 | var clusterSizes = [Int](repeating: 0, count: k)
45 |
46 | var error: Float = 0
47 | var previousError: Float = 0
48 |
49 | repeat {
50 | error = 0
51 | var newCentroids = [T](repeating: T.identity, count: k)
52 | var newClusterSizes = [Int](repeating: 0, count: k)
53 |
54 | for i in 0.. 0 {
67 | centroids[i] = newCentroids[i] / size
68 | }
69 | }
70 |
71 | clusterSizes = newClusterSizes
72 | previousError = error
73 | } while abs(error - previousError) > threshold
74 |
75 | return zip(centroids, clusterSizes).map { Cluster(centroid: $0, size: $1) }
76 | }
77 |
78 | private func findNearestCluster(_ point: T, centroids: [T], k: Int, distance: (T, T) -> Float) -> Int {
79 | var minDistance = Float.infinity
80 | var clusterIndex = 0
81 | for i in 0.. [Element] {
93 | if self.isEmpty {
94 | return self
95 | }
96 | let rs = GKMersenneTwisterRandomSource()
97 | rs.seed = seed
98 |
99 | let rd = GKRandomDistribution(randomSource: rs, lowestValue: 0, highestValue: self.count - 1)
100 |
101 | var indices = [Int]()
102 | indices.reserveCapacity(num)
103 |
104 | for _ in 0.. [NSColor] {
40 | let image = cgImage(forProposedRect: nil, context: nil, hints: nil)!
41 | let colors = dominantColorsInImage(image, maxSampledPixels: maxSampledPixels, accuracy: accuracy, seed: seed, memoizeConversions: memoizeConversions)
42 | return colors.map { NSColor(cgColor: $0)! }
43 | }
44 | }
45 |
46 | #elseif os(iOS)
47 | import UIKit
48 |
49 | public extension UIImage {
50 | /**
51 | Computes the dominant colors in the receiver
52 |
53 | - parameter maxSampledPixels: Maximum number of pixels to sample in the image. If
54 | the total number of pixels in the image exceeds this
55 | value, it will be downsampled to meet the constraint.
56 | - parameter accuracy: Level of accuracy to use when grouping similar colors.
57 | Higher accuracy will come with a performance tradeoff.
58 | - parameter seed: Seed to use when choosing the initial points for grouping
59 | of similar colors. The same seed is guaranteed to return
60 | the same colors every time.
61 | - parameter memoizeConversions: Whether to memoize conversions from RGB to the LAB color
62 | space (used for grouping similar colors). Memoization
63 | will only yield better performance for large values of
64 | `maxSampledPixels` in images that are primarily comprised
65 | of flat colors. If this information about the image is
66 | not known beforehand, it is best to not memoize.
67 |
68 | - returns: A list of dominant colors in the image sorted from most dominant to
69 | least dominant.
70 | */
71 | func dominantColors(
72 | _ maxSampledPixels: Int = DefaultParameterValues.maxSampledPixels,
73 | accuracy: GroupingAccuracy = DefaultParameterValues.accuracy,
74 | seed: UInt64 = DefaultParameterValues.seed,
75 | memoizeConversions: Bool = DefaultParameterValues.memoizeConversions
76 | ) -> [UIColor] {
77 | if let CGImage = self.cgImage {
78 | let colors = dominantColorsInImage(CGImage, maxSampledPixels: maxSampledPixels, accuracy: accuracy, seed: seed, memoizeConversions: memoizeConversions)
79 | return colors.map { UIColor(cgColor: $0) }
80 | } else {
81 | return []
82 | }
83 | }
84 | }
85 |
86 | #endif
87 |
88 |
--------------------------------------------------------------------------------
/DominantColor/Shared/ColorDifference.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorDifference.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/22/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import simd
10 |
11 | @inlinable func SIMDMathDegreesToRadians(_ degrees: Float) -> Float {
12 | return degrees * (Float.pi / 180.0)
13 | }
14 |
15 | @inlinable func SIMDMathRadiansToDegrees(_ radians: Float) -> Float {
16 | return radians * (180.0 / Float.pi)
17 | }
18 |
19 | // These functions return the squared color difference because for distance
20 | // calculations it doesn't matter and saves an unnecessary computation.
21 |
22 | // From http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE76.html
23 | func CIE76SquaredColorDifference(_ lab1: simd_float3, lab2: simd_float3) -> Float {
24 | let (L1, a1, b1) = lab1.unpack()
25 | let (L2, a2, b2) = lab2.unpack()
26 |
27 | return pow(L2 - L1, 2) + pow(a2 - a1, 2) + pow(b2 - b1, 2)
28 | }
29 |
30 | private func C(_ a: Float, b: Float) -> Float {
31 | return sqrt(pow(a, 2) + pow(b, 2))
32 | }
33 |
34 | // From http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE94.html
35 | func CIE94SquaredColorDifference(
36 | _ kL: Float = 1,
37 | kC: Float = 1,
38 | kH: Float = 1,
39 | K1: Float = 0.045,
40 | K2: Float = 0.015
41 | ) -> (_ lab1:simd_float3, _ lab2:simd_float3) -> Float {
42 |
43 | return { (lab1:simd_float3, lab2:simd_float3) -> Float in
44 |
45 | let (L1, a1, b1) = lab1.unpack()
46 | let (L2, a2, b2) = lab2.unpack()
47 | let ΔL = L1 - L2
48 |
49 | let (C1, C2) = (C(a1, b: b1), C(a2, b: b2))
50 | let ΔC = C1 - C2
51 |
52 | let ΔH = sqrt(pow(a1 - a2, 2) + pow(b1 - b2, 2) - pow(ΔC, 2))
53 |
54 | let Sl: Float = 1
55 | let Sc = 1 + K1 * C1
56 | let Sh = 1 + K2 * C1
57 |
58 | return pow(ΔL / (kL * Sl), 2) + pow(ΔC / (kC * Sc), 2) + pow(ΔH / (kH * Sh), 2)
59 | }
60 | }
61 |
62 | // From http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html
63 | func CIE2000SquaredColorDifference(
64 | _ kL: Float = 1,
65 | kC: Float = 1,
66 | kH: Float = 1
67 | ) -> (_ lab1:simd_float3, _ lab2:simd_float3) -> Float {
68 |
69 | return { (lab1:simd_float3, lab2:simd_float3) -> Float in
70 | let (L1, a1, b1) = lab1.unpack()
71 | let (L2, a2, b2) = lab2.unpack()
72 |
73 | let ΔLp = L2 - L1
74 | let Lbp = (L1 + L2) / 2
75 |
76 | let (C1, C2) = (C(a1, b: b1), C(a2, b: b2))
77 | let Cb = (C1 + C2) / 2
78 |
79 | let G = (1 - sqrt(pow(Cb, 7) / (pow(Cb, 7) + pow(25, 7)))) / 2
80 | let ap: (Float) -> Float = { a in
81 | return a * (1 + G)
82 | }
83 | let (a1p, a2p) = (ap(a1), ap(a2))
84 |
85 | let (C1p, C2p) = (C(a1p, b: b1), C(a2p, b: b2))
86 | let ΔCp = C2p - C1p
87 | let Cbp = (C1p + C2p) / 2
88 |
89 | let hp: (Float, Float) -> Float = { ap, b in
90 | if ap == 0 && b == 0 { return 0 }
91 | let θ = SIMDMathRadiansToDegrees(atan2(b, ap))
92 | return fmod(θ < 0 ? (θ + 360) : θ, 360)
93 | }
94 | let (h1p, h2p) = (hp(a1p, b1), hp(a2p, b2))
95 | let Δhabs = abs(h1p - h2p)
96 | let Δhp: Float = {
97 | if (C1p == 0 || C2p == 0) {
98 | return 0
99 | } else if Δhabs <= 180 {
100 | return h2p - h1p
101 | } else if h2p <= h1p {
102 | return h2p - h1p + 360
103 | } else {
104 | return h2p - h1p - 360
105 | }
106 | }()
107 |
108 | let ΔHp = 2 * sqrt(C1p * C2p) * sin(SIMDMathDegreesToRadians(Δhp / 2))
109 | let Hbp: Float = {
110 | if (C1p == 0 || C2p == 0) {
111 | return h1p + h2p
112 | } else if Δhabs > 180 {
113 | return (h1p + h2p + 360) / 2
114 | } else {
115 | return (h1p + h2p) / 2
116 | }
117 | }()
118 |
119 | var T = 1
120 | - 0.17 * cos(SIMDMathDegreesToRadians(Hbp - 30))
121 | + 0.24 * cos(SIMDMathDegreesToRadians(2 * Hbp))
122 |
123 | T = T
124 | + 0.32 * cos(SIMDMathDegreesToRadians(3 * Hbp + 6))
125 | - 0.20 * cos(SIMDMathDegreesToRadians(4 * Hbp - 63))
126 |
127 | let Sl = 1 + (0.015 * pow(Lbp - 50, 2)) / sqrt(20 + pow(Lbp - 50, 2))
128 | let Sc = 1 + 0.045 * Cbp
129 | let Sh = 1 + 0.015 * Cbp * T
130 |
131 | let Δθ = 30 * exp(-pow((Hbp - 275) / 25, 2))
132 | let Rc = 2 * sqrt(pow(Cbp, 7) / (pow(Cbp, 7) + pow(25, 7)))
133 | let Rt = -Rc * sin(SIMDMathDegreesToRadians(2 * Δθ))
134 |
135 | let Lterm = ΔLp / (kL * Sl)
136 | let Cterm = ΔCp / (kC * Sc)
137 | let Hterm = ΔHp / (kH * Sh)
138 | return pow(Lterm, 2) + pow(Cterm, 2) + pow(Hterm, 2) + Rt * Cterm * Hterm
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/DominantColor/Shared/ColorSpaceConversion.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorSpaceConversion.swift
3 | // DominantColor
4 | //
5 | // Created by Jernej Strasner on 2/5/19.
6 | // Copyright © 2019 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #if os(iOS) || os(tvOS)
10 | import UIKit
11 | #elseif os(OSX)
12 | import AppKit
13 | #endif
14 |
15 | import simd
16 |
17 | // MARK: - RGB
18 |
19 | func RGBToSRGB(_ rgbVector: simd_float3) -> simd_float3 {
20 | #if os(iOS) || os(tvOS)
21 | return rgbVector
22 | #elseif os(OSX)
23 | let rgbColor = NSColor(deviceRed: CGFloat(rgbVector.x), green: CGFloat(rgbVector.y), blue: CGFloat(rgbVector.z), alpha: 1.0)
24 | guard let srgbColor = rgbColor.usingColorSpace(.sRGB) else {
25 | fatalError("Could not convert color space")
26 | }
27 | return simd_float3(Float(srgbColor.redComponent), Float(srgbColor.greenComponent), Float(srgbColor.blueComponent))
28 | #endif
29 | }
30 |
31 | func SRGBToRGB(_ srgbVector: simd_float3) -> simd_float3 {
32 | #if os(iOS) || os(tvOS)
33 | return srgbVector
34 | #elseif os(OSX)
35 | let components: [CGFloat] = [CGFloat(srgbVector.x), CGFloat(srgbVector.y), CGFloat(srgbVector.z), 1.0]
36 | let srgbColor = NSColor(colorSpace: .sRGB, components: components, count: 4)
37 | guard let rgbColor = srgbColor.usingColorSpace(.deviceRGB) else {
38 | fatalError("Could not convert color space")
39 | }
40 | return simd_float3(Float(rgbColor.redComponent), Float(rgbColor.greenComponent), Float(rgbColor.blueComponent))
41 | #endif
42 | }
43 |
44 | // MARK: - SRGB
45 |
46 | func SRGBToLinearSRGB(_ srgbVector: simd_float3) -> simd_float3 {
47 | func f(_ c: Float) -> Float {
48 | if (c <= 0.04045) {
49 | return c / 12.92
50 | } else {
51 | return powf((c + 0.055) / 1.055, 2.4)
52 | }
53 | }
54 | return simd_float3(f(srgbVector.x), f(srgbVector.y), f(srgbVector.z))
55 | }
56 |
57 | func LinearSRGBToSRGB(_ lSrgbVector: simd_float3) -> simd_float3 {
58 | func f(_ c: Float) -> Float {
59 | if (c <= 0.0031308) {
60 | return c * 12.92
61 | } else {
62 | return (1.055 * powf(c, 1.0 / 2.4)) - 0.055
63 | }
64 | };
65 | return simd_float3(f(lSrgbVector.x), f(lSrgbVector.y), f(lSrgbVector.z));
66 | }
67 |
68 | // MARK: - XYZ (CIE 1931)
69 | // http://en.wikipedia.org/wiki/CIE_1931_color_space#Construction_of_the_CIE_XYZ_color_space_from_the_Wright.E2.80.93Guild_data
70 |
71 | let LinearSRGBToXYZMatrix = simd_float3x3([
72 | SIMD3(0.4124, 0.2126, 0.0193),
73 | SIMD3(0.3576, 0.7152, 0.1192),
74 | SIMD3(0.1805, 0.0722, 0.9505)
75 | ])
76 |
77 | func LinearSRGBToXYZ(_ linearSrgbVector: simd_float3) -> simd_float3 {
78 | let unscaledXYZVector = LinearSRGBToXYZMatrix * linearSrgbVector
79 | return unscaledXYZVector * 100.0
80 | }
81 |
82 | let XYZToLinearSRGBMatrix = simd_float3x3([
83 | SIMD3(3.2406, -0.9689, 0.0557),
84 | SIMD3(-1.5372, 1.8758, -0.2040),
85 | SIMD3(-0.4986, 0.0415, 1.0570)
86 | ])
87 |
88 | func XYZToLinearSRGB(_ xyzVector: simd_float3) -> simd_float3 {
89 | let scaledXYZVector = xyzVector / 100.0
90 | return XYZToLinearSRGBMatrix * scaledXYZVector
91 | }
92 |
93 |
94 | // MARK: - LAB
95 | // http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions
96 |
97 | func XYZToLAB(_ xyzVector: simd_float3, _ tristimulus: simd_float3) -> simd_float3 {
98 | func f(_ t: Float) -> Float {
99 | if (t > powf(6.0 / 29.0, 3.0)) {
100 | return powf(t, 1.0 / 3.0)
101 | } else {
102 | return ((1.0 / 3.0) * powf(29.0 / 6.0, 2.0) * t) + (4.0 / 29.0)
103 | }
104 | };
105 | let fx = f(xyzVector.x / tristimulus.x)
106 | let fy = f(xyzVector.y / tristimulus.y)
107 | let fz = f(xyzVector.z / tristimulus.z)
108 |
109 | let l = (116.0 * fy) - 16.0
110 | let a = 500 * (fx - fy)
111 | let b = 200 * (fy - fz)
112 |
113 | return simd_float3(l, a, b)
114 | }
115 |
116 | func LABToXYZ(_ labVector: simd_float3, _ tristimulus: simd_float3) -> simd_float3 {
117 | func f(_ t: Float) -> Float {
118 | if (t > (6.0 / 29.0)) {
119 | return powf(t, 3.0)
120 | } else {
121 | return 3.0 * powf(6.0 / 29.0, 2.0) * (t - (4.0 / 29.0))
122 | }
123 | };
124 | let c = (1.0 / 116.0) * (labVector.x + 16.0)
125 |
126 | let y = tristimulus.y * f(c)
127 | let x = tristimulus.x * f(c + ((1.0 / 500.0) * labVector.y))
128 | let z = tristimulus.z * f(c - ((1.0 / 200.0) * labVector.z))
129 |
130 | return simd_float3(x, y, z)
131 | }
132 |
133 | // MARK: - Public
134 |
135 | // From http://www.easyrgb.com/index.php?X=MATH&H=15#text15
136 | let D65Tristimulus = simd_float3(95.047, 100.0, 108.883)
137 |
138 | func IN_RGBToLAB(_ gVector: simd_float3) -> simd_float3 {
139 | let srgbVector = RGBToSRGB(gVector)
140 | let lSrgbVector = SRGBToLinearSRGB(srgbVector)
141 | let xyzVector = LinearSRGBToXYZ(lSrgbVector)
142 | let labVector = XYZToLAB(xyzVector, D65Tristimulus)
143 | return labVector
144 | }
145 |
146 | func IN_LABToRGB(_ gVector: simd_float3) -> simd_float3 {
147 | let xyzVector = LABToXYZ(gVector, D65Tristimulus)
148 | let lSrgbVector = XYZToLinearSRGB(xyzVector)
149 | let srgbVector = LinearSRGBToSRGB(lSrgbVector)
150 | let rgbVector = SRGBToRGB(srgbVector)
151 | return rgbVector
152 | }
153 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## DominantColor
2 |
3 | Finding the dominant colors of an image using the CIE LAB color space and the k-means clustering algorithm.
4 |
5 | ### Algorithm
6 |
7 | #### Color Space
8 |
9 | The RGB color space does not take human perception into account, so the CIELAB color space is used instead, which is designed to approximate human vision [[1]](http://en.wikipedia.org/wiki/Lab_color_space#Advantages).
10 |
11 | ##### Conversion from RGB
12 |
13 | The conversion from RGB values to LAB values requires first transforming the RGB values to an absolute color space like sRGB[[2]](http://en.wikipedia.org/wiki/Lab_color_space#RGB_and_CMYK_conversions). On iOS, this conversion is not necessary because sRGB is the native device color space[[3]](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGColorSpace/index.html). On OS X, the conversion can be performed using [`-[NSColorSpace sRGBColorSpace]`](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSColorSpace_Class/index.html).
14 |
15 | Once the colors have been converted to sRGB, they are first converted to linear sRGB values and then to CIE XYZ values[[4]](http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29). Finally, they are converted to the CIE LAB color space[[5]](http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions) using a D65 standard illuminant[[6]](http://en.wikipedia.org/wiki/Illuminant_D65)[[7]](http://www.easyrgb.com/index.php?X=MATH&H=15#text15).
16 |
17 | #### Color Difference
18 |
19 | A color difference algorithm is used to group similar colors. API consumers can choose between the CIE 76[[8]](http://en.wikipedia.org/wiki/Color_difference#CIE76), CIE 94[[9]](http://en.wikipedia.org/wiki/Color_difference#CIE94), and CIE 2000[[10]](http://en.wikipedia.org/wiki/Color_difference#CIEDE2000) algorithms for low, medium, and high color grouping accuracy, respectively. The default algorithm is CIE 94, as it provides results that are close to CIE 2000 with a negligible performance impact in comparison to CIE 76.
20 |
21 | #### Clustering (k-means)
22 |
23 | Pixels are grouped into clusters of dominant colors using a standard k-means clustering algorithm[[11](http://en.wikipedia.org/wiki/K-means_clustering)][[12](http://users.eecs.northwestern.edu/~wkliao/Kmeans/)][[13](http://cs.smu.ca/~r_zhang/code/kmeans.c)].
24 |
25 | ##### Choosing K
26 |
27 | The k-value was originally chosen based on the rule of thumb `k = sqrt(n/2)`[[14](http://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set#cite_note-1)] but this resulted in `k`-values that were too large to run in a reasonable amount of time for large values of `n`. Right now, I'm using a magic value of `16` because empirical testing showed that it yielded the best results for many different images but I'm still looking into a number of more data-driven alternate approaches.
28 |
29 | ##### Selecting Initial Centroids
30 |
31 | The initial centroids are currently selected on a random basis. An alternative approach is to use the [k-means++ algorithm](http://en.wikipedia.org/wiki/K-means%2B%2B), in which after the first centroid is selected randomly, the subsequent centroids are selected with probability proportional to the distance from the randomly selected centroid.
32 |
33 | #### Downsampling
34 |
35 | The k-means algorithm has a worst case runtime that is super-polynomial in the input size[[15](http://en.wikipedia.org/wiki/K-means%2B%2B)], so sampling large numbers of pixels is a problem. Images are automatically downsampled such that the total number of pixels is less than or equal to a specified maximum number of pixels to sample. The value I've been using is `1000`, which is a good balance between accurate results and runtime.
36 |
37 | ### Implementation
38 |
39 | Everything is implemented in Swift except for the functions that convert between color spaces, which use GLKit and thus must be written in C (since Swift doesn't support C unions at this time).
40 |
41 | ### Apps
42 |
43 | The project includes Mac and iOS apps that can be used to see the results of the algorithm and to run a simple benchmark.
44 |
45 | 
46 | 
47 |
48 | ### Contact
49 |
50 | * Indragie Karunaratne
51 | * [@indragie](http://twitter.com/indragie)
52 | * [http://indragie.com](http://indragie.com)
53 |
54 | ### License
55 |
56 | Licensed under the MIT License.
57 |
58 | ### References
59 |
60 | 1 [http://en.wikipedia.org/wiki/Lab_color_space#Advantages](http://en.wikipedia.org/wiki/Lab_color_space#Advantages)
61 | 2 [http://en.wikipedia.org/wiki/Lab_color_space#RGB_and_CMYK_conversions](http://en.wikipedia.org/wiki/Lab_color_space#RGB_and_CMYK_conversions)
62 | 3 [https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGColorSpace/index.html](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGColorSpace/index.html)
63 | 4 [http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29](http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29)
64 | 5 [http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions](http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions)
65 | 6 [http://en.wikipedia.org/wiki/Illuminant_D65](http://en.wikipedia.org/wiki/Illuminant_D65)
66 | 7 [http://www.easyrgb.com/index.php?X=MATH&H=15#text15](http://www.easyrgb.com/index.php?X=MATH&H=15#text15)
67 | 8 [http://en.wikipedia.org/wiki/Color_difference#CIE76](http://en.wikipedia.org/wiki/Color_difference#CIE76)
68 | 9 [http://en.wikipedia.org/wiki/Color_difference#CIEDE2000](http://en.wikipedia.org/wiki/Color_difference#CIE94)
69 | 10 [http://en.wikipedia.org/wiki/Color_difference#CIEDE2000](http://en.wikipedia.org/wiki/Color_difference#CIEDE2000)
70 | 11 [http://en.wikipedia.org/wiki/K-means_clustering](http://en.wikipedia.org/wiki/K-means_clustering)
71 | 12 [http://users.eecs.northwestern.edu/~wkliao/Kmeans/](http://users.eecs.northwestern.edu/~wkliao/Kmeans/)
72 | 13 [http://cs.smu.ca/~r_zhang/code/kmeans.c](http://cs.smu.ca/~r_zhang/code/kmeans.c)
73 | 14 [http://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set#cite_note-1](http://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set#cite_note-1)
74 | 15 [http://en.wikipedia.org/wiki/K-means%2B%2B](http://en.wikipedia.org/wiki/K-means%2B%2B)
--------------------------------------------------------------------------------
/DominantColor/Shared/DominantColors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DominantColors.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/20/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #if os(OSX)
10 | import Foundation
11 | #elseif os(iOS)
12 | import UIKit
13 | #endif
14 |
15 | import CoreGraphics
16 | import simd
17 |
18 | // MARK: Bitmaps
19 |
20 | private struct RGBAPixel {
21 | let r: UInt8
22 | let g: UInt8
23 | let b: UInt8
24 | let a: UInt8
25 | }
26 |
27 | extension RGBAPixel: Hashable {
28 | func hash(into hasher: inout Hasher) {
29 | hasher.combine(r)
30 | hasher.combine(g)
31 | hasher.combine(b)
32 | }
33 | }
34 |
35 | private func ==(lhs: RGBAPixel, rhs: RGBAPixel) -> Bool {
36 | return lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b
37 | }
38 |
39 | private func createRGBAContext(_ width: Int, height: Int) -> CGContext {
40 | return CGContext(
41 | data: nil,
42 | width: width,
43 | height: height,
44 | bitsPerComponent: 8, // bits per component
45 | bytesPerRow: width * 4, // bytes per row
46 | space: CGColorSpaceCreateDeviceRGB(),
47 | bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).rawValue
48 | )!
49 | }
50 |
51 | // Enumerates over all of the pixels in an RGBA bitmap context
52 | // in the order that they are stored in memory, for faster access.
53 | //
54 | // From: https://www.mikeash.com/pyblog/friday-qa-2012-08-31-obtaining-and-interpreting-image-data.html
55 | private func enumerateRGBAContext(_ context: CGContext, handler: (Int, Int, RGBAPixel) -> Void) {
56 | let (width, height) = (context.width, context.height)
57 | let data = unsafeBitCast(context.data, to: UnsafeMutablePointer.self)
58 | for y in 0.. CGColor {
68 | return CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [CGFloat(rgbVector.x), CGFloat(rgbVector.y), CGFloat(rgbVector.z), 1.0])!
69 | }
70 |
71 | private extension RGBAPixel {
72 | func toRGBVector() -> simd_float3 {
73 | return simd_float3(
74 | Float(r) / Float(UInt8.max),
75 | Float(g) / Float(UInt8.max),
76 | Float(b) / Float(UInt8.max)
77 | )
78 | }
79 | }
80 |
81 | // MARK: Clustering
82 |
83 | extension simd_float3 : ClusteredType {}
84 |
85 | // MARK: Main
86 |
87 | public enum GroupingAccuracy {
88 | case low // CIE 76 - Euclidian distance
89 | case medium // CIE 94 - Perceptual non-uniformity corrections
90 | case high // CIE 2000 - Additional corrections for neutral colors, lightness, chroma, and hue
91 | }
92 |
93 | public struct DefaultParameterValues {
94 | public static var maxSampledPixels: Int = 1000
95 | public static var accuracy: GroupingAccuracy = .medium
96 | public static var seed: UInt64 = 3571
97 | public static var memoizeConversions: Bool = false
98 | }
99 |
100 | /**
101 | Computes the dominant colors in an image
102 |
103 | - parameter image: The image
104 | - parameter maxSampledPixels: Maximum number of pixels to sample in the image. If
105 | the total number of pixels in the image exceeds this
106 | value, it will be downsampled to meet the constraint.
107 | - parameter accuracy: Level of accuracy to use when grouping similar colors.
108 | Higher accuracy will come with a performance tradeoff.
109 | - parameter seed: Seed to use when choosing the initial points for grouping
110 | of similar colors. The same seed is guaranteed to return
111 | the same colors every time.
112 | - parameter memoizeConversions: Whether to memoize conversions from RGB to the LAB color
113 | space (used for grouping similar colors). Memoization
114 | will only yield better performance for large values of
115 | `maxSampledPixels` in images that are primarily comprised
116 | of flat colors. If this information about the image is
117 | not known beforehand, it is best to not memoize.
118 |
119 | - returns: A list of dominant colors in the image sorted from most dominant to
120 | least dominant.
121 | */
122 | public func dominantColorsInImage(
123 | _ image: CGImage,
124 | maxSampledPixels: Int = DefaultParameterValues.maxSampledPixels,
125 | accuracy: GroupingAccuracy = DefaultParameterValues.accuracy,
126 | seed: UInt64 = DefaultParameterValues.seed,
127 | memoizeConversions: Bool = DefaultParameterValues.memoizeConversions
128 | ) -> [CGColor] {
129 |
130 | let (width, height) = (image.width, image.height)
131 | let (scaledWidth, scaledHeight) = scaledDimensionsForPixelLimit(maxSampledPixels, width: width, height: height)
132 |
133 | // Downsample the image if necessary, so that the total number of
134 | // pixels sampled does not exceed the specified maximum.
135 | let context = createRGBAContext(scaledWidth, height: scaledHeight)
136 | context.draw(image, in: CGRect(x: 0, y: 0, width: Int(scaledWidth), height: Int(scaledHeight)))
137 |
138 | // Get the RGB colors from the bitmap context, ignoring any pixels
139 | // that have alpha transparency.
140 | // Also convert the colors to the LAB color space
141 | var labValues = [simd_float3]()
142 | labValues.reserveCapacity(Int(scaledWidth * scaledHeight))
143 |
144 | let RGBToLAB: (RGBAPixel) -> simd_float3 = {
145 | let f: (RGBAPixel) -> simd_float3 = { IN_RGBToLAB($0.toRGBVector()) }
146 | return memoizeConversions ? memoize(f) : f
147 | }()
148 | enumerateRGBAContext(context) { (_, _, pixel) in
149 | if pixel.a == UInt8.max {
150 | labValues.append(RGBToLAB(pixel))
151 | }
152 | }
153 | // Cluster the colors using the k-means algorithm
154 | let k = selectKForElements(labValues)
155 | var clusters = kmeans(labValues, k: k, seed: seed, distance: distanceForAccuracy(accuracy))
156 |
157 | // Sort the clusters by size in descending order so that the
158 | // most dominant colors come first.
159 | clusters.sort { $0.size > $1.size }
160 |
161 | return clusters.map { RGBVectorToCGColor(IN_LABToRGB($0.centroid)) }
162 | }
163 |
164 | private func distanceForAccuracy(_ accuracy: GroupingAccuracy) -> (simd_float3, simd_float3) -> Float {
165 | switch accuracy {
166 | case .low:
167 | return CIE76SquaredColorDifference
168 | case .medium:
169 | return CIE94SquaredColorDifference()
170 | case .high:
171 | return CIE2000SquaredColorDifference()
172 | }
173 | }
174 |
175 | // Computes the proportionally scaled dimensions such that the
176 | // total number of pixels does not exceed the specified limit.
177 | private func scaledDimensionsForPixelLimit(_ limit: Int, width: Int, height: Int) -> (Int, Int) {
178 | if (width * height > limit) {
179 | let ratio = Float(width) / Float(height)
180 | let maxWidth = sqrtf(ratio * Float(limit))
181 | return (Int(maxWidth), Int(Float(limit) / maxWidth))
182 | }
183 | return (width, height)
184 | }
185 |
186 | private func selectKForElements(_ elements: [T]) -> Int {
187 | // Seems like a magic number...
188 | return 16
189 | }
190 |
--------------------------------------------------------------------------------
/DominantColor/iOS/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 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
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 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 194EDC4F220AB84C00B4EE26 /* ColorSpaceConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194EDC4E220AB84C00B4EE26 /* ColorSpaceConversion.swift */; };
11 | 194EDC50220AB84C00B4EE26 /* ColorSpaceConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 194EDC4E220AB84C00B4EE26 /* ColorSpaceConversion.swift */; };
12 | 2F3D09861A4C212A001ED0BF /* Memoization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F3D09851A4C212A001ED0BF /* Memoization.swift */; };
13 | 2F3D09871A4C212A001ED0BF /* Memoization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F3D09851A4C212A001ED0BF /* Memoization.swift */; };
14 | 723DE5291A4934E200C357E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5261A4934E200C357E3 /* AppDelegate.swift */; };
15 | 723DE52A1A4934E200C357E3 /* DragAndDropImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5271A4934E200C357E3 /* DragAndDropImageView.swift */; };
16 | 723DE52B1A4934E200C357E3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 723DE5281A4934E200C357E3 /* Images.xcassets */; };
17 | 723DE52E1A4934F800C357E3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 723DE52C1A4934F800C357E3 /* MainMenu.xib */; };
18 | 723DE54D1A49353C00C357E3 /* DominantColor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE5361A49353C00C357E3 /* DominantColor.framework */; };
19 | 723DE54E1A49353C00C357E3 /* DominantColor.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE5361A49353C00C357E3 /* DominantColor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
20 | 723DE5591A49358F00C357E3 /* DominantColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5581A49358F00C357E3 /* DominantColor.h */; settings = {ATTRIBUTES = (Public, ); }; };
21 | 723DE55C1A49360F00C357E3 /* ColorDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5141A49348D00C357E3 /* ColorDifference.swift */; };
22 | 723DE55D1A49361100C357E3 /* DominantColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5171A49348D00C357E3 /* DominantColors.swift */; };
23 | 723DE55E1A49361200C357E3 /* KMeans.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE51A1A49348D00C357E3 /* KMeans.swift */; };
24 | 723DE5691A49385C00C357E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5661A49385C00C357E3 /* AppDelegate.swift */; };
25 | 723DE56A1A49385C00C357E3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 723DE5671A49385C00C357E3 /* Images.xcassets */; };
26 | 723DE56B1A49385C00C357E3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5681A49385C00C357E3 /* ViewController.swift */; };
27 | 723DE5701A49386B00C357E3 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 723DE56C1A49386B00C357E3 /* LaunchScreen.xib */; };
28 | 723DE5711A49386B00C357E3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 723DE56E1A49386B00C357E3 /* Main.storyboard */; };
29 | 723DE5B61A4938DD00C357E3 /* DominantColor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE59F1A4938DD00C357E3 /* DominantColor.framework */; };
30 | 723DE5B71A4938DD00C357E3 /* DominantColor.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE59F1A4938DD00C357E3 /* DominantColor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
31 | 723DE5C01A49395A00C357E3 /* DominantColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5581A49358F00C357E3 /* DominantColor.h */; settings = {ATTRIBUTES = (Public, ); }; };
32 | 723DE5C31A49399E00C357E3 /* ColorDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5141A49348D00C357E3 /* ColorDifference.swift */; };
33 | 723DE5C41A4939A000C357E3 /* DominantColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5171A49348D00C357E3 /* DominantColors.swift */; };
34 | 723DE5C51A4939A200C357E3 /* KMeans.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE51A1A49348D00C357E3 /* KMeans.swift */; };
35 | 72431C0F1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */; };
36 | 72431C101A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */; };
37 | 724C2DCD1A4CD64600472402 /* PlatformExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */; };
38 | 724C2DCE1A4CD64600472402 /* PlatformExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */; };
39 | /* End PBXBuildFile section */
40 |
41 | /* Begin PBXContainerItemProxy section */
42 | 723DE54B1A49353C00C357E3 /* PBXContainerItemProxy */ = {
43 | isa = PBXContainerItemProxy;
44 | containerPortal = 72D797B01A43F44D00D32E7C /* Project object */;
45 | proxyType = 1;
46 | remoteGlobalIDString = 723DE5351A49353C00C357E3;
47 | remoteInfo = DominantColor;
48 | };
49 | 723DE5B41A4938DD00C357E3 /* PBXContainerItemProxy */ = {
50 | isa = PBXContainerItemProxy;
51 | containerPortal = 72D797B01A43F44D00D32E7C /* Project object */;
52 | proxyType = 1;
53 | remoteGlobalIDString = 723DE59E1A4938DD00C357E3;
54 | remoteInfo = DominantColor;
55 | };
56 | /* End PBXContainerItemProxy section */
57 |
58 | /* Begin PBXCopyFilesBuildPhase section */
59 | 723DE5521A49353C00C357E3 /* Embed Frameworks */ = {
60 | isa = PBXCopyFilesBuildPhase;
61 | buildActionMask = 2147483647;
62 | dstPath = "";
63 | dstSubfolderSpec = 10;
64 | files = (
65 | 723DE54E1A49353C00C357E3 /* DominantColor.framework in Embed Frameworks */,
66 | );
67 | name = "Embed Frameworks";
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | 723DE5921A4938B500C357E3 /* Embed Frameworks */ = {
71 | isa = PBXCopyFilesBuildPhase;
72 | buildActionMask = 2147483647;
73 | dstPath = "";
74 | dstSubfolderSpec = 10;
75 | files = (
76 | 723DE5B71A4938DD00C357E3 /* DominantColor.framework in Embed Frameworks */,
77 | );
78 | name = "Embed Frameworks";
79 | runOnlyForDeploymentPostprocessing = 0;
80 | };
81 | /* End PBXCopyFilesBuildPhase section */
82 |
83 | /* Begin PBXFileReference section */
84 | 194EDC4E220AB84C00B4EE26 /* ColorSpaceConversion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorSpaceConversion.swift; sourceTree = ""; };
85 | 2F3D09851A4C212A001ED0BF /* Memoization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Memoization.swift; sourceTree = ""; };
86 | 723DE5141A49348D00C357E3 /* ColorDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorDifference.swift; sourceTree = ""; };
87 | 723DE5171A49348D00C357E3 /* DominantColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DominantColors.swift; sourceTree = ""; };
88 | 723DE51A1A49348D00C357E3 /* KMeans.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KMeans.swift; sourceTree = ""; };
89 | 723DE5261A4934E200C357E3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Mac/AppDelegate.swift; sourceTree = ""; };
90 | 723DE5271A4934E200C357E3 /* DragAndDropImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DragAndDropImageView.swift; path = Mac/DragAndDropImageView.swift; sourceTree = ""; };
91 | 723DE5281A4934E200C357E3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Mac/Images.xcassets; sourceTree = ""; };
92 | 723DE52D1A4934F800C357E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Mac/Base.lproj/MainMenu.xib; sourceTree = ""; };
93 | 723DE52F1A49350000C357E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Mac/Info.plist; sourceTree = ""; };
94 | 723DE5361A49353C00C357E3 /* DominantColor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DominantColor.framework; sourceTree = BUILT_PRODUCTS_DIR; };
95 | 723DE5561A49357D00C357E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Shared/Info.plist; sourceTree = ""; };
96 | 723DE5581A49358F00C357E3 /* DominantColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DominantColor.h; sourceTree = ""; };
97 | 723DE5641A4937AD00C357E3 /* ExampleMac-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ExampleMac-Bridging-Header.h"; path = "Mac/ExampleMac-Bridging-Header.h"; sourceTree = ""; };
98 | 723DE5661A49385C00C357E3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = DominantColor/iOS/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
99 | 723DE5671A49385C00C357E3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = DominantColor/iOS/Images.xcassets; sourceTree = SOURCE_ROOT; };
100 | 723DE5681A49385C00C357E3 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = DominantColor/iOS/ViewController.swift; sourceTree = SOURCE_ROOT; };
101 | 723DE56D1A49386B00C357E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = DominantColor/iOS/Base.lproj/LaunchScreen.xib; sourceTree = SOURCE_ROOT; };
102 | 723DE56F1A49386B00C357E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = DominantColor/iOS/Base.lproj/Main.storyboard; sourceTree = SOURCE_ROOT; };
103 | 723DE5721A49387800C357E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = DominantColor/iOS/Info.plist; sourceTree = SOURCE_ROOT; };
104 | 723DE5741A49388400C357E3 /* ExampleiOS-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ExampleiOS-Bridging-Header.h"; path = "DominantColor/iOS/ExampleiOS-Bridging-Header.h"; sourceTree = SOURCE_ROOT; };
105 | 723DE59F1A4938DD00C357E3 /* DominantColor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DominantColor.framework; sourceTree = BUILT_PRODUCTS_DIR; };
106 | 723DE5BE1A49394D00C357E3 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/System/Library/Frameworks/GLKit.framework; sourceTree = DEVELOPER_DIR; };
107 | 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = INVector3SwiftExtensions.swift; sourceTree = ""; };
108 | 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformExtensions.swift; sourceTree = ""; };
109 | 72D797B81A43F44D00D32E7C /* ExampleMac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleMac.app; sourceTree = BUILT_PRODUCTS_DIR; };
110 | 72D797DE1A43F89000D32E7C /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
111 | 898845D21A490CE000003EF2 /* ExampleiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleiOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
112 | /* End PBXFileReference section */
113 |
114 | /* Begin PBXFrameworksBuildPhase section */
115 | 723DE5321A49353C00C357E3 /* Frameworks */ = {
116 | isa = PBXFrameworksBuildPhase;
117 | buildActionMask = 2147483647;
118 | files = (
119 | );
120 | runOnlyForDeploymentPostprocessing = 0;
121 | };
122 | 723DE59B1A4938DD00C357E3 /* Frameworks */ = {
123 | isa = PBXFrameworksBuildPhase;
124 | buildActionMask = 2147483647;
125 | files = (
126 | );
127 | runOnlyForDeploymentPostprocessing = 0;
128 | };
129 | 72D797B51A43F44D00D32E7C /* Frameworks */ = {
130 | isa = PBXFrameworksBuildPhase;
131 | buildActionMask = 2147483647;
132 | files = (
133 | 723DE54D1A49353C00C357E3 /* DominantColor.framework in Frameworks */,
134 | );
135 | runOnlyForDeploymentPostprocessing = 0;
136 | };
137 | 898845CF1A490CE000003EF2 /* Frameworks */ = {
138 | isa = PBXFrameworksBuildPhase;
139 | buildActionMask = 2147483647;
140 | files = (
141 | 723DE5B61A4938DD00C357E3 /* DominantColor.framework in Frameworks */,
142 | );
143 | runOnlyForDeploymentPostprocessing = 0;
144 | };
145 | /* End PBXFrameworksBuildPhase section */
146 |
147 | /* Begin PBXGroup section */
148 | 723DE5131A49348D00C357E3 /* Shared */ = {
149 | isa = PBXGroup;
150 | children = (
151 | 723DE5581A49358F00C357E3 /* DominantColor.h */,
152 | 723DE5141A49348D00C357E3 /* ColorDifference.swift */,
153 | 723DE5171A49348D00C357E3 /* DominantColors.swift */,
154 | 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */,
155 | 723DE51A1A49348D00C357E3 /* KMeans.swift */,
156 | 2F3D09851A4C212A001ED0BF /* Memoization.swift */,
157 | 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */,
158 | 194EDC4E220AB84C00B4EE26 /* ColorSpaceConversion.swift */,
159 | 723DE5381A49353C00C357E3 /* Supporting Files */,
160 | );
161 | name = Shared;
162 | path = DominantColor/Shared;
163 | sourceTree = "";
164 | };
165 | 723DE5381A49353C00C357E3 /* Supporting Files */ = {
166 | isa = PBXGroup;
167 | children = (
168 | 723DE5561A49357D00C357E3 /* Info.plist */,
169 | );
170 | name = "Supporting Files";
171 | path = ..;
172 | sourceTree = "";
173 | };
174 | 72B92ADA1A44CCCD00A2C24C /* Frameworks */ = {
175 | isa = PBXGroup;
176 | children = (
177 | 723DE5BE1A49394D00C357E3 /* GLKit.framework */,
178 | 72D797DE1A43F89000D32E7C /* GLKit.framework */,
179 | );
180 | name = Frameworks;
181 | path = DominantColor;
182 | sourceTree = "";
183 | };
184 | 72D797AF1A43F44D00D32E7C = {
185 | isa = PBXGroup;
186 | children = (
187 | 723DE5131A49348D00C357E3 /* Shared */,
188 | 72D797BA1A43F44D00D32E7C /* Mac */,
189 | 898845D31A490CE000003EF2 /* iOS */,
190 | 72B92ADA1A44CCCD00A2C24C /* Frameworks */,
191 | 72D797B91A43F44D00D32E7C /* Products */,
192 | );
193 | sourceTree = "";
194 | };
195 | 72D797B91A43F44D00D32E7C /* Products */ = {
196 | isa = PBXGroup;
197 | children = (
198 | 72D797B81A43F44D00D32E7C /* ExampleMac.app */,
199 | 898845D21A490CE000003EF2 /* ExampleiOS.app */,
200 | 723DE5361A49353C00C357E3 /* DominantColor.framework */,
201 | 723DE59F1A4938DD00C357E3 /* DominantColor.framework */,
202 | );
203 | name = Products;
204 | sourceTree = "";
205 | };
206 | 72D797BA1A43F44D00D32E7C /* Mac */ = {
207 | isa = PBXGroup;
208 | children = (
209 | 723DE5261A4934E200C357E3 /* AppDelegate.swift */,
210 | 723DE5271A4934E200C357E3 /* DragAndDropImageView.swift */,
211 | 723DE5641A4937AD00C357E3 /* ExampleMac-Bridging-Header.h */,
212 | 723DE5281A4934E200C357E3 /* Images.xcassets */,
213 | 723DE52C1A4934F800C357E3 /* MainMenu.xib */,
214 | 72D797BB1A43F44D00D32E7C /* Supporting Files */,
215 | );
216 | name = Mac;
217 | path = DominantColor;
218 | sourceTree = "";
219 | };
220 | 72D797BB1A43F44D00D32E7C /* Supporting Files */ = {
221 | isa = PBXGroup;
222 | children = (
223 | 723DE52F1A49350000C357E3 /* Info.plist */,
224 | );
225 | name = "Supporting Files";
226 | sourceTree = "";
227 | };
228 | 898845D31A490CE000003EF2 /* iOS */ = {
229 | isa = PBXGroup;
230 | children = (
231 | 723DE5661A49385C00C357E3 /* AppDelegate.swift */,
232 | 723DE5681A49385C00C357E3 /* ViewController.swift */,
233 | 723DE5741A49388400C357E3 /* ExampleiOS-Bridging-Header.h */,
234 | 723DE5671A49385C00C357E3 /* Images.xcassets */,
235 | 723DE56C1A49386B00C357E3 /* LaunchScreen.xib */,
236 | 723DE56E1A49386B00C357E3 /* Main.storyboard */,
237 | 898845D41A490CE000003EF2 /* Supporting Files */,
238 | );
239 | name = iOS;
240 | path = "DominantColor-iOS";
241 | sourceTree = "";
242 | };
243 | 898845D41A490CE000003EF2 /* Supporting Files */ = {
244 | isa = PBXGroup;
245 | children = (
246 | 723DE5721A49387800C357E3 /* Info.plist */,
247 | );
248 | name = "Supporting Files";
249 | sourceTree = "";
250 | };
251 | /* End PBXGroup section */
252 |
253 | /* Begin PBXHeadersBuildPhase section */
254 | 723DE5331A49353C00C357E3 /* Headers */ = {
255 | isa = PBXHeadersBuildPhase;
256 | buildActionMask = 2147483647;
257 | files = (
258 | 723DE5591A49358F00C357E3 /* DominantColor.h in Headers */,
259 | );
260 | runOnlyForDeploymentPostprocessing = 0;
261 | };
262 | 723DE59C1A4938DD00C357E3 /* Headers */ = {
263 | isa = PBXHeadersBuildPhase;
264 | buildActionMask = 2147483647;
265 | files = (
266 | 723DE5C01A49395A00C357E3 /* DominantColor.h in Headers */,
267 | );
268 | runOnlyForDeploymentPostprocessing = 0;
269 | };
270 | /* End PBXHeadersBuildPhase section */
271 |
272 | /* Begin PBXNativeTarget section */
273 | 723DE5351A49353C00C357E3 /* DominantColor-Mac */ = {
274 | isa = PBXNativeTarget;
275 | buildConfigurationList = 723DE54F1A49353C00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-Mac" */;
276 | buildPhases = (
277 | 723DE5311A49353C00C357E3 /* Sources */,
278 | 723DE5321A49353C00C357E3 /* Frameworks */,
279 | 723DE5331A49353C00C357E3 /* Headers */,
280 | 723DE5341A49353C00C357E3 /* Resources */,
281 | );
282 | buildRules = (
283 | );
284 | dependencies = (
285 | );
286 | name = "DominantColor-Mac";
287 | productName = DominantColor;
288 | productReference = 723DE5361A49353C00C357E3 /* DominantColor.framework */;
289 | productType = "com.apple.product-type.framework";
290 | };
291 | 723DE59E1A4938DD00C357E3 /* DominantColor-iOS */ = {
292 | isa = PBXNativeTarget;
293 | buildConfigurationList = 723DE5B81A4938DD00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-iOS" */;
294 | buildPhases = (
295 | 723DE59A1A4938DD00C357E3 /* Sources */,
296 | 723DE59B1A4938DD00C357E3 /* Frameworks */,
297 | 723DE59C1A4938DD00C357E3 /* Headers */,
298 | 723DE59D1A4938DD00C357E3 /* Resources */,
299 | );
300 | buildRules = (
301 | );
302 | dependencies = (
303 | );
304 | name = "DominantColor-iOS";
305 | productName = DominantColor;
306 | productReference = 723DE59F1A4938DD00C357E3 /* DominantColor.framework */;
307 | productType = "com.apple.product-type.framework";
308 | };
309 | 72D797B71A43F44D00D32E7C /* ExampleMac */ = {
310 | isa = PBXNativeTarget;
311 | buildConfigurationList = 72D797D21A43F44D00D32E7C /* Build configuration list for PBXNativeTarget "ExampleMac" */;
312 | buildPhases = (
313 | 72D797B41A43F44D00D32E7C /* Sources */,
314 | 72D797B51A43F44D00D32E7C /* Frameworks */,
315 | 72D797B61A43F44D00D32E7C /* Resources */,
316 | 723DE5521A49353C00C357E3 /* Embed Frameworks */,
317 | );
318 | buildRules = (
319 | );
320 | dependencies = (
321 | 723DE54C1A49353C00C357E3 /* PBXTargetDependency */,
322 | );
323 | name = ExampleMac;
324 | productName = DominantColor;
325 | productReference = 72D797B81A43F44D00D32E7C /* ExampleMac.app */;
326 | productType = "com.apple.product-type.application";
327 | };
328 | 898845D11A490CE000003EF2 /* ExampleiOS */ = {
329 | isa = PBXNativeTarget;
330 | buildConfigurationList = 898845F21A490CE000003EF2 /* Build configuration list for PBXNativeTarget "ExampleiOS" */;
331 | buildPhases = (
332 | 898845CE1A490CE000003EF2 /* Sources */,
333 | 898845CF1A490CE000003EF2 /* Frameworks */,
334 | 898845D01A490CE000003EF2 /* Resources */,
335 | 723DE5921A4938B500C357E3 /* Embed Frameworks */,
336 | );
337 | buildRules = (
338 | );
339 | dependencies = (
340 | 723DE5B51A4938DD00C357E3 /* PBXTargetDependency */,
341 | );
342 | name = ExampleiOS;
343 | productName = "DominantColor-iOS";
344 | productReference = 898845D21A490CE000003EF2 /* ExampleiOS.app */;
345 | productType = "com.apple.product-type.application";
346 | };
347 | /* End PBXNativeTarget section */
348 |
349 | /* Begin PBXProject section */
350 | 72D797B01A43F44D00D32E7C /* Project object */ = {
351 | isa = PBXProject;
352 | attributes = {
353 | LastSwiftMigration = 0700;
354 | LastSwiftUpdateCheck = 0700;
355 | LastUpgradeCheck = 1170;
356 | ORGANIZATIONNAME = "Indragie Karunaratne";
357 | TargetAttributes = {
358 | 723DE5351A49353C00C357E3 = {
359 | CreatedOnToolsVersion = 6.1.1;
360 | LastSwiftMigration = 1170;
361 | };
362 | 723DE59E1A4938DD00C357E3 = {
363 | CreatedOnToolsVersion = 6.1.1;
364 | LastSwiftMigration = 1170;
365 | };
366 | 72D797B71A43F44D00D32E7C = {
367 | CreatedOnToolsVersion = 6.1.1;
368 | LastSwiftMigration = 1170;
369 | };
370 | 898845D11A490CE000003EF2 = {
371 | CreatedOnToolsVersion = 6.1;
372 | LastSwiftMigration = 1170;
373 | };
374 | };
375 | };
376 | buildConfigurationList = 72D797B31A43F44D00D32E7C /* Build configuration list for PBXProject "DominantColor" */;
377 | compatibilityVersion = "Xcode 3.2";
378 | developmentRegion = en;
379 | hasScannedForEncodings = 0;
380 | knownRegions = (
381 | en,
382 | Base,
383 | );
384 | mainGroup = 72D797AF1A43F44D00D32E7C;
385 | productRefGroup = 72D797B91A43F44D00D32E7C /* Products */;
386 | projectDirPath = "";
387 | projectRoot = "";
388 | targets = (
389 | 72D797B71A43F44D00D32E7C /* ExampleMac */,
390 | 898845D11A490CE000003EF2 /* ExampleiOS */,
391 | 723DE5351A49353C00C357E3 /* DominantColor-Mac */,
392 | 723DE59E1A4938DD00C357E3 /* DominantColor-iOS */,
393 | );
394 | };
395 | /* End PBXProject section */
396 |
397 | /* Begin PBXResourcesBuildPhase section */
398 | 723DE5341A49353C00C357E3 /* Resources */ = {
399 | isa = PBXResourcesBuildPhase;
400 | buildActionMask = 2147483647;
401 | files = (
402 | );
403 | runOnlyForDeploymentPostprocessing = 0;
404 | };
405 | 723DE59D1A4938DD00C357E3 /* Resources */ = {
406 | isa = PBXResourcesBuildPhase;
407 | buildActionMask = 2147483647;
408 | files = (
409 | );
410 | runOnlyForDeploymentPostprocessing = 0;
411 | };
412 | 72D797B61A43F44D00D32E7C /* Resources */ = {
413 | isa = PBXResourcesBuildPhase;
414 | buildActionMask = 2147483647;
415 | files = (
416 | 723DE52B1A4934E200C357E3 /* Images.xcassets in Resources */,
417 | 723DE52E1A4934F800C357E3 /* MainMenu.xib in Resources */,
418 | );
419 | runOnlyForDeploymentPostprocessing = 0;
420 | };
421 | 898845D01A490CE000003EF2 /* Resources */ = {
422 | isa = PBXResourcesBuildPhase;
423 | buildActionMask = 2147483647;
424 | files = (
425 | 723DE5711A49386B00C357E3 /* Main.storyboard in Resources */,
426 | 723DE5701A49386B00C357E3 /* LaunchScreen.xib in Resources */,
427 | 723DE56A1A49385C00C357E3 /* Images.xcassets in Resources */,
428 | );
429 | runOnlyForDeploymentPostprocessing = 0;
430 | };
431 | /* End PBXResourcesBuildPhase section */
432 |
433 | /* Begin PBXSourcesBuildPhase section */
434 | 723DE5311A49353C00C357E3 /* Sources */ = {
435 | isa = PBXSourcesBuildPhase;
436 | buildActionMask = 2147483647;
437 | files = (
438 | 724C2DCD1A4CD64600472402 /* PlatformExtensions.swift in Sources */,
439 | 2F3D09861A4C212A001ED0BF /* Memoization.swift in Sources */,
440 | 194EDC4F220AB84C00B4EE26 /* ColorSpaceConversion.swift in Sources */,
441 | 723DE55C1A49360F00C357E3 /* ColorDifference.swift in Sources */,
442 | 72431C0F1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */,
443 | 723DE55D1A49361100C357E3 /* DominantColors.swift in Sources */,
444 | 723DE55E1A49361200C357E3 /* KMeans.swift in Sources */,
445 | );
446 | runOnlyForDeploymentPostprocessing = 0;
447 | };
448 | 723DE59A1A4938DD00C357E3 /* Sources */ = {
449 | isa = PBXSourcesBuildPhase;
450 | buildActionMask = 2147483647;
451 | files = (
452 | 724C2DCE1A4CD64600472402 /* PlatformExtensions.swift in Sources */,
453 | 2F3D09871A4C212A001ED0BF /* Memoization.swift in Sources */,
454 | 194EDC50220AB84C00B4EE26 /* ColorSpaceConversion.swift in Sources */,
455 | 723DE5C31A49399E00C357E3 /* ColorDifference.swift in Sources */,
456 | 72431C101A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */,
457 | 723DE5C41A4939A000C357E3 /* DominantColors.swift in Sources */,
458 | 723DE5C51A4939A200C357E3 /* KMeans.swift in Sources */,
459 | );
460 | runOnlyForDeploymentPostprocessing = 0;
461 | };
462 | 72D797B41A43F44D00D32E7C /* Sources */ = {
463 | isa = PBXSourcesBuildPhase;
464 | buildActionMask = 2147483647;
465 | files = (
466 | 723DE5291A4934E200C357E3 /* AppDelegate.swift in Sources */,
467 | 723DE52A1A4934E200C357E3 /* DragAndDropImageView.swift in Sources */,
468 | );
469 | runOnlyForDeploymentPostprocessing = 0;
470 | };
471 | 898845CE1A490CE000003EF2 /* Sources */ = {
472 | isa = PBXSourcesBuildPhase;
473 | buildActionMask = 2147483647;
474 | files = (
475 | 723DE56B1A49385C00C357E3 /* ViewController.swift in Sources */,
476 | 723DE5691A49385C00C357E3 /* AppDelegate.swift in Sources */,
477 | );
478 | runOnlyForDeploymentPostprocessing = 0;
479 | };
480 | /* End PBXSourcesBuildPhase section */
481 |
482 | /* Begin PBXTargetDependency section */
483 | 723DE54C1A49353C00C357E3 /* PBXTargetDependency */ = {
484 | isa = PBXTargetDependency;
485 | target = 723DE5351A49353C00C357E3 /* DominantColor-Mac */;
486 | targetProxy = 723DE54B1A49353C00C357E3 /* PBXContainerItemProxy */;
487 | };
488 | 723DE5B51A4938DD00C357E3 /* PBXTargetDependency */ = {
489 | isa = PBXTargetDependency;
490 | target = 723DE59E1A4938DD00C357E3 /* DominantColor-iOS */;
491 | targetProxy = 723DE5B41A4938DD00C357E3 /* PBXContainerItemProxy */;
492 | };
493 | /* End PBXTargetDependency section */
494 |
495 | /* Begin PBXVariantGroup section */
496 | 723DE52C1A4934F800C357E3 /* MainMenu.xib */ = {
497 | isa = PBXVariantGroup;
498 | children = (
499 | 723DE52D1A4934F800C357E3 /* Base */,
500 | );
501 | name = MainMenu.xib;
502 | sourceTree = "";
503 | };
504 | 723DE56C1A49386B00C357E3 /* LaunchScreen.xib */ = {
505 | isa = PBXVariantGroup;
506 | children = (
507 | 723DE56D1A49386B00C357E3 /* Base */,
508 | );
509 | name = LaunchScreen.xib;
510 | sourceTree = "";
511 | };
512 | 723DE56E1A49386B00C357E3 /* Main.storyboard */ = {
513 | isa = PBXVariantGroup;
514 | children = (
515 | 723DE56F1A49386B00C357E3 /* Base */,
516 | );
517 | name = Main.storyboard;
518 | sourceTree = "";
519 | };
520 | /* End PBXVariantGroup section */
521 |
522 | /* Begin XCBuildConfiguration section */
523 | 723DE5501A49353C00C357E3 /* Debug */ = {
524 | isa = XCBuildConfiguration;
525 | buildSettings = {
526 | CODE_SIGN_IDENTITY = "";
527 | COMBINE_HIDPI_IMAGES = YES;
528 | CURRENT_PROJECT_VERSION = 2;
529 | DEFINES_MODULE = YES;
530 | DYLIB_COMPATIBILITY_VERSION = 1;
531 | DYLIB_CURRENT_VERSION = 1;
532 | DYLIB_INSTALL_NAME_BASE = "@rpath";
533 | FRAMEWORK_VERSION = A;
534 | GCC_PREPROCESSOR_DEFINITIONS = (
535 | "DEBUG=1",
536 | "$(inherited)",
537 | );
538 | INFOPLIST_FILE = DominantColor/Shared/Info.plist;
539 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
540 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
541 | MACOSX_DEPLOYMENT_TARGET = 10.11;
542 | MARKETING_VERSION = 0.2.0;
543 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
544 | PRODUCT_NAME = DominantColor;
545 | SKIP_INSTALL = YES;
546 | SWIFT_OBJC_BRIDGING_HEADER = "";
547 | SWIFT_VERSION = 5.0;
548 | VERSIONING_SYSTEM = "apple-generic";
549 | VERSION_INFO_PREFIX = "";
550 | };
551 | name = Debug;
552 | };
553 | 723DE5511A49353C00C357E3 /* Release */ = {
554 | isa = XCBuildConfiguration;
555 | buildSettings = {
556 | CODE_SIGN_IDENTITY = "";
557 | COMBINE_HIDPI_IMAGES = YES;
558 | CURRENT_PROJECT_VERSION = 2;
559 | DEFINES_MODULE = YES;
560 | DYLIB_COMPATIBILITY_VERSION = 1;
561 | DYLIB_CURRENT_VERSION = 1;
562 | DYLIB_INSTALL_NAME_BASE = "@rpath";
563 | FRAMEWORK_VERSION = A;
564 | INFOPLIST_FILE = DominantColor/Shared/Info.plist;
565 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
566 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
567 | MACOSX_DEPLOYMENT_TARGET = 10.11;
568 | MARKETING_VERSION = 0.2.0;
569 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
570 | PRODUCT_NAME = DominantColor;
571 | SKIP_INSTALL = YES;
572 | SWIFT_OBJC_BRIDGING_HEADER = "";
573 | SWIFT_VERSION = 5.0;
574 | VERSIONING_SYSTEM = "apple-generic";
575 | VERSION_INFO_PREFIX = "";
576 | };
577 | name = Release;
578 | };
579 | 723DE5B91A4938DD00C357E3 /* Debug */ = {
580 | isa = XCBuildConfiguration;
581 | buildSettings = {
582 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
583 | CURRENT_PROJECT_VERSION = 2;
584 | DEFINES_MODULE = YES;
585 | DYLIB_COMPATIBILITY_VERSION = 1;
586 | DYLIB_CURRENT_VERSION = 1;
587 | DYLIB_INSTALL_NAME_BASE = "@rpath";
588 | GCC_PREPROCESSOR_DEFINITIONS = (
589 | "DEBUG=1",
590 | "$(inherited)",
591 | );
592 | INFOPLIST_FILE = DominantColor/iOS/Info.plist;
593 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
594 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
595 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
596 | MARKETING_VERSION = 0.2.0;
597 | PRODUCT_BUNDLE_IDENTIFIER = "com.jamalDesigns.$(PRODUCT_NAME:rfc1034identifier)";
598 | PRODUCT_NAME = DominantColor;
599 | SDKROOT = iphoneos;
600 | SKIP_INSTALL = YES;
601 | SWIFT_VERSION = 5.0;
602 | TARGETED_DEVICE_FAMILY = "1,2";
603 | VERSIONING_SYSTEM = "apple-generic";
604 | VERSION_INFO_PREFIX = "";
605 | };
606 | name = Debug;
607 | };
608 | 723DE5BA1A4938DD00C357E3 /* Release */ = {
609 | isa = XCBuildConfiguration;
610 | buildSettings = {
611 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
612 | CURRENT_PROJECT_VERSION = 2;
613 | DEFINES_MODULE = YES;
614 | DYLIB_COMPATIBILITY_VERSION = 1;
615 | DYLIB_CURRENT_VERSION = 1;
616 | DYLIB_INSTALL_NAME_BASE = "@rpath";
617 | INFOPLIST_FILE = DominantColor/iOS/Info.plist;
618 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
619 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
620 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
621 | MARKETING_VERSION = 0.2.0;
622 | PRODUCT_BUNDLE_IDENTIFIER = "com.jamalDesigns.$(PRODUCT_NAME:rfc1034identifier)";
623 | PRODUCT_NAME = DominantColor;
624 | SDKROOT = iphoneos;
625 | SKIP_INSTALL = YES;
626 | SWIFT_VERSION = 5.0;
627 | TARGETED_DEVICE_FAMILY = "1,2";
628 | VALIDATE_PRODUCT = YES;
629 | VERSIONING_SYSTEM = "apple-generic";
630 | VERSION_INFO_PREFIX = "";
631 | };
632 | name = Release;
633 | };
634 | 72D797D01A43F44D00D32E7C /* Debug */ = {
635 | isa = XCBuildConfiguration;
636 | buildSettings = {
637 | ALWAYS_SEARCH_USER_PATHS = NO;
638 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
639 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
640 | CLANG_CXX_LIBRARY = "libc++";
641 | CLANG_ENABLE_MODULES = YES;
642 | CLANG_ENABLE_OBJC_ARC = YES;
643 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
644 | CLANG_WARN_BOOL_CONVERSION = YES;
645 | CLANG_WARN_COMMA = YES;
646 | CLANG_WARN_CONSTANT_CONVERSION = YES;
647 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
648 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
649 | CLANG_WARN_EMPTY_BODY = YES;
650 | CLANG_WARN_ENUM_CONVERSION = YES;
651 | CLANG_WARN_INFINITE_RECURSION = YES;
652 | CLANG_WARN_INT_CONVERSION = YES;
653 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
654 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
655 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
656 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
657 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
658 | CLANG_WARN_STRICT_PROTOTYPES = YES;
659 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
660 | CLANG_WARN_UNREACHABLE_CODE = YES;
661 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
662 | CODE_SIGN_IDENTITY = "-";
663 | COPY_PHASE_STRIP = NO;
664 | ENABLE_STRICT_OBJC_MSGSEND = YES;
665 | ENABLE_TESTABILITY = YES;
666 | GCC_C_LANGUAGE_STANDARD = gnu99;
667 | GCC_DYNAMIC_NO_PIC = NO;
668 | GCC_NO_COMMON_BLOCKS = YES;
669 | GCC_OPTIMIZATION_LEVEL = 0;
670 | GCC_PREPROCESSOR_DEFINITIONS = (
671 | "DEBUG=1",
672 | "$(inherited)",
673 | );
674 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
675 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
676 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
677 | GCC_WARN_UNDECLARED_SELECTOR = YES;
678 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
679 | GCC_WARN_UNUSED_FUNCTION = YES;
680 | GCC_WARN_UNUSED_VARIABLE = YES;
681 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
682 | MACOSX_DEPLOYMENT_TARGET = 10.11;
683 | MTL_ENABLE_DEBUG_INFO = YES;
684 | ONLY_ACTIVE_ARCH = YES;
685 | SDKROOT = macosx;
686 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
687 | SWIFT_VERSION = 4.0;
688 | };
689 | name = Debug;
690 | };
691 | 72D797D11A43F44D00D32E7C /* Release */ = {
692 | isa = XCBuildConfiguration;
693 | buildSettings = {
694 | ALWAYS_SEARCH_USER_PATHS = NO;
695 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
696 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
697 | CLANG_CXX_LIBRARY = "libc++";
698 | CLANG_ENABLE_MODULES = YES;
699 | CLANG_ENABLE_OBJC_ARC = YES;
700 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
701 | CLANG_WARN_BOOL_CONVERSION = YES;
702 | CLANG_WARN_COMMA = YES;
703 | CLANG_WARN_CONSTANT_CONVERSION = YES;
704 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
705 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
706 | CLANG_WARN_EMPTY_BODY = YES;
707 | CLANG_WARN_ENUM_CONVERSION = YES;
708 | CLANG_WARN_INFINITE_RECURSION = YES;
709 | CLANG_WARN_INT_CONVERSION = YES;
710 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
711 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
712 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
713 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
714 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
715 | CLANG_WARN_STRICT_PROTOTYPES = YES;
716 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
717 | CLANG_WARN_UNREACHABLE_CODE = YES;
718 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
719 | CODE_SIGN_IDENTITY = "-";
720 | COPY_PHASE_STRIP = NO;
721 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
722 | ENABLE_NS_ASSERTIONS = NO;
723 | ENABLE_STRICT_OBJC_MSGSEND = YES;
724 | GCC_C_LANGUAGE_STANDARD = gnu99;
725 | GCC_NO_COMMON_BLOCKS = YES;
726 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
727 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
728 | GCC_WARN_UNDECLARED_SELECTOR = YES;
729 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
730 | GCC_WARN_UNUSED_FUNCTION = YES;
731 | GCC_WARN_UNUSED_VARIABLE = YES;
732 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
733 | MACOSX_DEPLOYMENT_TARGET = 10.11;
734 | MTL_ENABLE_DEBUG_INFO = NO;
735 | SDKROOT = macosx;
736 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
737 | SWIFT_VERSION = 4.0;
738 | };
739 | name = Release;
740 | };
741 | 72D797D31A43F44D00D32E7C /* Debug */ = {
742 | isa = XCBuildConfiguration;
743 | buildSettings = {
744 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
745 | CLANG_ENABLE_MODULES = YES;
746 | CODE_SIGN_IDENTITY = "-";
747 | COMBINE_HIDPI_IMAGES = YES;
748 | CURRENT_PROJECT_VERSION = 2;
749 | HEADER_SEARCH_PATHS = (
750 | "$(inherited)",
751 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
752 | );
753 | INFOPLIST_FILE = DominantColor/Mac/Info.plist;
754 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
755 | MACOSX_DEPLOYMENT_TARGET = 10.11;
756 | MARKETING_VERSION = 0.2.0;
757 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
758 | PRODUCT_NAME = "$(TARGET_NAME)";
759 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/Mac/ExampleMac-Bridging-Header.h";
760 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
761 | SWIFT_VERSION = 5.0;
762 | };
763 | name = Debug;
764 | };
765 | 72D797D41A43F44D00D32E7C /* Release */ = {
766 | isa = XCBuildConfiguration;
767 | buildSettings = {
768 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
769 | CLANG_ENABLE_MODULES = YES;
770 | CODE_SIGN_IDENTITY = "-";
771 | COMBINE_HIDPI_IMAGES = YES;
772 | CURRENT_PROJECT_VERSION = 2;
773 | HEADER_SEARCH_PATHS = (
774 | "$(inherited)",
775 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
776 | );
777 | INFOPLIST_FILE = DominantColor/Mac/Info.plist;
778 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
779 | MACOSX_DEPLOYMENT_TARGET = 10.11;
780 | MARKETING_VERSION = 0.2.0;
781 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
782 | PRODUCT_NAME = "$(TARGET_NAME)";
783 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/Mac/ExampleMac-Bridging-Header.h";
784 | SWIFT_VERSION = 5.0;
785 | };
786 | name = Release;
787 | };
788 | 898845EE1A490CE000003EF2 /* Debug */ = {
789 | isa = XCBuildConfiguration;
790 | buildSettings = {
791 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
792 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
793 | CURRENT_PROJECT_VERSION = 2;
794 | GCC_PREPROCESSOR_DEFINITIONS = (
795 | "DEBUG=1",
796 | "$(inherited)",
797 | );
798 | INFOPLIST_FILE = DominantColor/iOS/Info.plist;
799 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
800 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
801 | MARKETING_VERSION = 0.2.0;
802 | PRODUCT_BUNDLE_IDENTIFIER = "com.jamalDesigns.$(PRODUCT_NAME:rfc1034identifier)";
803 | PRODUCT_NAME = "$(TARGET_NAME)";
804 | SDKROOT = iphoneos;
805 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/iOS/ExampleiOS-Bridging-Header.h";
806 | SWIFT_VERSION = 5.0;
807 | };
808 | name = Debug;
809 | };
810 | 898845EF1A490CE000003EF2 /* Release */ = {
811 | isa = XCBuildConfiguration;
812 | buildSettings = {
813 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
814 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
815 | CURRENT_PROJECT_VERSION = 2;
816 | INFOPLIST_FILE = DominantColor/iOS/Info.plist;
817 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
818 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
819 | MARKETING_VERSION = 0.2.0;
820 | PRODUCT_BUNDLE_IDENTIFIER = "com.jamalDesigns.$(PRODUCT_NAME:rfc1034identifier)";
821 | PRODUCT_NAME = "$(TARGET_NAME)";
822 | SDKROOT = iphoneos;
823 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/iOS/ExampleiOS-Bridging-Header.h";
824 | SWIFT_VERSION = 5.0;
825 | VALIDATE_PRODUCT = YES;
826 | };
827 | name = Release;
828 | };
829 | /* End XCBuildConfiguration section */
830 |
831 | /* Begin XCConfigurationList section */
832 | 723DE54F1A49353C00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-Mac" */ = {
833 | isa = XCConfigurationList;
834 | buildConfigurations = (
835 | 723DE5501A49353C00C357E3 /* Debug */,
836 | 723DE5511A49353C00C357E3 /* Release */,
837 | );
838 | defaultConfigurationIsVisible = 0;
839 | defaultConfigurationName = Release;
840 | };
841 | 723DE5B81A4938DD00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-iOS" */ = {
842 | isa = XCConfigurationList;
843 | buildConfigurations = (
844 | 723DE5B91A4938DD00C357E3 /* Debug */,
845 | 723DE5BA1A4938DD00C357E3 /* Release */,
846 | );
847 | defaultConfigurationIsVisible = 0;
848 | defaultConfigurationName = Release;
849 | };
850 | 72D797B31A43F44D00D32E7C /* Build configuration list for PBXProject "DominantColor" */ = {
851 | isa = XCConfigurationList;
852 | buildConfigurations = (
853 | 72D797D01A43F44D00D32E7C /* Debug */,
854 | 72D797D11A43F44D00D32E7C /* Release */,
855 | );
856 | defaultConfigurationIsVisible = 0;
857 | defaultConfigurationName = Release;
858 | };
859 | 72D797D21A43F44D00D32E7C /* Build configuration list for PBXNativeTarget "ExampleMac" */ = {
860 | isa = XCConfigurationList;
861 | buildConfigurations = (
862 | 72D797D31A43F44D00D32E7C /* Debug */,
863 | 72D797D41A43F44D00D32E7C /* Release */,
864 | );
865 | defaultConfigurationIsVisible = 0;
866 | defaultConfigurationName = Release;
867 | };
868 | 898845F21A490CE000003EF2 /* Build configuration list for PBXNativeTarget "ExampleiOS" */ = {
869 | isa = XCConfigurationList;
870 | buildConfigurations = (
871 | 898845EE1A490CE000003EF2 /* Debug */,
872 | 898845EF1A490CE000003EF2 /* Release */,
873 | );
874 | defaultConfigurationIsVisible = 0;
875 | defaultConfigurationName = Release;
876 | };
877 | /* End XCConfigurationList section */
878 | };
879 | rootObject = 72D797B01A43F44D00D32E7C /* Project object */;
880 | }
881 |
--------------------------------------------------------------------------------
/DominantColor/Mac/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
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 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
--------------------------------------------------------------------------------