├── 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 │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── AppDelegate.swift │ ├── ViewController.swift │ └── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard └── Shared │ ├── ColorSpaceConversion.h │ ├── INVector3.m │ ├── INVector3.h │ ├── Memoization.swift │ ├── DominantColor.h │ ├── INVector3SwiftExtensions.swift │ ├── Info.plist │ ├── KMeans.swift │ ├── ColorDifference.swift │ ├── PlatformExtensions.swift │ ├── ColorSpaceConversion.m │ └── DominantColors.swift ├── DominantColor.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ ├── DominantColor-Mac.xcscheme │ │ ├── DominantColor-iOS.xcscheme │ │ ├── ExampleMac.xcscheme │ │ └── ExampleiOS.xcscheme └── project.pbxproj ├── .gitignore ├── DominantColor.podspec ├── LICENSE └── README.md /ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/DominantColor/HEAD/ios.png -------------------------------------------------------------------------------- /mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objcio/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /DominantColor/Shared/ColorSpaceConversion.h: -------------------------------------------------------------------------------- 1 | // 2 | // ColorSpaceConversion.h 3 | // DominantColor 4 | // 5 | // Created by Indragie on 12/21/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "INVector3.h" 10 | 11 | // Simple RGB <-> LAB conversion functions assuming a D65 illuminant 12 | // with the standard 2° observer for CIE 1931. 13 | 14 | INVector3 IN_RGBToLAB(INVector3 rgbVector); 15 | INVector3 IN_LABToRGB(INVector3 labVector); 16 | -------------------------------------------------------------------------------- /DominantColor/Shared/INVector3.m: -------------------------------------------------------------------------------- 1 | // 2 | // INVector3.c 3 | // DominantColor 4 | // 5 | // Created by Indragie on 12/21/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "INVector3.h" 10 | 11 | GLKVector3 INVector3ToGLKVector3(INVector3 vector) { 12 | return GLKVector3Make(vector.x, vector.y, vector.z); 13 | } 14 | 15 | INVector3 GLKVector3ToINVector3(GLKVector3 vector) { 16 | return (INVector3){ vector.x, vector.y, vector.z }; 17 | } 18 | -------------------------------------------------------------------------------- /DominantColor/Shared/INVector3.h: -------------------------------------------------------------------------------- 1 | // 2 | // INVector3.h 3 | // DominantColor 4 | // 5 | // Created by Indragie on 12/21/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // Wrapping GLKVector3 values in a struct so that it can be used from Swift. 12 | 13 | typedef struct { 14 | float x; 15 | float y; 16 | float z; 17 | } INVector3; 18 | 19 | GLKVector3 INVector3ToGLKVector3(INVector3 vector); 20 | INVector3 GLKVector3ToINVector3(GLKVector3 vector); 21 | -------------------------------------------------------------------------------- /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: 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 | -------------------------------------------------------------------------------- /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 | 17 | #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /DominantColor.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'DominantColor' 3 | spec.version = '0.1.0' 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 = 'GLKit' 12 | spec.ios.deployment_target = '8.0' 13 | spec.osx.deployment_target = '10.9' 14 | spec.ios.frameworks = 'UIKit' 15 | spec.osx.frameworks = 'Cocoa' 16 | end -------------------------------------------------------------------------------- /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 | extension INVector3 { 10 | func unpack() -> (Float, Float, Float) { 11 | return (x, y, z) 12 | } 13 | 14 | static var identity: INVector3 { 15 | return INVector3(x: 0, y: 0, z: 0) 16 | } 17 | } 18 | 19 | func +(lhs: INVector3, rhs: INVector3) -> INVector3 { 20 | return INVector3(x: lhs.x + rhs.x, y: lhs.y + rhs.y, z: lhs.z + rhs.z) 21 | } 22 | 23 | func /(lhs: INVector3, rhs: Float) -> INVector3 { 24 | return INVector3(x: lhs.x / rhs, y: lhs.y / rhs, z: lhs.z / rhs) 25 | } 26 | 27 | func /(lhs: INVector3, rhs: Int) -> INVector3 { 28 | return lhs / Float(rhs) 29 | } 30 | -------------------------------------------------------------------------------- /DominantColor/Shared/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.indragie.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 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 | com.indragie.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | 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 | com.jamalDesigns.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /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 | required init?(coder: NSCoder) { 19 | super.init(coder: coder) 20 | registerForDraggedTypes([NSFilenamesPboardType, NSTIFFPboardType]) 21 | } 22 | 23 | // MARK: NSDraggingDestination 24 | 25 | override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation { 26 | let pasteboard = sender.draggingPasteboard() 27 | if let data = pasteboard.dataForType(NSTIFFPboardType) { 28 | return .Copy 29 | } else if let files = pasteboard.propertyListForType(NSFilenamesPboardType) as? [String] { 30 | let UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, files[0].pathExtension, nil).takeRetainedValue() 31 | return (UTTypeConformsTo(UTI, kUTTypeImage) == 1) ? .Copy : .None 32 | } 33 | return .None 34 | } 35 | 36 | override func performDragOperation(sender: NSDraggingInfo) -> Bool { 37 | let pasteboard = sender.draggingPasteboard() 38 | if let data = pasteboard.dataForType(NSTIFFPboardType) { 39 | self.delegate?.dragAndDropImageView(self, droppedImage: NSImage(data: data)) 40 | } else if let files = pasteboard.propertyListForType(NSFilenamesPboardType) as? [String] { 41 | self.delegate?.dragAndDropImageView(self, droppedImage: NSImage(contentsOfFile: files[0])) 42 | } 43 | return true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /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 | imageView.delegate = self 28 | } 29 | 30 | // MARK: DragAndDropImageViewDelegate 31 | 32 | @IBAction func runBenchmark(sender: NSButton) { 33 | if let image = image { 34 | let nValues: [Int] = [100, 1000, 2000, 5000, 10000] 35 | let CGImage = image.CGImageForProposedRect(nil, context: nil, hints: nil)!.takeUnretainedValue() 36 | for n in nValues { 37 | let ns = dispatch_benchmark(5) { 38 | dominantColorsInImage(CGImage, maxSampledPixels: n) 39 | return 40 | } 41 | println("n = \(n) averaged \(ns/1000000) ms") 42 | } 43 | } 44 | } 45 | 46 | func dragAndDropImageView(imageView: DragAndDropImageView, droppedImage image: NSImage?) { 47 | if let image = image { 48 | imageView.image = image 49 | 50 | self.image = image 51 | let colors = image.dominantColors() 52 | let boxes = [box1, box2, box3, box4, box5, box6] 53 | 54 | for box in boxes { 55 | box.fillColor = NSColor.clearColor() 56 | } 57 | for i in 0.. Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /DominantColor/iOS/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Dominant Color 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 | import DominantColor 11 | 12 | class ViewController: UIViewController , UIImagePickerControllerDelegate, UINavigationControllerDelegate { 13 | 14 | @IBOutlet var boxes: [UIView]! 15 | @IBOutlet weak var imageView: UIImageView! 16 | 17 | var image: UIImage! 18 | 19 | // MARK: IBActions 20 | 21 | @IBAction func selectTapped(sender: AnyObject) { 22 | let imagePicker = UIImagePickerController() 23 | imagePicker.delegate = self 24 | imagePicker.sourceType = .PhotoLibrary 25 | self.presentViewController(imagePicker, animated: true, completion: nil) 26 | } 27 | 28 | @IBAction func runBenchmarkTapped(sender: AnyObject) { 29 | if let image = image { 30 | let nValues: [Int] = [100, 1000, 2000, 5000, 10000] 31 | let CGImage = image.CGImage 32 | for n in nValues { 33 | let ns = dispatch_benchmark(5) { 34 | dominantColorsInImage(CGImage, maxSampledPixels: n) 35 | return 36 | } 37 | println("n = \(n) averaged \(ns/1000000) ms") 38 | } 39 | } 40 | } 41 | 42 | // MARK: ImagePicker Delegate 43 | 44 | func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) { 45 | if let imageSelected = image { 46 | self.image = imageSelected 47 | imageView.image = imageSelected 48 | 49 | let colors = imageSelected.dominantColors() 50 | for box in boxes { 51 | box.backgroundColor = UIColor.clearColor() 52 | } 53 | for i in 0.. 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /DominantColor.xcodeproj/xcshareddata/xcschemes/DominantColor-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /DominantColor.xcodeproj/xcshareddata/xcschemes/ExampleMac.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /DominantColor.xcodeproj/xcshareddata/xcschemes/ExampleiOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /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 | 11 | // Represents a type that can be clustered using the k-means clustering 12 | // algorithm. 13 | protocol ClusteredType { 14 | // Used to compute average values to determine the cluster centroids. 15 | func +(lhs: Self, rhs: Self) -> Self 16 | func /(lhs: Self, rhs: Int) -> Self 17 | 18 | // Identity value such that x + identity = x. Typically the 0 vector. 19 | static var identity: Self { get } 20 | } 21 | 22 | struct Cluster { 23 | let centroid: T 24 | let size: Int 25 | } 26 | 27 | // k-means clustering algorithm from 28 | // http://users.eecs.northwestern.edu/~wkliao/Kmeans/ 29 | 30 | func kmeans( 31 | points: [T], 32 | k: Int, 33 | seed: UInt32, 34 | distance: ((T, T) -> Float), 35 | threshold: Float = 0.0001 36 | ) -> [Cluster] { 37 | 38 | let n = count(points) 39 | assert(k <= n, "k cannot be larger than the total number of points") 40 | 41 | var centroids = points.randomValues(seed, num: k) 42 | var memberships = [Int](count: n, repeatedValue: -1) 43 | var clusterSizes = [Int](count: k, repeatedValue: 0) 44 | 45 | var error: Float = 0 46 | var previousError: Float = 0 47 | 48 | do { 49 | error = 0 50 | var newCentroids = [T](count: k, repeatedValue: T.identity) 51 | var newClusterSizes = [Int](count: k, repeatedValue: 0) 52 | 53 | for i in 0.. 0 { 66 | centroids[i] = newCentroids[i] / size 67 | } 68 | } 69 | 70 | clusterSizes = newClusterSizes 71 | previousError = error 72 | } while abs(error - previousError) > threshold 73 | 74 | return map(Zip2(centroids, clusterSizes)) { Cluster(centroid: $0, size: $1) } 75 | } 76 | 77 | private func findNearestCluster(point: T, centroids: [T], k: Int, distance: (T, T) -> Float) -> Int { 78 | var minDistance = Float.infinity 79 | var clusterIndex = 0 80 | for i in 0..) -> Int { 91 | let interval = range.endIndex - range.startIndex - 1 92 | let buckets = Int(RAND_MAX) / interval 93 | let limit = buckets * interval 94 | var r = 0 95 | do { 96 | r = Int(rand()) 97 | } while r >= limit 98 | return range.startIndex + (r / buckets) 99 | } 100 | 101 | private extension Array { 102 | private func randomValues(seed: UInt32, num: Int) -> [T] { 103 | srand(seed) 104 | 105 | var indices = [Int]() 106 | indices.reserveCapacity(num) 107 | let range = 0.. Float { 16 | let (L1, a1, b1) = lab1.unpack() 17 | let (L2, a2, b2) = lab2.unpack() 18 | 19 | return pow(L2 - L1, 2) + pow(a2 - a1, 2) + pow(b2 - b1, 2) 20 | } 21 | 22 | private func C(a: Float, b: Float) -> Float { 23 | return sqrt(pow(a, 2) + pow(b, 2)) 24 | } 25 | 26 | // From http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE94.html 27 | func CIE94SquaredColorDifference( 28 | kL: Float = 1, 29 | kC: Float = 1, 30 | kH: Float = 1, 31 | K1: Float = 0.045, 32 | K2: Float = 0.015 33 | )(lab1:INVector3, lab2:INVector3) -> Float { 34 | 35 | let (L1, a1, b1) = lab1.unpack() 36 | let (L2, a2, b2) = lab2.unpack() 37 | let ΔL = L1 - L2 38 | 39 | let (C1, C2) = (C(a1, b1), C(a2, b2)) 40 | let ΔC = C1 - C2 41 | 42 | let ΔH = sqrt(pow(a1 - a2, 2) + pow(b1 - b2, 2) - pow(ΔC, 2)) 43 | 44 | let Sl: Float = 1 45 | let Sc = 1 + K1 * C1 46 | let Sh = 1 + K2 * C1 47 | 48 | return pow(ΔL / (kL * Sl), 2) + pow(ΔC / (kC * Sc), 2) + pow(ΔH / (kH * Sh), 2) 49 | } 50 | 51 | // From http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html 52 | func CIE2000SquaredColorDifference( 53 | kL: Float = 1, 54 | kC: Float = 1, 55 | kH: Float = 1 56 | )(lab1:INVector3, lab2:INVector3) -> Float { 57 | 58 | let (L1, a1, b1) = lab1.unpack() 59 | let (L2, a2, b2) = lab2.unpack() 60 | 61 | let ΔLp = L2 - L1 62 | let Lbp = (L1 + L2) / 2 63 | 64 | let (C1, C2) = (C(a1, b1), C(a2, b2)) 65 | let Cb = (C1 + C2) / 2 66 | 67 | let G = (1 - sqrt(pow(Cb, 7) / (pow(Cb, 7) + pow(25, 7)))) / 2 68 | let ap: Float -> Float = { a in 69 | return a * (1 + G) 70 | } 71 | let (a1p, a2p) = (ap(a1), ap(a2)) 72 | 73 | let (C1p, C2p) = (C(a1p, b1), C(a2p, b2)) 74 | let ΔCp = C2p - C1p 75 | let Cbp = (C1p + C2p) / 2 76 | 77 | let hp: (Float, Float) -> Float = { ap, b in 78 | if ap == 0 && b == 0 { return 0 } 79 | let θ = GLKMathRadiansToDegrees(atan2(b, ap)) 80 | return fmod(θ < 0 ? (θ + 360) : θ, 360) 81 | } 82 | let (h1p, h2p) = (hp(a1p, b1), hp(a2p, b2)) 83 | let Δhabs = abs(h1p - h2p) 84 | let Δhp: Float = { 85 | if (C1p == 0 || C2p == 0) { 86 | return 0 87 | } else if Δhabs <= 180 { 88 | return h2p - h1p 89 | } else if h2p <= h1p { 90 | return h2p - h1p + 360 91 | } else { 92 | return h2p - h1p - 360 93 | } 94 | }() 95 | 96 | let ΔHp = 2 * sqrt(C1p * C2p) * sin(GLKMathDegreesToRadians(Δhp / 2)) 97 | let Hbp: Float = { 98 | if (C1p == 0 || C2p == 0) { 99 | return h1p + h2p 100 | } else if Δhabs > 180 { 101 | return (h1p + h2p + 360) / 2 102 | } else { 103 | return (h1p + h2p) / 2 104 | } 105 | }() 106 | 107 | let T = 1 108 | - 0.17 * cos(GLKMathDegreesToRadians(Hbp - 30)) 109 | + 0.24 * cos(GLKMathDegreesToRadians(2 * Hbp)) 110 | + 0.32 * cos(GLKMathDegreesToRadians(3 * Hbp + 6)) 111 | - 0.20 * cos(GLKMathDegreesToRadians(4 * Hbp - 63)) 112 | 113 | let Sl = 1 + (0.015 * pow(Lbp - 50, 2)) / sqrt(20 + pow(Lbp - 50, 2)) 114 | let Sc = 1 + 0.045 * Cbp 115 | let Sh = 1 + 0.015 * Cbp * T 116 | 117 | let Δθ = 30 * exp(-pow((Hbp - 275) / 25, 2)) 118 | let Rc = 2 * sqrt(pow(Cbp, 7) / (pow(Cbp, 7) + pow(25, 7))) 119 | let Rt = -Rc * sin(GLKMathDegreesToRadians(2 * Δθ)) 120 | 121 | let Lterm = ΔLp / (kL * Sl) 122 | let Cterm = ΔCp / (kC * Sc) 123 | let Hterm = ΔHp / (kH * Sh) 124 | return pow(Lterm, 2) + pow(Cterm, 2) + pow(Hterm, 2) + Rt * Cterm * Hterm 125 | } 126 | -------------------------------------------------------------------------------- /DominantColor/Shared/PlatformExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlatformExtensions.swift 3 | // DominantColor 4 | // 5 | // Created by Indragie on 12/25/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if os(OSX) 10 | import Cocoa 11 | 12 | public extension NSImage { 13 | /** 14 | Computes the dominant colors in the receiver 15 | 16 | :param: maxSampledPixels Maximum number of pixels to sample in the image. If 17 | the total number of pixels in the image exceeds this 18 | value, it will be downsampled to meet the constraint. 19 | :param: accuracy Level of accuracy to use when grouping similar colors. 20 | Higher accuracy will come with a performance tradeoff. 21 | :param: seed Seed to use when choosing the initial points for grouping 22 | of similar colors. The same seed is guaranteed to return 23 | the same colors every time. 24 | :param: memoizeConversions Whether to memoize conversions from RGB to the LAB color 25 | space (used for grouping similar colors). Memoization 26 | will only yield better performance for large values of 27 | `maxSampledPixels` in images that are primarily comprised 28 | of flat colors. If this information about the image is 29 | not known beforehand, it is best to not memoize. 30 | 31 | :returns: A list of dominant colors in the image sorted from most dominant to 32 | least dominant. 33 | */ 34 | public func dominantColors( 35 | maxSampledPixels: Int = DefaultParameterValues.maxSampledPixels, 36 | accuracy: GroupingAccuracy = DefaultParameterValues.accuracy, 37 | seed: UInt32 = DefaultParameterValues.seed, 38 | memoizeConversions: Bool = DefaultParameterValues.memoizeConversions 39 | ) -> [NSColor] { 40 | let image = CGImageForProposedRect(nil, context: nil, hints: nil)!.takeUnretainedValue() 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 | :param: 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 | :param: accuracy Level of accuracy to use when grouping similar colors. 57 | Higher accuracy will come with a performance tradeoff. 58 | :param: 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 | :param: 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 | public func dominantColors( 72 | maxSampledPixels: Int = DefaultParameterValues.maxSampledPixels, 73 | accuracy: GroupingAccuracy = DefaultParameterValues.accuracy, 74 | seed: UInt32 = DefaultParameterValues.seed, 75 | memoizeConversions: Bool = DefaultParameterValues.memoizeConversions 76 | ) -> [UIColor] { 77 | let colors = dominantColorsInImage(self.CGImage, maxSampledPixels: maxSampledPixels, accuracy: accuracy, seed: seed, memoizeConversions: memoizeConversions) 78 | return colors.map { UIColor(CGColor: $0)! } 79 | } 80 | } 81 | 82 | #endif 83 | 84 | -------------------------------------------------------------------------------- /DominantColor/Shared/ColorSpaceConversion.m: -------------------------------------------------------------------------------- 1 | // 2 | // ColorSpaceConversion.m 3 | // DominantColor 4 | // 5 | // Created by Indragie on 12/21/14. 6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "ColorSpaceConversion.h" 10 | 11 | #pragma mark - RGB 12 | 13 | static GLKVector3 RGBToSRGB(GLKVector3 rgbVector) { 14 | #if TARGET_OS_IPHONE 15 | // sRGB is the native device color space on iOS, no conversion is required. 16 | return rgbVector; 17 | #else 18 | NSColor *rgbColor = [NSColor colorWithDeviceRed:rgbVector.x green:rgbVector.y blue:rgbVector.z alpha:1.0]; 19 | NSColor *srgbColor = [rgbColor colorUsingColorSpace:NSColorSpace.sRGBColorSpace]; 20 | return GLKVector3Make(srgbColor.redComponent, srgbColor.greenComponent, srgbColor.blueComponent); 21 | #endif 22 | } 23 | 24 | static GLKVector3 SRGBToRGB(GLKVector3 srgbVector) { 25 | #if TARGET_OS_IPHONE 26 | // sRGB is the native device color space on iOS, no conversion is required. 27 | return srgbVector; 28 | #else 29 | const CGFloat components[4] = { srgbVector.x, srgbVector.y, srgbVector.z, 1.0 }; 30 | NSColor *srgbColor = [NSColor colorWithColorSpace:NSColorSpace.sRGBColorSpace components:components count:4]; 31 | NSColor *rgbColor = [srgbColor colorUsingColorSpace:NSColorSpace.deviceRGBColorSpace]; 32 | return GLKVector3Make(rgbColor.redComponent, rgbColor.greenComponent, rgbColor.blueComponent); 33 | #endif 34 | } 35 | 36 | #pragma mark - SRGB 37 | // http://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation 38 | 39 | static GLKVector3 SRGBToLinearSRGB(GLKVector3 srgbVector) { 40 | float (^f)(float) = ^float (float c) { 41 | if (c <= 0.04045f) { 42 | return c / 12.92f; 43 | } else { 44 | return powf((c + 0.055f) / 1.055f, 2.4f); 45 | } 46 | }; 47 | return GLKVector3Make(f(srgbVector.x), f(srgbVector.y), f(srgbVector.z)); 48 | } 49 | 50 | static GLKVector3 LinearSRGBToSRGB(GLKVector3 lSrgbVector) { 51 | float (^f)(float) = ^float (float c) { 52 | if (c <= 0.0031308f) { 53 | return c * 12.92f; 54 | } else { 55 | return (1.055f * powf(c, 1.f / 2.4f)) - 0.055f; 56 | } 57 | }; 58 | return GLKVector3Make(f(lSrgbVector.x), f(lSrgbVector.y), f(lSrgbVector.z)); 59 | } 60 | 61 | #pragma mark - XYZ (CIE 1931) 62 | // http://en.wikipedia.org/wiki/CIE_1931_color_space#Construction_of_the_CIE_XYZ_color_space_from_the_Wright.E2.80.93Guild_data 63 | 64 | static const GLKMatrix3 LinearSRGBToXYZMatrix = (GLKMatrix3){ 65 | 0.4124f, 0.2126f, 0.0193f, 66 | 0.3576f, 0.7152f, 0.1192f, 67 | 0.1805f, 0.0722f, 0.9505f 68 | }; 69 | 70 | static GLKVector3 LinearSRGBToXYZ(GLKVector3 linearSrgbVector) { 71 | const GLKVector3 unscaledXYZVector = GLKMatrix3MultiplyVector3(LinearSRGBToXYZMatrix, linearSrgbVector); 72 | return GLKVector3MultiplyScalar(unscaledXYZVector, 100.f); 73 | } 74 | 75 | static const GLKMatrix3 XYZToLinearSRGBMatrix = (GLKMatrix3){ 76 | 3.2406f, -0.9689f, 0.0557f, 77 | -1.5372f, 1.8758f, -0.2040f, 78 | -0.4986f, 0.0415f, 1.0570f 79 | }; 80 | 81 | static GLKVector3 XYZToLinearSRGB(GLKVector3 xyzVector) { 82 | const GLKVector3 scaledXYZVector = GLKVector3DivideScalar(xyzVector, 100.f); 83 | return GLKMatrix3MultiplyVector3(XYZToLinearSRGBMatrix, scaledXYZVector); 84 | } 85 | 86 | #pragma mark - LAB 87 | // http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions 88 | 89 | static GLKVector3 XYZToLAB(GLKVector3 xyzVector, GLKVector3 tristimulus) { 90 | float (^f)(float) = ^float (float t) { 91 | if (t > powf(6.f / 29.f, 3.f)) { 92 | return powf(t, 1.f / 3.f); 93 | } else { 94 | return ((1.f / 3.f) * powf(29.f / 6.f, 2.f) * t) + (4.f / 29.f); 95 | } 96 | }; 97 | const float fx = f(xyzVector.x / tristimulus.x); 98 | const float fy = f(xyzVector.y / tristimulus.y); 99 | const float fz = f(xyzVector.z / tristimulus.z); 100 | 101 | const float l = (116.f * fy) - 16.f; 102 | const float a = 500 * (fx - fy); 103 | const float b = 200 * (fy - fz); 104 | 105 | return GLKVector3Make(l, a, b); 106 | } 107 | 108 | static GLKVector3 LABToXYZ(GLKVector3 labVector, GLKVector3 tristimulus) { 109 | float (^f)(float) = ^float (float t) { 110 | if (t > (6.f / 29.f)) { 111 | return powf(t, 3.f); 112 | } else { 113 | return 3.f * powf(6.f / 29.f, 2.f) * (t - (4.f / 29.f)); 114 | } 115 | }; 116 | const float c = (1.f / 116.f) * (labVector.x + 16.f); 117 | 118 | const float y = tristimulus.y * f(c); 119 | const float x = tristimulus.x * f(c + ((1.f / 500.f) * labVector.y)); 120 | const float z = tristimulus.z * f(c - ((1.f / 200.f) * labVector.z)); 121 | 122 | return GLKVector3Make(x, y, z); 123 | } 124 | 125 | #pragma mark - Public 126 | 127 | // From http://www.easyrgb.com/index.php?X=MATH&H=15#text15 128 | static const GLKVector3 D65Tristimulus = (GLKVector3){ 95.047f, 100.f, 108.883f }; 129 | 130 | INVector3 IN_RGBToLAB(INVector3 rgbVector) { 131 | const GLKVector3 gVector = INVector3ToGLKVector3(rgbVector); 132 | const GLKVector3 srgbVector = RGBToSRGB(gVector); 133 | const GLKVector3 lSrgbVector = SRGBToLinearSRGB(srgbVector); 134 | const GLKVector3 xyzVector = LinearSRGBToXYZ(lSrgbVector); 135 | const GLKVector3 labVector = XYZToLAB(xyzVector, D65Tristimulus); 136 | return GLKVector3ToINVector3(labVector); 137 | } 138 | 139 | INVector3 IN_LABToRGB(INVector3 labVector) { 140 | const GLKVector3 gVector = INVector3ToGLKVector3(labVector); 141 | const GLKVector3 xyzVector = LABToXYZ(gVector, D65Tristimulus); 142 | const GLKVector3 lSrgbVector = XYZToLinearSRGB(xyzVector); 143 | const GLKVector3 srgbVector = LinearSRGBToSRGB(lSrgbVector); 144 | const GLKVector3 rgbVector = SRGBToRGB(srgbVector); 145 | return GLKVector3ToINVector3(rgbVector); 146 | } 147 | -------------------------------------------------------------------------------- /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 | ![Mac app](mac.png) 46 | ![iOS app](ios.png) 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 | // MARK: Bitmaps 16 | 17 | private struct RGBAPixel { 18 | let r: UInt8 19 | let g: UInt8 20 | let b: UInt8 21 | let a: UInt8 22 | } 23 | 24 | extension RGBAPixel: Hashable { 25 | private var hashValue: Int { 26 | return (((Int(r) << 8) | Int(g)) << 8) | Int(b) 27 | } 28 | } 29 | 30 | private func ==(lhs: RGBAPixel, rhs: RGBAPixel) -> Bool { 31 | return lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b 32 | } 33 | 34 | private func createRGBAContext(width: Int, height: Int) -> CGContext { 35 | return CGBitmapContextCreate( 36 | nil, 37 | width, 38 | height, 39 | 8, // bits per component 40 | width * 4, // bytes per row 41 | CGColorSpaceCreateDeviceRGB(), 42 | CGBitmapInfo(CGImageAlphaInfo.PremultipliedLast.rawValue) 43 | ) 44 | } 45 | 46 | // Enumerates over all of the pixels in an RGBA bitmap context 47 | // in the order that they are stored in memory, for faster access. 48 | // 49 | // From: https://www.mikeash.com/pyblog/friday-qa-2012-08-31-obtaining-and-interpreting-image-data.html 50 | private func enumerateRGBAContext(context: CGContext, handler: (Int, Int, RGBAPixel) -> Void) { 51 | let (width, height) = (CGBitmapContextGetWidth(context), CGBitmapContextGetHeight(context)) 52 | let data = unsafeBitCast(CGBitmapContextGetData(context), UnsafeMutablePointer.self) 53 | for y in 0.. CGColor { 63 | return CGColorCreate(CGColorSpaceCreateDeviceRGB(), [CGFloat(rgbVector.x), CGFloat(rgbVector.y), CGFloat(rgbVector.z), 1.0]) 64 | } 65 | 66 | private extension RGBAPixel { 67 | func toRGBVector() -> INVector3 { 68 | return INVector3( 69 | x: Float(r) / Float(UInt8.max), 70 | y: Float(g) / Float(UInt8.max), 71 | z: Float(b) / Float(UInt8.max) 72 | ) 73 | } 74 | } 75 | 76 | // MARK: Clustering 77 | 78 | extension INVector3 : ClusteredType {} 79 | 80 | // MARK: Main 81 | 82 | public enum GroupingAccuracy { 83 | case Low // CIE 76 - Euclidian distance 84 | case Medium // CIE 94 - Perceptual non-uniformity corrections 85 | case High // CIE 2000 - Additional corrections for neutral colors, lightness, chroma, and hue 86 | } 87 | 88 | struct DefaultParameterValues { 89 | static var maxSampledPixels: Int = 1000 90 | static var accuracy: GroupingAccuracy = .Medium 91 | static var seed: UInt32 = 3571 92 | static var memoizeConversions: Bool = false 93 | } 94 | 95 | /** 96 | Computes the dominant colors in an image 97 | 98 | :param: image The image 99 | :param: maxSampledPixels Maximum number of pixels to sample in the image. If 100 | the total number of pixels in the image exceeds this 101 | value, it will be downsampled to meet the constraint. 102 | :param: accuracy Level of accuracy to use when grouping similar colors. 103 | Higher accuracy will come with a performance tradeoff. 104 | :param: seed Seed to use when choosing the initial points for grouping 105 | of similar colors. The same seed is guaranteed to return 106 | the same colors every time. 107 | :param: memoizeConversions Whether to memoize conversions from RGB to the LAB color 108 | space (used for grouping similar colors). Memoization 109 | will only yield better performance for large values of 110 | `maxSampledPixels` in images that are primarily comprised 111 | of flat colors. If this information about the image is 112 | not known beforehand, it is best to not memoize. 113 | 114 | :returns: A list of dominant colors in the image sorted from most dominant to 115 | least dominant. 116 | */ 117 | public func dominantColorsInImage( 118 | image: CGImage, 119 | maxSampledPixels: Int = DefaultParameterValues.maxSampledPixels, 120 | accuracy: GroupingAccuracy = DefaultParameterValues.accuracy, 121 | seed: UInt32 = DefaultParameterValues.seed, 122 | memoizeConversions: Bool = DefaultParameterValues.memoizeConversions 123 | ) -> [CGColor] { 124 | 125 | let (width, height) = (CGImageGetWidth(image), CGImageGetHeight(image)) 126 | let (scaledWidth, scaledHeight) = scaledDimensionsForPixelLimit(maxSampledPixels, width, height) 127 | 128 | // Downsample the image if necessary, so that the total number of 129 | // pixels sampled does not exceed the specified maximum. 130 | let context = createRGBAContext(scaledWidth, scaledHeight) 131 | CGContextDrawImage(context, CGRect(x: 0, y: 0, width: Int(scaledWidth), height: Int(scaledHeight)), image) 132 | 133 | // Get the RGB colors from the bitmap context, ignoring any pixels 134 | // that have alpha transparency. 135 | // Also convert the colors to the LAB color space 136 | var labValues = [INVector3]() 137 | labValues.reserveCapacity(Int(scaledWidth * scaledHeight)) 138 | 139 | let RGBToLAB: RGBAPixel -> INVector3 = { 140 | let f: RGBAPixel -> INVector3 = { IN_RGBToLAB($0.toRGBVector()) } 141 | return memoizeConversions ? memoize(f) : f 142 | }() 143 | enumerateRGBAContext(context) { (_, _, pixel) in 144 | if pixel.a == UInt8.max { 145 | labValues.append(RGBToLAB(pixel)) 146 | } 147 | } 148 | // Cluster the colors using the k-means algorithm 149 | let k = selectKForElements(labValues) 150 | var clusters = kmeans(labValues, k, seed, distanceForAccuracy(accuracy)) 151 | 152 | // Sort the clusters by size in descending order so that the 153 | // most dominant colors come first. 154 | clusters.sort { $0.size > $1.size } 155 | 156 | return clusters.map { RGBVectorToCGColor(IN_LABToRGB($0.centroid)) } 157 | } 158 | 159 | private func distanceForAccuracy(accuracy: GroupingAccuracy) -> (INVector3, INVector3) -> Float { 160 | switch accuracy { 161 | case .Low: 162 | return CIE76SquaredColorDifference 163 | case .Medium: 164 | return CIE94SquaredColorDifference() 165 | case .High: 166 | return CIE2000SquaredColorDifference() 167 | } 168 | } 169 | 170 | // Computes the proportionally scaled dimensions such that the 171 | // total number of pixels does not exceed the specified limit. 172 | private func scaledDimensionsForPixelLimit(limit: Int, width: Int, height: Int) -> (Int, Int) { 173 | if (width * height > limit) { 174 | let ratio = Float(width) / Float(height) 175 | let maxWidth = sqrtf(ratio * Float(limit)) 176 | return (Int(maxWidth), Int(Float(limit) / maxWidth)) 177 | } 178 | return (width, height) 179 | } 180 | 181 | private func selectKForElements(elements: [T]) -> Int { 182 | // Seems like a magic number... 183 | return 16 184 | } 185 | -------------------------------------------------------------------------------- /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 | 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 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /DominantColor.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2F3D09861A4C212A001ED0BF /* Memoization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F3D09851A4C212A001ED0BF /* Memoization.swift */; }; 11 | 2F3D09871A4C212A001ED0BF /* Memoization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F3D09851A4C212A001ED0BF /* Memoization.swift */; }; 12 | 723DE5291A4934E200C357E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5261A4934E200C357E3 /* AppDelegate.swift */; }; 13 | 723DE52A1A4934E200C357E3 /* DragAndDropImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5271A4934E200C357E3 /* DragAndDropImageView.swift */; }; 14 | 723DE52B1A4934E200C357E3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 723DE5281A4934E200C357E3 /* Images.xcassets */; }; 15 | 723DE52E1A4934F800C357E3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 723DE52C1A4934F800C357E3 /* MainMenu.xib */; }; 16 | 723DE54D1A49353C00C357E3 /* DominantColor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE5361A49353C00C357E3 /* DominantColor.framework */; }; 17 | 723DE54E1A49353C00C357E3 /* DominantColor.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE5361A49353C00C357E3 /* DominantColor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 18 | 723DE5591A49358F00C357E3 /* DominantColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5581A49358F00C357E3 /* DominantColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | 723DE55A1A49360A00C357E3 /* INVector3.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5181A49348D00C357E3 /* INVector3.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20 | 723DE55B1A49360C00C357E3 /* ColorSpaceConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5151A49348D00C357E3 /* ColorSpaceConversion.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 | 723DE55F1A49361700C357E3 /* INVector3.m in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5191A49348D00C357E3 /* INVector3.m */; }; 25 | 723DE5601A49361900C357E3 /* ColorSpaceConversion.m in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5161A49348D00C357E3 /* ColorSpaceConversion.m */; }; 26 | 723DE5611A49372D00C357E3 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D797DE1A43F89000D32E7C /* GLKit.framework */; }; 27 | 723DE5691A49385C00C357E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5661A49385C00C357E3 /* AppDelegate.swift */; }; 28 | 723DE56A1A49385C00C357E3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 723DE5671A49385C00C357E3 /* Images.xcassets */; }; 29 | 723DE56B1A49385C00C357E3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5681A49385C00C357E3 /* ViewController.swift */; }; 30 | 723DE5701A49386B00C357E3 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 723DE56C1A49386B00C357E3 /* LaunchScreen.xib */; }; 31 | 723DE5711A49386B00C357E3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 723DE56E1A49386B00C357E3 /* Main.storyboard */; }; 32 | 723DE5B61A4938DD00C357E3 /* DominantColor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE59F1A4938DD00C357E3 /* DominantColor.framework */; }; 33 | 723DE5B71A4938DD00C357E3 /* DominantColor.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE59F1A4938DD00C357E3 /* DominantColor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34 | 723DE5BF1A49394D00C357E3 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE5BE1A49394D00C357E3 /* GLKit.framework */; }; 35 | 723DE5C01A49395A00C357E3 /* DominantColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5581A49358F00C357E3 /* DominantColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 36 | 723DE5C11A49395C00C357E3 /* ColorSpaceConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5151A49348D00C357E3 /* ColorSpaceConversion.h */; settings = {ATTRIBUTES = (Public, ); }; }; 37 | 723DE5C21A49395D00C357E3 /* INVector3.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5181A49348D00C357E3 /* INVector3.h */; settings = {ATTRIBUTES = (Public, ); }; }; 38 | 723DE5C31A49399E00C357E3 /* ColorDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5141A49348D00C357E3 /* ColorDifference.swift */; }; 39 | 723DE5C41A4939A000C357E3 /* DominantColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5171A49348D00C357E3 /* DominantColors.swift */; }; 40 | 723DE5C51A4939A200C357E3 /* KMeans.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE51A1A49348D00C357E3 /* KMeans.swift */; }; 41 | 723DE5C61A4939A300C357E3 /* ColorSpaceConversion.m in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5161A49348D00C357E3 /* ColorSpaceConversion.m */; }; 42 | 723DE5C71A4939A500C357E3 /* INVector3.m in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5191A49348D00C357E3 /* INVector3.m */; }; 43 | 72431C0F1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */; }; 44 | 72431C101A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */; }; 45 | 724C2DCD1A4CD64600472402 /* PlatformExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */; }; 46 | 724C2DCE1A4CD64600472402 /* PlatformExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */; }; 47 | /* End PBXBuildFile section */ 48 | 49 | /* Begin PBXContainerItemProxy section */ 50 | 723DE54B1A49353C00C357E3 /* PBXContainerItemProxy */ = { 51 | isa = PBXContainerItemProxy; 52 | containerPortal = 72D797B01A43F44D00D32E7C /* Project object */; 53 | proxyType = 1; 54 | remoteGlobalIDString = 723DE5351A49353C00C357E3; 55 | remoteInfo = DominantColor; 56 | }; 57 | 723DE5B41A4938DD00C357E3 /* PBXContainerItemProxy */ = { 58 | isa = PBXContainerItemProxy; 59 | containerPortal = 72D797B01A43F44D00D32E7C /* Project object */; 60 | proxyType = 1; 61 | remoteGlobalIDString = 723DE59E1A4938DD00C357E3; 62 | remoteInfo = DominantColor; 63 | }; 64 | /* End PBXContainerItemProxy section */ 65 | 66 | /* Begin PBXCopyFilesBuildPhase section */ 67 | 723DE5521A49353C00C357E3 /* Embed Frameworks */ = { 68 | isa = PBXCopyFilesBuildPhase; 69 | buildActionMask = 2147483647; 70 | dstPath = ""; 71 | dstSubfolderSpec = 10; 72 | files = ( 73 | 723DE54E1A49353C00C357E3 /* DominantColor.framework in Embed Frameworks */, 74 | ); 75 | name = "Embed Frameworks"; 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 723DE5921A4938B500C357E3 /* Embed Frameworks */ = { 79 | isa = PBXCopyFilesBuildPhase; 80 | buildActionMask = 2147483647; 81 | dstPath = ""; 82 | dstSubfolderSpec = 10; 83 | files = ( 84 | 723DE5B71A4938DD00C357E3 /* DominantColor.framework in Embed Frameworks */, 85 | ); 86 | name = "Embed Frameworks"; 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | /* End PBXCopyFilesBuildPhase section */ 90 | 91 | /* Begin PBXFileReference section */ 92 | 2F3D09851A4C212A001ED0BF /* Memoization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Memoization.swift; sourceTree = ""; }; 93 | 723DE5141A49348D00C357E3 /* ColorDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorDifference.swift; sourceTree = ""; }; 94 | 723DE5151A49348D00C357E3 /* ColorSpaceConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorSpaceConversion.h; sourceTree = ""; }; 95 | 723DE5161A49348D00C357E3 /* ColorSpaceConversion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ColorSpaceConversion.m; sourceTree = ""; }; 96 | 723DE5171A49348D00C357E3 /* DominantColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DominantColors.swift; sourceTree = ""; }; 97 | 723DE5181A49348D00C357E3 /* INVector3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INVector3.h; sourceTree = ""; }; 98 | 723DE5191A49348D00C357E3 /* INVector3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = INVector3.m; sourceTree = ""; }; 99 | 723DE51A1A49348D00C357E3 /* KMeans.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KMeans.swift; sourceTree = ""; }; 100 | 723DE5261A4934E200C357E3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Mac/AppDelegate.swift; sourceTree = ""; }; 101 | 723DE5271A4934E200C357E3 /* DragAndDropImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DragAndDropImageView.swift; path = Mac/DragAndDropImageView.swift; sourceTree = ""; }; 102 | 723DE5281A4934E200C357E3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Mac/Images.xcassets; sourceTree = ""; }; 103 | 723DE52D1A4934F800C357E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Mac/Base.lproj/MainMenu.xib; sourceTree = ""; }; 104 | 723DE52F1A49350000C357E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Mac/Info.plist; sourceTree = ""; }; 105 | 723DE5361A49353C00C357E3 /* DominantColor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DominantColor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 106 | 723DE5561A49357D00C357E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Shared/Info.plist; sourceTree = ""; }; 107 | 723DE5581A49358F00C357E3 /* DominantColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DominantColor.h; sourceTree = ""; }; 108 | 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 = ""; }; 109 | 723DE5661A49385C00C357E3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = DominantColor/iOS/AppDelegate.swift; sourceTree = SOURCE_ROOT; }; 110 | 723DE5671A49385C00C357E3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = DominantColor/iOS/Images.xcassets; sourceTree = SOURCE_ROOT; }; 111 | 723DE5681A49385C00C357E3 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = DominantColor/iOS/ViewController.swift; sourceTree = SOURCE_ROOT; }; 112 | 723DE56D1A49386B00C357E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = DominantColor/iOS/Base.lproj/LaunchScreen.xib; sourceTree = SOURCE_ROOT; }; 113 | 723DE56F1A49386B00C357E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = DominantColor/iOS/Base.lproj/Main.storyboard; sourceTree = SOURCE_ROOT; }; 114 | 723DE5721A49387800C357E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = DominantColor/iOS/Info.plist; sourceTree = SOURCE_ROOT; }; 115 | 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; }; 116 | 723DE59F1A4938DD00C357E3 /* DominantColor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DominantColor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 117 | 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; }; 118 | 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = INVector3SwiftExtensions.swift; sourceTree = ""; }; 119 | 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformExtensions.swift; sourceTree = ""; }; 120 | 72D797B81A43F44D00D32E7C /* ExampleMac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleMac.app; sourceTree = BUILT_PRODUCTS_DIR; }; 121 | 72D797DE1A43F89000D32E7C /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; }; 122 | 898845D21A490CE000003EF2 /* ExampleiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleiOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; 123 | /* End PBXFileReference section */ 124 | 125 | /* Begin PBXFrameworksBuildPhase section */ 126 | 723DE5321A49353C00C357E3 /* Frameworks */ = { 127 | isa = PBXFrameworksBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | 723DE5611A49372D00C357E3 /* GLKit.framework in Frameworks */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | 723DE59B1A4938DD00C357E3 /* Frameworks */ = { 135 | isa = PBXFrameworksBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | 723DE5BF1A49394D00C357E3 /* GLKit.framework in Frameworks */, 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | 72D797B51A43F44D00D32E7C /* Frameworks */ = { 143 | isa = PBXFrameworksBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | 723DE54D1A49353C00C357E3 /* DominantColor.framework in Frameworks */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | 898845CF1A490CE000003EF2 /* Frameworks */ = { 151 | isa = PBXFrameworksBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | 723DE5B61A4938DD00C357E3 /* DominantColor.framework in Frameworks */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXFrameworksBuildPhase section */ 159 | 160 | /* Begin PBXGroup section */ 161 | 723DE5131A49348D00C357E3 /* Shared */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 723DE5581A49358F00C357E3 /* DominantColor.h */, 165 | 723DE5141A49348D00C357E3 /* ColorDifference.swift */, 166 | 723DE5171A49348D00C357E3 /* DominantColors.swift */, 167 | 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */, 168 | 723DE51A1A49348D00C357E3 /* KMeans.swift */, 169 | 2F3D09851A4C212A001ED0BF /* Memoization.swift */, 170 | 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */, 171 | 723DE5151A49348D00C357E3 /* ColorSpaceConversion.h */, 172 | 723DE5161A49348D00C357E3 /* ColorSpaceConversion.m */, 173 | 723DE5181A49348D00C357E3 /* INVector3.h */, 174 | 723DE5191A49348D00C357E3 /* INVector3.m */, 175 | 723DE5381A49353C00C357E3 /* Supporting Files */, 176 | ); 177 | name = Shared; 178 | path = DominantColor/Shared; 179 | sourceTree = ""; 180 | }; 181 | 723DE5381A49353C00C357E3 /* Supporting Files */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 723DE5561A49357D00C357E3 /* Info.plist */, 185 | ); 186 | name = "Supporting Files"; 187 | path = ..; 188 | sourceTree = ""; 189 | }; 190 | 72B92ADA1A44CCCD00A2C24C /* Frameworks */ = { 191 | isa = PBXGroup; 192 | children = ( 193 | 723DE5BE1A49394D00C357E3 /* GLKit.framework */, 194 | 72D797DE1A43F89000D32E7C /* GLKit.framework */, 195 | ); 196 | name = Frameworks; 197 | path = DominantColor; 198 | sourceTree = ""; 199 | }; 200 | 72D797AF1A43F44D00D32E7C = { 201 | isa = PBXGroup; 202 | children = ( 203 | 723DE5131A49348D00C357E3 /* Shared */, 204 | 72D797BA1A43F44D00D32E7C /* Mac */, 205 | 898845D31A490CE000003EF2 /* iOS */, 206 | 72B92ADA1A44CCCD00A2C24C /* Frameworks */, 207 | 72D797B91A43F44D00D32E7C /* Products */, 208 | ); 209 | sourceTree = ""; 210 | }; 211 | 72D797B91A43F44D00D32E7C /* Products */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 72D797B81A43F44D00D32E7C /* ExampleMac.app */, 215 | 898845D21A490CE000003EF2 /* ExampleiOS.app */, 216 | 723DE5361A49353C00C357E3 /* DominantColor.framework */, 217 | 723DE59F1A4938DD00C357E3 /* DominantColor.framework */, 218 | ); 219 | name = Products; 220 | sourceTree = ""; 221 | }; 222 | 72D797BA1A43F44D00D32E7C /* Mac */ = { 223 | isa = PBXGroup; 224 | children = ( 225 | 723DE5261A4934E200C357E3 /* AppDelegate.swift */, 226 | 723DE5271A4934E200C357E3 /* DragAndDropImageView.swift */, 227 | 723DE5641A4937AD00C357E3 /* ExampleMac-Bridging-Header.h */, 228 | 723DE5281A4934E200C357E3 /* Images.xcassets */, 229 | 723DE52C1A4934F800C357E3 /* MainMenu.xib */, 230 | 72D797BB1A43F44D00D32E7C /* Supporting Files */, 231 | ); 232 | name = Mac; 233 | path = DominantColor; 234 | sourceTree = ""; 235 | }; 236 | 72D797BB1A43F44D00D32E7C /* Supporting Files */ = { 237 | isa = PBXGroup; 238 | children = ( 239 | 723DE52F1A49350000C357E3 /* Info.plist */, 240 | ); 241 | name = "Supporting Files"; 242 | sourceTree = ""; 243 | }; 244 | 898845D31A490CE000003EF2 /* iOS */ = { 245 | isa = PBXGroup; 246 | children = ( 247 | 723DE5661A49385C00C357E3 /* AppDelegate.swift */, 248 | 723DE5681A49385C00C357E3 /* ViewController.swift */, 249 | 723DE5741A49388400C357E3 /* ExampleiOS-Bridging-Header.h */, 250 | 723DE5671A49385C00C357E3 /* Images.xcassets */, 251 | 723DE56C1A49386B00C357E3 /* LaunchScreen.xib */, 252 | 723DE56E1A49386B00C357E3 /* Main.storyboard */, 253 | 898845D41A490CE000003EF2 /* Supporting Files */, 254 | ); 255 | name = iOS; 256 | path = "DominantColor-iOS"; 257 | sourceTree = ""; 258 | }; 259 | 898845D41A490CE000003EF2 /* Supporting Files */ = { 260 | isa = PBXGroup; 261 | children = ( 262 | 723DE5721A49387800C357E3 /* Info.plist */, 263 | ); 264 | name = "Supporting Files"; 265 | sourceTree = ""; 266 | }; 267 | /* End PBXGroup section */ 268 | 269 | /* Begin PBXHeadersBuildPhase section */ 270 | 723DE5331A49353C00C357E3 /* Headers */ = { 271 | isa = PBXHeadersBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | 723DE5591A49358F00C357E3 /* DominantColor.h in Headers */, 275 | 723DE55B1A49360C00C357E3 /* ColorSpaceConversion.h in Headers */, 276 | 723DE55A1A49360A00C357E3 /* INVector3.h in Headers */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | 723DE59C1A4938DD00C357E3 /* Headers */ = { 281 | isa = PBXHeadersBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 723DE5C01A49395A00C357E3 /* DominantColor.h in Headers */, 285 | 723DE5C11A49395C00C357E3 /* ColorSpaceConversion.h in Headers */, 286 | 723DE5C21A49395D00C357E3 /* INVector3.h in Headers */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | /* End PBXHeadersBuildPhase section */ 291 | 292 | /* Begin PBXNativeTarget section */ 293 | 723DE5351A49353C00C357E3 /* DominantColor-Mac */ = { 294 | isa = PBXNativeTarget; 295 | buildConfigurationList = 723DE54F1A49353C00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-Mac" */; 296 | buildPhases = ( 297 | 723DE5311A49353C00C357E3 /* Sources */, 298 | 723DE5321A49353C00C357E3 /* Frameworks */, 299 | 723DE5331A49353C00C357E3 /* Headers */, 300 | 723DE5341A49353C00C357E3 /* Resources */, 301 | ); 302 | buildRules = ( 303 | ); 304 | dependencies = ( 305 | ); 306 | name = "DominantColor-Mac"; 307 | productName = DominantColor; 308 | productReference = 723DE5361A49353C00C357E3 /* DominantColor.framework */; 309 | productType = "com.apple.product-type.framework"; 310 | }; 311 | 723DE59E1A4938DD00C357E3 /* DominantColor-iOS */ = { 312 | isa = PBXNativeTarget; 313 | buildConfigurationList = 723DE5B81A4938DD00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-iOS" */; 314 | buildPhases = ( 315 | 723DE59A1A4938DD00C357E3 /* Sources */, 316 | 723DE59B1A4938DD00C357E3 /* Frameworks */, 317 | 723DE59C1A4938DD00C357E3 /* Headers */, 318 | 723DE59D1A4938DD00C357E3 /* Resources */, 319 | ); 320 | buildRules = ( 321 | ); 322 | dependencies = ( 323 | ); 324 | name = "DominantColor-iOS"; 325 | productName = DominantColor; 326 | productReference = 723DE59F1A4938DD00C357E3 /* DominantColor.framework */; 327 | productType = "com.apple.product-type.framework"; 328 | }; 329 | 72D797B71A43F44D00D32E7C /* ExampleMac */ = { 330 | isa = PBXNativeTarget; 331 | buildConfigurationList = 72D797D21A43F44D00D32E7C /* Build configuration list for PBXNativeTarget "ExampleMac" */; 332 | buildPhases = ( 333 | 72D797B41A43F44D00D32E7C /* Sources */, 334 | 72D797B51A43F44D00D32E7C /* Frameworks */, 335 | 72D797B61A43F44D00D32E7C /* Resources */, 336 | 723DE5521A49353C00C357E3 /* Embed Frameworks */, 337 | ); 338 | buildRules = ( 339 | ); 340 | dependencies = ( 341 | 723DE54C1A49353C00C357E3 /* PBXTargetDependency */, 342 | ); 343 | name = ExampleMac; 344 | productName = DominantColor; 345 | productReference = 72D797B81A43F44D00D32E7C /* ExampleMac.app */; 346 | productType = "com.apple.product-type.application"; 347 | }; 348 | 898845D11A490CE000003EF2 /* ExampleiOS */ = { 349 | isa = PBXNativeTarget; 350 | buildConfigurationList = 898845F21A490CE000003EF2 /* Build configuration list for PBXNativeTarget "ExampleiOS" */; 351 | buildPhases = ( 352 | 898845CE1A490CE000003EF2 /* Sources */, 353 | 898845CF1A490CE000003EF2 /* Frameworks */, 354 | 898845D01A490CE000003EF2 /* Resources */, 355 | 723DE5921A4938B500C357E3 /* Embed Frameworks */, 356 | ); 357 | buildRules = ( 358 | ); 359 | dependencies = ( 360 | 723DE5B51A4938DD00C357E3 /* PBXTargetDependency */, 361 | ); 362 | name = ExampleiOS; 363 | productName = "DominantColor-iOS"; 364 | productReference = 898845D21A490CE000003EF2 /* ExampleiOS.app */; 365 | productType = "com.apple.product-type.application"; 366 | }; 367 | /* End PBXNativeTarget section */ 368 | 369 | /* Begin PBXProject section */ 370 | 72D797B01A43F44D00D32E7C /* Project object */ = { 371 | isa = PBXProject; 372 | attributes = { 373 | LastUpgradeCheck = 0610; 374 | ORGANIZATIONNAME = "Indragie Karunaratne"; 375 | TargetAttributes = { 376 | 723DE5351A49353C00C357E3 = { 377 | CreatedOnToolsVersion = 6.1.1; 378 | }; 379 | 723DE59E1A4938DD00C357E3 = { 380 | CreatedOnToolsVersion = 6.1.1; 381 | }; 382 | 72D797B71A43F44D00D32E7C = { 383 | CreatedOnToolsVersion = 6.1.1; 384 | }; 385 | 898845D11A490CE000003EF2 = { 386 | CreatedOnToolsVersion = 6.1; 387 | }; 388 | }; 389 | }; 390 | buildConfigurationList = 72D797B31A43F44D00D32E7C /* Build configuration list for PBXProject "DominantColor" */; 391 | compatibilityVersion = "Xcode 3.2"; 392 | developmentRegion = English; 393 | hasScannedForEncodings = 0; 394 | knownRegions = ( 395 | en, 396 | Base, 397 | ); 398 | mainGroup = 72D797AF1A43F44D00D32E7C; 399 | productRefGroup = 72D797B91A43F44D00D32E7C /* Products */; 400 | projectDirPath = ""; 401 | projectRoot = ""; 402 | targets = ( 403 | 72D797B71A43F44D00D32E7C /* ExampleMac */, 404 | 898845D11A490CE000003EF2 /* ExampleiOS */, 405 | 723DE5351A49353C00C357E3 /* DominantColor-Mac */, 406 | 723DE59E1A4938DD00C357E3 /* DominantColor-iOS */, 407 | ); 408 | }; 409 | /* End PBXProject section */ 410 | 411 | /* Begin PBXResourcesBuildPhase section */ 412 | 723DE5341A49353C00C357E3 /* Resources */ = { 413 | isa = PBXResourcesBuildPhase; 414 | buildActionMask = 2147483647; 415 | files = ( 416 | ); 417 | runOnlyForDeploymentPostprocessing = 0; 418 | }; 419 | 723DE59D1A4938DD00C357E3 /* Resources */ = { 420 | isa = PBXResourcesBuildPhase; 421 | buildActionMask = 2147483647; 422 | files = ( 423 | ); 424 | runOnlyForDeploymentPostprocessing = 0; 425 | }; 426 | 72D797B61A43F44D00D32E7C /* Resources */ = { 427 | isa = PBXResourcesBuildPhase; 428 | buildActionMask = 2147483647; 429 | files = ( 430 | 723DE52B1A4934E200C357E3 /* Images.xcassets in Resources */, 431 | 723DE52E1A4934F800C357E3 /* MainMenu.xib in Resources */, 432 | ); 433 | runOnlyForDeploymentPostprocessing = 0; 434 | }; 435 | 898845D01A490CE000003EF2 /* Resources */ = { 436 | isa = PBXResourcesBuildPhase; 437 | buildActionMask = 2147483647; 438 | files = ( 439 | 723DE5711A49386B00C357E3 /* Main.storyboard in Resources */, 440 | 723DE5701A49386B00C357E3 /* LaunchScreen.xib in Resources */, 441 | 723DE56A1A49385C00C357E3 /* Images.xcassets in Resources */, 442 | ); 443 | runOnlyForDeploymentPostprocessing = 0; 444 | }; 445 | /* End PBXResourcesBuildPhase section */ 446 | 447 | /* Begin PBXSourcesBuildPhase section */ 448 | 723DE5311A49353C00C357E3 /* Sources */ = { 449 | isa = PBXSourcesBuildPhase; 450 | buildActionMask = 2147483647; 451 | files = ( 452 | 724C2DCD1A4CD64600472402 /* PlatformExtensions.swift in Sources */, 453 | 2F3D09861A4C212A001ED0BF /* Memoization.swift in Sources */, 454 | 723DE55C1A49360F00C357E3 /* ColorDifference.swift in Sources */, 455 | 72431C0F1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */, 456 | 723DE55D1A49361100C357E3 /* DominantColors.swift in Sources */, 457 | 723DE55E1A49361200C357E3 /* KMeans.swift in Sources */, 458 | 723DE5601A49361900C357E3 /* ColorSpaceConversion.m in Sources */, 459 | 723DE55F1A49361700C357E3 /* INVector3.m in Sources */, 460 | ); 461 | runOnlyForDeploymentPostprocessing = 0; 462 | }; 463 | 723DE59A1A4938DD00C357E3 /* Sources */ = { 464 | isa = PBXSourcesBuildPhase; 465 | buildActionMask = 2147483647; 466 | files = ( 467 | 724C2DCE1A4CD64600472402 /* PlatformExtensions.swift in Sources */, 468 | 2F3D09871A4C212A001ED0BF /* Memoization.swift in Sources */, 469 | 723DE5C31A49399E00C357E3 /* ColorDifference.swift in Sources */, 470 | 72431C101A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */, 471 | 723DE5C41A4939A000C357E3 /* DominantColors.swift in Sources */, 472 | 723DE5C51A4939A200C357E3 /* KMeans.swift in Sources */, 473 | 723DE5C61A4939A300C357E3 /* ColorSpaceConversion.m in Sources */, 474 | 723DE5C71A4939A500C357E3 /* INVector3.m in Sources */, 475 | ); 476 | runOnlyForDeploymentPostprocessing = 0; 477 | }; 478 | 72D797B41A43F44D00D32E7C /* Sources */ = { 479 | isa = PBXSourcesBuildPhase; 480 | buildActionMask = 2147483647; 481 | files = ( 482 | 723DE5291A4934E200C357E3 /* AppDelegate.swift in Sources */, 483 | 723DE52A1A4934E200C357E3 /* DragAndDropImageView.swift in Sources */, 484 | ); 485 | runOnlyForDeploymentPostprocessing = 0; 486 | }; 487 | 898845CE1A490CE000003EF2 /* Sources */ = { 488 | isa = PBXSourcesBuildPhase; 489 | buildActionMask = 2147483647; 490 | files = ( 491 | 723DE56B1A49385C00C357E3 /* ViewController.swift in Sources */, 492 | 723DE5691A49385C00C357E3 /* AppDelegate.swift in Sources */, 493 | ); 494 | runOnlyForDeploymentPostprocessing = 0; 495 | }; 496 | /* End PBXSourcesBuildPhase section */ 497 | 498 | /* Begin PBXTargetDependency section */ 499 | 723DE54C1A49353C00C357E3 /* PBXTargetDependency */ = { 500 | isa = PBXTargetDependency; 501 | target = 723DE5351A49353C00C357E3 /* DominantColor-Mac */; 502 | targetProxy = 723DE54B1A49353C00C357E3 /* PBXContainerItemProxy */; 503 | }; 504 | 723DE5B51A4938DD00C357E3 /* PBXTargetDependency */ = { 505 | isa = PBXTargetDependency; 506 | target = 723DE59E1A4938DD00C357E3 /* DominantColor-iOS */; 507 | targetProxy = 723DE5B41A4938DD00C357E3 /* PBXContainerItemProxy */; 508 | }; 509 | /* End PBXTargetDependency section */ 510 | 511 | /* Begin PBXVariantGroup section */ 512 | 723DE52C1A4934F800C357E3 /* MainMenu.xib */ = { 513 | isa = PBXVariantGroup; 514 | children = ( 515 | 723DE52D1A4934F800C357E3 /* Base */, 516 | ); 517 | name = MainMenu.xib; 518 | sourceTree = ""; 519 | }; 520 | 723DE56C1A49386B00C357E3 /* LaunchScreen.xib */ = { 521 | isa = PBXVariantGroup; 522 | children = ( 523 | 723DE56D1A49386B00C357E3 /* Base */, 524 | ); 525 | name = LaunchScreen.xib; 526 | sourceTree = ""; 527 | }; 528 | 723DE56E1A49386B00C357E3 /* Main.storyboard */ = { 529 | isa = PBXVariantGroup; 530 | children = ( 531 | 723DE56F1A49386B00C357E3 /* Base */, 532 | ); 533 | name = Main.storyboard; 534 | sourceTree = ""; 535 | }; 536 | /* End PBXVariantGroup section */ 537 | 538 | /* Begin XCBuildConfiguration section */ 539 | 723DE5501A49353C00C357E3 /* Debug */ = { 540 | isa = XCBuildConfiguration; 541 | buildSettings = { 542 | COMBINE_HIDPI_IMAGES = YES; 543 | CURRENT_PROJECT_VERSION = 1; 544 | DEFINES_MODULE = YES; 545 | DYLIB_COMPATIBILITY_VERSION = 1; 546 | DYLIB_CURRENT_VERSION = 1; 547 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 548 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 549 | FRAMEWORK_VERSION = A; 550 | GCC_PREPROCESSOR_DEFINITIONS = ( 551 | "DEBUG=1", 552 | "$(inherited)", 553 | ); 554 | INFOPLIST_FILE = DominantColor/Shared/Info.plist; 555 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 556 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 557 | PRODUCT_NAME = DominantColor; 558 | SKIP_INSTALL = YES; 559 | SWIFT_OBJC_BRIDGING_HEADER = ""; 560 | VERSIONING_SYSTEM = "apple-generic"; 561 | VERSION_INFO_PREFIX = ""; 562 | }; 563 | name = Debug; 564 | }; 565 | 723DE5511A49353C00C357E3 /* Release */ = { 566 | isa = XCBuildConfiguration; 567 | buildSettings = { 568 | COMBINE_HIDPI_IMAGES = YES; 569 | CURRENT_PROJECT_VERSION = 1; 570 | DEFINES_MODULE = YES; 571 | DYLIB_COMPATIBILITY_VERSION = 1; 572 | DYLIB_CURRENT_VERSION = 1; 573 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 574 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 575 | FRAMEWORK_VERSION = A; 576 | INFOPLIST_FILE = DominantColor/Shared/Info.plist; 577 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 578 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 579 | PRODUCT_NAME = DominantColor; 580 | SKIP_INSTALL = YES; 581 | SWIFT_OBJC_BRIDGING_HEADER = ""; 582 | VERSIONING_SYSTEM = "apple-generic"; 583 | VERSION_INFO_PREFIX = ""; 584 | }; 585 | name = Release; 586 | }; 587 | 723DE5B91A4938DD00C357E3 /* Debug */ = { 588 | isa = XCBuildConfiguration; 589 | buildSettings = { 590 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 591 | CURRENT_PROJECT_VERSION = 1; 592 | DEFINES_MODULE = YES; 593 | DYLIB_COMPATIBILITY_VERSION = 1; 594 | DYLIB_CURRENT_VERSION = 1; 595 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 596 | GCC_PREPROCESSOR_DEFINITIONS = ( 597 | "DEBUG=1", 598 | "$(inherited)", 599 | ); 600 | INFOPLIST_FILE = DominantColor/iOS/Info.plist; 601 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 602 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 603 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 604 | PRODUCT_NAME = DominantColor; 605 | SDKROOT = iphoneos; 606 | SKIP_INSTALL = YES; 607 | TARGETED_DEVICE_FAMILY = "1,2"; 608 | VERSIONING_SYSTEM = "apple-generic"; 609 | VERSION_INFO_PREFIX = ""; 610 | }; 611 | name = Debug; 612 | }; 613 | 723DE5BA1A4938DD00C357E3 /* Release */ = { 614 | isa = XCBuildConfiguration; 615 | buildSettings = { 616 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 617 | CURRENT_PROJECT_VERSION = 1; 618 | DEFINES_MODULE = YES; 619 | DYLIB_COMPATIBILITY_VERSION = 1; 620 | DYLIB_CURRENT_VERSION = 1; 621 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 622 | INFOPLIST_FILE = DominantColor/iOS/Info.plist; 623 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 624 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 625 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 626 | PRODUCT_NAME = DominantColor; 627 | SDKROOT = iphoneos; 628 | SKIP_INSTALL = YES; 629 | TARGETED_DEVICE_FAMILY = "1,2"; 630 | VALIDATE_PRODUCT = YES; 631 | VERSIONING_SYSTEM = "apple-generic"; 632 | VERSION_INFO_PREFIX = ""; 633 | }; 634 | name = Release; 635 | }; 636 | 72D797D01A43F44D00D32E7C /* Debug */ = { 637 | isa = XCBuildConfiguration; 638 | buildSettings = { 639 | ALWAYS_SEARCH_USER_PATHS = NO; 640 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 641 | CLANG_CXX_LIBRARY = "libc++"; 642 | CLANG_ENABLE_MODULES = YES; 643 | CLANG_ENABLE_OBJC_ARC = YES; 644 | CLANG_WARN_BOOL_CONVERSION = YES; 645 | CLANG_WARN_CONSTANT_CONVERSION = YES; 646 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 647 | CLANG_WARN_EMPTY_BODY = YES; 648 | CLANG_WARN_ENUM_CONVERSION = YES; 649 | CLANG_WARN_INT_CONVERSION = YES; 650 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 651 | CLANG_WARN_UNREACHABLE_CODE = YES; 652 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 653 | CODE_SIGN_IDENTITY = "-"; 654 | COPY_PHASE_STRIP = NO; 655 | ENABLE_STRICT_OBJC_MSGSEND = YES; 656 | GCC_C_LANGUAGE_STANDARD = gnu99; 657 | GCC_DYNAMIC_NO_PIC = NO; 658 | GCC_OPTIMIZATION_LEVEL = 0; 659 | GCC_PREPROCESSOR_DEFINITIONS = ( 660 | "DEBUG=1", 661 | "$(inherited)", 662 | ); 663 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 664 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 665 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 666 | GCC_WARN_UNDECLARED_SELECTOR = YES; 667 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 668 | GCC_WARN_UNUSED_FUNCTION = YES; 669 | GCC_WARN_UNUSED_VARIABLE = YES; 670 | MACOSX_DEPLOYMENT_TARGET = 10.10; 671 | MTL_ENABLE_DEBUG_INFO = YES; 672 | ONLY_ACTIVE_ARCH = YES; 673 | SDKROOT = macosx; 674 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 675 | }; 676 | name = Debug; 677 | }; 678 | 72D797D11A43F44D00D32E7C /* Release */ = { 679 | isa = XCBuildConfiguration; 680 | buildSettings = { 681 | ALWAYS_SEARCH_USER_PATHS = NO; 682 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 683 | CLANG_CXX_LIBRARY = "libc++"; 684 | CLANG_ENABLE_MODULES = YES; 685 | CLANG_ENABLE_OBJC_ARC = YES; 686 | CLANG_WARN_BOOL_CONVERSION = YES; 687 | CLANG_WARN_CONSTANT_CONVERSION = YES; 688 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 689 | CLANG_WARN_EMPTY_BODY = YES; 690 | CLANG_WARN_ENUM_CONVERSION = YES; 691 | CLANG_WARN_INT_CONVERSION = YES; 692 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 693 | CLANG_WARN_UNREACHABLE_CODE = YES; 694 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 695 | CODE_SIGN_IDENTITY = "-"; 696 | COPY_PHASE_STRIP = YES; 697 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 698 | ENABLE_NS_ASSERTIONS = NO; 699 | ENABLE_STRICT_OBJC_MSGSEND = YES; 700 | GCC_C_LANGUAGE_STANDARD = gnu99; 701 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 702 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 703 | GCC_WARN_UNDECLARED_SELECTOR = YES; 704 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 705 | GCC_WARN_UNUSED_FUNCTION = YES; 706 | GCC_WARN_UNUSED_VARIABLE = YES; 707 | MACOSX_DEPLOYMENT_TARGET = 10.10; 708 | MTL_ENABLE_DEBUG_INFO = NO; 709 | SDKROOT = macosx; 710 | }; 711 | name = Release; 712 | }; 713 | 72D797D31A43F44D00D32E7C /* Debug */ = { 714 | isa = XCBuildConfiguration; 715 | buildSettings = { 716 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 717 | CLANG_ENABLE_MODULES = YES; 718 | COMBINE_HIDPI_IMAGES = YES; 719 | HEADER_SEARCH_PATHS = ( 720 | "$(inherited)", 721 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 722 | ); 723 | INFOPLIST_FILE = DominantColor/Mac/Info.plist; 724 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 725 | PRODUCT_NAME = "$(TARGET_NAME)"; 726 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/Mac/ExampleMac-Bridging-Header.h"; 727 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 728 | }; 729 | name = Debug; 730 | }; 731 | 72D797D41A43F44D00D32E7C /* Release */ = { 732 | isa = XCBuildConfiguration; 733 | buildSettings = { 734 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 735 | CLANG_ENABLE_MODULES = YES; 736 | COMBINE_HIDPI_IMAGES = YES; 737 | HEADER_SEARCH_PATHS = ( 738 | "$(inherited)", 739 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 740 | ); 741 | INFOPLIST_FILE = DominantColor/Mac/Info.plist; 742 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 743 | PRODUCT_NAME = "$(TARGET_NAME)"; 744 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/Mac/ExampleMac-Bridging-Header.h"; 745 | }; 746 | name = Release; 747 | }; 748 | 898845EE1A490CE000003EF2 /* Debug */ = { 749 | isa = XCBuildConfiguration; 750 | buildSettings = { 751 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 752 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 753 | GCC_PREPROCESSOR_DEFINITIONS = ( 754 | "DEBUG=1", 755 | "$(inherited)", 756 | ); 757 | INFOPLIST_FILE = DominantColor/iOS/Info.plist; 758 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 759 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 760 | PRODUCT_NAME = "$(TARGET_NAME)"; 761 | SDKROOT = iphoneos; 762 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/iOS/ExampleiOS-Bridging-Header.h"; 763 | }; 764 | name = Debug; 765 | }; 766 | 898845EF1A490CE000003EF2 /* Release */ = { 767 | isa = XCBuildConfiguration; 768 | buildSettings = { 769 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 770 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 771 | INFOPLIST_FILE = DominantColor/iOS/Info.plist; 772 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 773 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 774 | PRODUCT_NAME = "$(TARGET_NAME)"; 775 | SDKROOT = iphoneos; 776 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/iOS/ExampleiOS-Bridging-Header.h"; 777 | VALIDATE_PRODUCT = YES; 778 | }; 779 | name = Release; 780 | }; 781 | /* End XCBuildConfiguration section */ 782 | 783 | /* Begin XCConfigurationList section */ 784 | 723DE54F1A49353C00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-Mac" */ = { 785 | isa = XCConfigurationList; 786 | buildConfigurations = ( 787 | 723DE5501A49353C00C357E3 /* Debug */, 788 | 723DE5511A49353C00C357E3 /* Release */, 789 | ); 790 | defaultConfigurationIsVisible = 0; 791 | defaultConfigurationName = Release; 792 | }; 793 | 723DE5B81A4938DD00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-iOS" */ = { 794 | isa = XCConfigurationList; 795 | buildConfigurations = ( 796 | 723DE5B91A4938DD00C357E3 /* Debug */, 797 | 723DE5BA1A4938DD00C357E3 /* Release */, 798 | ); 799 | defaultConfigurationIsVisible = 0; 800 | defaultConfigurationName = Release; 801 | }; 802 | 72D797B31A43F44D00D32E7C /* Build configuration list for PBXProject "DominantColor" */ = { 803 | isa = XCConfigurationList; 804 | buildConfigurations = ( 805 | 72D797D01A43F44D00D32E7C /* Debug */, 806 | 72D797D11A43F44D00D32E7C /* Release */, 807 | ); 808 | defaultConfigurationIsVisible = 0; 809 | defaultConfigurationName = Release; 810 | }; 811 | 72D797D21A43F44D00D32E7C /* Build configuration list for PBXNativeTarget "ExampleMac" */ = { 812 | isa = XCConfigurationList; 813 | buildConfigurations = ( 814 | 72D797D31A43F44D00D32E7C /* Debug */, 815 | 72D797D41A43F44D00D32E7C /* Release */, 816 | ); 817 | defaultConfigurationIsVisible = 0; 818 | defaultConfigurationName = Release; 819 | }; 820 | 898845F21A490CE000003EF2 /* Build configuration list for PBXNativeTarget "ExampleiOS" */ = { 821 | isa = XCConfigurationList; 822 | buildConfigurations = ( 823 | 898845EE1A490CE000003EF2 /* Debug */, 824 | 898845EF1A490CE000003EF2 /* Release */, 825 | ); 826 | defaultConfigurationIsVisible = 0; 827 | defaultConfigurationName = Release; 828 | }; 829 | /* End XCConfigurationList section */ 830 | }; 831 | rootObject = 72D797B01A43F44D00D32E7C /* Project object */; 832 | } 833 | -------------------------------------------------------------------------------- /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 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | Default 546 | 547 | 548 | 549 | 550 | 551 | 552 | Left to Right 553 | 554 | 555 | 556 | 557 | 558 | 559 | Right to Left 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | Default 571 | 572 | 573 | 574 | 575 | 576 | 577 | Left to Right 578 | 579 | 580 | 581 | 582 | 583 | 584 | Right to Left 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 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 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | --------------------------------------------------------------------------------