Skip to content

Commit da5d072

Browse files
committed
Handle missing components in gamut mapping functions
1 parent 01ae144 commit da5d072

File tree

2 files changed

+103
-11
lines changed

2 files changed

+103
-11
lines changed

src/clamp.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ const rgb = converter('rgb');
77
const fixup_rgb = c => {
88
const res = {
99
mode: c.mode,
10-
r: Math.max(0, Math.min(c.r, 1)),
11-
g: Math.max(0, Math.min(c.g, 1)),
12-
b: Math.max(0, Math.min(c.b, 1))
10+
r: Math.max(0, Math.min(c.r !== undefined ? c.r : 0, 1)),
11+
g: Math.max(0, Math.min(c.g !== undefined ? c.g : 0, 1)),
12+
b: Math.max(0, Math.min(c.b !== undefined ? c.b : 0, 1))
1313
};
1414
if (c.alpha !== undefined) {
1515
res.alpha = c.alpha;
@@ -22,12 +22,9 @@ const to_displayable_srgb = c => fixup_rgb(rgb(c));
2222
const inrange_rgb = c => {
2323
return (
2424
c !== undefined &&
25-
c.r >= 0 &&
26-
c.r <= 1 &&
27-
c.g >= 0 &&
28-
c.g <= 1 &&
29-
c.b >= 0 &&
30-
c.b <= 1
25+
(c.r === undefined || (c.r >= 0 && c.r <= 1)) &&
26+
(c.g === undefined || (c.g >= 0 && c.g <= 1)) &&
27+
(c.b === undefined || (c.b >= 0 && c.b <= 1))
3128
);
3229
};
3330

@@ -146,10 +143,10 @@ export function clampChroma(color, mode = 'lch', rgbGamut = 'rgb') {
146143
// By this time we know chroma = 0 is displayable and our current chroma is not.
147144
// Find the displayable chroma through the bisection method.
148145
let start = 0;
149-
let end = color.c;
146+
let end = color.c !== undefined ? color.c : 0;
150147
let range = getMode(mode).ranges.c;
151148
let resolution = (range[1] - range[0]) / Math.pow(2, 13);
152-
let _last_good_c;
149+
let _last_good_c = clamped.c;
153150

154151
while (end - start > resolution) {
155152
clamped.c = start + (end - start) * 0.5;
@@ -215,6 +212,11 @@ export function toGamut(
215212
return undefined;
216213
}
217214
const candidate = { ...ucs(color) };
215+
216+
// account for missing components
217+
if (candidate.l === undefined) candidate.l = 0;
218+
if (candidate.c === undefined) candidate.c = 0;
219+
218220
if (candidate.l >= ranges.l[1]) {
219221
const res = { ...destMode.white, mode: dest };
220222
if (color.alpha !== undefined) {

test/clamp.test.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
clampChroma,
44
displayable,
55
inGamut,
6+
clampRgb,
67
clampGamut,
78
formatCss,
89
toGamut,
@@ -48,6 +49,21 @@ tape('clampChroma (lch)', function (test) {
4849
h: 5
4950
});
5051
test.equal(displayable(clampChroma('lch(50% 120 5)')), true);
52+
test.deepEqual(
53+
clampChroma({
54+
mode: 'lch',
55+
l: 0,
56+
c: 100,
57+
h: 30
58+
}),
59+
{
60+
mode: 'lch',
61+
l: 0,
62+
c: 0,
63+
h: 30
64+
},
65+
'for l = 0, only c = 0 is in gamut'
66+
);
5167
test.end();
5268
});
5369

@@ -176,3 +192,77 @@ tape('toGamut()', t => {
176192

177193
t.end();
178194
});
195+
196+
tape('missing components', t => {
197+
t.ok(displayable('rgb(none none none)'), 'displayable');
198+
199+
t.ok(inGamut('p3')('color(display-p3 none none none)'), 'inGamut');
200+
201+
t.deepEqual(
202+
clampRgb('rgb(none 300 none)'),
203+
{
204+
mode: 'rgb',
205+
r: 0,
206+
g: 1,
207+
b: 0
208+
},
209+
'clampRgb'
210+
);
211+
212+
t.deepEqual(
213+
clampGamut('p3')('color(display-p3 none 3 none)'),
214+
{
215+
mode: 'p3',
216+
r: 0,
217+
g: 1,
218+
b: 0
219+
},
220+
'clampGamut'
221+
);
222+
223+
t.deepEqual(
224+
clampChroma({
225+
mode: 'lch',
226+
l: 120
227+
}),
228+
{
229+
mode: 'lch',
230+
l: 100.00000139649632,
231+
c: 0
232+
},
233+
'clampChroma, lch color (no conversion)'
234+
);
235+
236+
t.deepEqual(
237+
clampChroma('color(srgb 1.1 none none)'),
238+
{
239+
mode: 'rgb',
240+
r: 0.9999762593315072,
241+
g: 0.3000275449561938,
242+
b: 0.17168509121325368
243+
},
244+
'clampChroma, rgb color'
245+
);
246+
247+
t.deepEqual(
248+
toGamut()({
249+
mode: 'lch',
250+
l: 120
251+
}),
252+
{ r: 1, g: 1, b: 1, mode: 'rgb' },
253+
'toGamut(), oklch color (no conversion)'
254+
);
255+
256+
t.deepEqual(
257+
toGamut()('color(srgb 1.1 none none)'),
258+
{
259+
mode: 'rgb',
260+
r: 0.9999999999999994,
261+
g: 0.24780803212382269,
262+
b: 0.18935507566673854
263+
},
264+
'toGamut(), rgb color'
265+
);
266+
267+
t.end();
268+
});

0 commit comments

Comments
 (0)