Skip to content

Commit 09915b8

Browse files
authored
Adds methods: inGamut(), clampGamut() (#165)
1 parent 3e17444 commit 09915b8

File tree

16 files changed

+621
-374
lines changed

16 files changed

+621
-374
lines changed

docs/api.md

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ codebase: 'https://github.com/evercoder/culori/blob/main'
6262
<li><a href='#color-spaces'>hsl</a></li>
6363
<li><a href='#color-spaces'>hsv</a></li>
6464
<li><a href='#color-spaces'>hwb</a></li>
65+
<li><a href='#inGamut'>inGamut</a></li>
6566
<li><a href='#interpolate'>interpolate</a></li>
6667
<li><a href='#interpolateWith'>interpolateWith</a></li>
6768
<li><a href='#interpolateWithPremultipliedAlpha'>interpolateWithPremultipliedAlpha</a></li>
@@ -100,6 +101,7 @@ codebase: 'https://github.com/evercoder/culori/blob/main'
100101
<li><a href='#color-spaces'>rgb</a></li>
101102
<li><a href='#round'>round</a></li>
102103
<li><a href='#samples'>samples</a></li>
104+
<li><a href='#toGamut'>toGamut</a></li>
103105
<li><a href='#unlerp'>unlerp</a></li>
104106
<li><a href='#useMode'>useMode</a></li>
105107
<li><a href='#wcagContrast'>wcagContrast</a></li>
@@ -277,23 +279,43 @@ formatCss({ mode: 'lrgb', r: 0.5, s: 0.25, b: 1, alpha: 0.25 });
277279
// ⇒ 'color(--srgb-linear 0.5 0.25 1 / 0.25)'
278280
```
279281

280-
## Clamping
282+
## Gamut mapping
281283

282284
Some color spaces (Lab and LCh in particular) allow you to express colors that can't be displayed on-screen. The methods below allow you to identify when that's the case and to produce displayable versions of the colors.
283285

286+
<a id="inGamut" href="#inGamut">#</a> **inGamut**(_mode = "rgb"_) → _function (color | string)_
287+
288+
<span aria-label='Source:'>☞</span> [src/clamp.js]({{codebase}}/src/clamp.js)
289+
290+
Given a color space, returns a function with which to check whether a particular color is within the gamut of that color space.
291+
292+
This is meant to be used with RGB-based color spaces and their derivates (`hsl`, `hsv`, etc.). If the color space has no gamut limits, the function will always return `true`, regardless of the color passed to it. To find out which color spaces have gamut limits, see the [Color Spaces](/color-spaces/) page.
293+
294+
```js
295+
import { inGamut } from 'culori';
296+
297+
const inRgb = inGamut('rgb');
298+
299+
inRgb('red');
300+
// ⇒ true
301+
302+
inRgb('color(srgb 1.1 0 0)');
303+
// ⇒ false
304+
```
305+
284306
<a id="displayable" href="#displayable">#</a> **displayable**(_color_ or _string_) → _boolean_
285307

286-
<span aria-label='Source:'>☞</span> [src/displayable.js]({{codebase}}/src/displayable.js)
308+
<span aria-label='Source:'>☞</span> [src/clamp.js]({{codebase}}/src/clamp.js)
287309

288-
Checks whether a particular color fits inside the sRGB gamut, by verifying that the `r`, `g`, and `b` channels are all in the interval `[0, 1]`.
310+
Checks whether a particular color fits inside the sRGB gamut. Equivalent to `inGamut('rgb')`.
289311

290312
```js
291313
import { displayable } from 'culori';
292314

293315
displayable('red');
294316
// ⇒ true
295317

296-
displayable('rgb(300 255 255)');
318+
displayable('color(srgb 1.1 0 0)');
297319
// ⇒ false
298320
```
299321

@@ -315,6 +337,26 @@ clampRgb('lab(50% 100 100)');
315337
// ⇒ { mode: "lab", l: 54.29…, a: 80.81…, b: 69.88… }
316338
```
317339

340+
<a id="clampGamut" href="#clampGamut">#</a> **clampGamut**(_mode = 'rgb'_) → _function(color | string)_
341+
342+
This function extends the functionality of `clampRgb` to other color spaces. Given a color space, it returns a function with which to obtain colors within the gamut of that color space.
343+
344+
If the color space has no gamut limits, colors are returned unchanged. To find out which color spaces have gamut limits, see the [Color Spaces](/color-spaces/) page.
345+
346+
The in-gamut color is always returned in the color space of the original color.
347+
348+
```js
349+
import { formatCss, clampGamut } from 'culori';
350+
351+
const crimson = 'color(display-p3 0.8 0.1 0.3)';
352+
const toRgb = clampGamut('rgb');
353+
354+
formatCss(toRgb(crimson));
355+
// ⇒ 'color(display-p3 0.801… 0.169… 0.302…)'
356+
```
357+
358+
<span aria-label='Source:'>☞</span> [src/clamp.js]({{codebase}}/src/clamp.js)
359+
318360
<a id="clampChroma" href="#clampChroma">#</a> **clampChroma**(_color_ or _string_, _mode = 'lch'_) → _color_
319361

320362
<span aria-label='Source:'>☞</span> [src/clamp.js]({{codebase}}/src/clamp.js)

docs/color-spaces.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,37 +39,48 @@ The [sRGB color space](https://en.wikipedia.org/wiki/SRGB), which most people re
3939

4040
Serialized as `color(srgb r g b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
4141

42+
Has gamut limits.
43+
4244
#### `lrgb`
4345

4446
The linear-light form of the sRGB color space.
4547

4648
Serialized as `color(srgb-linear r g b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
4749

50+
Has gamut limits.
51+
4852
#### `a98`
4953

5054
The A98 RGB color space, compatible with the [Adobe RGB (1998) color space](https://en.wikipedia.org/wiki/Adobe_RGB_color_space).
5155

5256
Serialized as `color(a98-rgb r g b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
5357

58+
Has gamut limits.
59+
5460
#### `p3`
5561

5662
The [Display P3 color space](https://en.wikipedia.org/wiki/DCI-P3#Display_P3).
5763

5864
Serialized as `color(display-p3 r g b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
5965

66+
Has gamut limits.
6067

6168
#### `prophoto`
6269

6370
The [ProPhoto RGB color space](https://en.wikipedia.org/wiki/ProPhoto_RGB_color_space).
6471

6572
Serialized as `color(prophoto-rgb r g b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
6673

74+
Has gamut limits.
75+
6776
#### `rec2020`
6877

6978
The [Rec. 2020 color space](https://en.wikipedia.org/wiki/Rec._2020).
7079

7180
Serialized as `color(rec2020 r g b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
7281

82+
Has gamut limits.
83+
7384
### The HSL/HSV/HSI family
7485

7586
[HSL, HSV, and HSI](https://en.wikipedia.org/wiki/HSL_and_HSV) are alternative representations of the RGB color model, created in an attempt to provide a more intuitive way to specify colors.
@@ -90,6 +101,8 @@ The HSL color space.
90101

91102
Serialized as `hsl(h s% l%)`. A missing hue is serialized as `0`, with the `none` keyword for any other missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
92103

104+
Has gamut limits.
105+
93106
#### `hsv`
94107

95108
The HSV color space.
@@ -102,6 +115,8 @@ The HSV color space.
102115

103116
Serialized as `color(--hsv h s v)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
104117

118+
Has gamut limits.
119+
105120
#### `hsi`
106121

107122
The HSI color space.
@@ -114,6 +129,8 @@ The HSI color space.
114129

115130
Serialized as `color(--hsi h s i)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
116131

132+
Has gamut limits.
133+
117134
### HWB
118135

119136
[The HWB color model](https://en.wikipedia.org/wiki/HWB_color_model) was developed by Alvy Ray Smith, who also created the HSV color model. It's meant to be more intuitive for humans to use and faster to compute.
@@ -128,6 +145,8 @@ Serialized as `hwb(h w% b%)`.
128145

129146
Serialized as `hwb(h w% b%)`. A missing hue is serialized as `0`, with the `none` keyword for any other missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
130147

148+
Has gamut limits.
149+
131150
> Smith, Alvy Ray (1996) — ["HWB — A More Intuitive Hue-Based Color Model"](http://alvyray.com/Papers/CG/HWB_JGTv208.pdf), Journal of Graphics, GPU and Game tools.
132151
133152
### CIELAB
@@ -146,6 +165,8 @@ The CIELAB color space using the [D50 standard illuminant](https://en.wikipedia.
146165

147166
Serialized as `lab(l a b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
148167

168+
Does not have gamut limits.
169+
149170
#### `lch`
150171

151172
The CIELCh color space using the D50 standard illuminant.
@@ -158,6 +179,8 @@ The CIELCh color space using the D50 standard illuminant.
158179

159180
Serialized as `lch(l c h)`. A missing hue is serialized as `0`, with the `none` keyword for any other missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
160181

182+
Does not have gamut limits.
183+
161184
#### `lab65`
162185

163186
CIELAB relative to the D65 standard illuminant.
@@ -170,6 +193,8 @@ CIELAB relative to the D65 standard illuminant.
170193

171194
Serialized as `color(--lab-d65 l a b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
172195

196+
Does not have gamut limits.
197+
173198
#### `lch65`
174199

175200
CIELCh relative to the D65 standard illuminant.
@@ -182,6 +207,8 @@ CIELCh relative to the D65 standard illuminant.
182207

183208
Serialized as `color(--lch-d65 l c h)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
184209

210+
Does not have gamut limits.
211+
185212
### CIELUV
186213

187214
The [CIELUV color space](https://en.wikipedia.org/wiki/CIELUV) in Cartesian (Luv) and cylindrical (LCh) forms, using the D50 standard illuminant.
@@ -202,6 +229,8 @@ let deltaE_uv = culori.colorDifferenceEuclidean('luv');
202229

203230
Serialized as `color(--luv l u v)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
204231

232+
Does not have gamut limits.
233+
205234
#### `lchuv`
206235

207236
| Channel | Range | Description |
@@ -212,6 +241,8 @@ Serialized as `color(--luv l u v)`, with the `none` keyword for any missing colo
212241

213242
Serialized as `color(--lchuv l c h)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
214243

244+
Does not have gamut limits.
245+
215246
### DIN99 Lab / LCh
216247

217248
The [DIN99](https://de.wikipedia.org/wiki/DIN99-Farbraum) color space "squishes" the CIELAB D65 color space to obtain an [effective color difference](#culoriDifferenceDin99o) metric that can be expressed as a simple Euclidean distance. The latest iteration of the the standard, DIN99o, is available in Cartesian (`dlab`) and plar (`dlch`) form.
@@ -230,6 +261,8 @@ The DIN99o color space in Cartesian form.
230261

231262
Serialized as `color(--din99o-lab l a b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
232263

264+
Does not have gamut limits.
265+
233266
#### `dlch`
234267

235268
The DIN99o color space in cylindrical form.
@@ -242,6 +275,8 @@ The DIN99o color space in cylindrical form.
242275

243276
Serialized as `color(--din99o-lch l c h)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
244277

278+
Does not have gamut limits.
279+
245280
### Oklab, Oklch, Okhsl, Okhsv
246281

247282
The [Oklab color space](https://bottosson.github.io/posts/oklab/), in Cartesian (Lab) and cylindrical (LCh) forms. It uses the D65 standard illuminant.
@@ -260,6 +295,8 @@ The Oklab color space in Cartesian form.
260295

261296
Serialized as `oklab(l a b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
262297

298+
Does not have gamut limits.
299+
263300
#### `oklch`
264301

265302
The Oklab color space in cylindrical form.
@@ -272,6 +309,8 @@ The Oklab color space in cylindrical form.
272309

273310
Serialized as `oklch(l c h)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
274311

312+
Does not have gamut limits.
313+
275314
### `okhsl`
276315

277316
| Channel | Range | Description |
@@ -282,6 +321,8 @@ Serialized as `oklch(l c h)`, with the `none` keyword for any missing color chan
282321

283322
Serialized as `color(--okhsl h s l)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
284323

324+
Does not have gamut limits.
325+
285326
### `okhsv`
286327

287328
| Channel | Range | Description |
@@ -292,6 +333,8 @@ Serialized as `color(--okhsl h s l)`, with the `none` keyword for any missing co
292333

293334
Serialized as `color(--okhsv h s v)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
294335

336+
Does not have gamut limits.
337+
295338
#### Further reading
296339

297340
* [An interactive review of Oklab](https://raphlinus.github.io/color/2021/01/18/oklab-critique.html) by Raph Levien
@@ -316,6 +359,8 @@ The J<sub>z</sub>a<sub>z</sub>b<sub>z</sub> color space in Cartesian form.
316359

317360
Serialized as `color(--jzazbz j a b)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
318361

362+
Does not have gamut limits.
363+
319364
#### `jch`
320365

321366
The J<sub>z</sub>a<sub>z</sub>b<sub>z</sub> color space in cylindrical form.
@@ -328,6 +373,8 @@ The J<sub>z</sub>a<sub>z</sub>b<sub>z</sub> color space in cylindrical form.
328373

329374
Serialized as `color(--jzczhz j c h)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
330375

376+
Does not have gamut limits.
377+
331378
### YIQ (`yiq`)
332379

333380
[YIQ](https://en.wikipedia.org/wiki/YIQ) is the color space used by the NTSC color TV system. It contains the following channels:
@@ -340,6 +387,8 @@ Serialized as `color(--jzczhz j c h)`, with the `none` keyword for any missing c
340387

341388
Serialized as `color(--yiq y i q)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
342389

390+
Does not have gamut limits.
391+
343392
The conversion matrices between the sRGB and YIQ color spaces are taken from:
344393

345394
> Yuriy Kotsarenko, Fernando Ramos, [_Measuring perceived color difference using YIQ NTSC transmission color space in mobile applications_](http://www.progmat.uaem.mx:8080/artVol2Num2/Articulo3Vol2Num2.pdf), Programación Matemática y Software (2010), Vol. 2, No 2.
@@ -360,6 +409,8 @@ The CIE XYZ color space in respect to the D50 standard illuminant.
360409

361410
Serialized as `color(xyz-d50 x y z)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
362411

412+
Does not have gamut limits.
413+
363414
#### `xyz65`
364415

365416
The CIE XYZ color space in respect to the D65 standard illuminant.
@@ -372,6 +423,8 @@ The CIE XYZ color space in respect to the D65 standard illuminant.
372423

373424
Serialized as `color(xyz-d65 x y z)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
374425

426+
Does not have gamut limits.
427+
375428
### XYB
376429

377430
The XYB color model is part of [the JPEG XL Image Coding System](https://ds.jpeg.org/whitepapers/jpeg-xl-whitepaper.pdf), as an <q>LMS-based colour model inspired by the human visual system, facilitating perceptually uniform quantization. It uses a gamma of 3 for computationally efficient decoding</q>.
@@ -388,6 +441,8 @@ It has the default _Chroma from Luma_ adjustment applied (effectively Y is subtr
388441
| `y` | `[0, 0.8453]`| Luma |
389442
| `b` | `[ -0.2778, 0.3880 ]`| Blue-yellow component |
390443

444+
Does not have gamut limits.
445+
391446
### Cubehelix
392447

393448
[The Cubehelix color scheme](https://www.mrao.cam.ac.uk/~dag/CUBEHELIX/) was described by Dave Green in this paper:
@@ -408,3 +463,4 @@ The channels in the `cubehelix` color space maintain the conventions from D3, na
408463

409464
Serialized as `color(--cubehelix h s l)`, with the `none` keyword for any missing color channel. An explicit `alpha < 1` is included as ` / alpha`.
410465

466+
Does not have gamut limits.

0 commit comments

Comments
 (0)