Skip to content

Commit e1f1ee1

Browse files
authored
Fix hue blending between a gray and a non-gray (#30)
1 parent 080cb3d commit e1f1ee1

File tree

6 files changed

+49
-8
lines changed

6 files changed

+49
-8
lines changed

docs/src/color-space-dependence.md

+15
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,21 @@ julia> blend(c1, blend(c2, c3))
6464
HSL{Float64}(240.0,1.0,0.8)
6565
```
6666

67+
In addition, in the case of blending a gray, i.e., a color with zero saturation,
68+
and a non-gray, the hue of the non-gray color is used.
69+
70+
```jldoctest; setup=(using ColorTypes, ColorBlendModes;)
71+
julia> for w in 0.0:0.2:1.0 # gray to yellow green, not via brown
72+
println(blend(HSI(12.3, 0.0, 0.5), HSI(78.9, 1.0, 0.5), opacity=w))
73+
end
74+
HSI{Float64}(78.9,0.0,0.5)
75+
HSI{Float64}(78.9,0.2,0.5)
76+
HSI{Float64}(78.9,0.4,0.5)
77+
HSI{Float64}(78.9,0.6,0.5)
78+
HSI{Float64}(78.9,0.8,0.5)
79+
HSI{Float64}(78.9,1.0,0.5)
80+
```
81+
6782
## Gamma correction
6883

6984
The [`blend`](@ref) function blends colors in a "uniform" space. Note that this

src/operations.jl

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11

22
mapch(f, x, y) = mapc(f, x, y)
33
mapch(f, x::C, y::C) where C <: Union{HSV, HSL, HSI} =
4-
C(f(Hue(x.h), Hue(y.h)), f(x.s, y.s), f(comp3(x), comp3(y)))
4+
C(f(Hue(x), Hue(y)), f(x.s, y.s), f(comp3(x), comp3(y)))
55
mapch(f, x::C, y::C) where C <: Union{LCHab, LCHuv} =
6-
C(f(x.l, y.l), f(x.c, y.c), f(Hue(x.h), Hue(y.h)))
6+
C(f(x.l, y.l), f(x.c, y.c), f(Hue(x), Hue(y)))
77

88
mapca(fc, a, x::C) where C <: TransparentColorN{2} = C(fc(comp1(x)), a)
99
mapca(fc, a, x::C) where C <: TransparentColorN{3} = C(fc(comp1(x)), fc(comp2(x)), a)
@@ -15,9 +15,9 @@ mapca(fc, a, x::C, y::C) where C <: TransparentColorN{3} =
1515
mapca(fc, a, x::C, y::C) where C <: TransparentColorN{4} =
1616
C(fc(comp1(x), comp1(y)), fc(comp2(x), comp2(y)), fc(comp3(x), comp3(y)), a)
1717
mapca(fc, a, x::C, y::C) where C <: TransparentColorN{4, <:Union{HSV, HSL, HSI}} =
18-
C(fc(Hue(x.h), Hue(y.h)), fc(x.s, y.s), fc(comp3(x), comp3(y)), a)
18+
C(fc(Hue(x), Hue(y)), fc(x.s, y.s), fc(comp3(x), comp3(y)), a)
1919
mapca(fc, a, x::C, y::C) where C <: TransparentColorN{4, <:Union{LCHab, LCHuv}} =
20-
C(fc(x.l, y.l), fc(x.c, y.c), fc(Hue(x.h), Hue(y.h)))
20+
C(fc(x.l, y.l), fc(x.c, y.c), fc(Hue(x), Hue(y)))
2121

2222
# complement
2323
_n(v::T) where T = oneunit(T) - v
@@ -26,9 +26,11 @@ _n(v::T) where T = oneunit(T) - v
2626
_w(v1::T, v2::T, w) where T = convert(T, muladd(_n(w), v1, w * v2))
2727

2828
function _w(h1::Hue{T}, h2::Hue{T}, w) where T
29-
d0 = h2.angle - h1.angle
29+
h1a = ifelse(h1.isgray, h2.angle, h1.angle)
30+
h2a = ifelse(h2.isgray, h1.angle, h2.angle)
31+
d0 = h2a - h1a
3032
d = ifelse(abs(d0) > T(180), d0 - copysign(T(360), d0), d0)
31-
a = muladd(d, w, h1.angle)
33+
a = muladd(d, w, h1a)
3234
convert(T, ifelse(a > T(360), a - T(360), ifelse(a < T(0), a + T(360), a)))
3335
end
3436

@@ -45,8 +47,7 @@ _w_safe(v1::T, w1, v2::T, w2) where T = min(oneunit(T), _w(v1, w1, v2, w2))
4547
_w_safe(v1::T, w1, v2::T, w2) where T <: FixedPoint =
4648
convert(T, min(float(typemax(T)), muladd(w1, float(v1), w2 * float(v2))))
4749

48-
_comp(op::CompositeOperation{:clear}, c1, c2) =
49-
mapc(v1 -> zero(v1), c1)
50+
_comp(op::CompositeOperation{:clear}, c1, c2) = mapc(v1 -> zero(v1), c1)
5051

5152
_comp(op::CompositeOperation{:copy}, c1, c2) = c2
5253

src/types.jl

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ const TransparentColorN{N,C<:Color,T} = TransparentColor{C,T,N}
33

44
struct Hue{T<:Real}
55
angle::T
6+
isgray::Bool
7+
Hue(angle::T, isgray::Bool=false) where {T <: Real} = new{T}(angle, isgray)
8+
Hue(c::C) where {T, C<:Union{HSV{T}, HSL{T}, HSI{T},
9+
AHSV{T}, AHSL{T}, AHSI{T}, HSVA{T}, HSLA{T}, HSIA{T}}} =
10+
new{T}(c.h, c.s == zero(T))
11+
Hue(c::C) where {T, C<:Union{LCHab{T}, LCHuv{T},
12+
ALCHab{T}, ALCHuv{T}, LCHabA{T}, LCHuvA{T}}} =
13+
new{T}(c.h, c.c == zero(T))
614
end
715

816
const SeparableBlendMode = Union{

test/blend_hsx.jl

+4
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,8 @@
2020
@testset "$A over $A: $T" for T in (Float64, Float32)
2121
@test BlendNormal(A{T}(100, 0.75, 0, 0.6), A{T}(0, 0.5, 1, 0.6)) A{T}(200/7, 4/7, 5/7, 0.84)
2222
end
23+
24+
@testset "$C over gray $C: $T" for T in (Float64, Float32)
25+
@test blend(C{T}(100, 0, 1), C{T}(200, 1, 0.5), opacity=0.5) C{T}(200, 0.5, 0.75)
26+
end
2327
end

test/blend_lab.jl

+6
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,9 @@
3636
@test BlendNormal(A{T}(90, 80, 70, 0.6), A{T}(60, 50, -40, 0.6)) A{T}(68.57142857142857, 58.57142857142857, -8.57142857142857, 0.84)
3737
end
3838
end
39+
40+
@testset "$C" for C in (LCHab, LCHuv)
41+
@testset "$C over gray $C: $T" for T in (Float64, Float32)
42+
@test blend(C{T}(40, 0, 100), C{T}(60, 100, 200), opacity=0.5) C{T}(50, 50, 200)
43+
end
44+
end

test/runtests.jl

+7
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ end
9595
@test ColorBlendModes._w(Hue(175.0), 0.4, Hue(185.0), 0.6) 181.0
9696
@test ColorBlendModes._w(Hue(230.0), 0.4, Hue(30.0), 0.6) 326.0
9797
@test ColorBlendModes._w(Hue(5.0), 0.4, Hue(355.0), 0.6) 359.0
98+
99+
@test ColorBlendModes._w(Hue(30.0, true), Hue(130.0), 0.0) 130.0
100+
@test ColorBlendModes._w(Hue(30.0, true), Hue(130.0), 0.6) 130.0
101+
@test ColorBlendModes._w(Hue(30.0, true), Hue(130.0), 1.0) 130.0
102+
@test ColorBlendModes._w(Hue(30.0), Hue(130.0, true), 0.0) 30.0
103+
@test ColorBlendModes._w(Hue(30.0), Hue(130.0, true), 0.6) 30.0
104+
@test ColorBlendModes._w(Hue(30.0), Hue(130.0, true), 1.0) 30.0
98105
end
99106

100107
@testset "blend: RGB" begin

0 commit comments

Comments
 (0)