UNPKG

@tbela99/css-parser

Version:

CSS parser for node and the browser

339 lines (336 loc) 11.5 kB
import { EnumToken } from '../../ast/types.js'; import '../../ast/minify.js'; import '../../ast/walk.js'; import '../../parser/parse.js'; import { isRectangularOrthogonalColorspace, isPolarColorspace } from '../../syntax/syntax.js'; import '../../parser/utils/config.js'; import { getNumber } from './color.js'; import { srgb2rgb } from './rgb.js'; import './utils/constants.js'; import { getComponents } from './utils/components.js'; import { srgb2hwb } from './hwb.js'; import { srgb2hsl } from './hsl.js'; import { srgbvalues, srgb2lsrgbvalues } from './srgb.js'; import { srgb2lch, xyz2lchvalues } from './lch.js'; import { srgb2lab } from './lab.js'; import { srgb2p3values } from './p3.js'; import { srgb2oklch } from './oklch.js'; import { srgb2oklab } from './oklab.js'; import { srgb2a98values } from './a98rgb.js'; import { srgb2prophotorgbvalues } from './prophotorgb.js'; import { srgb2xyz } from './xyz.js'; import { XYZ_D65_to_D50, xyzd502lch } from './xyzd50.js'; import { srgb2rec2020values } from './rec2020.js'; import '../sourcemap/lib/encode.js'; function interpolateHue(interpolationMethod, h1, h2) { switch (interpolationMethod.val) { case 'longer': if (h2 - h1 < 180 && h2 - h1 > 0) { h1 += 360; } else if (h2 - h1 <= 0 && h2 - h1 > -180) { h2 += 360; } break; case 'increasing': if (h2 < h1) { h2 += 360; } break; case 'decreasing': if (h2 > h1) { h1 += 360; } break; case 'shorter': default: // shorter if (h2 - h1 > 180) { h1 += 360; } else if (h2 - h1 < -180) { h2 += 360; } break; } return [h1, h2]; } function colorMix(colorSpace, hueInterpolationMethod, color1, percentage1, color2, percentage2) { if (hueInterpolationMethod != null && isRectangularOrthogonalColorspace(colorSpace)) { return null; } if (isPolarColorspace(colorSpace) && hueInterpolationMethod == null) { hueInterpolationMethod = { typ: EnumToken.IdenTokenType, val: 'shorter' }; } if (percentage1 == null) { if (percentage2 == null) { // @ts-ignore percentage1 = { typ: EnumToken.NumberTokenType, val: '.5' }; // @ts-ignore percentage2 = { typ: EnumToken.NumberTokenType, val: '.5' }; } else { if (+percentage2.val <= 0) { return null; } if (+percentage2.val >= 100) { // @ts-ignore percentage2 = { typ: EnumToken.NumberTokenType, val: '1' }; } // @ts-ignore percentage1 = { typ: EnumToken.NumberTokenType, val: String(1 - percentage2.val / 100) }; } } else { // @ts-ignore if (percentage1.val <= 0) { return null; } if (percentage2 == null) { // @ts-ignore if (percentage1.val >= 100) { // @ts-ignore percentage1 = { typ: EnumToken.NumberTokenType, val: '1' }; } // @ts-ignore percentage2 = { typ: EnumToken.NumberTokenType, val: String(1 - percentage1.val / 100) }; } else { // @ts-ignore if (percentage2.val <= 0) { return null; } } } let values1 = srgbvalues(color1); let values2 = srgbvalues(color2); if (values1 == null || values2 == null) { return null; } const components1 = getComponents(color1); const components2 = getComponents(color2); if ((components1[3] != null && components1[3].typ == EnumToken.IdenTokenType && components1[3].val == 'none') && values2.length == 4) { values1[3] = values2[3]; } if ((components2[3] != null && components2[3].typ == EnumToken.IdenTokenType && components2[3].val == 'none') && values1.length == 4) { values2[3] = values1[3]; } const p1 = getNumber(percentage1); const p2 = getNumber(percentage2); const mul1 = values1.length == 4 ? values1.pop() : 1; const mul2 = values2.length == 4 ? values2.pop() : 1; const mul = mul1 * p1 + mul2 * p2; // @ts-ignore const calculate = () => [colorSpace].concat(values1.map((v1, i) => { return { // @ts-ignore typ: EnumToken.NumberTokenType, val: String((mul1 * v1 * p1 + mul2 * values2[i] * p2) / mul) }; }).concat(mul == 1 ? [] : [{ typ: EnumToken.NumberTokenType, val: String(mul) }])); switch (colorSpace.val) { case 'srgb': break; case 'display-p3': // @ts-ignore values1 = srgb2p3values(...values1); // @ts-ignore values2 = srgb2p3values(...values2); break; case 'a98-rgb': // @ts-ignore values1 = srgb2a98values(...values1); // @ts-ignore values2 = srgb2a98values(...values2); break; case 'prophoto-rgb': // @ts-ignore values1 = srgb2prophotorgbvalues(...values1); // @ts-ignore values2 = srgb2prophotorgbvalues(...values2); break; case 'srgb-linear': // @ts-ignore values1 = srgb2lsrgbvalues(...values1); // @ts-ignore values2 = srgb2lsrgbvalues(...values2); break; case 'rec2020': // @ts-ignore values1 = srgb2rec2020values(...values1); // @ts-ignore values2 = srgb2rec2020values(...values2); break; case 'xyz': case 'xyz-d65': case 'xyz-d50': // @ts-ignore values1 = srgb2xyz(...values1); // @ts-ignore values2 = srgb2xyz(...values2); if (colorSpace.val == 'xyz-d50') { // @ts-ignore values1 = XYZ_D65_to_D50(...values1); // @ts-ignore values2 = XYZ_D65_to_D50(...values2); } break; case 'rgb': // @ts-ignore values1 = srgb2rgb(...values1); // @ts-ignore values2 = srgb2rgb(...values2); break; case 'hsl': // @ts-ignore values1 = srgb2hsl(...values1); // @ts-ignore values2 = srgb2hsl(...values2); break; case 'hwb': // @ts-ignore values1 = srgb2hwb(...values1); // @ts-ignore values2 = srgb2hwb(...values2); break; case 'lab': // @ts-ignore values1 = srgb2lab(...values1); // @ts-ignore values2 = srgb2lab(...values2); break; case 'lch': // @ts-ignore values1 = srgb2lch(...values1); // @ts-ignore values2 = srgb2lch(...values2); break; case 'oklab': // @ts-ignore values1 = srgb2oklab(...values1); // @ts-ignore values2 = srgb2oklab(...values2); break; case 'oklch': // @ts-ignore values1 = srgb2oklch(...values1); // @ts-ignore values2 = srgb2oklch(...values2); break; default: return null; } const lchSpaces = ['lch', 'oklch']; // powerless if (lchSpaces.includes(color1.kin) || lchSpaces.includes(colorSpace.val)) { if ((components1[2].typ == EnumToken.IdenTokenType && components1[2].val == 'none') || values1[2] == 0) { values1[2] = values2[2]; } } // powerless if (lchSpaces.includes(color1.kin) || lchSpaces.includes(colorSpace.val)) { if ((components2[2].typ == EnumToken.IdenTokenType && components2[2].val == 'none') || values2[2] == 0) { values2[2] = values1[2]; } } if (hueInterpolationMethod != null) { let hueIndex = 2; let multiplier = 1; if (['hwb', 'hsl'].includes(colorSpace.val)) { hueIndex = 0; multiplier = 360; } const [h1, h2] = interpolateHue(hueInterpolationMethod, values1[hueIndex] * multiplier, values2[hueIndex] * multiplier); values1[hueIndex] = h1 / multiplier; values2[hueIndex] = h2 / multiplier; } switch (colorSpace.val) { case 'xyz': case 'xyz-d65': case 'xyz-d50': let values = values1.map((v1, i) => (mul1 * v1 * p1 + mul2 * values2[i] * p2) / mul) .concat(mul == 1 ? [] : [mul]); if (colorSpace.val == 'xyz-d50') { // @ts-ignore values = xyzd502lch(...values); } else { // @ts-ignore values = xyz2lchvalues(...values); } // @ts-ignore return { typ: EnumToken.ColorTokenType, val: 'lch', chi: values.map(v => { return { typ: EnumToken.NumberTokenType, val: String(v) }; }), kin: 'lch' }; case 'srgb': case 'srgb-linear': case 'a98-rgb': case 'rec2020': // @ts-ignore return { typ: EnumToken.ColorTokenType, val: 'color', chi: calculate(), kin: 'color', cal: 'col' }; case 'rgb': case 'hsl': case 'hwb': case 'lab': case 'lch': case 'oklab': case 'oklch': if (['hsl', 'hwb'].includes(colorSpace.val)) { // @ts-ignore if (values1[2] < 0) { // @ts-ignore values1[2] += 1; } // @ts-ignore if (values2[2] < 0) { // @ts-ignore values2[2] += 1; } } else if (['lch', 'oklch'].includes(colorSpace.val)) { // @ts-ignore if (values1[2] < 0) { // @ts-ignore values1[2] += 360; } // @ts-ignore if (values2[2] < 0) { // @ts-ignore values2[2] += 360; } } // @ts-ignore const result = { typ: EnumToken.ColorTokenType, val: colorSpace.val, chi: calculate().slice(1), kin: colorSpace.val }; if (colorSpace.val == 'hsl' || colorSpace.val == 'hwb') { // @ts-ignore result.chi[0] = { typ: EnumToken.AngleTokenType, val: String(result.chi[0].val * 360), unit: 'deg' }; // @ts-ignore result.chi[1] = { typ: EnumToken.PercentageTokenType, val: String(result.chi[1].val * 100) }; // @ts-ignore result.chi[2] = { typ: EnumToken.PercentageTokenType, val: String(result.chi[2].val * 100) }; } return result; } return null; } export { colorMix };