diff --git a/Sources/HandySwiftUI/Extensions/ColorExt.swift b/Sources/HandySwiftUI/Extensions/ColorExt.swift index 229f535..b71dbc1 100644 --- a/Sources/HandySwiftUI/Extensions/ColorExt.swift +++ b/Sources/HandySwiftUI/Extensions/ColorExt.swift @@ -504,7 +504,7 @@ extension Color { return self.change(attribute, to: self.hlco.chroma + addition) case .opacity: - return self.change(attribute, to: self.hlco.opacity + addition) + return self.change(attribute, to: self.rgbo.opacity + addition) } } @@ -516,13 +516,14 @@ extension Color { /// - Returns: The resulting new `Color` with the specified change applied. public func change(_ attribute: ChangeableAttribute, to newValue: Double) -> Self { switch attribute { - case .red, .green, .blue: + case .red, .green, .blue, .opacity: + // Opacity is the same in every color space, so the RGB path changes it without a lossy round trip through HLC return self.newRgboColor(attribute, newValue) case .hueHSB, .saturation, .brightness: return self.newHsboColor(attribute, newValue) - case .hueHLC, .luminance, .chroma, .opacity: + case .hueHLC, .luminance, .chroma: return self.newHlcoColor(attribute, newValue) } } @@ -540,9 +541,6 @@ extension Color { case .chroma: newHlco.chroma = newValue - case .opacity: - newHlco.opacity = newValue - default: break } @@ -563,9 +561,6 @@ extension Color { case .brightness: newHsbo.brightness = newValue - case .opacity: - newHsbo.opacity = newValue - default: break } diff --git a/Tests/HandySwiftUITests/ColorExtTests.swift b/Tests/HandySwiftUITests/ColorExtTests.swift index d5b0bc4..f8b3a6b 100644 --- a/Tests/HandySwiftUITests/ColorExtTests.swift +++ b/Tests/HandySwiftUITests/ColorExtTests.swift @@ -46,4 +46,21 @@ struct ColorExtTests { // An in-gamut Display P3 color must be converted, not reported with its raw P3 components (which would be "80CC4D") #expect(Color(.displayP3, red: 0.5, green: 0.8, blue: 0.3).toHex() == "64CE2F") } + + @Test(arguments: [(1.0, 0.0, 0.5), (0.2, 0.4, 0.6), (0.0, 1.0, 0.0)]) + func changingOpacityKeepsTheRGBComponents(red: Double, green: Double, blue: Double) { + let color = Color(red: red, green: green, blue: blue) + + // Float precision of the platform colors allows for deviations around 1e-7, the HLC round trip caused around 1e-6 + func expectSameRGB(_ changed: (red: Double, green: Double, blue: Double, opacity: Double), opacity: Double) { + let tolerance = 0.000_000_5 + #expect(abs(changed.red - color.rgbo.red) < tolerance) + #expect(abs(changed.green - color.rgbo.green) < tolerance) + #expect(abs(changed.blue - color.rgbo.blue) < tolerance) + #expect(abs(changed.opacity - opacity) < tolerance) + } + + expectSameRGB(color.change(.opacity, to: 0.5).rgbo, opacity: 0.5) + expectSameRGB(color.change(.opacity, by: -0.25).rgbo, opacity: 0.75) + } }