Skip to content

[Issue-46] Clamp sRGB display values #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions Sources/YCoreUI/Extensions/UIKit/UIColor+rgbValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,36 @@ public extension UIColor {
/// - isUppercase: whether the hex values should be upper or lower case
/// - Returns: the formatted hexadecimal string
func rgbDisplayString(prefix: String? = nil, isUppercase: Bool = true) -> String {
_rgbDisplayString(prefix: prefix, isUppercase: isUppercase, isDebug: false)
}

/// Formats a color as an RGB hexadecimal string. Appropriate for debug printing.
/// - Parameters:
/// - prefix: optional prefix to precede the hexadecimal value such as `0x` or `#` (default = nil)
/// - isUppercase: whether the hex values should be upper or lower case
/// - Returns: the formatted hexadecimal string (with an `⚠️` for colors that fall outside of the sRGB color space)
func rgbDebugDisplayString(prefix: String? = nil, isUppercase: Bool = true) -> String {
_rgbDisplayString(prefix: prefix, isUppercase: isUppercase, isDebug: true)
}

private func _rgbDisplayString(prefix: String?, isUppercase: Bool, isDebug: Bool) -> String {
let comp = rgbaComponents
let format = isUppercase ? "%02X%02X%02X" : "%02x%02x%02x"
let r = Int(round(comp.red * 255))
let g = Int(round(comp.green * 255))
let b = Int(round(comp.blue * 255))
let isRGB = (r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)
let prefix = prefix ?? ""
let value = String(
format: format,
Int(round(comp.red * 255)),
Int(round(comp.green * 255)),
Int(round(comp.blue * 255))
min(max(r, 0), 255),
min(max(g, 0), 255),
min(max(b, 0), 255)
)
return "\(prefix ?? "")\(value)"
let suffix = (isDebug && !isRGB) ? "⚠️" : ""
if !isRGB && YCoreUI.isLoggingEnabled {
YCoreUI.colorLogger.warning("Color \(self) falls outside of the sRGB color space.")
}
return "\(prefix)\(value)\(suffix)"
}
}
2 changes: 2 additions & 0 deletions Sources/YCoreUI/YCoreUI+Logging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ public struct YCoreUI {
internal extension YCoreUI {
/// Logger for warnings related to image loading. cf. `ImageAsset` and `SystemImage`
static let imageLogger = Logger(subsystem: "YCoreUI", category: "images")
/// Logger for warnings related to colors. cf `UIColor+rgbValue.swift`
static let colorLogger = Logger(subsystem: "YCoreUI", category: "colors")
}
32 changes: 32 additions & 0 deletions Tests/YCoreUITests/Extensions/UIKit/UIColor+rgbValueTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

import XCTest
@testable import YCoreUI

// Large tuples help us build unit test expectations concisely
// swiftlint:disable large_tuple
Expand Down Expand Up @@ -42,4 +43,35 @@ final class UIColorRgbValueTests: XCTestCase {
// We exect each rgb color channel value to be rounded appropriately
XCTAssertEqual(color.rgbDisplayString(), "80BFA6")
}

func testSuffixes() {
testCases.forEach {
XCTAssertFalse($0.color.rgbDisplayString().hasSuffix("⚠️"))
XCTAssertFalse($0.color.rgbDebugDisplayString().hasSuffix("⚠️"))
XCTAssertEqual($0.color.rgbDisplayString(), $0.color.rgbDebugDisplayString())
}

let p3Colors = [
UIColor(displayP3Red: 1, green: 0, blue: 0, alpha: 1),
UIColor(displayP3Red: 0, green: 1, blue: 0, alpha: 1),
UIColor(displayP3Red: 0, green: 0, blue: 1, alpha: 1),
UIColor(displayP3Red: 1, green: 1, blue: 0, alpha: 1),
UIColor(displayP3Red: 1, green: 0, blue: 1, alpha: 1),
UIColor(displayP3Red: 0, green: 1, blue: 1, alpha: 1),
UIColor(red: 1.25, green: 0, blue: 0, alpha: 1),
UIColor(red: 0, green: 1.25, blue: 0, alpha: 1),
UIColor(red: 0, green: 0, blue: 1.25, alpha: 1),
UIColor(red: -0.25, green: 0, blue: 0, alpha: 1),
UIColor(red: 0, green: -0.25, blue: 0, alpha: 1),
UIColor(red: 0, green: 0, blue: -0.25, alpha: 1)
]

p3Colors.forEach {
XCTAssertFalse($0.rgbDisplayString().hasSuffix("⚠️"))
XCTAssertTrue($0.rgbDebugDisplayString().hasSuffix("⚠️"))
YCoreUI.isLoggingEnabled = false
}

YCoreUI.isLoggingEnabled = true
}
}