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
|
// equivalent to
|
||||||
// desaturatedColor = originalColor.desaturated(amount: 0.2)
|
// desaturatedColor = originalColor.desaturated(amount: 0.2)
|
||||||
|
|
||||||
|
// equivalent to
|
||||||
|
// let grayscaledColor = originalColor.grayscaled(mode: .luminance)
|
||||||
let grayscaledColor = originalColor.grayscaled()
|
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
|
#### Adjust-hue & Complement
|
||||||
|
|
|
@ -99,13 +99,25 @@ public extension DynamicColor {
|
||||||
/**
|
/**
|
||||||
Creates and returns a color object converted to grayscale.
|
Creates and returns a color object converted to grayscale.
|
||||||
|
|
||||||
This is identical to desaturateColor(1).
|
|
||||||
|
|
||||||
- returns: A grayscale DynamicColor.
|
- returns: A grayscale DynamicColor.
|
||||||
- seealso: desaturateColor:
|
- seealso: desaturated:
|
||||||
*/
|
*/
|
||||||
final func grayscaled() -> DynamicColor {
|
final func grayscaled(mode: GrayscalingMode = .lightness) -> DynamicColor {
|
||||||
return desaturated(amount: 1)
|
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)
|
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() {
|
func testGrayscaleColor() {
|
||||||
let grayscale = DynamicColor(hex: 0xc0392b).grayscaled()
|
let baseColor = DynamicColor(hex: 0xc0392b)
|
||||||
let desaturated = DynamicColor(hex: 0xc0392b).desaturated(amount: 1)
|
|
||||||
|
|
||||||
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() {
|
func testInvertColor() {
|
||||||
|
|
Loading…
Reference in New Issue