diff --git a/demo/Palette.xcodeproj/project.pbxproj b/demo/Palette.xcodeproj/project.pbxproj index 1db02ce..559aadd 100644 --- a/demo/Palette.xcodeproj/project.pbxproj +++ b/demo/Palette.xcodeproj/project.pbxproj @@ -26,6 +26,9 @@ 28C697D81F452FDB009A5771 /* AlbumLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C697D71F452FDB009A5771 /* AlbumLoader.swift */; }; 28C697DA1F4532D2009A5771 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C697D91F4532D2009A5771 /* Album.swift */; }; A1DE2F2A2C4B09440055D11A /* Pods_Palette.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B7A88A7EC8D7FFAE125DE51 /* Pods_Palette.framework */; }; + FB00716D22B6114E0057F6B1 /* ColorBGPalleteCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB00716B22B6114E0057F6B1 /* ColorBGPalleteCache.swift */; }; + FB00716E22B6114E0057F6B1 /* PalleteiOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB00716C22B6114E0057F6B1 /* PalleteiOS.swift */; }; + FB80BE1422CBC99500F906D3 /* PalleteConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB80BE1322CBC99500F906D3 /* PalleteConfig.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -52,6 +55,9 @@ 28C697D91F4532D2009A5771 /* Album.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = ""; }; 64C0B1589F99CD2762F894BA /* Pods-Palette.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Palette.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Palette/Pods-Palette.debug.xcconfig"; sourceTree = ""; }; B34F0A4CB368DE962771DCBA /* Pods-Palette.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Palette.release.xcconfig"; path = "Pods/Target Support Files/Pods-Palette/Pods-Palette.release.xcconfig"; sourceTree = ""; }; + FB00716B22B6114E0057F6B1 /* ColorBGPalleteCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorBGPalleteCache.swift; sourceTree = ""; }; + FB00716C22B6114E0057F6B1 /* PalleteiOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PalleteiOS.swift; sourceTree = ""; }; + FB80BE1322CBC99500F906D3 /* PalleteConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PalleteConfig.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -111,6 +117,7 @@ 288FC1E61E834C88003679D4 /* src */ = { isa = PBXGroup; children = ( + FB00716A22B6114E0057F6B1 /* PalleteiOS */, 288FC1E71E834C88003679D4 /* ColorCutQuantizer.swift */, 288FC1E81E834C88003679D4 /* ColorHistogram.swift */, 288FC1E91E834C88003679D4 /* DefaultPaletteGenerator.swift */, @@ -144,6 +151,16 @@ name = Frameworks; sourceTree = ""; }; + FB00716A22B6114E0057F6B1 /* PalleteiOS */ = { + isa = PBXGroup; + children = ( + FB00716B22B6114E0057F6B1 /* ColorBGPalleteCache.swift */, + FB00716C22B6114E0057F6B1 /* PalleteiOS.swift */, + FB80BE1322CBC99500F906D3 /* PalleteConfig.swift */, + ); + path = PalleteiOS; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -156,7 +173,6 @@ 2845C2361B85E33F00696E22 /* Frameworks */, 2845C2371B85E33F00696E22 /* Resources */, 8CB204674A918BD36A66A257 /* [CP] Embed Pods Frameworks */, - EB34DF4753C79AB11CADBB56 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -173,7 +189,7 @@ 2845C2311B85E33F00696E22 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0830; + LastUpgradeCheck = 1010; ORGANIZATIONNAME = shnhrrsn; TargetAttributes = { 2845C2381B85E33F00696E22 = { @@ -219,13 +235,16 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Palette/Pods-Palette-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/SwiftPriorityQueue/SwiftPriorityQueue.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftPriorityQueue.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Palette/Pods-Palette-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Palette/Pods-Palette-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; EA35B8BE675491BCBFF60592 /* [CP] Check Pods Manifest.lock */ = { @@ -234,28 +253,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Palette-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; - EB34DF4753C79AB11CADBB56 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Palette/Pods-Palette-resources.sh\"\n"; + 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"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -265,8 +272,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FB00716D22B6114E0057F6B1 /* ColorBGPalleteCache.swift in Sources */, + FB00716E22B6114E0057F6B1 /* PalleteiOS.swift in Sources */, 288FC1F51E834C88003679D4 /* HexColor.swift in Sources */, 288FC1F21E834C88003679D4 /* ColorCutQuantizer.swift in Sources */, + FB80BE1422CBC99500F906D3 /* PalleteConfig.swift in Sources */, 288FC1F91E834C88003679D4 /* PaletteGenerator.swift in Sources */, 288FC1FA1E834C88003679D4 /* PaletteSwatch.swift in Sources */, 288FC1F81E834C88003679D4 /* PaletteConfiguration.swift in Sources */, @@ -306,14 +316,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -338,7 +356,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -355,14 +373,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -380,7 +406,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; STRIP_INSTALLED_PRODUCT = NO; @@ -403,7 +429,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; STRIP_INSTALLED_PRODUCT = NO; STRIP_STYLE = debugging; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -424,7 +450,7 @@ STRIP_INSTALLED_PRODUCT = NO; STRIP_STYLE = debugging; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/demo/Palette.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/demo/Palette.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/demo/Palette.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/demo/Palette/AlbumLoader.swift b/demo/Palette/AlbumLoader.swift index 9607031..54d85de 100644 --- a/demo/Palette/AlbumLoader.swift +++ b/demo/Palette/AlbumLoader.swift @@ -39,7 +39,7 @@ struct AlbumLoader { var rank = 0 - let albums: [Album] = entries.flatMap { + let albums: [Album] = entries.compactMap { guard let artwork = ($0["im:image"] as? [JsonObject])?.first?["label"] as? String else { return nil } diff --git a/demo/Palette/AppDelegate.swift b/demo/Palette/AppDelegate.swift index 3ae9de4..b103d0c 100644 --- a/demo/Palette/AppDelegate.swift +++ b/demo/Palette/AppDelegate.swift @@ -13,20 +13,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { - let viewController = DemoViewController() - let navigationController = UINavigationController(rootViewController: viewController) - navigationController.navigationBar.barStyle = .black - - let window = UIWindow(frame: UIScreen.main.bounds) - window.rootViewController = navigationController - window.tintColor = .white - window.makeKeyAndVisible() - - self.window = window - - return true - } + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + let viewController = DemoViewController() + let navigationController = UINavigationController(rootViewController: viewController) + navigationController.navigationBar.barStyle = .black + + let window = UIWindow(frame: UIScreen.main.bounds) + window.rootViewController = navigationController + window.tintColor = .white + window.makeKeyAndVisible() + + self.window = window + + return true + } } diff --git a/demo/Palette/DemoTableViewCell.swift b/demo/Palette/DemoTableViewCell.swift index df762cd..3300c68 100644 --- a/demo/Palette/DemoTableViewCell.swift +++ b/demo/Palette/DemoTableViewCell.swift @@ -11,7 +11,7 @@ import UIKit class DemoTableViewCell: UITableViewCell { - override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } diff --git a/demo/Podfile b/demo/Podfile index 7b492c4..3916b9f 100644 --- a/demo/Podfile +++ b/demo/Podfile @@ -5,6 +5,6 @@ use_frameworks! target 'Palette' do - pod 'SwiftPriorityQueue', '~> 1.1.2' + pod 'SwiftPriorityQueue', '~> 1.2.1' end diff --git a/demo/Podfile.lock b/demo/Podfile.lock index 31c3c5c..3e14481 100644 --- a/demo/Podfile.lock +++ b/demo/Podfile.lock @@ -1,12 +1,16 @@ PODS: - - SwiftPriorityQueue (1.1.2) + - SwiftPriorityQueue (1.2.1) DEPENDENCIES: - - SwiftPriorityQueue (~> 1.1.2) + - SwiftPriorityQueue (~> 1.2.1) + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - SwiftPriorityQueue SPEC CHECKSUMS: - SwiftPriorityQueue: bafec150153c600a6b9f97b42abe25538745fadb + SwiftPriorityQueue: 2ce5768829a9ef84d8b8fec5e73546d72ee3d8f6 -PODFILE CHECKSUM: e7603849e959536067cf8be98e9be8f937e101c8 +PODFILE CHECKSUM: 96cfa9f6bf4613e4c4577c95de4649ff08865241 -COCOAPODS: 1.0.1 +COCOAPODS: 1.7.1 diff --git a/src/ColorCutQuantizer.swift b/src/ColorCutQuantizer.swift index 637d4a1..36b50dc 100644 --- a/src/ColorCutQuantizer.swift +++ b/src/ColorCutQuantizer.swift @@ -224,9 +224,11 @@ private class Vbox: Hashable { private let quantizer: ColorCutQuantizer - private static var ordinal = Int32(0) - - let hashValue = Int(OSAtomicIncrement32(&Vbox.ordinal)) + var hashValue: Int { + let counter = Counter() + counter.increment() + return counter.value + } init(quantizer: ColorCutQuantizer, lowerIndex: Int, upperIndex: Int) { self.quantizer = quantizer @@ -434,3 +436,14 @@ private func <(lhs: Vbox, rhs: Vbox) -> Bool { private func >(lhs: Vbox, rhs: Vbox) -> Bool { return lhs.volume > rhs.volume } + +class Counter { + private var queue = DispatchQueue(label: "true.pallete") + private (set) var value: Int = 0 + + func increment() { + queue.sync { + value += 1 + } + } +} diff --git a/src/PaletteSwatch.swift b/src/PaletteSwatch.swift index b0125b7..42c714d 100644 --- a/src/PaletteSwatch.swift +++ b/src/PaletteSwatch.swift @@ -18,10 +18,10 @@ open class PaletteSwatch { private let hex: Int64 /** This swatch’s color */ - open let color: UIColor + public let color: UIColor /** The number of pixels represented by this swatch */ - open let population: Int64 + public let population: Int64 private var generatedTextColors: Bool = false private var _titleTextColor: UIColor? diff --git a/src/PalleteiOS/ColorBGPalleteCache.swift b/src/PalleteiOS/ColorBGPalleteCache.swift new file mode 100644 index 0000000..36f1202 --- /dev/null +++ b/src/PalleteiOS/ColorBGPalleteCache.swift @@ -0,0 +1,26 @@ +// +// ColorBGPalleteCache.swift +// POCLatestFeedBackground +// +// Created by Apinun Wongintawang on 6/16/19. +// Copyright © 2019 Apinun Wongintawang. All rights reserved. +// + +import UIKit + +class ColorBGPalleteCache { + static let shared = ColorBGPalleteCache() + var colorCache = NSCache() + + func addColor(key: String, color: UIColor) { + colorCache.setObject(color, forKey: NSString(string: key)) + } + + func getColor(key: String) -> UIColor? { + return colorCache.object(forKey: NSString(string: key)) + } + + func removeAllColor() { + colorCache.removeAllObjects() + } +} diff --git a/src/PalleteiOS/PalleteConfig.swift b/src/PalleteiOS/PalleteConfig.swift new file mode 100644 index 0000000..62aaa4a --- /dev/null +++ b/src/PalleteiOS/PalleteConfig.swift @@ -0,0 +1,13 @@ +// +// PalleteConfig.swift +// Palette +// +// Created by Apinun Wongintawang on 7/3/19. +// Copyright © 2019 shnhrrsn. All rights reserved. +// + +import Foundation + +enum PalleteConfig { + static let maxColor: Int = 24 +} diff --git a/src/PalleteiOS/PalleteiOS.swift b/src/PalleteiOS/PalleteiOS.swift new file mode 100644 index 0000000..933fd29 --- /dev/null +++ b/src/PalleteiOS/PalleteiOS.swift @@ -0,0 +1,89 @@ +// +// PalleteiOS.swift +// POCLatestFeedBackground +// +// Created by Apinun Wongintawang on 6/14/19. +// Copyright © 2019 Apinun Wongintawang. All rights reserved. +// + +import UIKit + +extension UIView { + public func generateColorBy(img: UIImage?, path: String, defaultColor: UIColor?, complete: ((_ color: UIColor) -> Void)?) { + //set default color when user is not take default color. + let _defaultColor: UIColor = defaultColor ?? .lightGray + + guard let _img = img else { + self.setUpBackgroudColor(color: _defaultColor) + return + } + + //Get color from cache + if let color = ColorBGPalleteCache.shared.getColor(key: path) { + self.setUpBackgroudColor(color: color) + return + } + + //Resize image + let newSize = getSizeOfImageFromDisplay(size: _img.size) + let imageSmall = _img.resized(to: newSize) + + DispatchQueue.global(qos: .userInteractive).async { + var config = PaletteConfiguration(image: imageSmall) + let maxColor = PalleteConfig.maxColor + + config.maxColors = maxColor + + Palette.generateWith(configuration: config, completion: { (pallette) in + if let palleteColor = pallette.darkVibrantSwatch?.color { + self.addColorToCache(color: palleteColor, path: path) + self.setUpBackgroudColor(color: palleteColor) + complete?(palleteColor) + } else { + self.addColorToCache(color: _defaultColor, path: path) + self.setUpBackgroudColor(color: _defaultColor) + complete?(_defaultColor) + } + self.layoutIfNeeded() + self.setNeedsDisplay() + }) + } + } + + private func addColorToCache(color: UIColor, path: String) { + guard !path.isEmpty else { return } + + //save color to cache + ColorBGPalleteCache.shared.addColor(key: path, color: color) + } + + private func setUpBackgroudColor(color: UIColor) { + DispatchQueue.main.async { + self.backgroundColor = color + } + } + + private func getSizeOfImageFromDisplay(size: CGSize) -> CGSize { + let ratio = size.width / size.height + let screenSize = UIScreen.main.bounds + let newSize = CGSize(width: screenSize.size.width, height: screenSize.size.width/ratio) + return newSize + } +} + +extension UIImage { + func resized(to targetSize: CGSize) -> UIImage { + let size = self.size + let widthRatio = targetSize.width / size.width + let heightRatio = targetSize.height / size.height + let newSize = widthRatio > heightRatio ? CGSize(width: size.width * heightRatio, height: size.height * heightRatio) : CGSize(width: size.width * widthRatio, height: size.height * widthRatio) + let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) + + UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0) + self.draw(in: rect) + let newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return newImage! + } +}