Skip to content

Commit

Permalink
Ensure Okhsl and Okhsv return undefined hues for achromatic colors (#517
Browse files Browse the repository at this point in the history
)

* Ensure Okhsl and Okhsv return undefined hues for achromatic colors

Fixes #516

* Provide variables for epsilon values

* Update to use null and rework Okhsl saturation handling
  • Loading branch information
facelessuser committed May 29, 2024
1 parent fcb5436 commit cf5f15e
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 15 deletions.
9 changes: 4 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 22 additions & 3 deletions src/spaces/okhsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,8 @@ function okhslToOklab (hsl, lmsToRgb, okCoeff) {

let [h, s, l] = hsl;
let L = toeInv(l);
let a = 0.0;
let b = 0.0;
let a = null;
let b = null;
h = constrain(h) / 360.0;

if (L !== 0.0 && L !== 1.0 && s !== 0) {
Expand Down Expand Up @@ -423,6 +423,10 @@ function okhslToOklab (hsl, lmsToRgb, okCoeff) {
function oklabToOkhsl (lab, lmsToRgb, okCoeff) {
// Oklab to Okhsl.

// Epsilon for lightness should approach close to 32 bit lightness
// Epsilon for saturation just needs to be sufficiently close when denoting achromatic
let εL = 1e-7;
let εS = 1e-4;
let L = lab[0];
let s = 0.0;
let l = toe(L);
Expand Down Expand Up @@ -458,7 +462,22 @@ function oklabToOkhsl (lab, lmsToRgb, okCoeff) {
}
}

return [constrain(h * 360), s, l];
const achromatic = Math.abs(s) < εS;
if (achromatic || l === 0.0 || Math.abs(1 - l) < εL) {
h = null;
// Due to floating point imprecision near lightness of 1, we can end up
// with really high around white, this is to provide consistency as
// saturation can be really high for white due this imprecision.
if (!achromatic) {
s = 0.0;
}
}

else {
h = constrain(h * 360);
}

return [h, s, l];
}


Expand Down
16 changes: 13 additions & 3 deletions src/spaces/okhsv.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ function okhsvToOklab (hsv, lmsToRgb, okCoeff) {
h = constrain(h) / 360.0;

let l = toeInv(v);
let a = 0.0;
let b = 0.0;
let a = null;
let b = null;

// Avoid processing gray or colors with undefined hues
if (l !== 0.0 && s !== 0.0) {
Expand Down Expand Up @@ -95,6 +95,8 @@ function okhsvToOklab (hsv, lmsToRgb, okCoeff) {
function oklabToOkhsv (lab, lmsToRgb, okCoeff) {
// Oklab to Okhsv.

// Epsilon for saturation just needs to be sufficiently close when denoting achromatic
let ε = 1e-4;
let l = lab[0];
let s = 0.0;
let v = toe(l);
Expand Down Expand Up @@ -134,7 +136,15 @@ function oklabToOkhsv (lab, lmsToRgb, okCoeff) {
s = (s0 + tMax) * cv / ((tMax * s0) + tMax * k * cv);
}

return [constrain(h * 360), s, v];
if (Math.abs(s) < ε || v === 0.0) {
h = null;
}

else {
h = constrain(h * 360);
}

return [h, s, v];
}


Expand Down
8 changes: 4 additions & 4 deletions test/conversions.js
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,7 @@ const tests = {
{
name: "sRGB white to Okhsl",
args: "white",
expect: [180, 0.6519721306444567, 1.0000000000000002],
expect: [ null, 0.0, 1.0000000000000002 ],
},
{
name: "sRGB red to Okhsl",
Expand Down Expand Up @@ -1000,7 +1000,7 @@ const tests = {
{
name: "sRGB black to Okhsl",
args: "black",
expect: [0.0, 0.0, 0.0],
expect: [ null, 0.0, 0.0 ],
},
],
},
Expand All @@ -1013,7 +1013,7 @@ const tests = {
{
name: "sRGB white to Okhsv",
args: "white",
expect: [ 180, 1.3189507366749435e-15, 1.0000000000000007 ],
expect: [ null, 1.3189507366749435e-15, 1.0000000000000007 ],
},
{
name: "sRGB red to Okhsv",
Expand Down Expand Up @@ -1048,7 +1048,7 @@ const tests = {
{
name: "sRGB black to Okhsv",
args: "black",
expect: [0.0, 0.0, 0.0],
expect: [ null, 0.0, 0.0],
},
],
},
Expand Down

0 comments on commit cf5f15e

Please sign in to comment.