@thi.ng/color 5.3.3 → 5.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/README.md +22 -19
  3. package/alpha.d.ts +8 -0
  4. package/alpha.js +8 -0
  5. package/analog.d.ts +4 -0
  6. package/analog.js +5 -1
  7. package/api.d.ts +13 -2
  8. package/clamp.d.ts +13 -3
  9. package/clamp.js +13 -3
  10. package/color.d.ts +23 -0
  11. package/color.js +6 -1
  12. package/convert.d.ts +9 -0
  13. package/convert.js +9 -0
  14. package/css/css.d.ts +22 -4
  15. package/css/css.js +33 -12
  16. package/css/parse-css.d.ts +25 -0
  17. package/css/parse-css.js +83 -33
  18. package/defcolor.js +2 -2
  19. package/distance.d.ts +8 -3
  20. package/distance.js +12 -8
  21. package/hcy/hcy.d.ts +5 -0
  22. package/hcy/hcy.js +5 -0
  23. package/hsi/hsi.d.ts +5 -0
  24. package/hsi/hsi.js +5 -0
  25. package/hsl/hsl.d.ts +5 -0
  26. package/hsl/hsl.js +5 -0
  27. package/hsv/hsv.d.ts +5 -0
  28. package/hsv/hsv.js +5 -0
  29. package/index.d.ts +5 -0
  30. package/index.js +5 -0
  31. package/internal/css.d.ts +4 -0
  32. package/internal/css.js +18 -0
  33. package/invert.d.ts +9 -0
  34. package/invert.js +9 -0
  35. package/lab/lab-css.js +2 -10
  36. package/lch/lch-css.js +2 -11
  37. package/lch/lch.d.ts +5 -1
  38. package/lch/lch.js +5 -1
  39. package/lighten.d.ts +3 -0
  40. package/lighten.js +3 -0
  41. package/mix.d.ts +3 -1
  42. package/mix.js +3 -1
  43. package/oklab/oklab-css.d.ts +11 -0
  44. package/oklab/oklab-css.js +10 -0
  45. package/oklab/oklab-rgb.js +11 -2
  46. package/oklab/oklab.js +2 -2
  47. package/oklch/oklab-oklch.d.ts +3 -0
  48. package/oklch/oklab-oklch.js +15 -0
  49. package/oklch/oklch-css.d.ts +11 -0
  50. package/oklch/oklch-css.js +10 -0
  51. package/oklch/oklch-oklab.d.ts +11 -0
  52. package/oklch/oklch-oklab.js +13 -0
  53. package/oklch/oklch.d.ts +37 -0
  54. package/oklch/oklch.js +26 -0
  55. package/package.json +32 -14
  56. package/rgb/rgb-oklab.js +11 -2
  57. package/rotate.d.ts +33 -0
  58. package/rotate.js +34 -1
  59. package/tint.d.ts +47 -0
  60. package/tint.js +48 -1
package/css/parse-css.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { rotateRight } from "@thi.ng/binary/rotate";
1
2
  import { interleave4_12_24, interleave4_16_32 } from "@thi.ng/binary/splat";
2
3
  import { isString } from "@thi.ng/checks/is-string";
3
4
  import { assert } from "@thi.ng/errors/assert";
@@ -7,6 +8,7 @@ import { TAU } from "@thi.ng/math/api";
7
8
  import { clamp01 } from "@thi.ng/math/interval";
8
9
  import { fract } from "@thi.ng/math/prec";
9
10
  import { ParsedColor } from "../api.js";
11
+ import { INV8BIT } from "../api/constants.js";
10
12
  import { CSS_NAMES } from "../api/names.js";
11
13
  import { CSS_SYSTEM_COLORS } from "../api/system.js";
12
14
  import { intArgb32Srgb } from "../int/int-srgb.js";
@@ -29,11 +31,21 @@ import { intArgb32Srgb } from "../int/int-srgb.js";
29
31
  * - `hsl(h,s%,l%)`
30
32
  * - `hsla(h,s%,l%,a)`
31
33
  * - `lab(l a b / alpha?)`
34
+ * - `lab(l% a% b% / alpha?)`
32
35
  * - `lch(l c h / alpha?)`
36
+ * - `lch(l% c% h / alpha?)`
37
+ * - `oklab(l a b / alpha?)`
38
+ * - `oklab(l% a% b% / alpha?)`
39
+ * - `oklch(l c h / alpha?)`
40
+ * - `oklch(l% c% h / alpha?)`
33
41
  *
34
42
  * Hue values can be given according to CSS Color L4 spec (raw, deg, rad, grad,
35
43
  * turn): https://www.w3.org/TR/css-color-4/#typedef-hue
36
44
  *
45
+ * For (ok)lab/(ok)lch color channel values given as percentages, the scale
46
+ * ranges defined in the spec are used:
47
+ * https://www.w3.org/TR/css-color-4/#specifying-lab-lch
48
+ *
37
49
  * If no alpha channel is given, it will default to 1.0 (fully opaque).
38
50
  *
39
51
  * Note that any named or system CSS colors, hex colors and any RGB colors will
@@ -52,82 +64,120 @@ export const parseCss = (src) => {
52
64
  return new ParsedColor("srgb", intArgb32Srgb([], parseHex(named || src)));
53
65
  const parts = src.split(/[(),/ ]+/);
54
66
  const [mode, a, b, c, d] = parts;
55
- assert(parts.length === 5 || parts.length === 6, `invalid ${mode} color: ${src}`);
67
+ assert(parts.length === 5 || parts.length === 6, `invalid color: ${src}`);
56
68
  switch (mode) {
57
69
  case "rgb":
58
70
  case "rgba":
59
71
  return new ParsedColor("srgb", [
60
- parseNumOrPercent(a),
61
- parseNumOrPercent(b),
62
- parseNumOrPercent(c),
63
- parseAlpha(d),
72
+ __numOrPercent(a, 1, INV8BIT, true),
73
+ __numOrPercent(b, 1, INV8BIT, true),
74
+ __numOrPercent(c, 1, INV8BIT, true),
75
+ __alpha(d),
64
76
  ]);
65
77
  case "hsl":
66
78
  case "hsla":
67
79
  return new ParsedColor("hsl", [
68
- parseHue(a),
69
- parsePercent(b),
70
- parsePercent(c),
71
- parseAlpha(d),
80
+ __hue(a),
81
+ __percent(b),
82
+ __percent(c),
83
+ __alpha(d),
72
84
  ]);
73
85
  case "lab":
74
86
  return new ParsedColor("lab50", [
75
- parsePercent(a, false),
76
- parseNumber(b) * 0.01,
77
- parseNumber(c) * 0.01,
78
- parseAlpha(d),
87
+ __numOrPercent(a),
88
+ __numOrPercent(b, 1.25),
89
+ __numOrPercent(c, 1.25),
90
+ __alpha(d),
79
91
  ]);
80
92
  case "lch":
81
- return new ParsedColor("lch", [
82
- parsePercent(a, false),
83
- parseNumber(b) * 0.01,
84
- parseHue(c),
85
- parseAlpha(d),
93
+ return new ParsedColor(mode, [
94
+ __numOrPercent(a),
95
+ __numOrPercent(b, 1.5),
96
+ __hue(c),
97
+ __alpha(d),
98
+ ]);
99
+ case "oklab":
100
+ return new ParsedColor(mode, [
101
+ __numOrPercent(a, 1, 1),
102
+ __numOrPercent(b, 0.4, 1),
103
+ __numOrPercent(c, 0.4, 1),
104
+ __alpha(d),
105
+ ]);
106
+ case "oklch":
107
+ return new ParsedColor(mode, [
108
+ __numOrPercent(a, 1, 1),
109
+ __numOrPercent(b, 0.4, 1),
110
+ __hue(c),
111
+ __alpha(d),
86
112
  ]);
87
113
  default:
88
114
  unsupported(`color mode: ${mode}`);
89
115
  }
90
116
  };
117
+ /**
118
+ * Scale factors for various CSS angle units
119
+ *
120
+ * @remarks
121
+ * Reference:
122
+ * - https://www.w3.org/TR/css-values-4/#angle-value
123
+ *
124
+ * @internal
125
+ */
91
126
  const HUE_NORMS = {
92
127
  rad: TAU,
93
128
  grad: 400,
94
129
  turn: 1,
95
130
  deg: 360,
96
- undefined: 360,
97
131
  };
98
- const parseHue = (x) => {
132
+ /** @internal */
133
+ const __hue = (x) => {
99
134
  const match = /^(-?[0-9.]+)(deg|rad|grad|turn)?$/.exec(x);
100
135
  assert(!!match, `expected hue, got: ${x}`);
101
- return fract(parseFloat(match[1]) / HUE_NORMS[match[2]]);
136
+ return fract(parseFloat(match[1]) / (HUE_NORMS[match[2]] || 360));
102
137
  };
103
- const parseAlpha = (x) => (x ? parseNumOrPercent(x, 1) : 1);
104
- const parsePercent = (x, clamp = true) => {
138
+ /** @internal */
139
+ const __alpha = (x) => (x ? __numOrPercent(x, 1, 1, true) : 1);
140
+ /** @internal */
141
+ const __percent = (x, clamp = true) => {
105
142
  assert(/^([0-9.]+)%$/.test(x), `expected percentage, got: ${x}`);
106
143
  const res = parseFloat(x) / 100;
107
144
  return clamp ? clamp01(res) : res;
108
145
  };
109
- const parseNumber = (x) => {
110
- assert(/^-?[0-9.]+$/.test(x), `expected number, got: ${x}`);
111
- return parseFloat(x);
112
- };
113
- const parseNumOrPercent = (x, norm = 255, clamp = true) => {
146
+ /** @internal */
147
+ const __numOrPercent = (x, scalePerc = 1, scale = 0.01, clamp = false) => {
114
148
  assert(/^-?[0-9.]+%?$/.test(x), `expected number or percentage, got: ${x}`);
115
- const res = parseFloat(x) / (x.endsWith("%") ? 100 : norm);
149
+ const res = parseFloat(x) * (x.endsWith("%") ? 0.01 * scalePerc : scale);
116
150
  return clamp ? clamp01(res) : res;
117
151
  };
152
+ /**
153
+ * Parses a CSS hex color string into an uint32. Throws an error if given string
154
+ * doesn't conform to any of the supported formats.
155
+ *
156
+ * @remarks
157
+ * Supports the following input formats (`#` always optional and each letter a
158
+ * hex digit):
159
+ *
160
+ * - `#rgb`
161
+ * - `#rgba`
162
+ * - `#rrggbb`
163
+ * - `#rrggbbaa`
164
+ *
165
+ * @param src
166
+ */
118
167
  export const parseHex = (src) => {
119
168
  const match = /^#?([0-9a-f]{3,8})$/i.exec(src);
120
169
  if (match) {
121
170
  const hex = match[1];
171
+ const val = parseInt(hex, 16);
122
172
  switch (hex.length) {
123
173
  case 3:
124
- return ((interleave4_12_24(parseInt(hex, 16)) | 0xff000000) >>> 0);
174
+ return (interleave4_12_24(val) | 0xff000000) >>> 0;
125
175
  case 4:
126
- return interleave4_16_32(parseInt(hex, 16)) >>> 0;
176
+ return rotateRight(interleave4_16_32(val), 8);
127
177
  case 6:
128
- return (parseInt(hex, 16) | 0xff000000) >>> 0;
178
+ return (val | 0xff000000) >>> 0;
129
179
  case 8:
130
- return parseInt(hex, 16) >>> 0;
180
+ return rotateRight(val, 8);
131
181
  default:
132
182
  }
133
183
  }
package/defcolor.js CHANGED
@@ -28,8 +28,8 @@ export const defColor = (spec) => {
28
28
  };
29
29
  return acc;
30
30
  }, channels);
31
- const min = order.map((id) => channels[id].range[0]);
32
- const max = order.map((id) => channels[id].range[1]);
31
+ const min = Object.freeze(order.map((id) => channels[id].range[0]));
32
+ const max = Object.freeze(order.map((id) => channels[id].range[1]));
33
33
  // fix alpha channel for randomize()
34
34
  const minR = set4([], min);
35
35
  const maxR = set4([], max);
package/distance.d.ts CHANGED
@@ -18,10 +18,15 @@ export declare const distHsv: ColorDistance;
18
18
  /**
19
19
  * Similar to {@link distHsv}, but computes distance between two LCH colors
20
20
  * (with different channel order), i.e. the eucledian distance between points in
21
- * a cyclinder.
21
+ * a cyclinder (using Law of Cosines).
22
22
  *
23
- * @param a -
24
- * @param b -
23
+ * @remarks
24
+ * Reference:
25
+ * - https://math.stackexchange.com/a/3612602
26
+ * - https://en.wikipedia.org/wiki/Law_of_cosines
27
+ *
28
+ * @param a
29
+ * @param b
25
30
  */
26
31
  export declare const distLch: ColorDistance;
27
32
  /**
package/distance.js CHANGED
@@ -28,16 +28,20 @@ export const distHsv = (a, b) => {
28
28
  /**
29
29
  * Similar to {@link distHsv}, but computes distance between two LCH colors
30
30
  * (with different channel order), i.e. the eucledian distance between points in
31
- * a cyclinder.
31
+ * a cyclinder (using Law of Cosines).
32
32
  *
33
- * @param a -
34
- * @param b -
33
+ * @remarks
34
+ * Reference:
35
+ * - https://math.stackexchange.com/a/3612602
36
+ * - https://en.wikipedia.org/wiki/Law_of_cosines
37
+ *
38
+ * @param a
39
+ * @param b
35
40
  */
36
- export const distLch = (a, b) => {
37
- const aa = cossin(a[2] * TAU, a[1]);
38
- const bb = cossin(b[2] * TAU, b[1]);
39
- return hypot(aa[0] - bb[0], aa[1] - bb[1], a[0] - b[0]);
40
- };
41
+ export const distLch = ({ 0: la, 1: ca, 2: ha }, { 0: lb, 1: cb, 2: hb }) => Math.sqrt(ca * ca +
42
+ cb * cb -
43
+ 2 * ca * cb * Math.cos((ha - hb) * TAU) +
44
+ (la - lb) ** 2);
41
45
  /**
42
46
  * Computes difference in saturation between two HSV colors.
43
47
  *
package/hcy/hcy.d.ts CHANGED
@@ -24,5 +24,10 @@ export declare class HCY implements TypedColor<HCY> {
24
24
  set(src: ReadonlyColor): this;
25
25
  toJSON(): number[];
26
26
  }
27
+ /**
28
+ * @remarks
29
+ * Note: As with other hue-based color modes in this package, the hue is stored
30
+ * normalized (in [0..1] interval) and NOT as degrees.
31
+ */
27
32
  export declare const hcy: ColorFactory<HCY>;
28
33
  //# sourceMappingURL=hcy.d.ts.map
package/hcy/hcy.js CHANGED
@@ -4,6 +4,11 @@ import { labRgb } from "../lab/lab-rgb.js";
4
4
  import { rgbHcy } from "../rgb/rgb-hcy.js";
5
5
  import { rgbSrgb } from "../rgb/rgb-srgb.js";
6
6
  import { hcyRgb } from "./hcy-rgb.js";
7
+ /**
8
+ * @remarks
9
+ * Note: As with other hue-based color modes in this package, the hue is stored
10
+ * normalized (in [0..1] interval) and NOT as degrees.
11
+ */
7
12
  export const hcy = defColor({
8
13
  mode: "hcy",
9
14
  channels: { h: { hue: true } },
package/hsi/hsi.d.ts CHANGED
@@ -24,5 +24,10 @@ export declare class HSI implements TypedColor<HSI> {
24
24
  set(src: ReadonlyColor): this;
25
25
  toJSON(): number[];
26
26
  }
27
+ /**
28
+ * @remarks
29
+ * Note: As with other hue-based color modes in this package, the hue is stored
30
+ * normalized (in [0..1] interval) and NOT as degrees.
31
+ */
27
32
  export declare const hsi: ColorFactory<HSI>;
28
33
  //# sourceMappingURL=hsi.d.ts.map
package/hsi/hsi.js CHANGED
@@ -4,6 +4,11 @@ import { labRgb } from "../lab/lab-rgb.js";
4
4
  import { rgbHsi } from "../rgb/rgb-hsi.js";
5
5
  import { rgbSrgb } from "../rgb/rgb-srgb.js";
6
6
  import { hsiRgb } from "./hsi-rgb.js";
7
+ /**
8
+ * @remarks
9
+ * Note: As with other hue-based color modes in this package, the hue is stored
10
+ * normalized (in [0..1] interval) and NOT as degrees.
11
+ */
7
12
  export const hsi = defColor({
8
13
  mode: "hsi",
9
14
  channels: { h: { hue: true } },
package/hsl/hsl.d.ts CHANGED
@@ -24,5 +24,10 @@ export declare class HSL implements TypedColor<HSL> {
24
24
  set(src: ReadonlyColor): this;
25
25
  toJSON(): number[];
26
26
  }
27
+ /**
28
+ * @remarks
29
+ * Note: As with other hue-based color modes in this package, the hue is stored
30
+ * normalized (in [0..1] interval) and NOT as degrees.
31
+ */
27
32
  export declare const hsl: ColorFactory<HSL>;
28
33
  //# sourceMappingURL=hsl.d.ts.map
package/hsl/hsl.js CHANGED
@@ -5,6 +5,11 @@ import { labRgb } from "../lab/lab-rgb.js";
5
5
  import { rgbHsl } from "../rgb/rgb-hsl.js";
6
6
  import { rgbSrgb } from "../rgb/rgb-srgb.js";
7
7
  import { hslRgb } from "./hsl-rgb.js";
8
+ /**
9
+ * @remarks
10
+ * Note: As with other hue-based color modes in this package, the hue is stored
11
+ * normalized (in [0..1] interval) and NOT as degrees.
12
+ */
8
13
  export const hsl = defColor({
9
14
  mode: "hsl",
10
15
  channels: { h: { hue: true } },
package/hsv/hsv.d.ts CHANGED
@@ -24,5 +24,10 @@ export declare class HSV implements TypedColor<HSV> {
24
24
  set(src: ReadonlyColor): this;
25
25
  toJSON(): number[];
26
26
  }
27
+ /**
28
+ * @remarks
29
+ * Note: As with other hue-based color modes in this package, the hue is stored
30
+ * normalized (in [0..1] interval) and NOT as degrees.
31
+ */
27
32
  export declare const hsv: ColorFactory<HSV>;
28
33
  //# sourceMappingURL=hsv.d.ts.map
package/hsv/hsv.js CHANGED
@@ -5,6 +5,11 @@ import { labRgb } from "../lab/lab-rgb.js";
5
5
  import { rgbHsv } from "../rgb/rgb-hsv.js";
6
6
  import { rgbSrgb } from "../rgb/rgb-srgb.js";
7
7
  import { hsvRgb } from "./hsv-rgb.js";
8
+ /**
9
+ * @remarks
10
+ * Note: As with other hue-based color modes in this package, the hue is stored
11
+ * normalized (in [0..1] interval) and NOT as degrees.
12
+ */
8
13
  export const hsv = defColor({
9
14
  mode: "hsv",
10
15
  channels: { h: { hue: true } },
package/index.d.ts CHANGED
@@ -36,9 +36,14 @@ export * from "./lab/lab50.js";
36
36
  export * from "./lab/lab65.js";
37
37
  export * from "./lch/lch-css.js";
38
38
  export * from "./lch/lch.js";
39
+ export * from "./oklab/oklab-css.js";
39
40
  export * from "./oklab/oklab-rgb.js";
40
41
  export * from "./oklab/oklab-xyz.js";
41
42
  export * from "./oklab/oklab.js";
43
+ export * from "./oklch/oklch-css.js";
44
+ export * from "./oklch/oklch-oklab.js";
45
+ export * from "./oklch/oklab-oklch.js";
46
+ export * from "./oklch/oklch.js";
42
47
  export * from "./alpha.js";
43
48
  export * from "./analog.js";
44
49
  export * from "./clamp.js";
package/index.js CHANGED
@@ -36,9 +36,14 @@ export * from "./lab/lab50.js";
36
36
  export * from "./lab/lab65.js";
37
37
  export * from "./lch/lch-css.js";
38
38
  export * from "./lch/lch.js";
39
+ export * from "./oklab/oklab-css.js";
39
40
  export * from "./oklab/oklab-rgb.js";
40
41
  export * from "./oklab/oklab-xyz.js";
41
42
  export * from "./oklab/oklab.js";
43
+ export * from "./oklch/oklch-css.js";
44
+ export * from "./oklch/oklch-oklab.js";
45
+ export * from "./oklch/oklab-oklch.js";
46
+ export * from "./oklch/oklch.js";
42
47
  export * from "./alpha.js";
43
48
  export * from "./analog.js";
44
49
  export * from "./clamp.js";
@@ -0,0 +1,4 @@
1
+ import type { ReadonlyColor } from "../api.js";
2
+ export declare const __labCss: (mode: string, src: ReadonlyColor, scale: number) => string;
3
+ export declare const __lchCss: (mode: string, src: ReadonlyColor, scaleC: number) => string;
4
+ //# sourceMappingURL=css.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { clamp0 } from "@thi.ng/math/interval";
2
+ import { fract } from "@thi.ng/math/prec";
3
+ import { FF, PC } from "../api/constants.js";
4
+ import { __ensureAlpha } from "./ensure.js";
5
+ export const __labCss = (mode, src, scale) => {
6
+ const l = PC(clamp0(src[0]));
7
+ const a = FF(src[1] * scale);
8
+ const b = FF(src[2] * scale);
9
+ const alpha = __ensureAlpha(src[3]);
10
+ return `${mode}(${l} ${a} ${b}` + (alpha < 1 ? `/${FF(alpha)})` : ")");
11
+ };
12
+ export const __lchCss = (mode, src, scaleC) => {
13
+ const l = PC(clamp0(src[0]));
14
+ const c = FF(clamp0(src[1]) * scaleC);
15
+ const h = FF(fract(src[2]) * 360);
16
+ const a = __ensureAlpha(src[3]);
17
+ return `${mode}(${l} ${c} ${h}` + (a < 1 ? `/${FF(a)})` : ")");
18
+ };
package/invert.d.ts CHANGED
@@ -3,6 +3,8 @@ import type { ColorOp, TypedColor } from "./api.js";
3
3
  * Inverts the RGB channels of an RGBA color.
4
4
  *
5
5
  * @remarks
6
+ * If `out` is null, the resulting color will be written back into `src`.
7
+ *
6
8
  * Also see {@link TypedColor.invert}.
7
9
  *
8
10
  * @param out - result
@@ -18,5 +20,12 @@ export declare const invertRgb: ColorOp;
18
20
  * @param src - packed RGB int
19
21
  */
20
22
  export declare const invertInt: (src: number) => number;
23
+ /**
24
+ * Inverts given color and writes result into `out` (or if null, mutates `src`
25
+ * in place).
26
+ *
27
+ * @param out
28
+ * @param src
29
+ */
21
30
  export declare const invert: import("@thi.ng/defmulti").MultiFn2<import("@thi.ng/vectors/api").Vec | null, TypedColor<any>, import("@thi.ng/vectors/api").Vec>;
22
31
  //# sourceMappingURL=invert.d.ts.map
package/invert.js CHANGED
@@ -11,6 +11,8 @@ import { __ensureAlpha } from "./internal/ensure.js";
11
11
  * Inverts the RGB channels of an RGBA color.
12
12
  *
13
13
  * @remarks
14
+ * If `out` is null, the resulting color will be written back into `src`.
15
+ *
14
16
  * Also see {@link TypedColor.invert}.
15
17
  *
16
18
  * @param out - result
@@ -29,6 +31,13 @@ export const invertRgb = (out, src) => {
29
31
  * @param src - packed RGB int
30
32
  */
31
33
  export const invertInt = (src) => src ^ 0xffffff;
34
+ /**
35
+ * Inverts given color and writes result into `out` (or if null, mutates `src`
36
+ * in place).
37
+ *
38
+ * @param out
39
+ * @param src
40
+ */
32
41
  export const invert = defmulti(__dispatch1, {
33
42
  hcy: "hsv",
34
43
  hsi: "hsv",
package/lab/lab-css.js CHANGED
@@ -1,6 +1,4 @@
1
- import { clamp0 } from "@thi.ng/math/interval";
2
- import { FF, PC } from "../api/constants.js";
3
- import { __ensureAlpha } from "../internal/ensure.js";
1
+ import { __labCss } from "../internal/css.js";
4
2
  /**
5
3
  * @remarks
6
4
  * Only supported in CSS Color Level 4 onwards
@@ -9,10 +7,4 @@ import { __ensureAlpha } from "../internal/ensure.js";
9
7
  *
10
8
  * @param src -
11
9
  */
12
- export const labCss = (src) => {
13
- const l = PC(clamp0(src[0]));
14
- const a = FF(src[1] * 100);
15
- const b = FF(src[2] * 100);
16
- const alpha = __ensureAlpha(src[3]);
17
- return `lab(${l} ${a} ${b}` + (alpha < 1 ? `/${FF(alpha)})` : ")");
18
- };
10
+ export const labCss = (src) => __labCss("lab", src, 100);
package/lch/lch-css.js CHANGED
@@ -1,7 +1,4 @@
1
- import { clamp0 } from "@thi.ng/math/interval";
2
- import { fract } from "@thi.ng/math/prec";
3
- import { FF, PC } from "../api/constants.js";
4
- import { __ensureAlpha } from "../internal/ensure.js";
1
+ import { __lchCss } from "../internal/css.js";
5
2
  /**
6
3
  * @remarks
7
4
  * Only supported in CSS Color Level 4 onwards
@@ -10,10 +7,4 @@ import { __ensureAlpha } from "../internal/ensure.js";
10
7
  *
11
8
  * @param src -
12
9
  */
13
- export const lchCss = (src) => {
14
- const l = PC(clamp0(src[0]));
15
- const c = FF(clamp0(src[1]) * 100);
16
- const h = FF(fract(src[2]) * 360);
17
- const a = __ensureAlpha(src[3]);
18
- return `lch(${l} ${c} ${h}` + (a < 1 ? `/${FF(a)})` : ")");
19
- };
10
+ export const lchCss = (src) => __lchCss("lch", src, 100);
package/lch/lch.d.ts CHANGED
@@ -26,7 +26,11 @@ export declare class LCH implements TypedColor<LCH> {
26
26
  }
27
27
  /**
28
28
  * Luminance Chroma Hue (conversions assume {@link D50} white point, as per CSS
29
- * spec).
29
+ * spec). Polar version of {@link labD50}.
30
+ *
31
+ * @remarks
32
+ * Note: As with other hue-based color modes in this package, the hue is stored
33
+ * normalized (in [0..1] interval) and NOT as degrees.
30
34
  */
31
35
  export declare const lch: ColorFactory<LCH>;
32
36
  //# sourceMappingURL=lch.d.ts.map
package/lch/lch.js CHANGED
@@ -7,7 +7,11 @@ import { xyzLab } from "../xyz/xyz-lab.js";
7
7
  import { xyzXyzD65_50 } from "../xyz/xyz-xyz.js";
8
8
  /**
9
9
  * Luminance Chroma Hue (conversions assume {@link D50} white point, as per CSS
10
- * spec).
10
+ * spec). Polar version of {@link labD50}.
11
+ *
12
+ * @remarks
13
+ * Note: As with other hue-based color modes in this package, the hue is stored
14
+ * normalized (in [0..1] interval) and NOT as degrees.
11
15
  */
12
16
  export const lch = defColor({
13
17
  mode: "lch",
package/lighten.d.ts CHANGED
@@ -3,6 +3,9 @@ import type { TypedColor } from "./api.js";
3
3
  * Adjust the "lightness" (luma, brightness etc.) channel of given `src` color
4
4
  * and `delta` offset. Writes result into `out` (or if null, back into `src`).
5
5
  *
6
+ * @remarks
7
+ * If `offset` is negative, then color will be darkened.
8
+ *
6
9
  * @param out -
7
10
  * @param src -
8
11
  * @param delta -
package/lighten.js CHANGED
@@ -11,6 +11,9 @@ const $ = (id) => (out, src, n) => {
11
11
  * Adjust the "lightness" (luma, brightness etc.) channel of given `src` color
12
12
  * and `delta` offset. Writes result into `out` (or if null, back into `src`).
13
13
  *
14
+ * @remarks
15
+ * If `offset` is negative, then color will be darkened.
16
+ *
14
17
  * @param out -
15
18
  * @param src -
16
19
  * @param delta -
package/mix.d.ts CHANGED
@@ -41,10 +41,12 @@ export declare const mixNNHN: ColorMixFn<import("@thi.ng/vectors").ReadonlyVec>;
41
41
  export declare const mixNNNN: ColorMixFn;
42
42
  /**
43
43
  * Channelwise and {@link ColorMode}-aware interpolation between colors `a` and
44
- * `b` using `t` [0..1] as blend factor. `a` and `b` need to be of same color
44
+ * `b` using `t` [0..1] as blend factor. `a` and `b` MUST be of same color
45
45
  * type.
46
46
  *
47
47
  * @remarks
48
+ * If `out` is null, the resulting color will be written back into `a`.
49
+ *
48
50
  * Any hue channel will always be interpolated using the smallest angle
49
51
  * difference (using {@link mixH}).
50
52
  *
package/mix.js CHANGED
@@ -56,10 +56,12 @@ export const mixNNHN = defMix(mixN, mixN, mixH, mixN);
56
56
  export const mixNNNN = mixN4;
57
57
  /**
58
58
  * Channelwise and {@link ColorMode}-aware interpolation between colors `a` and
59
- * `b` using `t` [0..1] as blend factor. `a` and `b` need to be of same color
59
+ * `b` using `t` [0..1] as blend factor. `a` and `b` MUST be of same color
60
60
  * type.
61
61
  *
62
62
  * @remarks
63
+ * If `out` is null, the resulting color will be written back into `a`.
64
+ *
63
65
  * Any hue channel will always be interpolated using the smallest angle
64
66
  * difference (using {@link mixH}).
65
67
  *
@@ -0,0 +1,11 @@
1
+ import type { ReadonlyColor } from "../api.js";
2
+ /**
3
+ * @remarks
4
+ * Only supported in CSS Color Level 4 onwards
5
+ * https://www.w3.org/TR/css-color-4/#specifying-lab-lch
6
+ * https://test.csswg.org/harness/results/css-color-4_dev/grouped/ (test reports)
7
+ *
8
+ * @param src -
9
+ */
10
+ export declare const oklabCss: (src: ReadonlyColor) => string;
11
+ //# sourceMappingURL=oklab-css.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { __labCss } from "../internal/css.js";
2
+ /**
3
+ * @remarks
4
+ * Only supported in CSS Color Level 4 onwards
5
+ * https://www.w3.org/TR/css-color-4/#specifying-lab-lch
6
+ * https://test.csswg.org/harness/results/css-color-4_dev/grouped/ (test reports)
7
+ *
8
+ * @param src -
9
+ */
10
+ export const oklabCss = (src) => __labCss("oklab", src, 1);
@@ -1,7 +1,16 @@
1
1
  import { __mulV33 } from "../internal/matrix-ops.js";
2
+ /**
3
+ * @remarks
4
+ * Reference:
5
+ * - https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab
6
+ *
7
+ * @internal
8
+ */
9
+ // prettier-ignore
2
10
  const LMS_CONE = [
3
- 4.0767245293, -1.2681437731, -0.0041119885, -3.3072168827, 2.6093323231,
4
- -0.7034763098, 0.2307590544, -0.341134429, 1.7068625689,
11
+ 4.0767416621, -1.2684380046, -0.0041960863,
12
+ -3.307711591, 2.6097574011, -0.7034186147,
13
+ 0.2309699292, -0.3413193965, 1.707614701,
5
14
  ];
6
15
  /**
7
16
  * @remarks
package/oklab/oklab.js CHANGED
@@ -14,8 +14,8 @@ import { oklabRgb } from "./oklab-rgb.js";
14
14
  export const oklab = defColor({
15
15
  mode: "oklab",
16
16
  channels: {
17
- a: { range: [-0.2339, 0.2763] },
18
- b: { range: [-0.3116, 0.1985] },
17
+ a: { range: [-0.2339, 0.2762] },
18
+ b: { range: [-0.3115, 0.1986] },
19
19
  },
20
20
  order: ["l", "a", "b", "alpha"],
21
21
  from: {
@@ -0,0 +1,3 @@
1
+ import type { ColorOp } from "../api.js";
2
+ export declare const oklabOklch: ColorOp;
3
+ //# sourceMappingURL=oklab-oklch.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { atan2Abs } from "@thi.ng/math/angle";
2
+ import { INV_TAU } from "@thi.ng/math/api";
3
+ import { setC4 } from "@thi.ng/vectors/setc";
4
+ import { __ensureAlpha } from "../internal/ensure.js";
5
+ export const oklabOklch = (out, src) => setC4(out || src, src[0], Math.hypot(src[1], src[2]), atan2Abs(src[2], src[1]) * INV_TAU, __ensureAlpha(src[3]));
6
+ /*
7
+ export function OKLab_to_OKLCH(OKLab: Color): Color {
8
+ const hue = Math.atan2(OKLab[2], OKLab[1]) * 180 / Math.PI;
9
+ return [
10
+ OKLab[0], // L is still L
11
+ Math.sqrt(OKLab[1] ** 2 + OKLab[2] ** 2), // Chroma
12
+ hue >= 0 ? hue : hue + 360, // Hue, in degrees [0 to 360)
13
+ ];
14
+ }
15
+ */