├── .swift-version
├── _Pods.xcodeproj
├── Example
├── Example
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── bush.imageset
│ │ │ ├── 440px-George-W-Bush.jpeg
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ ├── ViewController.swift
│ ├── Info.plist
│ └── SceneDelegate.swift
├── Podfile
├── swiftVibrant.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── swiftVibrant-Example.xcscheme
│ └── project.pbxproj
└── Tests
│ ├── Info.plist
│ └── Tests.swift
├── swiftVibrant
├── Constants.swift
├── Filter.swift
├── Builder.swift
├── Quantizer.swift
├── Vibrant.swift
├── VibrantColors.swift
├── MMCQ.swift
├── Vbox.swift
├── Image.swift
├── Util.swift
├── ColorConverters.swift
└── Generator.swift
├── .travis.yml
├── .gitignore
├── LICENSE
├── swift-vibrant.podspec
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.0
2 |
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | use_frameworks!
2 | target 'swiftVibrant_Tests' do
3 | pod 'swift-vibrant', :path => '../'
4 | end
5 | target 'Example' do
6 | pod 'swift-vibrant', :path => '../'
7 | end
8 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/bush.imageset/440px-George-W-Bush.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bd452/swift-vibrant/HEAD/Example/Example/Assets.xcassets/bush.imageset/440px-George-W-Bush.jpeg
--------------------------------------------------------------------------------
/Example/swiftVibrant.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/bush.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "440px-George-W-Bush.jpeg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/swiftVibrant/Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // swift-vibrant
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | internal let signalBits = 5
12 | internal let rightShift = 8 - signalBits
13 | internal let multiplier = 1 << rightShift
14 | internal let histogramSize = 1 << (3 * signalBits)
15 | internal let vboxLength = 1 << signalBits
16 | internal let fractionByPopulation = 0.75
17 | internal let maxIterations = 1000
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # references:
2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/
3 | # * https://github.com/supermarin/xcpretty#usage
4 |
5 | osx_image: xcode7.3
6 | language: objective-c
7 | # cache: cocoapods
8 | # podfile: Example/Podfile
9 | # before_install:
10 | # - gem install cocoapods # Since Travis is not always on latest version
11 | # - pod install --project-directory=Example
12 | script:
13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/swiftVibrant.xcworkspace -scheme swiftVibrant-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
14 | - pod lib lint
15 |
--------------------------------------------------------------------------------
/Example/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata/
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 |
22 | # Bundler
23 | .bundle
24 |
25 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
26 | # Carthage/Checkouts
27 |
28 | Carthage/Build
29 |
30 | # We recommend against adding the Pods directory to your .gitignore. However
31 | # you should judge for yourself, the pros and cons are mentioned at:
32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
33 | #
34 | # Note: if you ignore the Pods directory, make sure to uncomment
35 | # `pod install` in .travis.yml
36 | #
37 | # Pods/
38 |
--------------------------------------------------------------------------------
/Example/Tests/Tests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import swiftVibrant
3 |
4 | class Tests: XCTestCase {
5 |
6 | override func setUp() {
7 | super.setUp()
8 | // Put setup code here. This method is called before the invocation of each test method in the class.
9 | }
10 |
11 | override func tearDown() {
12 | // Put teardown code here. This method is called after the invocation of each test method in the class.
13 | super.tearDown()
14 | }
15 |
16 | func testGetColors() {
17 |
18 | }
19 |
20 | func testExample() {
21 | // This is an example of a functional test case.
22 | XCTAssert(true, "Pass")
23 | }
24 |
25 | func testPerformanceExample() {
26 | // This is an example of a performance test case.
27 | self.measure() {
28 | // Put the code you want to measure the time of here.
29 | }
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Bryce Dougherty
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/swiftVibrant/Filter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Filter.swift
3 | // swift-vibrant-ios
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class Filter {
12 | public typealias filterFunction = (_ red: UInt8, _ green: UInt8, _ blue: UInt8, _ alpha: UInt8)->Bool
13 |
14 | var f: filterFunction
15 | var id: String
16 |
17 | public init(_ f: @escaping filterFunction) {
18 | self.f = f
19 | self.id = UUID().uuidString
20 | }
21 |
22 | private init(_ f: @escaping filterFunction, id: String) {
23 | self.f = f
24 | self.id = id
25 | }
26 |
27 | public static func combineFilters (filters: [Filter])->Filter? {
28 | if filters.count == 0 { return nil }
29 | let newFilterFunction:filterFunction = { r,g,b,a in
30 | if a == 0 { return false }
31 | for f in filters {
32 | if !f.f(r,g,b,a) { return false }
33 | }
34 | return true
35 | }
36 | return Filter(newFilterFunction)
37 | }
38 |
39 | public static let defaultFilter: Filter = Filter({r, g, b, a in
40 | return a >= 125 && !(r > 250 && g > 250 && b > 250)
41 | }, id: "default")
42 | }
43 |
--------------------------------------------------------------------------------
/Example/Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Example
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Example/Example/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/swift-vibrant.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint swiftVibrant.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'swift-vibrant'
11 | s.version = '0.1.0'
12 | s.summary = 'Extract prominent colors from an image.'
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 |
20 | s.description = <<-DESC
21 | A swift port of the node-vibrant npm module. Extracts the dominant colors from an image and returns them as an easily digestible palette.
22 | DESC
23 |
24 | s.homepage = 'https://github.com/bd452/swift-vibrant'
25 | s.license = { :type => 'MIT', :file => 'LICENSE' }
26 | s.author = { 'Bryce Dougherty' => 'bryce.dougherty@gmail.com' }
27 | s.source = { :git => 'https://github.com/bd452/swift-vibrant.git', :tag => s.version.to_s }
28 |
29 | s.ios.deployment_target = '8.0'
30 |
31 | s.source_files = 'swiftVibrant/**/*'
32 |
33 | s.swift_version = '5.0'
34 |
35 | # s.resource_bundles = {
36 | # 'swiftVibrant' => ['swiftVibrant/Assets/*.png']
37 | # }
38 |
39 | # s.public_header_files = 'Pod/Classes/**/*.h'
40 | # s.frameworks = 'UIKit', 'MapKit'
41 | # s.dependency 'AFNetworking', '~> 2.3'
42 | end
43 |
--------------------------------------------------------------------------------
/Example/Example/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/swiftVibrant/Builder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Builder.swift
3 | // swift-vibrant-ios
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 |
10 | import UIKit
11 |
12 | public typealias Callback = (T)->Void
13 |
14 | public class Builder {
15 |
16 | private var _src: UIImage
17 | private var _opts: Vibrant.Options
18 |
19 | init(_ src: UIImage, _ opts: Vibrant.Options = Vibrant.Options()) {
20 | self._src = src
21 | self._opts = opts
22 | }
23 | public func maxColorCount(_ n: Int)->Builder {
24 | self._opts.colorCount = n
25 | return self
26 | }
27 |
28 | public func maxDimension(_ d: CGFloat)->Builder {
29 | self._opts.maxDimension = d
30 | return self
31 | }
32 |
33 | public func quality(_ q: Int)->Builder {
34 | self._opts.quality = q
35 | return self
36 | }
37 |
38 | public func addFilter(_ f: Filter)->Builder {
39 | self._opts.filters.append(f)
40 | return self
41 | }
42 |
43 | public func removeFilter(_ f: Filter)->Builder {
44 | self._opts.filters.removeAll(where: { (callback: Filter) in
45 | callback.id == f.id
46 | })
47 | return self
48 | }
49 |
50 | public func useGenerator(_ generator: @escaping Generator.generator)->Builder {
51 | self._opts.generator = generator
52 | return self
53 | }
54 |
55 | public func useQuantizer(_ quantizer: @escaping Quantizer.quantizer)->Builder {
56 | self._opts.quantizer = quantizer
57 | return self
58 | }
59 |
60 | public func build()->Vibrant {
61 | return Vibrant(src: self._src, opts: self._opts)
62 | }
63 | public func getPalette()->Palette {
64 | return self.build().getPalette()
65 | }
66 | public func getPalette(_ cb: @escaping Callback) {
67 | return self.build().getPalette(cb)
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Example/Example/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Example
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import swiftVibrant
11 |
12 | class ViewController: UIViewController {
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 | let vibrantView = UIView(frame: CGRect(x: 0, y: 100, width: 50, height: 50))
17 | let darkVibrant = UIView(frame: CGRect(x: 50, y: 100, width: 50, height: 50))
18 | let lightVibrant = UIView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
19 | let muted = UIView(frame: CGRect(x: 150, y: 100, width: 50, height: 50))
20 | let lightmuted = UIView(frame: CGRect(x: 200, y: 100, width: 50, height: 50))
21 | let darkMuted = UIView(frame: CGRect(x: 250, y: 100, width: 50, height: 50))
22 | self.view.addSubview(vibrantView)
23 | self.view.addSubview(darkVibrant)
24 | self.view.addSubview(lightVibrant)
25 | self.view.addSubview(muted)
26 | self.view.addSubview(lightmuted)
27 | self.view.addSubview(darkMuted)
28 | super.viewDidLoad()
29 |
30 |
31 | vibrantView.backgroundColor = UIColor.white
32 | darkVibrant.backgroundColor = UIColor.white
33 | lightVibrant.backgroundColor = UIColor.white
34 | muted.backgroundColor = UIColor.white
35 | lightmuted.backgroundColor = UIColor.white
36 | darkMuted.backgroundColor = UIColor.white
37 |
38 | let img = UIImage(named: "bush")!
39 |
40 | Vibrant.from(img).maxDimension(100).getPalette { palette in
41 | vibrantView.backgroundColor = palette.Vibrant?.uiColor
42 | darkVibrant.backgroundColor = palette.DarkVibrant?.uiColor
43 | lightVibrant.backgroundColor = palette.LightVibrant?.uiColor
44 | muted.backgroundColor = palette.Muted?.uiColor
45 | lightmuted.backgroundColor = palette.LightMuted?.uiColor
46 | darkMuted.backgroundColor = palette.DarkMuted?.uiColor
47 | }
48 |
49 |
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/Example/Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 | UISceneStoryboardFile
37 | Main
38 |
39 |
40 |
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIMainStoryboardFile
45 | Main
46 | UIRequiredDeviceCapabilities
47 |
48 | armv7
49 |
50 | UISupportedInterfaceOrientations
51 |
52 | UIInterfaceOrientationPortrait
53 | UIInterfaceOrientationLandscapeLeft
54 | UIInterfaceOrientationLandscapeRight
55 |
56 | UISupportedInterfaceOrientations~ipad
57 |
58 | UIInterfaceOrientationPortrait
59 | UIInterfaceOrientationPortraitUpsideDown
60 | UIInterfaceOrientationLandscapeLeft
61 | UIInterfaceOrientationLandscapeRight
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/swiftVibrant/Quantizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Quantize.swift
3 | // swift-vibrant
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class Quantizer {
12 | public typealias quantizer = (_ pixels: [UInt8], _ options: Vibrant.Options)->[Swatch]
13 |
14 | public static let defaultQuantizer: quantizer = Quantizer().quantize
15 |
16 | private func quantize(pixels: [UInt8], options: Vibrant.Options)->[Swatch] {
17 | let quality = options.quality
18 | let colorcount = options.colorCount
19 | return Quantizer.vibrantQuantizer(pixels: pixels, quality: quality, colorCount: colorcount)
20 | }
21 |
22 | private static func splitBoxes (_ pq: inout [VBox], _ target: Int, hist: [Int]) {
23 | var lastSize = pq.count
24 | while pq.count < target {
25 | let vbox = pq.popLast()
26 |
27 | if (vbox != nil && vbox!.getCount() > 0) {
28 | let vboxes = applyMedianCut(with: hist, vbox: vbox!)
29 | let vbox1 = vboxes.count > 0 ? vboxes[0] : nil
30 | let vbox2 = vboxes.count > 1 ? vboxes[1] : nil
31 | pq.append(vbox1!)
32 | if vbox2 != nil && vbox2!.getCount() > 0 { pq.append(vbox2!) }
33 |
34 | if pq.count == lastSize {
35 | break
36 | } else {
37 | lastSize = pq.count
38 | }
39 | } else {
40 | break
41 | }
42 | }
43 | }
44 |
45 | static func vibrantQuantizer(pixels: [UInt8], quality: Int, colorCount: Int)->[Swatch] {
46 | let (hist, vbox) = makeHistogramAndVBox(from: pixels, quality: quality, ignoreWhite: false)
47 | var pq = [vbox]
48 | splitBoxes(&pq, Int(fractionByPopulation * Double(colorCount)), hist: hist)
49 | pq.sort { (a, b) -> Bool in
50 | a.getCount() * a.getVolume() > b.getCount() * b.getVolume()
51 | }
52 | splitBoxes(&pq, colorCount - pq.count, hist: hist)
53 | return generateSwatches(pq)
54 | }
55 |
56 | private static func generateSwatches (_ pq: [VBox])->[Swatch] {
57 | return pq.map { (box) in
58 | let color = box.rgb()
59 | return Swatch(color, box.getCount())
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/swiftVibrant/Vibrant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Vibrant.swift
3 | // swift-vibrant-ios
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | public class Vibrant {
13 |
14 | public struct Options {
15 | var colorCount: Int = 64
16 |
17 | var quality: Int = 5
18 |
19 | var quantizer: Quantizer.quantizer = Quantizer.defaultQuantizer
20 |
21 | var generator: Generator.generator = Generator.defaultGenerator
22 |
23 | var maxDimension: CGFloat?
24 |
25 | var filters: [Filter] = [Filter.defaultFilter]
26 |
27 | fileprivate var combinedFilter: Filter?
28 | }
29 |
30 | public static func from( _ src: UIImage)->Builder {
31 | return Builder(src)
32 | }
33 |
34 | var opts: Options
35 | var src: UIImage
36 |
37 | private var _palette: Palette?
38 | public var palette: Palette? { _palette }
39 |
40 | public init(src: UIImage, opts: Options?) {
41 | self.src = src
42 | self.opts = opts ?? Options()
43 | self.opts.combinedFilter = Filter.combineFilters(filters: self.opts.filters)
44 | }
45 |
46 | static func process(image: Image, opts: Options)->Palette {
47 | let quantizer = opts.quantizer
48 | let generator = opts.generator
49 | let combinedFilter = opts.combinedFilter!
50 | let maxDimension = opts.maxDimension
51 |
52 | image.scaleTo(size: maxDimension, quality: opts.quality)
53 |
54 |
55 | let imageData = image.applyFilter(combinedFilter)
56 | let swatches = quantizer(imageData, opts)
57 | let colors = Swatch.applyFilter(colors: swatches, filter: combinedFilter)
58 | let palette = generator(colors)
59 | return palette
60 | }
61 |
62 | public func getPalette(_ cb: @escaping Callback) {
63 | DispatchQueue.init(label: "colorProcessor", qos: .background).async {
64 | let palette = self.getPalette()
65 | DispatchQueue.main.async {
66 | cb(palette)
67 | }
68 | }
69 | }
70 |
71 | public func getPalette()->Palette {
72 | let image = Image(image: self.src)
73 | let palette = Vibrant.process(image: image, opts: self.opts)
74 | self._palette = palette
75 | return palette
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Example/Example/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Example
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
12 |
13 | var window: UIWindow?
14 |
15 |
16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
20 | guard let _ = (scene as? UIWindowScene) else { return }
21 | }
22 |
23 | func sceneDidDisconnect(_ scene: UIScene) {
24 | // Called as the scene is being released by the system.
25 | // This occurs shortly after the scene enters the background, or when its session is discarded.
26 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
27 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
28 | }
29 |
30 | func sceneDidBecomeActive(_ scene: UIScene) {
31 | // Called when the scene has moved from an inactive state to an active state.
32 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
33 | }
34 |
35 | func sceneWillResignActive(_ scene: UIScene) {
36 | // Called when the scene will move from an active state to an inactive state.
37 | // This may occur due to temporary interruptions (ex. an incoming phone call).
38 | }
39 |
40 | func sceneWillEnterForeground(_ scene: UIScene) {
41 | // Called as the scene transitions from the background to the foreground.
42 | // Use this method to undo the changes made on entering the background.
43 | }
44 |
45 | func sceneDidEnterBackground(_ scene: UIScene) {
46 | // Called as the scene transitions from the foreground to the background.
47 | // Use this method to save data, release shared resources, and store enough scene-specific state information
48 | // to restore the scene back to its current state.
49 | }
50 |
51 |
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/swiftVibrant/VibrantColors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VibrantColors.swift
3 | // swift-vibrant-ios
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public typealias Vec3 = (T, T, T)
12 | public typealias RGB = (r: UInt8, g: UInt8, b: UInt8)
13 | public typealias HSL = (h: Double, s: Double, l: Double)
14 | public typealias XYZ = (x: Double, y: Double, z: Double)
15 | public typealias LAB = (L: Double, a: Double, b: Double)
16 |
17 |
18 | //export type Vec3 = [Double, Double, Double]
19 | //
20 |
21 | public struct Palette {
22 | public var Vibrant: Swatch?
23 | public var Muted: Swatch?
24 | public var DarkVibrant: Swatch?
25 | public var DarkMuted: Swatch?
26 | public var LightVibrant: Swatch?
27 | public var LightMuted: Swatch?
28 | }
29 |
30 | public class Swatch: Equatable {
31 |
32 | private var _hsl: HSL?
33 |
34 | private var _rgb: RGB
35 |
36 | private var _yiq: Double?
37 |
38 | private var _population: Int
39 |
40 | private var _hex: String?
41 |
42 | private var _uiColor: UIColor?
43 |
44 | var r: UInt8 { self._rgb.r }
45 |
46 | var g: UInt8 { self._rgb.g }
47 |
48 | var b: UInt8 { self._rgb.b }
49 |
50 | var rgb: RGB { self._rgb }
51 |
52 | public var hsl: HSL {
53 | if self._hsl == nil {
54 | let rgb = self._rgb
55 | self._hsl = apply(rgbToHsl, rgb)
56 | }
57 | return self._hsl!
58 | }
59 |
60 | public var hex: String {
61 | if self._hex == nil {
62 | let rgb = self._rgb
63 | self._hex = apply(rgbToHex, rgb)
64 | }
65 | return self._hex!
66 | }
67 |
68 |
69 | public var uiColor: UIColor {
70 | if self._uiColor == nil {
71 | let rgb = self._rgb
72 | self._uiColor = apply(rgbToUIColor, rgb)
73 | }
74 | return self._uiColor!
75 | }
76 |
77 | static func applyFilter(colors: [Swatch], filter: Filter)->[Swatch] {
78 | var colors = colors
79 | colors = colors.filter { (swatch) -> Bool in
80 | let r = swatch.r
81 | let g = swatch.g
82 | let b = swatch.b
83 | return filter.f(r, g, b, 255)
84 | }
85 | return colors
86 | }
87 |
88 | public var population: Int { self._population }
89 |
90 |
91 | func toDict()->[String: Any] {
92 | return [
93 | "rgb": self.rgb,
94 | "population": self.population
95 | ]
96 | }
97 |
98 | var toJSON = toDict
99 |
100 | private func getYiq()->Double {
101 | if self._yiq == nil {
102 | let (r,g,b) = self._rgb
103 | let mr = Int(r) * 299
104 | let mg = Int(g) * 598
105 | let mb = Int(b) * 114
106 | let mult = mr + mg + mb
107 | self._yiq = Double(mult) / 1000
108 | }
109 | return self._yiq!
110 | }
111 |
112 | private var _titleTextColor: UIColor?
113 |
114 | private var _bodyTextColor: UIColor?
115 |
116 | public var titleTextColor: UIColor {
117 | if self._titleTextColor == nil {
118 | self._titleTextColor = self.getYiq() < 200 ? .white : .black
119 | }
120 | return self._titleTextColor!
121 | }
122 |
123 | public var bodyTextColor: UIColor {
124 | if self._bodyTextColor == nil {
125 | self._bodyTextColor = self.getYiq() < 150 ? .white : .black
126 | }
127 | return self._bodyTextColor!
128 | }
129 |
130 | public func getTitleTextColor()->UIColor {
131 | return self.titleTextColor
132 | }
133 |
134 | public func getBodyTextColor()->UIColor {
135 | return self.bodyTextColor
136 | }
137 |
138 | public static func == (lhs: Swatch, rhs: Swatch) -> Bool {
139 | return lhs.rgb == rhs.rgb
140 | }
141 |
142 | init(_ rgb: RGB, _ population: Int) {
143 | self._rgb = rgb
144 | self._population = population
145 | }
146 |
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/swiftVibrant/MMCQ.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MMCQ.swift
3 | // ColorThiefSwift
4 | //
5 | // Created by Kazuki Ohara on 2017/02/11.
6 | // Copyright © 2019 Kazuki Ohara. All rights reserved.
7 | //
8 | // License
9 | // -------
10 | // MIT License
11 | // https://github.com/yamoridon/ColorThiefSwift/blob/master/LICENSE
12 | //
13 | // Thanks
14 | // ------
15 | // Lokesh Dhakar - for the original Color Thief JavaScript version
16 | // http://lokeshdhakar.com/projects/color-thief/
17 | // Sven Woltmann - for the fast Java Implementation
18 | // https://github.com/SvenWoltmann/color-thief-java
19 |
20 | import Foundation
21 | import UIKit
22 |
23 | /// MMCQ (modified median cut quantization) algorithm from
24 | /// the Leptonica library (http://www.leptonica.com/).
25 |
26 | /// Get reduced-space color index for a pixel.
27 | ///
28 | /// - Parameters:
29 | /// - red: the red value
30 | /// - green: the green value
31 | /// - blue: the blue value
32 | /// - Returns: the color index
33 |
34 |
35 | public struct Color {
36 | public var r: UInt8
37 | public var g: UInt8
38 | public var b: UInt8
39 |
40 | init(r: UInt8, g: UInt8, b: UInt8) {
41 | self.r = r
42 | self.g = g
43 | self.b = b
44 | }
45 |
46 | public func makeUIColor() -> UIColor {
47 | return UIColor(red: CGFloat(r) / CGFloat(255), green: CGFloat(g) / CGFloat(255), blue: CGFloat(b) / CGFloat(255), alpha: CGFloat(1))
48 | }
49 | }
50 |
51 | enum ColorChannel {
52 | case r
53 | case g
54 | case b
55 | }
56 |
57 | /// 3D color space box.
58 |
59 |
60 | /// Color map.
61 | open class ColorMap {
62 |
63 | var vboxes = [VBox]()
64 |
65 | func push(_ vbox: VBox) {
66 | vboxes.append(vbox)
67 | }
68 |
69 | open func makePalette() -> [Color] {
70 | return vboxes.map { $0.getAverage() }
71 | }
72 |
73 | open func makeNearestColor(to color: Color) -> Color {
74 | var nearestDistance = Int.max
75 | var nearestColor = Color(r: 0, g: 0, b: 0)
76 |
77 | for vbox in vboxes {
78 | let vbColor = vbox.getAverage()
79 | let dr = abs(Int(color.r) - Int(vbColor.r))
80 | let dg = abs(Int(color.g) - Int(vbColor.g))
81 | let db = abs(Int(color.b) - Int(vbColor.b))
82 | let distance = dr + dg + db
83 | if distance < nearestDistance {
84 | nearestDistance = distance
85 | nearestColor = vbColor
86 | }
87 | }
88 |
89 | return nearestColor
90 | }
91 | }
92 |
93 | /// Histo (1-d array, giving the number of pixels in each quantized region of color space), or null on error.
94 | internal func makeHistogramAndVBox(from pixels: [UInt8], quality: Int, ignoreWhite: Bool) -> ([Int], VBox) {
95 | var histogram = [Int](repeating: 0, count: histogramSize)
96 | var rMin = UInt8.max
97 | var rMax = UInt8.min
98 | var gMin = UInt8.max
99 | var gMax = UInt8.min
100 | var bMin = UInt8.max
101 | var bMax = UInt8.min
102 |
103 | let pixelCount = pixels.count / 4
104 | for i in stride(from: 0, to: pixelCount, by: quality) {
105 | let a = pixels[i * 4 + 0]
106 | let b = pixels[i * 4 + 1]
107 | let g = pixels[i * 4 + 2]
108 | let r = pixels[i * 4 + 3]
109 |
110 | // If pixel is not mostly opaque or white
111 | guard a >= 125 && !(ignoreWhite && r > 250 && g > 250 && b > 250) else {
112 | continue
113 | }
114 |
115 | let shiftedR = r >> UInt8(rightShift)
116 | let shiftedG = g >> UInt8(rightShift)
117 | let shiftedB = b >> UInt8(rightShift)
118 |
119 | // find min/max
120 | rMin = min(rMin, shiftedR)
121 | rMax = max(rMax, shiftedR)
122 | gMin = min(gMin, shiftedG)
123 | gMax = max(gMax, shiftedG)
124 | bMin = min(bMin, shiftedB)
125 | bMax = max(bMax, shiftedB)
126 |
127 | // increment histgram
128 | let index = makeColorIndexOf(red: Int(shiftedR), green: Int(shiftedG), blue: Int(shiftedB))
129 | histogram[index] += 1
130 | }
131 |
132 | let vbox = VBox(rMin: rMin, rMax: rMax, gMin: gMin, gMax: gMax, bMin: bMin, bMax: bMax, histogram: histogram)
133 | return (histogram, vbox)
134 | }
135 |
--------------------------------------------------------------------------------
/Example/swiftVibrant.xcodeproj/xcshareddata/xcschemes/swiftVibrant-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
48 |
54 |
55 |
56 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
80 |
82 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
101 |
107 |
108 |
109 |
110 |
112 |
113 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/swiftVibrant/Vbox.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Vbox.swift
3 | // swift-vibrant
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class VBox {
12 |
13 | var rMin: UInt8
14 | var rMax: UInt8
15 | var gMin: UInt8
16 | var gMax: UInt8
17 | var bMin: UInt8
18 | var bMax: UInt8
19 |
20 | private let histogram: [Int]
21 |
22 | private var average: Color?
23 | private var volume: Int?
24 | private var count: Int?
25 |
26 | init(rMin: UInt8, rMax: UInt8, gMin: UInt8, gMax: UInt8, bMin: UInt8, bMax: UInt8, histogram: [Int]) {
27 | self.rMin = rMin
28 | self.rMax = rMax
29 | self.gMin = gMin
30 | self.gMax = gMax
31 | self.bMin = bMin
32 | self.bMax = bMax
33 | self.histogram = histogram
34 | }
35 |
36 | init(vbox: VBox) {
37 | self.rMin = vbox.rMin
38 | self.rMax = vbox.rMax
39 | self.gMin = vbox.gMin
40 | self.gMax = vbox.gMax
41 | self.bMin = vbox.bMin
42 | self.bMax = vbox.bMax
43 | self.histogram = vbox.histogram
44 | }
45 |
46 | func makeRange(min: UInt8, max: UInt8) -> CountableRange {
47 | if min <= max {
48 | return Int(min) ..< Int(max + 1)
49 | } else {
50 | return Int(max) ..< Int(max)
51 | }
52 | }
53 |
54 | var rRange: CountableRange { return makeRange(min: rMin, max: rMax) }
55 | var gRange: CountableRange { return makeRange(min: gMin, max: gMax) }
56 | var bRange: CountableRange { return makeRange(min: bMin, max: bMax) }
57 |
58 | /// Get 3 dimensional volume of the color space
59 | ///
60 | /// - Parameter force: force recalculate
61 | /// - Returns: the volume
62 | func getVolume(forceRecalculate force: Bool = false) -> Int {
63 | if let volume = volume, !force {
64 | return volume
65 | } else {
66 | let volume = (Int(rMax) - Int(rMin) + 1) * (Int(gMax) - Int(gMin) + 1) * (Int(bMax) - Int(bMin) + 1)
67 | self.volume = volume
68 | return volume
69 | }
70 | }
71 |
72 | /// Get total count of histogram samples
73 | ///
74 | /// - Parameter force: force recalculate
75 | /// - Returns: the volume
76 | func getCount(forceRecalculate force: Bool = false) -> Int {
77 | if let count = count, !force {
78 | return count
79 | } else {
80 | var count = 0
81 | for i in rRange {
82 | for j in gRange {
83 | for k in bRange {
84 | let index = makeColorIndexOf(red: i, green: j, blue: k)
85 | count += histogram[index]
86 | }
87 | }
88 | }
89 | self.count = count
90 | return count
91 | }
92 | }
93 |
94 | func getAverage(forceRecalculate force: Bool = false) -> Color {
95 | if let average = average, !force {
96 | return average
97 | } else {
98 | var ntot = 0
99 |
100 | var rSum = 0
101 | var gSum = 0
102 | var bSum = 0
103 |
104 | for i in rRange {
105 | for j in gRange {
106 | for k in bRange {
107 | let index = makeColorIndexOf(red: i, green: j, blue: k)
108 | let hval = histogram[index]
109 | ntot += hval
110 | rSum += Int(Double(hval) * (Double(i) + 0.5) * Double(multiplier))
111 | gSum += Int(Double(hval) * (Double(j) + 0.5) * Double(multiplier))
112 | bSum += Int(Double(hval) * (Double(k) + 0.5) * Double(multiplier))
113 | }
114 | }
115 | }
116 |
117 | let average: Color
118 | if ntot > 0 {
119 | let r = UInt8(rSum / ntot)
120 | let g = UInt8(gSum / ntot)
121 | let b = UInt8(bSum / ntot)
122 | average = Color(r: r, g: g, b: b)
123 | } else {
124 | let r = UInt8(min(multiplier * (Int(rMin) + Int(rMax) + 1) / 2, 255))
125 | let g = UInt8(min(multiplier * (Int(gMin) + Int(gMax) + 1) / 2, 255))
126 | let b = UInt8(min(multiplier * (Int(bMin) + Int(bMax) + 1) / 2, 255))
127 | average = Color(r: r, g: g, b: b)
128 | }
129 |
130 | self.average = average
131 | return average
132 | }
133 | }
134 |
135 |
136 | func rgb() -> RGB {
137 | let color = self.getAverage()
138 | return (color.r, color.g, color.b)
139 | }
140 |
141 | func widestColorChannel() -> ColorChannel {
142 | let rWidth = rMax - rMin
143 | let gWidth = gMax - gMin
144 | let bWidth = bMax - bMin
145 | switch max(rWidth, gWidth, bWidth) {
146 | case rWidth:
147 | return .r
148 | case gWidth:
149 | return .g
150 | default:
151 | return .b
152 | }
153 | }
154 |
155 | }
156 |
--------------------------------------------------------------------------------
/swiftVibrant/Image.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Image.swift
3 | // swift-vibrant-ios
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | public class Image {
13 | var image: UIImage
14 |
15 | init(image: UIImage) {
16 | self.image = image
17 | }
18 |
19 | func applyFilter(_ filter: Filter)->[UInt8] {
20 | guard let imageData = self.getImageData() else {
21 | return []
22 | }
23 | var pixels = imageData
24 | let n = pixels.count / 4
25 | var offset: Int
26 | var r, g, b, a: UInt8
27 |
28 | for i in 0..[UInt8]? {
43 | return Image.makeBytes(from: self.image)
44 | }
45 |
46 | func scaleTo(size maxSize: CGFloat?, quality: Int) {
47 | let width = image.size.width
48 | let height = image.size.height
49 |
50 | var ratio:CGFloat = 1.0
51 | if maxSize != nil && maxSize! > 0 {
52 | let maxSide = max(width, height)
53 | if maxSide > CGFloat(maxSize!) {
54 | ratio = CGFloat(maxSize!) / maxSide
55 | }
56 | } else {
57 | ratio = 1 / CGFloat(quality)
58 | }
59 | if ratio < 1 {
60 | self.scale(by: ratio)
61 | }
62 | }
63 |
64 | func scale(by scale: CGFloat) {
65 | self.image = Image.scaleImage(image: self.image, by: scale)
66 | }
67 |
68 | private static func scaleImage(image: UIImage, by scale: CGFloat)->UIImage {
69 | if scale == 1 { return image }
70 |
71 | let imageRef = image.cgImage!
72 | let width = imageRef.width
73 | let height = imageRef.height
74 |
75 | var bounds = CGSize(width: width, height: height)
76 |
77 | bounds.width = CGFloat(width) * scale
78 | bounds.height = CGFloat(height) * scale
79 |
80 | UIGraphicsBeginImageContext(bounds)
81 | image.draw(in: CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height))
82 | let imageCopy = UIGraphicsGetImageFromCurrentImageContext()
83 | UIGraphicsEndImageContext()
84 |
85 | return imageCopy ?? image
86 | }
87 |
88 | private static func makeBytes(from image: UIImage) -> [UInt8]? {
89 | guard let cgImage = image.cgImage else {
90 | return nil
91 | }
92 | if isCompatibleImage(cgImage) {
93 | return makeBytesFromCompatibleImage(cgImage)
94 | } else {
95 | return makeBytesFromIncompatibleImage(cgImage)
96 | }
97 | }
98 |
99 | private static func isCompatibleImage(_ cgImage: CGImage) -> Bool {
100 | guard let colorSpace = cgImage.colorSpace else {
101 | return false
102 | }
103 | if colorSpace.model != .rgb {
104 | return false
105 | }
106 | let bitmapInfo = cgImage.bitmapInfo
107 | let alpha = bitmapInfo.rawValue & CGBitmapInfo.alphaInfoMask.rawValue
108 | let alphaRequirement = (alpha == CGImageAlphaInfo.noneSkipLast.rawValue || alpha == CGImageAlphaInfo.last.rawValue)
109 | let byteOrder = bitmapInfo.rawValue & CGBitmapInfo.byteOrderMask.rawValue
110 | let byteOrderRequirement = (byteOrder == CGBitmapInfo.byteOrder32Little.rawValue)
111 | if !(alphaRequirement && byteOrderRequirement) {
112 | return false
113 | }
114 | if cgImage.bitsPerComponent != 8 {
115 | return false
116 | }
117 | if cgImage.bitsPerPixel != 32 {
118 | return false
119 | }
120 | if cgImage.bytesPerRow != cgImage.width * 4 {
121 | return false
122 | }
123 | return true
124 | }
125 | private static func makeBytesFromCompatibleImage(_ image: CGImage) -> [UInt8]? {
126 | guard let dataProvider = image.dataProvider else {
127 | return nil
128 | }
129 | guard let data = dataProvider.data else {
130 | return nil
131 | }
132 | let length = CFDataGetLength(data)
133 | var rawData = [UInt8](repeating: 0, count: length)
134 | CFDataGetBytes(data, CFRange(location: 0, length: length), &rawData)
135 | return rawData
136 | }
137 |
138 | private static func makeBytesFromIncompatibleImage(_ image: CGImage) -> [UInt8]? {
139 | let width = image.width
140 | let height = image.height
141 | var rawData = [UInt8](repeating: 0, count: width * height * 4)
142 | guard let context = CGContext(
143 | data: &rawData,
144 | width: width,
145 | height: height,
146 | bitsPerComponent: 8,
147 | bytesPerRow: 4 * width,
148 | space: CGColorSpaceCreateDeviceRGB(),
149 | bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue) else {
150 | return nil
151 | }
152 | context.draw(image, in: CGRect(x: 0, y: 0, width: width, height: height))
153 | return rawData
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/swiftVibrant/Util.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Util.swift
3 | // swift-vibrant
4 | //
5 | // Created by Bryce Dougherty on 5/3/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | internal func applyMedianCut(with histogram: [Int], vbox: VBox) -> [VBox] {
12 | guard vbox.getCount() != 0 else {
13 | return []
14 | }
15 |
16 | // only one pixel, no split
17 | guard vbox.getCount() != 1 else {
18 | return [vbox]
19 | }
20 |
21 | // Find the partial sum arrays along the selected axis.
22 | var total = 0
23 | var partialSum = [Int](repeating: -1, count: vboxLength) // -1 = not set / 0 = 0
24 |
25 | let axis = vbox.widestColorChannel()
26 | switch axis {
27 | case .r:
28 | for i in vbox.rRange {
29 | var sum = 0
30 | for j in vbox.gRange {
31 | for k in vbox.bRange {
32 | let index = makeColorIndexOf(red: i, green: j, blue: k)
33 | sum += histogram[index]
34 | }
35 | }
36 | total += sum
37 | partialSum[i] = total
38 | }
39 | case .g:
40 | for i in vbox.gRange {
41 | var sum = 0
42 | for j in vbox.rRange {
43 | for k in vbox.bRange {
44 | let index = makeColorIndexOf(red: j, green: i, blue: k)
45 | sum += histogram[index]
46 | }
47 | }
48 | total += sum
49 | partialSum[i] = total
50 | }
51 | case .b:
52 | for i in vbox.bRange {
53 | var sum = 0
54 | for j in vbox.rRange {
55 | for k in vbox.gRange {
56 | let index = makeColorIndexOf(red: j, green: k, blue: i)
57 | sum += histogram[index]
58 | }
59 | }
60 | total += sum
61 | partialSum[i] = total
62 | }
63 | }
64 |
65 | var lookAheadSum = [Int](repeating: -1, count: vboxLength) // -1 = not set / 0 = 0
66 | for (i, sum) in partialSum.enumerated() where sum != -1 {
67 | lookAheadSum[i] = total - sum
68 | }
69 |
70 | return cut(by: axis, vbox: vbox, partialSum: partialSum, lookAheadSum: lookAheadSum, total: total)
71 | }
72 |
73 | internal func cut(by axis: ColorChannel, vbox: VBox, partialSum: [Int], lookAheadSum: [Int], total: Int) -> [VBox] {
74 | let vboxMin: Int
75 | let vboxMax: Int
76 |
77 | switch axis {
78 | case .r:
79 | vboxMin = Int(vbox.rMin)
80 | vboxMax = Int(vbox.rMax)
81 | case .g:
82 | vboxMin = Int(vbox.gMin)
83 | vboxMax = Int(vbox.gMax)
84 | case .b:
85 | vboxMin = Int(vbox.bMin)
86 | vboxMax = Int(vbox.bMax)
87 | }
88 |
89 | for i in vboxMin ... vboxMax where partialSum[i] > total / 2 {
90 | let vbox1 = VBox(vbox: vbox)
91 | let vbox2 = VBox(vbox: vbox)
92 |
93 | let left = i - vboxMin
94 | let right = vboxMax - i
95 |
96 | var d2: Int
97 | if left <= right {
98 | d2 = min(vboxMax - 1, i + right / 2)
99 | } else {
100 | // 2.0 and cast to int is necessary to have the same
101 | // behaviour as in JavaScript
102 | d2 = max(vboxMin, Int(Double(i - 1) - Double(left) / 2.0))
103 | }
104 |
105 | // avoid 0-count
106 | while d2 < 0 || partialSum[d2] <= 0 {
107 | d2 += 1
108 | }
109 | var count2 = lookAheadSum[d2]
110 | while count2 == 0 && d2 > 0 && partialSum[d2 - 1] > 0 {
111 | d2 -= 1
112 | count2 = lookAheadSum[d2]
113 | }
114 |
115 | // set dimensions
116 | switch axis {
117 | case .r:
118 | vbox1.rMax = UInt8(d2)
119 | vbox2.rMin = UInt8(d2 + 1)
120 | case .g:
121 | vbox1.gMax = UInt8(d2)
122 | vbox2.gMin = UInt8(d2 + 1)
123 | case .b:
124 | vbox1.bMax = UInt8(d2)
125 | vbox2.bMin = UInt8(d2 + 1)
126 | }
127 |
128 | return [vbox1, vbox2]
129 | }
130 |
131 | fatalError("VBox can't be cut")
132 | }
133 |
134 | internal func iterate(over queue: inout [VBox], comparator: (VBox, VBox) -> Bool, target: Int, histogram: [Int]) {
135 | var color = 1
136 |
137 | for _ in 0 ..< maxIterations {
138 | guard let vbox = queue.last else {
139 | return
140 | }
141 |
142 | if vbox.getCount() == 0 {
143 | queue.sort(by: comparator)
144 | continue
145 | }
146 | queue.removeLast()
147 |
148 | // do the cut
149 | let vboxes = applyMedianCut(with: histogram, vbox: vbox)
150 | queue.append(vboxes[0])
151 | if vboxes.count == 2 {
152 | queue.append(vboxes[1])
153 | color += 1
154 | }
155 | queue.sort(by: comparator)
156 |
157 | if color >= target {
158 | return
159 | }
160 | }
161 | }
162 |
163 | internal func compareByCount(_ a: VBox, _ b: VBox) -> Bool {
164 | return a.getCount() < b.getCount()
165 | }
166 |
167 | internal func compareByProduct(_ a: VBox, _ b: VBox) -> Bool {
168 | let aCount = a.getCount()
169 | let bCount = b.getCount()
170 | let aVolume = a.getVolume()
171 | let bVolume = b.getVolume()
172 |
173 | if aCount == bCount {
174 | // If count is 0 for both (or the same), sort by volume
175 | return aVolume < bVolume
176 | } else {
177 | // Otherwise sort by products
178 | let aProduct = Int64(aCount) * Int64(aVolume)
179 | let bProduct = Int64(bCount) * Int64(bVolume)
180 | return aProduct < bProduct
181 | }
182 | }
183 |
184 | func makeColorIndexOf(red: Int, green: Int, blue: Int) -> Int {
185 | return (red << (2 * signalBits)) + (green << signalBits) + blue
186 | }
187 |
188 | func apply(_ fn: (T) -> V, _ args: T) -> V {
189 | return fn(args)
190 | }
191 |
--------------------------------------------------------------------------------
/swiftVibrant/ColorConverters.swift:
--------------------------------------------------------------------------------
1 | //
2 | // util.swift
3 | // swift-vibrant
4 | //
5 | // Created by Bryce Dougherty on 4/30/20.
6 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 |
13 | struct DELTAE94_DIFF_STATUS {
14 | static let NA:Int = 0
15 | static let PERFECT:Int = 1
16 | static let CLOSE:Int = 2
17 | static let GOOD:Int = 10
18 | static let SIMILAR:Int = 50
19 | }
20 |
21 | //public typealias Double = Double
22 |
23 | struct newErr: Error {
24 | init(_ message: String) {
25 | self.message = message
26 | }
27 | let message: String
28 | }
29 |
30 | public func uiColorToRgb(_ color: UIColor)->RGB {
31 | var r: CGFloat = 0
32 | var g: CGFloat = 0
33 | var b: CGFloat = 0
34 | color.getRed(&r, green: &g, blue: &b, alpha: nil)
35 | return (UInt8(r * 255), UInt8(g * 255), UInt8(b * 255))
36 | }
37 |
38 | public func rgbToUIColor(_ r: UInt8, _ g: UInt8, _ b: UInt8)->UIColor {
39 | return UIColor.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: 1)
40 | }
41 | public func uiColorToHsl(_ color: UIColor)->HSL {
42 | var h:CGFloat = 0
43 | var s:CGFloat = 0
44 | var l:CGFloat = 0
45 | color.getHue(&h, saturation: &s, brightness: &l, alpha: nil)
46 | return (Double(h),Double(s),Double(l))
47 | }
48 | public func hslToUIColor(_ h: Double, _ s: Double, _ l: Double)->UIColor {
49 | return UIColor.init(hue: CGFloat(h), saturation: CGFloat(s), brightness: CGFloat(l), alpha: 1)
50 | }
51 |
52 |
53 | public func hexToRgb(_ hex: String)->RGB? {
54 | let r, g, b: UInt8
55 |
56 | if hex.hasPrefix("#") {
57 | let start = hex.index(hex.startIndex, offsetBy: 1)
58 | let hexColor = String(hex[start...])
59 |
60 | if hexColor.count == 8 {
61 | let scanner = Scanner(string: hexColor)
62 | var hexDouble: UInt64 = 0
63 |
64 |
65 | if scanner.scanHexInt64(&hexDouble) {
66 | r = UInt8(hexDouble & 0xff000000) >> 24
67 | g = UInt8(hexDouble & 0x00ff0000) >> 16
68 | b = UInt8(hexDouble & 0x0000ff00) >> 8
69 |
70 | return (r, g, b)
71 | }
72 | }
73 | }
74 | return nil
75 | }
76 |
77 |
78 |
79 | public func rgbToHex(_ r: UInt8, _ g: UInt8, _ b: UInt8)->String {
80 | return "#" + String(format:"%02X", r) + String(format:"%02X", g) + String(format:"%02X", b)
81 | }
82 |
83 | public func rgbToHsl(r: UInt8, g: UInt8, b: UInt8)-> Vec3 {
84 | let r = Double(r) / 255
85 | let g = Double(g) / 255
86 | let b = Double(b) / 255
87 | let maxVal = max(r, g, b)
88 | let minVal = min(r, g, b)
89 | var h: Double
90 | let s: Double
91 | let l = (maxVal + minVal) / 2
92 | if (maxVal == minVal) {
93 | h = 0
94 | s = 0
95 | } else {
96 | let d = maxVal - minVal
97 | s = l > 0.5 ? d / (2 - maxVal - minVal) : d / (maxVal + minVal)
98 | switch (maxVal) {
99 | case r:
100 | h = (g - b) / d + (g < b ? 6 : 0)
101 | break
102 | case g:
103 | h = (b - r) / d + 2
104 | break
105 | case b:
106 | h = (r - g) / d + 4
107 | break
108 | default:
109 | h = 0
110 | break
111 | }
112 | h /= 6
113 | }
114 | return (h, s, l)
115 | }
116 |
117 | public func hslToRgb(_ h: Double, _ s: Double, _ l: Double)-> RGB {
118 | var r: Double
119 | var g: Double
120 | var b: Double
121 |
122 | func hue2rgb(_ p: Double, _ q: Double, _ t: Double)-> Double {
123 | var t = t
124 | if (t < 0) { t += 1 }
125 | if (t > 1) { t -= 1 }
126 | if (t < 1 / 6) { return p + (q - p) * 6 * t }
127 | if (t < 1 / 2) { return q }
128 | if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6 }
129 | return p
130 | }
131 |
132 | if (s == 0) {
133 | r = l
134 | g = l
135 | b = l
136 | } else {
137 | let q = l < 0.5 ? l * (1 + s) : l + s - (l * s)
138 | let p = 2 * l - q
139 | r = hue2rgb(p, q, h + 1 / 3)
140 | g = hue2rgb(p, q, h)
141 | b = hue2rgb(p, q, h - (1 / 3))
142 | }
143 | return(
144 | UInt8(r * 255),
145 | UInt8(g * 255),
146 | UInt8(b * 255)
147 | )
148 | }
149 |
150 |
151 | public func rgbToXyz(_ r: UInt8, _ g: UInt8, _ b: UInt8)->XYZ {
152 | var r = Double(r) / 255
153 | var g = Double(g) / 255
154 | var b = Double(b) / 255
155 |
156 | r = r > 0.04045 ? pow((r + 0.005) / 1.055, 2.4) : r / 12.92
157 | g = g > 0.04045 ? pow((g + 0.005) / 1.055, 2.4) : g / 12.92
158 | b = b > 0.04045 ? pow((b + 0.005) / 1.055, 2.4) : b / 12.92
159 |
160 | r *= 100
161 | g *= 100
162 | b *= 100
163 |
164 | let x = r * 0.4124 + g * 0.3576 + b * 0.1805
165 | let y = r * 0.2126 + g * 0.7152 + b * 0.0722
166 | let z = r * 0.0193 + g * 0.1192 + b * 0.9505
167 |
168 | return (x: x,y: y,z: z)
169 | }
170 |
171 | public func xyzToCIELab(_ x: Double, _ y: Double, _ z: Double)-> LAB {
172 | let REF_X: Double = 95.047
173 | let REF_Y: Double = 100
174 | let REF_Z: Double = 108.883
175 |
176 | var x = x / REF_X
177 | var y = y / REF_Y
178 | var z = z / REF_Z
179 |
180 | x = x > 0.008856 ? pow(x, 1 / 3) : 7.787 * x + 16 / 116
181 | y = y > 0.008856 ? pow(y, 1 / 3) : 7.787 * y + 16 / 116
182 | z = z > 0.008856 ? pow(z, 1 / 3) : 7.787 * z + 16 / 116
183 |
184 | let L = 116 * y - 16
185 | let a = 500 * (x - y)
186 | let b = 200 * (y - z)
187 |
188 | return (L: L, a: a, b: b)
189 | }
190 |
191 |
192 | public func rgbToCIELab(_ r: UInt8, _ g: UInt8, _ b: UInt8)->LAB {
193 | let (x,y,z) = rgbToXyz(r, g, b)
194 | return xyzToCIELab(x, y, z)
195 | }
196 |
197 | public func deltaE94(_ lab1: Vec3, _ lab2: Vec3)->Double {
198 | let WEIGHT_L:Double = 1
199 | let WEIGHT_C:Double = 1
200 | let WEIGHT_H:Double = 1
201 |
202 | let (L1, a1, b1) = lab1
203 | let (L2, a2, b2) = lab2
204 | let dL = L1 - L2
205 | let da = a1 - a2
206 | let db = b1 - b2
207 |
208 | let xC1 = sqrt(a1 * a1 + b1 * b1)
209 | let xC2 = sqrt(a2 * a2 + b2 * b2)
210 |
211 | var xDL = L2 - L1
212 | var xDC = xC2 - xC1
213 | let xDE = sqrt(dL * dL + da * da + db * db)
214 |
215 | var xDH = (sqrt(xDE) > sqrt(abs(xDL)) + sqrt(abs(xDC)))
216 | ? sqrt(xDE * xDE - xDL * xDL - xDC * xDC)
217 | : 0
218 |
219 | let xSC = 1 + 0.045 * xC1
220 | let xSH = 1 + 0.015 * xC1
221 |
222 | xDL /= WEIGHT_L
223 | xDC /= WEIGHT_C * xSC
224 | xDH /= WEIGHT_H * xSH
225 |
226 | return sqrt(xDL * xDL + xDC * xDC + xDH * xDH)
227 | }
228 |
229 | public func rgbDiff(_ rgb1: RGB, _ rgb2: RGB)->Double {
230 | let lab1 = apply(rgbToCIELab, rgb1)
231 | let lab2 = apply(rgbToCIELab, rgb2)
232 | return deltaE94(lab1, lab2)
233 | }
234 |
235 | public func hexDiff(_ hex1: String, _ hex2: String)->Double {
236 | let rgb1 = hexToRgb(hex1)!
237 | let rgb2 = hexToRgb(hex2)!
238 | return rgbDiff(rgb1, rgb2)
239 | }
240 |
241 | public func getColorDiffStatus(_ d: Int)->String {
242 | if (d < DELTAE94_DIFF_STATUS.NA) { return "N/A" }
243 | // Not perceptible by human eyes
244 | if (d <= DELTAE94_DIFF_STATUS.PERFECT) { return "Perfect" }
245 | // Perceptible through close observation
246 | if (d <= DELTAE94_DIFF_STATUS.CLOSE) { return "Close" }
247 | // Perceptible at a glance
248 | if (d <= DELTAE94_DIFF_STATUS.GOOD) { return "Good" }
249 | // Colors are more similar than opposite
250 | if (d < DELTAE94_DIFF_STATUS.SIMILAR) { return "Similar" }
251 | return "Wrong"
252 | }
253 |
254 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # swift-vibrant
2 |
3 | Generate a color palette from a UIImage.
4 |
5 | Other color palette generators will only give you a list of the dominant colors in an image. Vibrant does much more.
6 |
7 | Now I know what you're thinking: "But Bryce, colors from an image! That's what I want! That's why I'm on your repository in the first place!"
8 |
9 | Well I've got news for you: swift-vibrant (or [node-vibrant](https://github.com/Vibrant-Colors/node-vibrant/), if you use JavaScript. No affiliation but that's what this project is based off of) will not only give you the dominant colors in an image, oh no. It will *also* give you a fully featured *palette*. What's the difference you might ask?
10 |
11 | It provides:
12 | * A Vibrant Color (Duh)
13 | * A Muted Color
14 | * A Dark Vibrant Color
15 | * A Dark Muted Color
16 | * A Light Vibrant Color
17 | * A Light Muted Color
18 |
19 | As well as 2 different text colors for each of the above that look good on the color they're designed for.
20 |
21 | Don't believe me? There's a 100% money back guarantee! (Just kidding, it's MIT licensed aka free for your use).
22 |
23 | ## Installation
24 |
25 | swift-vibrant is available through [CocoaPods](https://cocoapods.org). To install
26 | it, simply add the following line to your Podfile:
27 |
28 | ```ruby
29 | pod 'swift-vibrant'
30 | ```
31 |
32 | ## Usage
33 | ```swift
34 | import swiftVibrant
35 |
36 | let image = UIImage(named: "name_of_image")
37 |
38 | // Calling from a background thread
39 | Vibrant.from(image).getPalette({ palette in
40 | // do stuff with your palette
41 | })
42 | // Calling in main thread
43 | Vibrant.from(image).getPalette()
44 | /// do stuff with your palette here
45 |
46 | // Using constructor
47 | let v = Vibrant(image, Vibrant.Options)
48 | v.getPalette({ palette in
49 | // do stuff with your palette
50 | })
51 | ```
52 |
53 |
54 | ## References
55 |
56 | ### `Vibrant`
57 | Main class of `swift-vibrant`.
58 |
59 | #### `Vibrant.from(src: UIImage): Builder`
60 | Make a `Builder` for an image. Returns a `Builder` instance.
61 |
62 | #### `constructor(src: UIImage, opts: Vibrant.Options)`
63 |
64 | Name | Description
65 | ------- | ---------------------------------------
66 | `image` | Your UIImage instance
67 | `opts` | Options (optional)
68 |
69 |
70 | ##### `Options`
71 |
72 | ```swift
73 | public struct Options {
74 | var colorCount: Int = 64
75 | var quality: Int = 5
76 | var quantizer: Quantizer.quantizer
77 | var generator: Generator.generator
78 | var maxDimension: CGFloat?
79 | var filters: [Filter]
80 | }
81 | ```
82 |
83 | Field | Default | Description
84 | -------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------
85 | `colorCount` | 64 | amount of colors in initial palette from which the swatches will be generated
86 | `quality` | 5 | Scale down factor used in downsampling stage. `1` means no downsampling. If `maxDimension` is set, this value will not be used.
87 | `quantizer` | `Quantizer.defaultQuantizer` | A `Quantizer` function
88 | `generator` | `Generator.defaultGenerator` | An `Generator` function
89 | `maxDimension` | `nil` | The max size of the image's longer side used in downsampling stage. This field will override `quality`.
90 | `filters` | `[]` | An array of `Filter`
91 |
92 | ##### `quantizer`
93 |
94 | ```swift
95 | public typealias quantizer = (_ pixels: [UInt8], _ options: Vibrant.Options)->[Swatch]
96 | ```
97 |
98 | ##### `generator`
99 |
100 | ```swift
101 | public typealias generator = (_ swatches: [Swatch])->Palette
102 | ```
103 |
104 |
105 | ##### `Filter`
106 |
107 | Returns `true` if the color is to be kept.
108 |
109 | ```swift
110 | public class Filter {
111 | public typealias filterFunction = (_ red: UInt8, _ green: UInt8, _ blue: UInt8, _ alpha: UInt8)->Bool
112 |
113 | var f: filterFunction
114 | }
115 | ```
116 |
117 | #### `getPalette(cb: (Palette)->Void)`
118 |
119 | Name | Description
120 | ---- | -----------------
121 | `cb` | callback function.
122 |
123 | ### `Vibrant.Builder`
124 | Helper class for change configurations and create a `Vibrant` instance. Methods of a `Builder` instance can be chained like:
125 |
126 | ```ts
127 | Vibrant.from(src)
128 | .quality(1)
129 | .clearFilters()
130 | // ...
131 | .getPalette()
132 | ```
133 |
134 | #### `quality(q: Int): Builder`
135 | Sets `opts.quality` to `q`. Returns this `Builder` instance.
136 |
137 | #### `maxColorCount(n: Int): Builder`
138 | Sets `opts.colorCount` to `n`. Returns this `Builder` instance.
139 |
140 | #### `maxDimension(d: Int): Builder`
141 | Sets `opts.maxDimension` to `d`. Returns this `Builder` instance.
142 |
143 | #### `addFilter(f: Filter): Builder`
144 | Adds a filter function. Returns this `Builder` instance.
145 |
146 | #### `removeFilter(f: Filter): Builder`
147 | Removes a filter function. Returns this `Builder` instance.
148 |
149 | #### `clearFilters(): Builder`
150 | Clear all filters. Returns this `Builder` instance.
151 |
152 | #### `useQuantizer(quantizer: Quantizer.quantizer): Builder`
153 | Specifies which `Quantizer` implementation class to use. Returns this `Builder` instance.
154 |
155 | #### `useGenerator(generator: Generator.generator): Builder`
156 | Sets `opts.generator` to `generator`. Returns this `Builder` instance.
157 |
158 | #### `build(): Vibrant`
159 | Builds and returns a `Vibrant` instance as configured.
160 |
161 | #### `getPalette(cb: (Palette)->Void)`
162 | Builds a `Vibrant` instance as configured and calls its `getPalette` method on a background thread. Calls `cb` on main thread.
163 |
164 | #### `getPalette()->Palette`
165 |
166 | Builds a `Vibrant` instance as configured and calls its `getPalette` method on the main thread.
167 |
168 | ### `Vibrant.Swatch`
169 | Represents a color swatch generated from an image's palette.
170 |
171 | #### `Vec3`
172 |
173 | ```swift
174 | public typealias Vec3 = (T, T, T)
175 | ```
176 |
177 | #### `RGB`
178 |
179 | ```swift
180 | public typealias RGB = (r: UInt8, g: UInt8, b: UInt8)
181 | ```
182 |
183 | #### `HSL`
184 |
185 | ```swift
186 | public typealias HSL = (r: Double, g: Double, b: Double)
187 | ```
188 |
189 | #### `XYZ`
190 |
191 | ```swift
192 | public typealias HSL = (r: Double, g: Double, b: Double)
193 | ```
194 |
195 | #### `LAB`
196 |
197 | ```swift
198 | public typealias HSL = (r: Double, g: Double, b: Double)
199 | ```
200 |
201 | ####
202 |
203 | #### `constructor(rgb: RGB, population: Int)`
204 |
205 | Internal use.
206 |
207 | Name | Description
208 | ------------ | -----------------------------------
209 | `rgb` | `[r, g, b]`
210 | `population` | Population of the color in an image
211 |
212 | #### `hsl: HSL`
213 | #### `getPopulation(): Int`
214 | #### `rgb: RGB`
215 | #### `hex: String`
216 |
217 | Returns a hexadecimal value of the swatch
218 |
219 | #### `getTitleTextColor(): UIColor`
220 | Returns an appropriate color to use for any 'title' text which is displayed over this `Swatch`'s color.
221 |
222 | #### `getBodyTextColor(): UIColor`
223 | Returns an appropriate color to use for any 'body' text which is displayed over this `Swatch`'s color.
224 |
225 | ### `Vibrant.Util`
226 | Utility methods. Internal usage.
227 |
228 | #### `hexToRgb(hex: string): RGB`
229 | #### `rgbToHex(r: UInt8, g: UInt8, b: UInt8): String`
230 | #### `hslToRgb(h: Double, s: Double, l: Double): RGB`
231 | #### `rgbToHsl(r: UInt8, g: UInt8, b: UInt8): HSL`
232 | #### `xyzToRgb(x: Double, y: Double, z: Double): RGB`
233 | #### `rgbToXyz(r: UInt8, g: UInt8, b: UInt8): XYZ`
234 | #### `xyzToCIELab(x: Double, y: Double, z: Double): LAB`
235 | #### `rgbToCIELab(l: UInt8, a: UInt8, b: UInt8): LAB`
236 | #### `deltaE94(lab1: Double, lab2: Double): Double`
237 | Computes CIE delta E 1994 diff between `lab1` and `lab2`. The 2 colors are in CIE-Lab color space. Used in tests to compare 2 colors' perceptual similarity.
238 |
239 | #### `rgbDiff(rgb1: RGB, rgb2: RGB): Double`
240 | Compute CIE delta E 1994 diff between `rgb1` and `rgb2`.
241 |
242 | #### `hexDiff(hex1: String, hex2: String): Double`
243 | Compute CIE delta E 1994 diff between `hex1` and `hex2`.
244 |
245 | #### `getColorDiffStatus(d: Double): String`
246 | Gets a string to describe the meaning of the color diff. Used in tests.
247 |
248 | Delta E | Perception | Returns
249 | -------- | -------------------------------------- | -----------
250 | <= 1.0 | Not perceptible by human eyes. | `"Perfect"`
251 | 1 - 2 | Perceptible through close observation. | `"Close"`
252 | 2 - 10 | Perceptible at a glance. | `"Good"`
253 | 11 - 49 | Colors are more similar than opposite | `"Similar"`
254 | 50 - 100 | Colors are exact opposite | `Wrong`
255 |
256 | ## Notes
257 | - This library uses code from [ColorThiefSwift](https://github.com/yamoridon/ColorThiefSwift), mostly borrowing from their implementation of the modified median cut quantization (MMCQ) algorithm
258 | - A majority of the rest was adapted from [node-vibrant](https://github.com/Vibrant-Colors/node-vibrant/)
259 |
260 | ## Author
261 |
262 | Bryce Dougherty: bryce.dougherty@gmail.com
263 |
264 | ## Credits
265 |
266 | Kazuki Ohara: [ColorThiefSwift](https://github.com/yamoridon/ColorThiefSwift)
267 |
268 | Jari Zwarts: [node-vibrant](https://github.com/Vibrant-Colors/node-vibrant/)
269 |
270 | ## License
271 |
272 | swiftVibrant is available under the MIT license. See the LICENSE file for more info.
273 |
--------------------------------------------------------------------------------
/swiftVibrant/Generator.swift:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // default.swift
4 | // swift-vibrant
5 | //
6 | // Created by Bryce Dougherty on 4/30/20.
7 | // Copyright © 2020 Bryce Dougherty. All rights reserved.
8 | //
9 |
10 | import UIKit
11 |
12 | public class Generator {
13 |
14 | public typealias generator = (_ swatches: [Swatch])->Palette
15 |
16 | public struct Options {
17 | var targetDarkLuma: Double = 0.26
18 | var maxDarkLuma: Double = 0.45
19 | var minLightLuma: Double = 0.55
20 | var targetLightLuma: Double = 0.74
21 | var minNormalLuma: Double = 0.3
22 | var targetNormalLuma: Double = 0.5
23 | var maxNormalLuma: Double = 0.7
24 | var targetMutesSaturation: Double = 0.3
25 | var maxMutesSaturation: Double = 0.4
26 | var targetVibrantSaturation: Double = 1.0
27 | var minVibrantSaturation: Double = 0.35
28 | var weightSaturation: Double = 3.0
29 | var weightLuma: Double = 6.5
30 | var weightPopulation: Double = 0.5
31 | }
32 | var options: Options
33 | init(options: Options) {
34 | self.options = options
35 | }
36 |
37 |
38 | public static let defaultGenerator:generator = Generator(options: Options()).generate
39 |
40 | private func generate(swatches: [Swatch])->Palette {
41 | let maxPopulation = findMaxPopulation(swatches: swatches)
42 | var palette = generateVariationColors(swatches: swatches, maxPopulation: maxPopulation, opts: options)
43 | palette = generateEmptySwatches(palette: palette, opts: options)
44 | return palette
45 | }
46 |
47 | func findMaxPopulation( swatches: [Swatch])->Int {
48 | var p: Int = 0
49 | swatches.forEach { (s: Swatch) in
50 | p = max(p, s.population)
51 | }
52 | return p
53 | }
54 |
55 | func isAlreadySelected (palette: Palette, s: Swatch)->Bool {
56 | return palette.Vibrant == s ||
57 | palette.DarkVibrant == s ||
58 | palette.LightVibrant == s ||
59 | palette.Muted == s ||
60 | palette.DarkMuted == s ||
61 | palette.LightMuted == s
62 | }
63 |
64 | func createComparisonValue (
65 | saturation: Double, targetSaturation: Double,
66 | luma: Double, targetLuma: Double,
67 | population: Int, maxPopulation: Int, opts: Options) -> Double {
68 |
69 | func weightedMean (values: Double...)->Double {
70 | var sum: Double = 0
71 | var weightSum: Double = 0
72 | var i = 0
73 | while i < values.count {
74 | let value = values[i]
75 | let weight = values[i + 1]
76 | sum += value * weight
77 | weightSum += weight
78 | i+=2
79 | }
80 | return sum / weightSum
81 | }
82 |
83 | func invertDiff (value: Double, targetValue: Double)->Double {
84 | return 1 - abs(value - targetValue)
85 | }
86 |
87 |
88 | return weightedMean(
89 | values: invertDiff(value: saturation, targetValue: targetSaturation), opts.weightSaturation,
90 | invertDiff(value: luma, targetValue: targetLuma), opts.weightLuma,
91 | Double(population) / Double(maxPopulation), opts.weightPopulation
92 | )
93 | }
94 |
95 | func findColorVariation (palette: Palette, swatches: [Swatch], maxPopulation: Int,
96 | targetLuma: Double,
97 | minLuma: Double,
98 | maxLuma: Double,
99 | targetSaturation: Double,
100 | minSaturation: Double,
101 | maxSaturation: Double,
102 | opts: Options)->Swatch? {
103 |
104 | var max: Swatch? = nil
105 | var maxValue: Double = 0
106 |
107 | swatches.forEach({swatch in
108 | let (_,s,l) = swatch.hsl
109 |
110 | if (s >= minSaturation && s <= maxSaturation &&
111 | l >= minLuma && l <= maxLuma &&
112 | !isAlreadySelected(palette: palette, s: swatch)
113 | ) {
114 | let value = createComparisonValue(saturation: s, targetSaturation: targetSaturation, luma: l, targetLuma: targetLuma, population: swatch.population, maxPopulation: maxPopulation, opts: opts)
115 | if (max == nil || value > maxValue) {
116 | max = swatch
117 | maxValue = value
118 | }
119 | }
120 | })
121 | return max
122 | }
123 |
124 | func generateVariationColors (swatches: [Swatch], maxPopulation: Int, opts: Options)->Palette {
125 |
126 | var palette = Palette()
127 |
128 | palette.Vibrant = findColorVariation(palette: palette, swatches: swatches, maxPopulation: maxPopulation,
129 | targetLuma: opts.targetNormalLuma,
130 | minLuma: opts.minNormalLuma,
131 | maxLuma: opts.maxNormalLuma,
132 | targetSaturation: opts.targetVibrantSaturation,
133 | minSaturation: opts.minVibrantSaturation,
134 | maxSaturation: 1,
135 | opts: opts
136 | )
137 |
138 | palette.LightVibrant = findColorVariation(palette: palette, swatches: swatches, maxPopulation: maxPopulation,
139 | targetLuma: opts.targetLightLuma,
140 | minLuma: opts.minLightLuma,
141 | maxLuma: 1,
142 | targetSaturation: opts.targetVibrantSaturation,
143 | minSaturation: opts.minVibrantSaturation,
144 | maxSaturation: 1,
145 | opts: opts
146 | )
147 |
148 | palette.DarkVibrant = findColorVariation(palette: palette, swatches: swatches, maxPopulation: maxPopulation,
149 | targetLuma: opts.targetDarkLuma,
150 | minLuma: 0,
151 | maxLuma: opts.maxDarkLuma,
152 | targetSaturation: opts.targetVibrantSaturation,
153 | minSaturation: opts.minVibrantSaturation,
154 | maxSaturation: 1,
155 | opts: opts
156 | )
157 |
158 | palette.Muted = findColorVariation(palette: palette, swatches: swatches, maxPopulation: maxPopulation,
159 | targetLuma: opts.targetNormalLuma,
160 | minLuma: opts.minNormalLuma,
161 | maxLuma: opts.maxNormalLuma,
162 | targetSaturation: opts.targetMutesSaturation,
163 | minSaturation: 0,
164 | maxSaturation: opts.maxMutesSaturation,
165 | opts: opts
166 | )
167 |
168 | palette.LightMuted = findColorVariation(palette: palette, swatches: swatches, maxPopulation: maxPopulation,
169 | targetLuma: opts.targetLightLuma,
170 | minLuma: opts.minLightLuma,
171 | maxLuma: 1,
172 | targetSaturation: opts.targetMutesSaturation,
173 | minSaturation: 0,
174 | maxSaturation: opts.maxMutesSaturation,
175 | opts: opts
176 | )
177 |
178 | palette.DarkMuted = findColorVariation(palette: palette, swatches: swatches, maxPopulation: maxPopulation,
179 | targetLuma: opts.targetDarkLuma,
180 | minLuma: 0,
181 | maxLuma: opts.maxDarkLuma,
182 | targetSaturation: opts.targetMutesSaturation,
183 | minSaturation: 0,
184 | maxSaturation: opts.maxMutesSaturation,
185 | opts: opts
186 | )
187 | return palette
188 |
189 | }
190 | //
191 | func generateEmptySwatches (palette: Palette, opts: Options)->Palette {
192 |
193 | var palette = palette
194 | //function _generateEmptySwatches (palette: Palette, maxPopulation: number, opts: DefaultGeneratorOptions): void {
195 | if (palette.Vibrant == nil && palette.DarkVibrant == nil && palette.LightVibrant == nil) {
196 | if (palette.DarkVibrant == nil && palette.DarkMuted != nil) {
197 | var (h, s, l) = palette.DarkMuted!.hsl
198 | l = opts.targetDarkLuma
199 | palette.DarkVibrant = Swatch(hslToRgb(h, s, l), 0)
200 | }
201 | if (palette.LightVibrant == nil && palette.LightMuted != nil) {
202 | var (h, s, l) = palette.LightMuted!.hsl
203 | l = opts.targetDarkLuma
204 | palette.DarkVibrant = Swatch(hslToRgb(h, s, l), 0)
205 | }
206 | }
207 | if (palette.Vibrant == nil && palette.DarkVibrant != nil) {
208 | var (h, s, l) = palette.DarkVibrant!.hsl
209 | l = opts.targetNormalLuma
210 | palette.Vibrant = Swatch(hslToRgb(h, s, l), 0)
211 | } else if (palette.Vibrant == nil && palette.LightVibrant != nil) {
212 | var (h, s, l) = palette.LightVibrant!.hsl
213 | l = opts.targetNormalLuma
214 | palette.Vibrant = Swatch(hslToRgb(h, s, l), 0)
215 | }
216 | if (palette.DarkVibrant == nil && palette.Vibrant != nil) {
217 | var (h, s, l) = palette.Vibrant!.hsl
218 | l = opts.targetDarkLuma
219 | palette.DarkVibrant = Swatch(hslToRgb(h, s, l), 0)
220 | }
221 | if (palette.LightVibrant == nil && palette.Vibrant != nil) {
222 | var (h, s, l) = palette.Vibrant!.hsl
223 | l = opts.targetLightLuma
224 | palette.LightVibrant = Swatch(hslToRgb(h, s, l), 0)
225 | }
226 | if (palette.Muted == nil && palette.Vibrant != nil) {
227 | var (h, s, l) = palette.Vibrant!.hsl
228 | l = opts.targetMutesSaturation
229 | palette.Muted = Swatch(hslToRgb(h, s, l), 0)
230 | }
231 | if (palette.DarkMuted == nil && palette.DarkVibrant != nil) {
232 | var (h, s, l) = palette.DarkVibrant!.hsl
233 | l = opts.targetMutesSaturation
234 | palette.DarkMuted = Swatch(hslToRgb(h, s, l), 0)
235 | }
236 | if (palette.LightMuted == nil && palette.LightVibrant != nil) {
237 | var (h, s, l) = palette.LightVibrant!.hsl
238 | l = opts.targetMutesSaturation
239 | palette.LightMuted = Swatch(hslToRgb(h, s, l), 0)
240 | }
241 | return palette
242 | }
243 | }
244 | /**
245 | ````
246 | const DefaultGenerator: Generator = (swatches: Array, opts?: DefaultGeneratorOptions): Palette => {
247 | opts = defaults({}, opts, DefaultOpts)
248 | let maxPopulation = _findMaxPopulation(swatches)
249 |
250 | let palette = _generateVariationColors(swatches, maxPopulation, opts)
251 | _generateEmptySwatches(palette, maxPopulation, opts)
252 |
253 | return palette
254 | }
255 | ````
256 | */
257 |
--------------------------------------------------------------------------------
/Example/swiftVibrant.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 02CE3D59245EF19700E786F8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CE3D58245EF19700E786F8 /* AppDelegate.swift */; };
11 | 02CE3D5B245EF19700E786F8 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CE3D5A245EF19700E786F8 /* SceneDelegate.swift */; };
12 | 02CE3D5D245EF19700E786F8 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CE3D5C245EF19700E786F8 /* ViewController.swift */; };
13 | 02CE3D60245EF19700E786F8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 02CE3D5E245EF19700E786F8 /* Main.storyboard */; };
14 | 02CE3D62245EF19900E786F8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02CE3D61245EF19900E786F8 /* Assets.xcassets */; };
15 | 02CE3D65245EF19900E786F8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 02CE3D63245EF19900E786F8 /* LaunchScreen.storyboard */; };
16 | 3BD5974AD22A1E555E8FBF93 /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F18D8FE66FC0E27DDB20C53E /* Pods_Example.framework */; };
17 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; };
18 | CBE417F370B6DE8A27E191C0 /* Pods_swiftVibrant_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCA06F1DE6F9C077DEB3AF1E /* Pods_swiftVibrant_Tests.framework */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXFileReference section */
22 | 02CE3D56245EF19700E786F8 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
23 | 02CE3D58245EF19700E786F8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
24 | 02CE3D5A245EF19700E786F8 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
25 | 02CE3D5C245EF19700E786F8 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
26 | 02CE3D5F245EF19700E786F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
27 | 02CE3D61245EF19900E786F8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
28 | 02CE3D64245EF19900E786F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
29 | 02CE3D66245EF19900E786F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
30 | 04B2C6316ED3FEA12D2E413E /* Pods-swiftVibrant_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-swiftVibrant_Tests.release.xcconfig"; path = "Target Support Files/Pods-swiftVibrant_Tests/Pods-swiftVibrant_Tests.release.xcconfig"; sourceTree = ""; };
31 | 04DDE9579463834E92864267 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = ""; };
32 | 1BF125B0EC659770D6757116 /* Pods-swiftVibrant_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-swiftVibrant_Tests.debug.xcconfig"; path = "Target Support Files/Pods-swiftVibrant_Tests/Pods-swiftVibrant_Tests.debug.xcconfig"; sourceTree = ""; };
33 | 607FACE51AFB9204008FA782 /* swiftVibrant_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = swiftVibrant_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
34 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
35 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; };
36 | 631AEB3044C059FA9826ECDF /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = ""; };
37 | C12DA4ED3ADB9413945322DE /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; };
38 | D77DF502E212097128D250B4 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; };
39 | DCA06F1DE6F9C077DEB3AF1E /* Pods_swiftVibrant_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_swiftVibrant_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
40 | EC2887334F293C46996DAFD8 /* swiftVibrant.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = swiftVibrant.podspec; path = ../swiftVibrant.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
41 | F18D8FE66FC0E27DDB20C53E /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
42 | /* End PBXFileReference section */
43 |
44 | /* Begin PBXFrameworksBuildPhase section */
45 | 02CE3D53245EF19700E786F8 /* Frameworks */ = {
46 | isa = PBXFrameworksBuildPhase;
47 | buildActionMask = 2147483647;
48 | files = (
49 | 3BD5974AD22A1E555E8FBF93 /* Pods_Example.framework in Frameworks */,
50 | );
51 | runOnlyForDeploymentPostprocessing = 0;
52 | };
53 | 607FACE21AFB9204008FA782 /* Frameworks */ = {
54 | isa = PBXFrameworksBuildPhase;
55 | buildActionMask = 2147483647;
56 | files = (
57 | CBE417F370B6DE8A27E191C0 /* Pods_swiftVibrant_Tests.framework in Frameworks */,
58 | );
59 | runOnlyForDeploymentPostprocessing = 0;
60 | };
61 | /* End PBXFrameworksBuildPhase section */
62 |
63 | /* Begin PBXGroup section */
64 | 02CE3D57245EF19700E786F8 /* Example */ = {
65 | isa = PBXGroup;
66 | children = (
67 | 02CE3D58245EF19700E786F8 /* AppDelegate.swift */,
68 | 02CE3D5A245EF19700E786F8 /* SceneDelegate.swift */,
69 | 02CE3D5C245EF19700E786F8 /* ViewController.swift */,
70 | 02CE3D5E245EF19700E786F8 /* Main.storyboard */,
71 | 02CE3D61245EF19900E786F8 /* Assets.xcassets */,
72 | 02CE3D63245EF19900E786F8 /* LaunchScreen.storyboard */,
73 | 02CE3D66245EF19900E786F8 /* Info.plist */,
74 | );
75 | path = Example;
76 | sourceTree = "";
77 | };
78 | 582F4894A1C95F6C9057F557 /* Frameworks */ = {
79 | isa = PBXGroup;
80 | children = (
81 | DCA06F1DE6F9C077DEB3AF1E /* Pods_swiftVibrant_Tests.framework */,
82 | F18D8FE66FC0E27DDB20C53E /* Pods_Example.framework */,
83 | );
84 | name = Frameworks;
85 | sourceTree = "";
86 | };
87 | 607FACC71AFB9204008FA782 = {
88 | isa = PBXGroup;
89 | children = (
90 | 607FACF51AFB993E008FA782 /* Podspec Metadata */,
91 | 607FACE81AFB9204008FA782 /* Tests */,
92 | 02CE3D57245EF19700E786F8 /* Example */,
93 | 607FACD11AFB9204008FA782 /* Products */,
94 | 694AD21C6B21895C373D1D37 /* Pods */,
95 | 582F4894A1C95F6C9057F557 /* Frameworks */,
96 | );
97 | sourceTree = "";
98 | };
99 | 607FACD11AFB9204008FA782 /* Products */ = {
100 | isa = PBXGroup;
101 | children = (
102 | 607FACE51AFB9204008FA782 /* swiftVibrant_Tests.xctest */,
103 | 02CE3D56245EF19700E786F8 /* Example.app */,
104 | );
105 | name = Products;
106 | sourceTree = "";
107 | };
108 | 607FACE81AFB9204008FA782 /* Tests */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 607FACEB1AFB9204008FA782 /* Tests.swift */,
112 | 607FACE91AFB9204008FA782 /* Supporting Files */,
113 | );
114 | path = Tests;
115 | sourceTree = "";
116 | };
117 | 607FACE91AFB9204008FA782 /* Supporting Files */ = {
118 | isa = PBXGroup;
119 | children = (
120 | 607FACEA1AFB9204008FA782 /* Info.plist */,
121 | );
122 | name = "Supporting Files";
123 | sourceTree = "";
124 | };
125 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = {
126 | isa = PBXGroup;
127 | children = (
128 | EC2887334F293C46996DAFD8 /* swiftVibrant.podspec */,
129 | D77DF502E212097128D250B4 /* README.md */,
130 | C12DA4ED3ADB9413945322DE /* LICENSE */,
131 | );
132 | name = "Podspec Metadata";
133 | sourceTree = "";
134 | };
135 | 694AD21C6B21895C373D1D37 /* Pods */ = {
136 | isa = PBXGroup;
137 | children = (
138 | 1BF125B0EC659770D6757116 /* Pods-swiftVibrant_Tests.debug.xcconfig */,
139 | 04B2C6316ED3FEA12D2E413E /* Pods-swiftVibrant_Tests.release.xcconfig */,
140 | 04DDE9579463834E92864267 /* Pods-Example.debug.xcconfig */,
141 | 631AEB3044C059FA9826ECDF /* Pods-Example.release.xcconfig */,
142 | );
143 | path = Pods;
144 | sourceTree = "";
145 | };
146 | /* End PBXGroup section */
147 |
148 | /* Begin PBXNativeTarget section */
149 | 02CE3D55245EF19700E786F8 /* Example */ = {
150 | isa = PBXNativeTarget;
151 | buildConfigurationList = 02CE3D67245EF19900E786F8 /* Build configuration list for PBXNativeTarget "Example" */;
152 | buildPhases = (
153 | 02E82B16C38838BFAAE5AD04 /* [CP] Check Pods Manifest.lock */,
154 | 02CE3D52245EF19700E786F8 /* Sources */,
155 | 02CE3D53245EF19700E786F8 /* Frameworks */,
156 | 02CE3D54245EF19700E786F8 /* Resources */,
157 | E35689F2C1DB016A71102447 /* [CP] Embed Pods Frameworks */,
158 | );
159 | buildRules = (
160 | );
161 | dependencies = (
162 | );
163 | name = Example;
164 | productName = Example;
165 | productReference = 02CE3D56245EF19700E786F8 /* Example.app */;
166 | productType = "com.apple.product-type.application";
167 | };
168 | 607FACE41AFB9204008FA782 /* swiftVibrant_Tests */ = {
169 | isa = PBXNativeTarget;
170 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "swiftVibrant_Tests" */;
171 | buildPhases = (
172 | C1CB5EA0B6D4E1478AF9E75C /* [CP] Check Pods Manifest.lock */,
173 | 607FACE11AFB9204008FA782 /* Sources */,
174 | 607FACE21AFB9204008FA782 /* Frameworks */,
175 | 607FACE31AFB9204008FA782 /* Resources */,
176 | B29FFF59C84F8D2B1B7169E3 /* [CP] Embed Pods Frameworks */,
177 | );
178 | buildRules = (
179 | );
180 | dependencies = (
181 | );
182 | name = swiftVibrant_Tests;
183 | productName = Tests;
184 | productReference = 607FACE51AFB9204008FA782 /* swiftVibrant_Tests.xctest */;
185 | productType = "com.apple.product-type.bundle.unit-test";
186 | };
187 | /* End PBXNativeTarget section */
188 |
189 | /* Begin PBXProject section */
190 | 607FACC81AFB9204008FA782 /* Project object */ = {
191 | isa = PBXProject;
192 | attributes = {
193 | LastSwiftUpdateCheck = 1140;
194 | LastUpgradeCheck = 0830;
195 | ORGANIZATIONNAME = CocoaPods;
196 | TargetAttributes = {
197 | 02CE3D55245EF19700E786F8 = {
198 | CreatedOnToolsVersion = 11.4.1;
199 | DevelopmentTeam = GDNLAH489Z;
200 | ProvisioningStyle = Automatic;
201 | };
202 | 607FACE41AFB9204008FA782 = {
203 | CreatedOnToolsVersion = 6.3.1;
204 | DevelopmentTeam = GDNLAH489Z;
205 | LastSwiftMigration = 0900;
206 | };
207 | };
208 | };
209 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "swiftVibrant" */;
210 | compatibilityVersion = "Xcode 3.2";
211 | developmentRegion = English;
212 | hasScannedForEncodings = 0;
213 | knownRegions = (
214 | English,
215 | en,
216 | Base,
217 | );
218 | mainGroup = 607FACC71AFB9204008FA782;
219 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */;
220 | projectDirPath = "";
221 | projectRoot = "";
222 | targets = (
223 | 607FACE41AFB9204008FA782 /* swiftVibrant_Tests */,
224 | 02CE3D55245EF19700E786F8 /* Example */,
225 | );
226 | };
227 | /* End PBXProject section */
228 |
229 | /* Begin PBXResourcesBuildPhase section */
230 | 02CE3D54245EF19700E786F8 /* Resources */ = {
231 | isa = PBXResourcesBuildPhase;
232 | buildActionMask = 2147483647;
233 | files = (
234 | 02CE3D65245EF19900E786F8 /* LaunchScreen.storyboard in Resources */,
235 | 02CE3D62245EF19900E786F8 /* Assets.xcassets in Resources */,
236 | 02CE3D60245EF19700E786F8 /* Main.storyboard in Resources */,
237 | );
238 | runOnlyForDeploymentPostprocessing = 0;
239 | };
240 | 607FACE31AFB9204008FA782 /* Resources */ = {
241 | isa = PBXResourcesBuildPhase;
242 | buildActionMask = 2147483647;
243 | files = (
244 | );
245 | runOnlyForDeploymentPostprocessing = 0;
246 | };
247 | /* End PBXResourcesBuildPhase section */
248 |
249 | /* Begin PBXShellScriptBuildPhase section */
250 | 02E82B16C38838BFAAE5AD04 /* [CP] Check Pods Manifest.lock */ = {
251 | isa = PBXShellScriptBuildPhase;
252 | buildActionMask = 2147483647;
253 | files = (
254 | );
255 | inputFileListPaths = (
256 | );
257 | inputPaths = (
258 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
259 | "${PODS_ROOT}/Manifest.lock",
260 | );
261 | name = "[CP] Check Pods Manifest.lock";
262 | outputFileListPaths = (
263 | );
264 | outputPaths = (
265 | "$(DERIVED_FILE_DIR)/Pods-Example-checkManifestLockResult.txt",
266 | );
267 | runOnlyForDeploymentPostprocessing = 0;
268 | shellPath = /bin/sh;
269 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
270 | showEnvVarsInLog = 0;
271 | };
272 | B29FFF59C84F8D2B1B7169E3 /* [CP] Embed Pods Frameworks */ = {
273 | isa = PBXShellScriptBuildPhase;
274 | buildActionMask = 2147483647;
275 | files = (
276 | );
277 | inputPaths = (
278 | "${PODS_ROOT}/Target Support Files/Pods-swiftVibrant_Tests/Pods-swiftVibrant_Tests-frameworks.sh",
279 | "${BUILT_PRODUCTS_DIR}/swiftVibrant-iOS9.3/swiftVibrant.framework",
280 | );
281 | name = "[CP] Embed Pods Frameworks";
282 | outputPaths = (
283 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/swiftVibrant.framework",
284 | );
285 | runOnlyForDeploymentPostprocessing = 0;
286 | shellPath = /bin/sh;
287 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-swiftVibrant_Tests/Pods-swiftVibrant_Tests-frameworks.sh\"\n";
288 | showEnvVarsInLog = 0;
289 | };
290 | C1CB5EA0B6D4E1478AF9E75C /* [CP] Check Pods Manifest.lock */ = {
291 | isa = PBXShellScriptBuildPhase;
292 | buildActionMask = 2147483647;
293 | files = (
294 | );
295 | inputFileListPaths = (
296 | );
297 | inputPaths = (
298 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
299 | "${PODS_ROOT}/Manifest.lock",
300 | );
301 | name = "[CP] Check Pods Manifest.lock";
302 | outputFileListPaths = (
303 | );
304 | outputPaths = (
305 | "$(DERIVED_FILE_DIR)/Pods-swiftVibrant_Tests-checkManifestLockResult.txt",
306 | );
307 | runOnlyForDeploymentPostprocessing = 0;
308 | shellPath = /bin/sh;
309 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
310 | showEnvVarsInLog = 0;
311 | };
312 | E35689F2C1DB016A71102447 /* [CP] Embed Pods Frameworks */ = {
313 | isa = PBXShellScriptBuildPhase;
314 | buildActionMask = 2147483647;
315 | files = (
316 | );
317 | inputPaths = (
318 | "${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-frameworks.sh",
319 | "${BUILT_PRODUCTS_DIR}/swiftVibrant-iOS13.4/swiftVibrant.framework",
320 | );
321 | name = "[CP] Embed Pods Frameworks";
322 | outputPaths = (
323 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/swiftVibrant.framework",
324 | );
325 | runOnlyForDeploymentPostprocessing = 0;
326 | shellPath = /bin/sh;
327 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-frameworks.sh\"\n";
328 | showEnvVarsInLog = 0;
329 | };
330 | /* End PBXShellScriptBuildPhase section */
331 |
332 | /* Begin PBXSourcesBuildPhase section */
333 | 02CE3D52245EF19700E786F8 /* Sources */ = {
334 | isa = PBXSourcesBuildPhase;
335 | buildActionMask = 2147483647;
336 | files = (
337 | 02CE3D5D245EF19700E786F8 /* ViewController.swift in Sources */,
338 | 02CE3D59245EF19700E786F8 /* AppDelegate.swift in Sources */,
339 | 02CE3D5B245EF19700E786F8 /* SceneDelegate.swift in Sources */,
340 | );
341 | runOnlyForDeploymentPostprocessing = 0;
342 | };
343 | 607FACE11AFB9204008FA782 /* Sources */ = {
344 | isa = PBXSourcesBuildPhase;
345 | buildActionMask = 2147483647;
346 | files = (
347 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */,
348 | );
349 | runOnlyForDeploymentPostprocessing = 0;
350 | };
351 | /* End PBXSourcesBuildPhase section */
352 |
353 | /* Begin PBXVariantGroup section */
354 | 02CE3D5E245EF19700E786F8 /* Main.storyboard */ = {
355 | isa = PBXVariantGroup;
356 | children = (
357 | 02CE3D5F245EF19700E786F8 /* Base */,
358 | );
359 | name = Main.storyboard;
360 | sourceTree = "";
361 | };
362 | 02CE3D63245EF19900E786F8 /* LaunchScreen.storyboard */ = {
363 | isa = PBXVariantGroup;
364 | children = (
365 | 02CE3D64245EF19900E786F8 /* Base */,
366 | );
367 | name = LaunchScreen.storyboard;
368 | sourceTree = "";
369 | };
370 | /* End PBXVariantGroup section */
371 |
372 | /* Begin XCBuildConfiguration section */
373 | 02CE3D68245EF19900E786F8 /* Debug */ = {
374 | isa = XCBuildConfiguration;
375 | baseConfigurationReference = 04DDE9579463834E92864267 /* Pods-Example.debug.xcconfig */;
376 | buildSettings = {
377 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
378 | CLANG_ANALYZER_NONNULL = YES;
379 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
380 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
381 | CLANG_ENABLE_OBJC_WEAK = YES;
382 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
383 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
385 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
386 | CODE_SIGN_STYLE = Automatic;
387 | DEBUG_INFORMATION_FORMAT = dwarf;
388 | DEVELOPMENT_TEAM = GDNLAH489Z;
389 | GCC_C_LANGUAGE_STANDARD = gnu11;
390 | INFOPLIST_FILE = Example/Info.plist;
391 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
393 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
394 | MTL_FAST_MATH = YES;
395 | PRODUCT_BUNDLE_IDENTIFIER = com.bd452.SwiftVibrantExample;
396 | PRODUCT_NAME = "$(TARGET_NAME)";
397 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
398 | SWIFT_VERSION = 5.0;
399 | TARGETED_DEVICE_FAMILY = "1,2";
400 | };
401 | name = Debug;
402 | };
403 | 02CE3D69245EF19900E786F8 /* Release */ = {
404 | isa = XCBuildConfiguration;
405 | baseConfigurationReference = 631AEB3044C059FA9826ECDF /* Pods-Example.release.xcconfig */;
406 | buildSettings = {
407 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
408 | CLANG_ANALYZER_NONNULL = YES;
409 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
410 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
411 | CLANG_ENABLE_OBJC_WEAK = YES;
412 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
413 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
414 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
415 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
416 | CODE_SIGN_STYLE = Automatic;
417 | DEVELOPMENT_TEAM = GDNLAH489Z;
418 | GCC_C_LANGUAGE_STANDARD = gnu11;
419 | INFOPLIST_FILE = Example/Info.plist;
420 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
421 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
422 | MTL_FAST_MATH = YES;
423 | PRODUCT_BUNDLE_IDENTIFIER = com.bd452.SwiftVibrantExample;
424 | PRODUCT_NAME = "$(TARGET_NAME)";
425 | SWIFT_VERSION = 5.0;
426 | TARGETED_DEVICE_FAMILY = "1,2";
427 | };
428 | name = Release;
429 | };
430 | 607FACED1AFB9204008FA782 /* Debug */ = {
431 | isa = XCBuildConfiguration;
432 | buildSettings = {
433 | ALWAYS_SEARCH_USER_PATHS = NO;
434 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
435 | CLANG_CXX_LIBRARY = "libc++";
436 | CLANG_ENABLE_MODULES = YES;
437 | CLANG_ENABLE_OBJC_ARC = YES;
438 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
439 | CLANG_WARN_BOOL_CONVERSION = YES;
440 | CLANG_WARN_COMMA = YES;
441 | CLANG_WARN_CONSTANT_CONVERSION = YES;
442 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
443 | CLANG_WARN_EMPTY_BODY = YES;
444 | CLANG_WARN_ENUM_CONVERSION = YES;
445 | CLANG_WARN_INFINITE_RECURSION = YES;
446 | CLANG_WARN_INT_CONVERSION = YES;
447 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
448 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
449 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
450 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
451 | CLANG_WARN_STRICT_PROTOTYPES = YES;
452 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
453 | CLANG_WARN_UNREACHABLE_CODE = YES;
454 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
455 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
456 | COPY_PHASE_STRIP = NO;
457 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
458 | ENABLE_STRICT_OBJC_MSGSEND = YES;
459 | ENABLE_TESTABILITY = YES;
460 | GCC_C_LANGUAGE_STANDARD = gnu99;
461 | GCC_DYNAMIC_NO_PIC = NO;
462 | GCC_NO_COMMON_BLOCKS = YES;
463 | GCC_OPTIMIZATION_LEVEL = 0;
464 | GCC_PREPROCESSOR_DEFINITIONS = (
465 | "DEBUG=1",
466 | "$(inherited)",
467 | );
468 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
469 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
470 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
471 | GCC_WARN_UNDECLARED_SELECTOR = YES;
472 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
473 | GCC_WARN_UNUSED_FUNCTION = YES;
474 | GCC_WARN_UNUSED_VARIABLE = YES;
475 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
476 | MTL_ENABLE_DEBUG_INFO = YES;
477 | ONLY_ACTIVE_ARCH = YES;
478 | SDKROOT = iphoneos;
479 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
480 | };
481 | name = Debug;
482 | };
483 | 607FACEE1AFB9204008FA782 /* Release */ = {
484 | isa = XCBuildConfiguration;
485 | buildSettings = {
486 | ALWAYS_SEARCH_USER_PATHS = NO;
487 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
488 | CLANG_CXX_LIBRARY = "libc++";
489 | CLANG_ENABLE_MODULES = YES;
490 | CLANG_ENABLE_OBJC_ARC = YES;
491 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
492 | CLANG_WARN_BOOL_CONVERSION = YES;
493 | CLANG_WARN_COMMA = YES;
494 | CLANG_WARN_CONSTANT_CONVERSION = YES;
495 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
496 | CLANG_WARN_EMPTY_BODY = YES;
497 | CLANG_WARN_ENUM_CONVERSION = YES;
498 | CLANG_WARN_INFINITE_RECURSION = YES;
499 | CLANG_WARN_INT_CONVERSION = YES;
500 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
501 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
502 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
503 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
504 | CLANG_WARN_STRICT_PROTOTYPES = YES;
505 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
506 | CLANG_WARN_UNREACHABLE_CODE = YES;
507 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
508 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
509 | COPY_PHASE_STRIP = NO;
510 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
511 | ENABLE_NS_ASSERTIONS = NO;
512 | ENABLE_STRICT_OBJC_MSGSEND = YES;
513 | GCC_C_LANGUAGE_STANDARD = gnu99;
514 | GCC_NO_COMMON_BLOCKS = YES;
515 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
516 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
517 | GCC_WARN_UNDECLARED_SELECTOR = YES;
518 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
519 | GCC_WARN_UNUSED_FUNCTION = YES;
520 | GCC_WARN_UNUSED_VARIABLE = YES;
521 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
522 | MTL_ENABLE_DEBUG_INFO = NO;
523 | SDKROOT = iphoneos;
524 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
525 | VALIDATE_PRODUCT = YES;
526 | };
527 | name = Release;
528 | };
529 | 607FACF31AFB9204008FA782 /* Debug */ = {
530 | isa = XCBuildConfiguration;
531 | baseConfigurationReference = 1BF125B0EC659770D6757116 /* Pods-swiftVibrant_Tests.debug.xcconfig */;
532 | buildSettings = {
533 | DEVELOPMENT_TEAM = GDNLAH489Z;
534 | FRAMEWORK_SEARCH_PATHS = (
535 | "$(PLATFORM_DIR)/Developer/Library/Frameworks",
536 | "$(inherited)",
537 | );
538 | GCC_PREPROCESSOR_DEFINITIONS = (
539 | "DEBUG=1",
540 | "$(inherited)",
541 | );
542 | INFOPLIST_FILE = Tests/Info.plist;
543 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
544 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
545 | PRODUCT_NAME = "$(TARGET_NAME)";
546 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
547 | SWIFT_VERSION = 4.0;
548 | };
549 | name = Debug;
550 | };
551 | 607FACF41AFB9204008FA782 /* Release */ = {
552 | isa = XCBuildConfiguration;
553 | baseConfigurationReference = 04B2C6316ED3FEA12D2E413E /* Pods-swiftVibrant_Tests.release.xcconfig */;
554 | buildSettings = {
555 | DEVELOPMENT_TEAM = GDNLAH489Z;
556 | FRAMEWORK_SEARCH_PATHS = (
557 | "$(PLATFORM_DIR)/Developer/Library/Frameworks",
558 | "$(inherited)",
559 | );
560 | INFOPLIST_FILE = Tests/Info.plist;
561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
562 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
563 | PRODUCT_NAME = "$(TARGET_NAME)";
564 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
565 | SWIFT_VERSION = 4.0;
566 | };
567 | name = Release;
568 | };
569 | /* End XCBuildConfiguration section */
570 |
571 | /* Begin XCConfigurationList section */
572 | 02CE3D67245EF19900E786F8 /* Build configuration list for PBXNativeTarget "Example" */ = {
573 | isa = XCConfigurationList;
574 | buildConfigurations = (
575 | 02CE3D68245EF19900E786F8 /* Debug */,
576 | 02CE3D69245EF19900E786F8 /* Release */,
577 | );
578 | defaultConfigurationIsVisible = 0;
579 | defaultConfigurationName = Release;
580 | };
581 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "swiftVibrant" */ = {
582 | isa = XCConfigurationList;
583 | buildConfigurations = (
584 | 607FACED1AFB9204008FA782 /* Debug */,
585 | 607FACEE1AFB9204008FA782 /* Release */,
586 | );
587 | defaultConfigurationIsVisible = 0;
588 | defaultConfigurationName = Release;
589 | };
590 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "swiftVibrant_Tests" */ = {
591 | isa = XCConfigurationList;
592 | buildConfigurations = (
593 | 607FACF31AFB9204008FA782 /* Debug */,
594 | 607FACF41AFB9204008FA782 /* Release */,
595 | );
596 | defaultConfigurationIsVisible = 0;
597 | defaultConfigurationName = Release;
598 | };
599 | /* End XCConfigurationList section */
600 | };
601 | rootObject = 607FACC81AFB9204008FA782 /* Project object */;
602 | }
603 |
--------------------------------------------------------------------------------