Skip to content
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

Add Oklab and Oklch #533

Merged
merged 5 commits into from
May 19, 2024
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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"

[compat]
ColorTypes = "0.10, 0.11"
ColorTypes = "0.11.5"
FixedPointNumbers = "0.6, 0.7, 0.8"
Reexport = "0.2, 1.0"
julia = "1"
Expand Down
6 changes: 6 additions & 0 deletions docs/crosssectionalcharts.jl
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ crosssection(::Type{LCHab}) = crosssection(LCHab, x=(2, "C*", 0:100),
crosssection(::Type{LCHuv}) = crosssection(LCHuv, x=(2, "C*", 0:100),
y=(1, "L*", 0:100),
z=(3, "H", 0:360))
crosssection(::Type{Oklab}) = crosssection(Oklab, x=(2, "a", -0.4:0.01:0.4),
y=(3, "b", -0.4:0.01:0.4),
z=(1, "L", 0:1))
crosssection(::Type{Oklch}) = crosssection(Oklch, x=(2, "C", 0:0.01:0.4),
y=(1, "L", 0:1),
z=(3, "h", 0:360))

crosssection(::Type{YIQ}) = crosssection(YIQ, x=(2, "I", -1:1),
y=(3, "Q", -1:1),
Expand Down
7 changes: 7 additions & 0 deletions docs/src/constructionandconversion.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ CrossSectionalCharts.crosssection(LCHab) # hide
```@example cross
CrossSectionalCharts.crosssection(LCHuv) # hide
```
- `Oklab`, `Oklch` and their transparent variants
```@example cross
CrossSectionalCharts.crosssection(Oklab) # hide
```
```@example cross
CrossSectionalCharts.crosssection(Oklch) # hide
```
- `DIN99`, `DIN99d`, `DIN99o` and all 6 transparent variants

- Storage formats `YIQ`, `YCbCr` and their transparent variants
Expand Down
57 changes: 52 additions & 5 deletions src/conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ convert(::Type{Lab{T}}, c, wp::XYZ) where {T} = cnvt(Lab{T}, c, wp)
convert(::Type{Luv{T}}, c, wp::XYZ) where {T} = cnvt(Luv{T}, c, wp)

# FIXME: inference helpers for LCH <--> RGB conversions
convert(::Type{RGB}, c::Union{LCHab{T}, LCHuv{T}}) where {T} = cnvt(RGB{T}, cnvt(XYZ{T}, c))
convert(::Type{RGB{T}}, c::Union{LCHab{T}, LCHuv{T}}) where {T} = cnvt(RGB{T}, cnvt(XYZ{T}, c))
convert(::Type{RGB}, c::Union{LCHab{T}, LCHuv{T}, Oklch{T}}) where {T} = cnvt(RGB{T}, cnvt(XYZ{T}, c))
convert(::Type{RGB{T}}, c::Union{LCHab{T}, LCHuv{T}, Oklch{T}}) where {T} = cnvt(RGB{T}, cnvt(XYZ{T}, c))
convert(::Type{Lab{T}}, c::RGB{T}) where {T} = cnvt(Lab{T}, cnvt(XYZ{T}, c))
convert(::Type{Luv{T}}, c::RGB{T}) where {T} = cnvt(Luv{T}, cnvt(XYZ{T}, c))
convert(::Type{Oklab{T}}, c::RGB{T}) where {T} = cnvt(Oklab{T}, cnvt(XYZ{T}, c))

# Fallback to catch undefined operations
cnvt(::Type{C}, c::TransparentColor) where {C<:Color} = cnvt(C, color(c))
Expand Down Expand Up @@ -202,9 +203,9 @@ end

# To avoid stack overflow, the source types which do not support direct or
# indirect conversion to RGB should be rejected.
cnvt(::Type{CV}, c::Union{LMS, xyY} ) where {CV<:AbstractRGB} = cnvt(CV, cnvt(XYZ{eltype(c)}, c))
cnvt(::Type{CV}, c::Union{Lab, Luv, LCHab, LCHuv}) where {CV<:AbstractRGB} = cnvt(CV, cnvt(XYZ{eltype(c)}, c))
cnvt(::Type{CV}, c::Union{DIN99d, DIN99o, DIN99} ) where {CV<:AbstractRGB} = cnvt(CV, cnvt(XYZ{eltype(c)}, c))
cnvt(::Type{CV}, c::Union{LMS, xyY}) where {CV<:AbstractRGB} = cnvt(CV, cnvt(XYZ{eltype(c)}, c))
cnvt(::Type{CV}, c::Union{Lab, Luv, Oklab, LCHab, LCHuv, Oklch}) where {CV<:AbstractRGB} = cnvt(CV, cnvt(XYZ{eltype(c)}, c))
cnvt(::Type{CV}, c::Union{DIN99d, DIN99o, DIN99}) where {CV<:AbstractRGB} = cnvt(CV, cnvt(XYZ{eltype(c)}, c))
@noinline function cnvt(::Type{CV}, @nospecialize(c::Color)) where {CV<:AbstractRGB}
error("No conversion of ", c, " to ", CV, " has been defined")
end
Expand Down Expand Up @@ -428,6 +429,14 @@ function cnvt(::Type{XYZ{T}}, c::LMS) where T
@mul3x3 XYZ{T} CAT02_INV c.l c.m c.s
end


function cnvt(::Type{XYZ{T}}, c::Oklab) where T
lmsp = @mul3x3 LMS{T} M_OKLMSP2OKLAB_INV c.l c.a c.b
@mul3x3 XYZ{T} M_XYZ2OKLMS_INV lmsp.l^3 lmsp.m^3 lmsp.s^3
end

cnvt(::Type{XYZ{T}}, c::Oklch) where {T} = cnvt(XYZ{T}, cnvt(Oklab{T}, c))

cnvt(::Type{XYZ{T}}, c::Union{LCHab, DIN99, DIN99o}) where {T} = cnvt(XYZ{T}, cnvt(Lab{T}, c))
cnvt(::Type{XYZ{T}}, c::LCHuv) where {T} = cnvt(XYZ{T}, cnvt(Luv{T}, c))
cnvt(::Type{XYZ{T}}, c::Color) where {T} = cnvt(XYZ{T}, convert(RGB{T}, c)::RGB{T})
Expand Down Expand Up @@ -593,6 +602,44 @@ end
cnvt(::Type{LCHab{T}}, c::Color) where {T} = cnvt(LCHab{T}, convert(Lab{T}, c)::Lab{T})


# Everything to Oklab
# -------------------

# Matrices as specified in https://bottosson.github.io/posts/oklab/
const M_XYZ2OKLMS = Mat3x3([0.8189330101 0.3618667424 -0.1288597137
0.0329845436 0.9293118715 0.0361456387
0.0482003018 0.2643662691 0.6338517070])

const M_XYZ2OKLMS_INV = Mat3x3(inv(Float64.(M_XYZ2OKLMS)))

const M_OKLMSP2OKLAB = Mat3x3([0.2104542553 0.7936177850 -0.0040720468
1.9779984951 -2.4285922050 0.4505937099
0.0259040371 0.7827717662 -0.8086757660])

const M_OKLMSP2OKLAB_INV = Mat3x3(inv(Float64.(M_OKLMSP2OKLAB)))

function cnvt(::Type{Oklab{T}}, c::XYZ) where T
lms = @mul3x3 LMS{T} M_XYZ2OKLMS c.x c.y c.z
@mul3x3 Oklab{T} M_OKLMSP2OKLAB cbrt(lms.l) cbrt(lms.m) cbrt(lms.s)
end

function cnvt(::Type{Oklab{T}}, c::Oklch) where T
Oklab{T}(c.l, polar_to_cartesian(c.c, c.h)...)
end

cnvt(::Type{Oklab{T}}, c::Color) where {T} = cnvt(Oklab{T}, convert(XYZ{T}, c)::XYZ{T})


# Everything to Oklch
# -------------------

function cnvt(::Type{Oklch{T}}, c::Oklab) where T
Oklch{T}(c.l, chroma(c), hue(c))
end

cnvt(::Type{Oklch{T}}, c::Color) where {T} = cnvt(Oklch{T}, convert(Oklab{T}, c)::Oklab{T})


# Everything to DIN99
# -------------------

Expand Down
4 changes: 2 additions & 2 deletions src/precompile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function _precompile_()
cctypes = (Gray24, AGray32, RGB24, ARGB32) # non-parametric colors
# conversions
## from/to XYZ
for T in feltypes, C in (HSV,LCHab,LCHuv,Lab,Luv)
for T in feltypes, C in (HSV,LCHab,LCHuv,Lab,Luv,Oklab,Oklch)
precompile(Tuple{typeof(convert),Type{C{T}},XYZ{T}})
precompile(Tuple{typeof(convert),Type{XYZ{T}},C{T}})
end
Expand All @@ -15,7 +15,7 @@ function _precompile_()
precompile(Tuple{typeof(convert),Type{XYZ{T}},RGB{F}})
end
## to RGB
for T in eltypes, F in feltypes, C in (HSV,LCHab,LCHuv,Lab,Luv)
for T in eltypes, F in feltypes, C in (HSV,LCHab,LCHuv,Lab,Luv,Oklab,Oklch)
precompile(Tuple{typeof(convert),Type{RGB{T}},C{F}})
end
# parse
Expand Down
19 changes: 10 additions & 9 deletions src/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,10 @@ atan360(y, x) = (a = atand(y, x); signbit(a) ? oftype(a, a + 360) : a)
return dd
end

# override only the `Lab` and `Luv` versions just for now
# override only the `Lab`, `Luv`, and `Oklab` versions just for now
@inline ColorTypes.hue(c::Lab) = atan360(c.b, c.a)
@inline ColorTypes.hue(c::Luv) = atan360(c.v, c.u)
@inline ColorTypes.hue(c::Oklab) = atan360(c.b, c.a)

@inline function sin_kernel(x::Float64)
x * @evalpoly(x^2,
Expand Down Expand Up @@ -376,8 +377,8 @@ hue, in degrees, in [0, 360]. The normalization is essentially equivalent to
normalize_hue(c::C) where {C <: Union{HSV, HSL, HSI}} = C(normalize_hue(c.h), c.s, comp3(c))
normalize_hue(c::C) where {Cb <: Union{HSV, HSL, HSI}, C <: Union{AlphaColor{Cb}, ColorAlpha{Cb}}} =
C(normalize_hue(c.h), c.s, comp3(c), alpha(c))
normalize_hue(c::C) where C <: Union{LCHab, LCHuv} = C(c.l, c.c, normalize_hue(c.h))
normalize_hue(c::C) where {Cb <: Union{LCHab, LCHuv}, C <: Union{AlphaColor{Cb}, ColorAlpha{Cb}}} =
normalize_hue(c::C) where C <: Union{LCHab, LCHuv, Oklch} = C(c.l, c.c, normalize_hue(c.h))
normalize_hue(c::C) where {Cb <: Union{LCHab, LCHuv, Oklch}, C <: Union{AlphaColor{Cb}, ColorAlpha{Cb}}} =
C(c.l, c.c, normalize_hue(c.h), c.alpha)

"""
Expand All @@ -401,7 +402,7 @@ function mean_hue(h1::T, h2::T) where {T <: Real}
mh = muladd(F(0.5), d, hmin)
return mh < 0 ? mh + 360 : mh
end
@inline function mean_hue(a::C, b::C) where {Cb <: Union{Lab, Luv},
@inline function mean_hue(a::C, b::C) where {Cb <: Union{Lab, Luv, Oklab},
C <: Union{Cb, AlphaColor{Cb}, ColorAlpha{Cb}}}
a1, b1, a2, b2 = comp2(a), comp3(a), comp2(b), comp3(b)
c1, c2 = chroma(a), chroma(b)
Expand All @@ -421,7 +422,7 @@ end
mb = muladd(k2, b1, k1 * b2)
hue(Cb(zero(ma), ma, mb))
end
function mean_hue(a::C, b::C) where {Cb <: Union{LCHab, LCHuv},
function mean_hue(a::C, b::C) where {Cb <: Union{LCHab, LCHuv, Oklch},
C <: Union{Cb, AlphaColor{Cb}, ColorAlpha{Cb}}}
mean_hue(a.c == 0 ? b.h : a.h, b.c == 0 ? a.h : b.h)
end
Expand All @@ -435,7 +436,7 @@ _delta_h_th(T) = zero(T)
_delta_h_th(::Type{Float32}) = 0.1f0
_delta_h_th(::Type{Float64}) = 6.5e-3

function delta_h(a::C, b::C) where {Cb <: Union{Lab, Luv},
function delta_h(a::C, b::C) where {Cb <: Union{Lab, Luv, Oklab},
C <: Union{Cb, AlphaColor{Cb}, ColorAlpha{Cb}}}
a1, b1, a2, b2 = comp2(a), comp3(a), comp2(b), comp3(b)
c1, c2 = chroma(a), chroma(b)
Expand All @@ -458,7 +459,7 @@ function delta_h(a::C, b::C) where {Cb <: Union{Lab, Luv},
return @fastmath sqrt(c1 * c2) * sn
end
end
function delta_h(a::C, b::C) where {Cb <: Union{LCHab, LCHuv},
function delta_h(a::C, b::C) where {Cb <: Union{LCHab, LCHuv, Oklch},
C <: Union{Cb, AlphaColor{Cb}, ColorAlpha{Cb}}}
dh0 = hue(a) - hue(b)
sh = muladd(dh0, oftype(dh0, 1 / 360), oftype(dh0, 0.5))
Expand All @@ -467,7 +468,7 @@ function delta_h(a::C, b::C) where {Cb <: Union{LCHab, LCHuv},
end
delta_h(a, b) = delta_h(promote(a, b)...)

@inline function delta_c(a::C, b::C) where {Cb <: Union{Lab{Float32}, Luv{Float32}},
@inline function delta_c(a::C, b::C) where {Cb <: Union{Lab{Float32}, Luv{Float32}, Oklab{Float32}},
C <: Union{Cb, AlphaColor{Cb}, ColorAlpha{Cb}}}
n1, m1 = @fastmath minmax(comp2(a)^2, comp3(a)^2)
n2, m2 = @fastmath minmax(comp2(b)^2, comp3(b)^2)
Expand All @@ -482,7 +483,7 @@ Returns the color `w1*c1 + (1-w1)*c2` that is the weighted mean of `c1` and
`c2`, where `c1` has a weight 0 ≤ `w1` ≤ 1.
"""
weighted_color_mean(w1::Real, c1::Colorant, c2::Colorant) = _weighted_color_mean(w1, c1, c2)
function weighted_color_mean(w1::Real, c1::C, c2::C) where {Cb <: Union{HSV, HSL, HSI, LCHab, LCHuv},
function weighted_color_mean(w1::Real, c1::C, c2::C) where {Cb <: Union{HSV, HSL, HSI, LCHab, LCHuv, Oklch},
C <: Union{Cb, AlphaColor{Cb}, ColorAlpha{Cb}}}
normalize_hue(_weighted_color_mean(w1, c1, c2))
end
Expand Down
36 changes: 34 additions & 2 deletions test/conversion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ using ColorTypes: eltype_default
# The filter mechanism should not be removed, as new types may be added in the future.
const supported_p3 = filter(ColorTypes.parametric3) do C
sym = Symbol(C)
# TODO: Remove the following exclusions when the color types are supported.
sym in (:Oklab, :Oklch) && return false
# NOTE: Add any currently unsupported color types here.
# sym in (:unsupported1, :unsupported2) && return false
return true
end
if supported_p3 == ColorTypes.parametric3
Expand Down Expand Up @@ -236,6 +236,32 @@ end
@test convert(RGB{Float64}, HSI{BigFloat}(-360120, .5, .5)) ≈ RGB{Float64}(.25,.25,1)
end

# Oklab examples from Björn Ottosson (https://bottosson.github.io/posts/oklab/)
@testset "Oklab <-> XYZ" begin
@test convert(Oklab, XYZ{Float32}(0.950, 1.000, 1.089)) ≈ Oklab{Float32}(1.000, 0.000, 0.000) atol=2e-3
@test convert(Oklab, XYZ{Float32}(1.000, 0.000, 0.000)) ≈ Oklab{Float32}(0.450, 1.236, -0.019) atol=2e-3
@test convert(Oklab, XYZ{Float32}(0.000, 1.000, 0.000)) ≈ Oklab{Float32}(0.922, -0.671, 0.263) atol=2e-3
@test convert(Oklab, XYZ{Float32}(0.000, 0.000, 1.000)) ≈ Oklab{Float32}(0.153, -1.415, -0.449) atol=2e-3

@test convert(XYZ, Oklab{Float32}(1.000, 0.000, 0.000)) ≈ XYZ{Float32}(0.950, 1.000, 1.089) atol=2e-3
@test convert(XYZ, Oklab{Float32}(0.450, 1.236, -0.019)) ≈ XYZ{Float32}(1.000, 0.000, 0.000) atol=2e-3
@test convert(XYZ, Oklab{Float32}(0.922, -0.671, 0.263)) ≈ XYZ{Float32}(0.000, 1.000, 0.000) atol=2e-3
@test convert(XYZ, Oklab{Float32}(0.153, -1.415, -0.449)) ≈ XYZ{Float32}(0.000, 0.000, 1.000) atol=2e-3
end

# Selection of Oklch tests from the WPT test suite (https://wpt.fyi/results/css/css-color)
@testset "Oklch <-> RGB" begin
@test convert(Oklch, RGB{Float32}(0.00000, 0.00000, 0.00000)) ≈ Oklch{Float32}(0.00, 0.00, 0) rtol=2e-2
@test convert(Oklch, RGB{Float32}(0.23056, 0.31730, 0.82628)) ≈ Oklch{Float32}(0.50, 0.20, 270) rtol=2e-2
@test convert(Oklch, RGB{Float32}(0.32022, 0.85805, 0.61147)) ≈ Oklch{Float32}(0.80, 0.15, 160) rtol=2e-2
@test convert(Oklch, RGB{Float32}(0.67293, 0.27791, 0.52280)) ≈ Oklch{Float32}(0.55, 0.15, 345) rtol=2e-2

@test convert(RGB, Oklch{Float32}(0.00, 0.00, 0)) ≈ RGB{Float32}(0.00000, 0.00000, 0.00000) rtol=2e-2
@test convert(RGB, Oklch{Float32}(0.50, 0.20, 270)) ≈ RGB{Float32}(0.23056, 0.31730, 0.82628) rtol=2e-2
@test convert(RGB, Oklch{Float32}(0.80, 0.15, 160)) ≈ RGB{Float32}(0.32022, 0.85805, 0.61147) rtol=2e-2
@test convert(RGB, Oklch{Float32}(0.55, 0.15, 345)) ≈ RGB{Float32}(0.67293, 0.27791, 0.52280) rtol=2e-2
end

@testset "custom types" begin
# issue #465
@test_throws ErrorException convert(RGB, C3{Float32}(1, 2, 3))
Expand Down Expand Up @@ -271,9 +297,15 @@ end
function diffnorm(a::T, b::T) where {T<:Union{Lab,Luv}}
sqrt(sqd(a.l, b.l, 100) + sqd(comp2(a), comp2(b), 200) + sqd(comp3(a), comp3(b), 200))/sqrt(3)
end
function diffnorm(a::T, b::T) where {T<:Oklab}
sqrt(sqd(a.l, b.l) + sqd(a.a, b.a, 0.4) + sqd(a.b, b.b, 0.4))/sqrt(3)
end
function diffnorm(a::T, b::T) where {T<:Union{LCHab,LCHuv}}
sqrt(sqd(a.l, b.l, 100) + sqd(a.c, b.c, 100) + sqd(a.h, b.h, 360))/sqrt(3)
end
function diffnorm(a::T, b::T) where {T<:Oklch}
sqrt(sqd(a.l, b.l, 1) + sqd(a.c, b.c, 0.4) + sqd(a.h, b.h, 360))/sqrt(3)
end
function diffnorm(a::T, b::T) where {T<:Union{DIN99,DIN99d,DIN99o}} # csconv has no DIN99 case
sqrt(sqd(a.l, b.l, 100) + sqd(a.a, b.a, 100) + sqd(a.b, b.b, 100))/sqrt(3)
end
Expand Down
42 changes: 42 additions & 0 deletions test/test_conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,48 @@ LCHab{Float64}(44.97666070400284,73.18288064267016,343.7064570495635),
LCHab{Float64}(60.664366637100485,50.33163619069481,134.3624622173942),
LCHab{Float64}(17.93658797983788,19.25047865923466,4.326814913733189),
LCHab{Float64}(22.70092488166712,79.69128178686806,301.8881183495275)],
Oklab{Float64} => Oklab{Float64}[
eprovst marked this conversation as resolved.
Show resolved Hide resolved
Oklab{Float64}(0.6473642587850058, -0.16640845649540867, 0.12765220713589567),
Oklab{Float64}(0.6282157000609071, 0.08183520074853895, 0.11452219273635467),
Oklab{Float64}(0.5091740287281353, -0.07733208893501779, 0.08085693619818175),
Oklab{Float64}(0.669802095404347, -0.04346788322947211, 0.11796988019003865),
Oklab{Float64}(0.6208435577510582, 0.2344081686546006, 0.06775684070354147),
Oklab{Float64}(0.46482609679999626, 0.03810249957927366, 0.012580885182442119),
Oklab{Float64}(0.7830976646978222, -0.07410740013992041, 0.14346504139165858),
Oklab{Float64}(0.7546751782330786, -0.026128400789299126, 0.1300180919169248),
Oklab{Float64}(0.4962641797550877, 0.0949917024525385, -0.23911049231795142),
Oklab{Float64}(0.5315894235973362, 0.18443388521737486, -0.13286781075363044),
Oklab{Float64}(0.3233886953836771, 0.004596623887945836, -0.181588589534855),
Oklab{Float64}(0.5991424390294658, 0.22132732851253745, 0.09690369827995632),
Oklab{Float64}(0.4767148234855445, -0.08670766827163175, 0.05896925338549625),
Oklab{Float64}(0.6443546915598534, -0.1459141388694792, 0.12863594238830386),
Oklab{Float64}(0.8157342282735532, 0.03810437558741276, -0.0592931671223586),
Oklab{Float64}(0.6328737374708884, 0.004205728077657682, -0.06868954690353435),
Oklab{Float64}(0.5551261175856945, 0.2139904180324278, -0.05463476841811064),
Oklab{Float64}(0.6488416605810022, -0.09659369398846307, 0.08652002524801804),
Oklab{Float64}(0.3000522124257983, 0.058284478234404524, 0.0039986286050455655),
Oklab{Float64}(0.3510586258806637, -0.015952114291585485, -0.196300479928)],
Oklch{Float64} => Oklch{Float64}[
Oklch{Float64}(0.6473642587850058, 0.20973044695477558, 142.50812592692188),
Oklch{Float64}(0.6282157000609071, 0.1407562883522311, 54.45118126144123),
Oklch{Float64}(0.5091740287281353, 0.1118842978724465, 133.7235179145194),
Oklch{Float64}(0.669802095404347, 0.1257233053355785, 110.2271240721123),
Oklch{Float64}(0.6208435577510582, 0.24400446511104826, 16.122199378967657),
Oklch{Float64}(0.46482609679999626, 0.04012579153315659, 18.272463586642523),
Oklch{Float64}(0.7830976646978222, 0.16147484279914498, 117.31878299737747),
Oklch{Float64}(0.7546751782330786, 0.13261748585131655, 101.3627962996733),
Oklch{Float64}(0.4962641797550877, 0.25728826454264236, 291.666497387129),
Oklch{Float64}(0.5315894235973362, 0.2273097295560363, 324.23068650784376),
Oklch{Float64}(0.3233886953836771, 0.1816467582986971, 271.4500410993016),
Oklch{Float64}(0.5991424390294658, 0.24161149204214083, 23.645235814467426),
Oklch{Float64}(0.4767148234855445, 0.10485987117074957, 145.7806655807504),
Oklch{Float64}(0.6443546915598534, 0.19452028582168157, 138.60103298820795),
Oklch{Float64}(0.8157342282735532, 0.0704813670859651, 302.7265689301318),
Oklch{Float64}(0.6328737374708884, 0.06881818075535014, 273.503735752975),
Oklch{Float64}(0.5551261175856945, 0.22085483225366337, 345.6775522473198),
Oklch{Float64}(0.6488416605810022, 0.1296767384200209, 138.14884921190213),
Oklch{Float64}(0.3000522124257983, 0.05842148092763366, 3.924648887029628),
Oklch{Float64}(0.3510586258806637, 0.19694757772142044, 265.3541385866738)],
LMS{Float64} => LMS{Float64}[
LMS{Float64}(0.2307455933908811,0.3966129226546093,0.06205539564220773),
LMS{Float64}(0.3107019890330763,0.1897841714619035,0.0498207460521362),
Expand Down
Loading