Suggest clamped chroma for out-of-gamut OKLCH
This commit is contained in:
parent
3a0e6a9b7f
commit
a881ba4f77
2 changed files with 41 additions and 21 deletions
|
@ -1,8 +1,8 @@
|
||||||
{ lib }:
|
{ lib }:
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (builtins) add floor mapAttrs split stringLength;
|
inherit (builtins) add all attrValues floor mapAttrs split stringLength;
|
||||||
inherit (lib) concatLines fixedWidthNumber fold isList max removeSuffix splitString throwIfNot toHexString;
|
inherit (lib) concatLines fixedWidthNumber fold isList max removeSuffix splitString throwIf throwIfNot toHexString;
|
||||||
inherit (lib.strings) replicate;
|
inherit (lib.strings) replicate;
|
||||||
inherit (import <nix-math> { inherit lib; }) cos pi polynomial pow sin;
|
inherit (import <nix-math> { inherit lib; }) cos pi polynomial pow sin;
|
||||||
|
|
||||||
|
@ -39,6 +39,14 @@ let
|
||||||
];
|
];
|
||||||
in
|
in
|
||||||
rec {
|
rec {
|
||||||
|
findHighest = accept: resolution: low: high:
|
||||||
|
if high - low < resolution then
|
||||||
|
throwIfNot (accept low) "Lower bound ${toString low} fails acceptance test" low
|
||||||
|
else
|
||||||
|
let half = low + (high - low) * 0.5; in
|
||||||
|
if accept half then findHighest accept resolution half high
|
||||||
|
else findHighest accept resolution low half;
|
||||||
|
|
||||||
frame = color: text:
|
frame = color: text:
|
||||||
let
|
let
|
||||||
lines = splitString "\n" (removeSuffix "\n" text);
|
lines = splitString "\n" (removeSuffix "\n" text);
|
||||||
|
@ -52,26 +60,39 @@ rec {
|
||||||
(color "└───${pad "─" ""}───┘")
|
(color "└───${pad "─" ""}───┘")
|
||||||
]);
|
]);
|
||||||
|
|
||||||
oklchToRgb = { l, c, h }:
|
oklchToCss = { l, c, h }: "oklch(${toString l} ${toString c} ${toString h})";
|
||||||
let
|
|
||||||
# Adapted from https://drafts.csswg.org/css-color-4/#color-conversion-code
|
|
||||||
a = c * cos (h * pi / 180);
|
|
||||||
b = c * sin (h * pi / 180);
|
|
||||||
|
|
||||||
# Adapted from https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab
|
oklchToRgb = target:
|
||||||
long = pow (l + 0.3963377774 * a + 0.2158037573 * b) 3;
|
let
|
||||||
medium = pow (l - 0.1055613458 * a - 0.0638541728 * b) 3;
|
toLinearRgb = { l, c, h }:
|
||||||
short = pow (l - 0.0894841775 * a - 1.2914855480 * b) 3;
|
let
|
||||||
linear = {
|
# Adapted from https://drafts.csswg.org/css-color-4/#color-conversion-code
|
||||||
r = 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short;
|
a = c * cos (h * pi / 180);
|
||||||
g = -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short;
|
b = c * sin (h * pi / 180);
|
||||||
b = -0.0041960863 * long - 0.7034186147 * medium + 1.7076147010 * short;
|
|
||||||
};
|
# Adapted from https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab
|
||||||
oetf = l':
|
long = pow (l + 0.3963377774 * a + 0.2158037573 * b) 3;
|
||||||
throwIfNot (0 <= l' && l' <= 1) "oklch(${toString l} ${toString c} ${toString h}) is not representable in sRGB"
|
medium = pow (l - 0.1055613458 * a - 0.0638541728 * b) 3;
|
||||||
(if l' > 0.0031308 then 1.055 * (pow124 l') - 0.055 else 12.92 * l');
|
short = pow (l - 0.0894841775 * a - 1.2914855480 * b) 3;
|
||||||
|
rgb = {
|
||||||
|
r = 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short;
|
||||||
|
g = -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short;
|
||||||
|
b = -0.0041960863 * long - 0.7034186147 * medium + 1.7076147010 * short;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
if all (u: 0 <= u && u <= 1) (attrValues rgb) then rgb else null;
|
||||||
|
|
||||||
|
# Adapted from https://bottosson.github.io/posts/colorwrong/#what-can-we-do%3F
|
||||||
|
oetf = u: if u > 0.0031308 then 1.055 * (pow124 u) - 0.055 else 12.92 * u;
|
||||||
|
|
||||||
|
inGamut = c: toLinearRgb (target // { inherit c; }) != null;
|
||||||
|
clamped = target // { c = findHighest inGamut 0.0000005 0 0.37; };
|
||||||
|
|
||||||
|
linearRgb = toLinearRgb target;
|
||||||
in
|
in
|
||||||
mapAttrs (_: oetf) linear;
|
throwIf (linearRgb == null)
|
||||||
|
"Not representable in sRGB\n Target: ${oklchToCss target}\n Clamped: ${oklchToCss clamped}"
|
||||||
|
(mapAttrs (_: oetf) linearRgb);
|
||||||
|
|
||||||
printableLength = text: fold add 0 (map (v: if isList v then 0 else stringLength v) (split "\\[[^m]*m" text));
|
printableLength = text: fold add 0 (map (v: if isList v then 0 else stringLength v) (split "\\[[^m]*m" text));
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ let
|
||||||
inherit (lib) concatLines imap0 mapAttrsRecursive mapAttrsToList nameValuePair;
|
inherit (lib) concatLines imap0 mapAttrsRecursive mapAttrsToList nameValuePair;
|
||||||
inherit (import ./lib.nix { inherit lib; }) oklchToRgb rgbToHex round;
|
inherit (import ./lib.nix { inherit lib; }) oklchToRgb rgbToHex round;
|
||||||
|
|
||||||
# TODO: Automatically clamp to sRGB
|
|
||||||
dark = c: c // { l = 0.35; c = if c.c == 0 then 0 else 0.060; };
|
dark = c: c // { l = 0.35; c = if c.c == 0 then 0 else 0.060; };
|
||||||
dim = c: c // { l = 0.50; c = if c.c == 0 then 0 else 0.150; };
|
dim = c: c // { l = 0.50; c = if c.c == 0 then 0 else 0.150; };
|
||||||
contrast-minimum = c: c // { l = c.l + 0.25 * (if c.l < 0.5 then 1 else - 1); };
|
contrast-minimum = c: c // { l = c.l + 0.25 * (if c.l < 0.5 then 1 else - 1); };
|
||||||
|
|
Loading…
Reference in a new issue