Merge pull request #55 from regexident/grayscaled-mode

Add support for grayscale modes
This commit is contained in:
Yannick Loriot 2020-01-25 20:48:22 +00:00 committed by GitHub
commit 31383de322
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 106 additions and 8 deletions

View File

@ -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

View File

@ -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()
}
/**

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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() {