Skip to content

Commit

Permalink
css-color : bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
romainmenke committed Aug 22, 2023
1 parent a8ea763 commit 8e37a9a
Show file tree
Hide file tree
Showing 26 changed files with 246 additions and 248 deletions.
4 changes: 4 additions & 0 deletions packages/color-helpers/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changes to Color Helpers

### Unreleased (patch)

- Clamp negative saturation in `hsl` to `0`.

### 3.0.0

_July 3, 2023_
Expand Down
2 changes: 1 addition & 1 deletion packages/color-helpers/dist/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ function OKLab_to_OKLCH(t){const _=180*Math.atan2(t[2],t[1])/Math.PI;return[t[0]
* @copyright This software or document includes material copied from or derived from https://github.com/w3c/csswg-drafts/blob/main/css-color-4/utilities.js. Copyright © 2022 W3C® (MIT, ERCIM, Keio, Beihang).
*
* @see https://github.com/w3c/csswg-drafts/blob/main/css-color-4/rgbToHsl.js
*/function sRGB_to_HSL(t){const _=t[0],o=t[1],n=t[2],r=Math.max(_,o,n),e=Math.min(_,o,n),a=(e+r)/2,i=r-e;let l=NaN,u=0;if(0!==Math.round(1e5*i)){switch(u=0===Math.round(1e5*a)||1e5===Math.round(1e5*a)?0:(r-a)/Math.min(a,1-a),r){case _:l=(o-n)/i+(o<n?6:0);break;case o:l=(n-_)/i+2;break;case n:l=(_-o)/i+4}l*=60}return[l,100*u,100*a]}
*/function sRGB_to_HSL(t){const _=t[0],o=t[1],n=t[2],r=Math.max(_,o,n),e=Math.min(_,o,n),a=(e+r)/2,i=r-e;let l=NaN,u=0;if(0!==Math.round(1e5*i)){switch(u=0===Math.round(1e5*a)||1e5===Math.round(1e5*a)?0:Math.max(0,(r-a)/Math.min(a,1-a)),r){case _:l=(o-n)/i+(o<n?6:0);break;case o:l=(n-_)/i+2;break;case n:l=(_-o)/i+4}l*=60}return[l,100*u,100*a]}
/**
* Assuming XYZ is relative to D50, convert to CIE Lab
* from CIE standard, which now defines these as a rational fraction
Expand Down
2 changes: 1 addition & 1 deletion packages/color-helpers/dist/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ function OKLab_to_OKLCH(t){const _=180*Math.atan2(t[2],t[1])/Math.PI;return[t[0]
* @copyright This software or document includes material copied from or derived from https://github.com/w3c/csswg-drafts/blob/main/css-color-4/utilities.js. Copyright © 2022 W3C® (MIT, ERCIM, Keio, Beihang).
*
* @see https://github.com/w3c/csswg-drafts/blob/main/css-color-4/rgbToHsl.js
*/function sRGB_to_HSL(t){const _=t[0],o=t[1],n=t[2],r=Math.max(_,o,n),e=Math.min(_,o,n),a=(e+r)/2,i=r-e;let l=NaN,u=0;if(0!==Math.round(1e5*i)){switch(u=0===Math.round(1e5*a)||1e5===Math.round(1e5*a)?0:(r-a)/Math.min(a,1-a),r){case _:l=(o-n)/i+(o<n?6:0);break;case o:l=(n-_)/i+2;break;case n:l=(_-o)/i+4}l*=60}return[l,100*u,100*a]}
*/function sRGB_to_HSL(t){const _=t[0],o=t[1],n=t[2],r=Math.max(_,o,n),e=Math.min(_,o,n),a=(e+r)/2,i=r-e;let l=NaN,u=0;if(0!==Math.round(1e5*i)){switch(u=0===Math.round(1e5*a)||1e5===Math.round(1e5*a)?0:Math.max(0,(r-a)/Math.min(a,1-a)),r){case _:l=(o-n)/i+(o<n?6:0);break;case o:l=(n-_)/i+2;break;case n:l=(_-o)/i+4}l*=60}return[l,100*u,100*a]}
/**
* Assuming XYZ is relative to D50, convert to CIE Lab
* from CIE standard, which now defines these as a rational fraction
Expand Down
2 changes: 1 addition & 1 deletion packages/color-helpers/src/conversions/srgb-to-hsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function sRGB_to_HSL(RGB: Color): Color {
if (Math.round(light * 100_000) === 0 || Math.round(light * 100_000) === 100_000) {
sat = 0;
} else {
sat = (max - light) / Math.min(light, 1 - light);
sat = Math.max(0, (max - light) / Math.min(light, 1 - light));
}

switch (max) {
Expand Down
6 changes: 6 additions & 0 deletions packages/css-color-parser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changes to CSS Color Parser

### Unreleased (minor)

- Add a `serializeOKLCH` function.
- Always convert to the target color space, even when the input color is already in that specific color space.
- Correctly apply the hue interpolation method when either angle is missing.

### 1.2.3

_July 24, 2023_
Expand Down
1 change: 0 additions & 1 deletion packages/css-color-parser/dist/color-data.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export declare function colorData_to_XYZ_D50(colorData: ColorData): ColorData;
export declare function colorDataTo(colorData: ColorData, toNotation: ColorNotation): ColorData;
export declare function convertPowerlessComponentsToMissingComponents(a: Color, colorNotation: ColorNotation): Color;
export declare function convertPowerlessComponentsToZeroValuesForDisplay(a: Color, colorNotation: ColorNotation): Color;
export declare function fillInMissingComponents(a: Color, b: Color): Color;
export declare function normalizeRelativeColorDataChannels(x: ColorData): Map<string, TokenNumber | TokenPercentage | TokenDimension>;
export declare function noneToZeroInRelativeColorDataChannels(x: Map<string, TokenNumber | TokenPercentage | TokenDimension>): Map<string, TokenNumber | TokenPercentage | TokenDimension>;
export declare function colorDataFitsRGB_Gamut(x: ColorData): boolean;
2 changes: 1 addition & 1 deletion packages/css-color-parser/dist/index.cjs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/css-color-parser/dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export { SyntaxFlag } from './color-data';
export { colorDataTo, colorDataFitsRGB_Gamut } from './color-data';
export { serializeP3 } from './serialize/p3';
export { serializeRGB } from './serialize/rgb';
export { serializeOKLCH } from './serialize/oklch';
export declare function color(colorNode: ComponentValue): ColorData | false;
2 changes: 1 addition & 1 deletion packages/css-color-parser/dist/index.mjs

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions packages/css-color-parser/dist/serialize/oklch.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { ColorData } from '../color-data';
import { FunctionNode } from '@csstools/css-parser-algorithms';
export declare function serializeOKLCH(color: ColorData): FunctionNode;
4 changes: 4 additions & 0 deletions packages/css-color-parser/dist/serialize/with-alpha.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ColorData } from '../color-data';
import type { TokenFunction, TokenWhitespace } from '@csstools/css-tokenizer';
import { FunctionNode, TokenNode, WhitespaceNode } from '@csstools/css-parser-algorithms';
export declare function serializeWithAlpha(color: ColorData, fn: TokenFunction, space: TokenWhitespace, channels: Array<TokenNode | WhitespaceNode>): FunctionNode;
159 changes: 75 additions & 84 deletions packages/css-color-parser/src/color-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,77 +135,81 @@ const predefinedRGB_or_XYZ_Spaces = new Set([
]);

export function colorDataTo(colorData: ColorData, toNotation: ColorNotation): ColorData {
// 1. Convert to XYZ_D50
const xyzColorData = colorData_to_XYZ_D50(colorData);
const outputColorData: ColorData = {
...colorData,
};

// 1. Convert to destination color notation
switch (toNotation) {
case ColorNotation.HEX:
case ColorNotation.RGB:
outputColorData.colorNotation = ColorNotation.RGB;
outputColorData.channels = xyz.XYZ_D50_to_sRGB(xyzColorData.channels);
break;
case ColorNotation.sRGB:
outputColorData.colorNotation = ColorNotation.sRGB;
outputColorData.channels = xyz.XYZ_D50_to_sRGB(xyzColorData.channels);
break;
case ColorNotation.Linear_sRGB:
outputColorData.colorNotation = ColorNotation.Linear_sRGB;
outputColorData.channels = xyz.XYZ_D50_to_lin_sRGB(xyzColorData.channels);
break;
case ColorNotation.Display_P3:
outputColorData.colorNotation = ColorNotation.Display_P3;
outputColorData.channels = xyz.XYZ_D50_to_P3(xyzColorData.channels);
break;
case ColorNotation.Rec2020:
outputColorData.colorNotation = ColorNotation.Rec2020;
outputColorData.channels = xyz.XYZ_D50_to_rec_2020(xyzColorData.channels);
break;
case ColorNotation.ProPhoto_RGB:
outputColorData.colorNotation = ColorNotation.ProPhoto_RGB;
outputColorData.channels = xyz.XYZ_D50_to_ProPhoto(xyzColorData.channels);
break;
case ColorNotation.A98_RGB:
outputColorData.colorNotation = ColorNotation.A98_RGB;
outputColorData.channels = xyz.XYZ_D50_to_a98_RGB(xyzColorData.channels);
break;
case ColorNotation.HSL:
outputColorData.colorNotation = ColorNotation.HSL;
outputColorData.channels = xyz.XYZ_D50_to_HSL(xyzColorData.channels);
break;
case ColorNotation.HWB:
outputColorData.colorNotation = ColorNotation.HWB;
outputColorData.channels = xyz.XYZ_D50_to_HWB(xyzColorData.channels);
break;
case ColorNotation.Lab:
outputColorData.colorNotation = ColorNotation.Lab;
outputColorData.channels = xyz.XYZ_D50_to_Lab(xyzColorData.channels);
break;
case ColorNotation.LCH:
outputColorData.colorNotation = ColorNotation.LCH;
outputColorData.channels = xyz.XYZ_D50_to_LCH(xyzColorData.channels);
break;
case ColorNotation.OKLCH:
outputColorData.colorNotation = ColorNotation.OKLCH;
outputColorData.channels = xyz.XYZ_D50_to_OKLCH(xyzColorData.channels);
break;
case ColorNotation.OKLab:
outputColorData.colorNotation = ColorNotation.OKLab;
outputColorData.channels = xyz.XYZ_D50_to_OKLab(xyzColorData.channels);
break;
case ColorNotation.XYZ_D50:
outputColorData.colorNotation = ColorNotation.XYZ_D50;
outputColorData.channels = xyz.XYZ_D50_to_XYZ_D50(xyzColorData.channels);
break;
case ColorNotation.XYZ_D65:
outputColorData.colorNotation = ColorNotation.XYZ_D65;
outputColorData.channels = xyz.XYZ_D50_to_XYZ_D65(xyzColorData.channels);
break;
default:
throw new Error('Unsupported color notation');
if (colorData.colorNotation !== toNotation) {
const xyzColorData = colorData_to_XYZ_D50(outputColorData);

// 1. Convert to destination color notation
switch (toNotation) {
case ColorNotation.HEX:
case ColorNotation.RGB:
outputColorData.colorNotation = ColorNotation.RGB;
outputColorData.channels = xyz.XYZ_D50_to_sRGB(xyzColorData.channels);
break;
case ColorNotation.sRGB:
outputColorData.colorNotation = ColorNotation.sRGB;
outputColorData.channels = xyz.XYZ_D50_to_sRGB(xyzColorData.channels);
break;
case ColorNotation.Linear_sRGB:
outputColorData.colorNotation = ColorNotation.Linear_sRGB;
outputColorData.channels = xyz.XYZ_D50_to_lin_sRGB(xyzColorData.channels);
break;
case ColorNotation.Display_P3:
outputColorData.colorNotation = ColorNotation.Display_P3;
outputColorData.channels = xyz.XYZ_D50_to_P3(xyzColorData.channels);
break;
case ColorNotation.Rec2020:
outputColorData.colorNotation = ColorNotation.Rec2020;
outputColorData.channels = xyz.XYZ_D50_to_rec_2020(xyzColorData.channels);
break;
case ColorNotation.ProPhoto_RGB:
outputColorData.colorNotation = ColorNotation.ProPhoto_RGB;
outputColorData.channels = xyz.XYZ_D50_to_ProPhoto(xyzColorData.channels);
break;
case ColorNotation.A98_RGB:
outputColorData.colorNotation = ColorNotation.A98_RGB;
outputColorData.channels = xyz.XYZ_D50_to_a98_RGB(xyzColorData.channels);
break;
case ColorNotation.HSL:
outputColorData.colorNotation = ColorNotation.HSL;
outputColorData.channels = xyz.XYZ_D50_to_HSL(xyzColorData.channels);
break;
case ColorNotation.HWB:
outputColorData.colorNotation = ColorNotation.HWB;
outputColorData.channels = xyz.XYZ_D50_to_HWB(xyzColorData.channels);
break;
case ColorNotation.Lab:
outputColorData.colorNotation = ColorNotation.Lab;
outputColorData.channels = xyz.XYZ_D50_to_Lab(xyzColorData.channels);
break;
case ColorNotation.LCH:
outputColorData.colorNotation = ColorNotation.LCH;
outputColorData.channels = xyz.XYZ_D50_to_LCH(xyzColorData.channels);
break;
case ColorNotation.OKLCH:
outputColorData.colorNotation = ColorNotation.OKLCH;
outputColorData.channels = xyz.XYZ_D50_to_OKLCH(xyzColorData.channels);
break;
case ColorNotation.OKLab:
outputColorData.colorNotation = ColorNotation.OKLab;
outputColorData.channels = xyz.XYZ_D50_to_OKLab(xyzColorData.channels);
break;
case ColorNotation.XYZ_D50:
outputColorData.colorNotation = ColorNotation.XYZ_D50;
outputColorData.channels = xyz.XYZ_D50_to_XYZ_D50(xyzColorData.channels);
break;
case ColorNotation.XYZ_D65:
outputColorData.colorNotation = ColorNotation.XYZ_D65;
outputColorData.channels = xyz.XYZ_D50_to_XYZ_D65(xyzColorData.channels);
break;
default:
throw new Error('Unsupported color notation');
}
} else {
outputColorData.channels = colorData.channels.map((x) => Number.isNaN(x) ? 0 : x) as Color;
}

// 2. Carry forward missing components
Expand Down Expand Up @@ -296,11 +300,6 @@ export function convertPowerlessComponentsToMissingComponents(a: Color, colorNot

switch (colorNotation) {
case ColorNotation.HSL:
if (reducePrecision(out[2], 4) <= 0 || reducePrecision(out[2], 4) >= 100) {
out[0] = NaN;
out[1] = NaN;
}

if (reducePrecision(out[1], 4) <= 0) {
out[0] = NaN;
}
Expand Down Expand Up @@ -398,18 +397,6 @@ function carryForwardMissingComponents(a: Color, aIndices: Array<number>, b: Col
return output;
}

export function fillInMissingComponents(a: Color, b: Color): Color {
const output: Color = [...a];

for (let i = 0; i < a.length; i++) {
if (Number.isNaN(a[i])) {
output[i] = b[i];
}
}

return output;
}

export function normalizeRelativeColorDataChannels(x: ColorData): Map<string, TokenNumber | TokenPercentage | TokenDimension> {
const globals: Map<string, TokenNumber | TokenPercentage | TokenDimension> = new Map();

Expand Down Expand Up @@ -519,6 +506,10 @@ function dummyNumberToken(x: number): TokenNumber {
}

function reducePrecision(x: number, precision = 7): number {
if (Number.isNaN(x)) {
return 0;
}

const factor = Math.pow(10, precision);
return Math.round(x * factor) / factor;
}
Expand Down
Loading

0 comments on commit 8e37a9a

Please sign in to comment.