Merge pull request #55 from regexident/grayscaled-mode
Add support for grayscale modes
This commit is contained in:
commit
31383de322
|
@ -99,7 +99,14 @@ let desaturatedColor = originalColor.desaturated()
|
|||
// equivalent to
|
||||
// desaturatedColor = originalColor.desaturated(amount: 0.2)
|
||||
|
||||
// equivalent to
|
||||
// let grayscaledColor = originalColor.grayscaled(mode: .luminance)
|
||||
let grayscaledColor = originalColor.grayscaled()
|
||||
|
||||
let grayscaledColorLuminance = originalColor.grayscaled(mode: .luminance)
|
||||
let grayscaledColorLightness = originalColor.grayscaled(mode: .lightness)
|
||||
let grayscaledColorAverage = originalColor.grayscaled(mode: .average)
|
||||
let grayscaledColorValue = originalColor.grayscaled(mode: .value)
|
||||
```
|
||||
|
||||
#### Adjust-hue & Complement
|
||||
|
|
|
@ -99,13 +99,25 @@ public extension DynamicColor {
|
|||
/**
|
||||
Creates and returns a color object converted to grayscale.
|
||||
|
||||
This is identical to desaturateColor(1).
|
||||
|
||||
- returns: A grayscale DynamicColor.
|
||||
- seealso: desaturateColor:
|
||||
- seealso: desaturated:
|
||||
*/
|
||||
final func grayscaled() -> DynamicColor {
|
||||
return desaturated(amount: 1)
|
||||
final func grayscaled(mode: GrayscalingMode = .lightness) -> DynamicColor {
|
||||
let (r, g, b, a) = self.toRGBAComponents()
|
||||
|
||||
let l: CGFloat
|
||||
switch mode {
|
||||
case .luminance:
|
||||
l = (0.299 * r) + (0.587 * g) + (0.114 * b)
|
||||
case .lightness:
|
||||
l = 0.5 * (max(r, g, b) + min(r, g, b))
|
||||
case .average:
|
||||
l = (1.0 / 3.0) * (r + g + b)
|
||||
case .value:
|
||||
l = max(r, g, b)
|
||||
}
|
||||
|
||||
return HSL(hue: 0.0, saturation: 0.0, lightness: l, alpha: a).toDynamicColor()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -71,3 +71,19 @@ internal func roundToHex(_ x: CGFloat) -> UInt32 {
|
|||
|
||||
return UInt32(rounded)
|
||||
}
|
||||
|
||||
/**
|
||||
Defines the mode (i.e color space) used for grayscaling.
|
||||
|
||||
[More info](https://en.wikipedia.org/wiki/Lightness#Lightness_and_human_perception)
|
||||
*/
|
||||
public enum GrayscalingMode {
|
||||
/// XYZ luminance
|
||||
case luminance
|
||||
/// HSL lightness
|
||||
case lightness
|
||||
/// RGB average
|
||||
case average
|
||||
/// HSV value
|
||||
case value
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* DynamicColor
|
||||
*
|
||||
* Copyright 2015-present Yannick Loriot.
|
||||
* http://yannickloriot.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
import XCTest
|
||||
@testable import DynamicColor
|
||||
|
||||
func XCTAssertEqual(_ expression1: @autoclosure () throws -> DynamicColor, _ expression2: @autoclosure () throws -> DynamicColor, accuracy: CGFloat, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) {
|
||||
|
||||
let message = message()
|
||||
|
||||
do {
|
||||
let (color1, color2) = (try expression1(), try expression2())
|
||||
|
||||
let (r1, g1, b1, a1) = color1.toRGBAComponents()
|
||||
let (r2, g2, b2, a2) = color2.toRGBAComponents()
|
||||
|
||||
XCTAssertEqual(r1, r2, accuracy: accuracy, message, file: file, line: line)
|
||||
XCTAssertEqual(g1, g2, accuracy: accuracy, message, file: file, line: line)
|
||||
XCTAssertEqual(b1, b2, accuracy: accuracy, message, file: file, line: line)
|
||||
XCTAssertEqual(a1, a2, accuracy: accuracy, message, file: file, line: line)
|
||||
} catch let error {
|
||||
XCTFail("\(error)", file: file, line: line)
|
||||
}
|
||||
}
|
|
@ -287,10 +287,26 @@ class DynamicColorTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testGrayscaleColor() {
|
||||
let grayscale = DynamicColor(hex: 0xc0392b).grayscaled()
|
||||
let desaturated = DynamicColor(hex: 0xc0392b).desaturated(amount: 1)
|
||||
let baseColor = DynamicColor(hex: 0xc0392b)
|
||||
|
||||
XCTAssert(grayscale.isEqual(desaturated), "Colors should be the same")
|
||||
let grayscaleByLuminance = baseColor.grayscaled(mode: .luminance)
|
||||
let expectedGrayscaleByLuminance = DynamicColor(white: 0.376, alpha: 1.0)
|
||||
XCTAssertEqual(grayscaleByLuminance, expectedGrayscaleByLuminance, accuracy: 0.001, "Colors should be the same")
|
||||
|
||||
let grayscaleByLightness = baseColor.grayscaled(mode: .lightness)
|
||||
let expectedGrayscaleByLightness = DynamicColor(white: 0.461, alpha: 1.0)
|
||||
XCTAssertEqual(grayscaleByLightness, expectedGrayscaleByLightness, accuracy: 0.001, "Colors should be the same")
|
||||
|
||||
let grayscaleByAverage = baseColor.grayscaled(mode: .average)
|
||||
let expectedGrayscaleByAverage = DynamicColor(white: 0.382, alpha: 1.0)
|
||||
XCTAssertEqual(grayscaleByAverage, expectedGrayscaleByAverage, accuracy: 0.001, "Colors should be the same")
|
||||
|
||||
let grayscaleByValue = baseColor.grayscaled(mode: .value)
|
||||
let expectedGrayscaleByValue = DynamicColor(white: 0.753, alpha: 1.0)
|
||||
XCTAssertEqual(grayscaleByValue, expectedGrayscaleByValue, accuracy: 0.001, "Colors should be the same")
|
||||
|
||||
let grayscaleByDefault = baseColor.grayscaled()
|
||||
XCTAssert(grayscaleByDefault.isEqual(grayscaleByLightness), "Colors should be the same")
|
||||
}
|
||||
|
||||
func testInvertColor() {
|
||||
|
|
Loading…
Reference in New Issue