Skip to content

Commit 7332d3d

Browse files
committed
Change opacity without a round trip through HLC
change(.opacity, …) converted the color to HLC and back just to set the opacity, which shifted the RGB components by up to about 1e-6 (e.g. green 0 became 1.7e-6). Opacity is the same in every color space, so it now goes through the RGB path. The never reached .opacity branches of the HSB and HLC helpers are removed.
1 parent 9e4459b commit 7332d3d

2 files changed

Lines changed: 28 additions & 9 deletions

File tree

‎Sources/HandySwiftUI/Extensions/ColorExt.swift‎

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ extension Color {
504504
return self.change(attribute, to: self.hlco.chroma + addition)
505505

506506
case .opacity:
507-
return self.change(attribute, to: self.hlco.opacity + addition)
507+
return self.change(attribute, to: self.rgbo.opacity + addition)
508508
}
509509
}
510510

@@ -516,13 +516,14 @@ extension Color {
516516
/// - Returns: The resulting new `Color` with the specified change applied.
517517
public func change(_ attribute: ChangeableAttribute, to newValue: Double) -> Self {
518518
switch attribute {
519-
case .red, .green, .blue:
519+
case .red, .green, .blue, .opacity:
520+
// Opacity is the same in every color space, so the RGB path changes it without a lossy round trip through HLC
520521
return self.newRgboColor(attribute, newValue)
521522

522523
case .hueHSB, .saturation, .brightness:
523524
return self.newHsboColor(attribute, newValue)
524525

525-
case .hueHLC, .luminance, .chroma, .opacity:
526+
case .hueHLC, .luminance, .chroma:
526527
return self.newHlcoColor(attribute, newValue)
527528
}
528529
}
@@ -540,9 +541,6 @@ extension Color {
540541
case .chroma:
541542
newHlco.chroma = newValue
542543

543-
case .opacity:
544-
newHlco.opacity = newValue
545-
546544
default:
547545
break
548546
}
@@ -563,9 +561,6 @@ extension Color {
563561
case .brightness:
564562
newHsbo.brightness = newValue
565563

566-
case .opacity:
567-
newHsbo.opacity = newValue
568-
569564
default:
570565
break
571566
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import SwiftUI
2+
import Testing
3+
4+
@testable import HandySwiftUI
5+
6+
@MainActor
7+
struct ColorExtTests {
8+
@Test(arguments: [(1.0, 0.0, 0.5), (0.2, 0.4, 0.6), (0.0, 1.0, 0.0)])
9+
func changingOpacityKeepsTheRGBComponents(red: Double, green: Double, blue: Double) {
10+
let color = Color(red: red, green: green, blue: blue)
11+
12+
// Float precision of the platform colors allows for deviations around 1e-7, the HLC round trip caused around 1e-6
13+
func expectSameRGB(_ changed: (red: Double, green: Double, blue: Double, opacity: Double), opacity: Double) {
14+
let tolerance = 0.000_000_5
15+
#expect(abs(changed.red - color.rgbo.red) < tolerance)
16+
#expect(abs(changed.green - color.rgbo.green) < tolerance)
17+
#expect(abs(changed.blue - color.rgbo.blue) < tolerance)
18+
#expect(abs(changed.opacity - opacity) < tolerance)
19+
}
20+
21+
expectSameRGB(color.change(.opacity, to: 0.5).rgbo, opacity: 0.5)
22+
expectSameRGB(color.change(.opacity, by: -0.25).rgbo, opacity: 0.75)
23+
}
24+
}

0 commit comments

Comments
 (0)