From e000030aff2d9145820545aa2ab3e6793c163704 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Thu, 7 Mar 2024 15:01:27 -0700 Subject: [PATCH 1/3] fix(color-contrast): support color blend modes hue, saturation, color, luminosity --- lib/commons/color/color.js | 48 ++++++ lib/commons/color/flatten-colors.js | 176 +++++++++++++++++---- test/commons/color/color.js | 78 +++++++++ test/commons/color/flatten-colors.js | 84 ++++++++++ test/integration/full/contrast/blending.js | 48 +++++- 5 files changed, 400 insertions(+), 34 deletions(-) diff --git a/lib/commons/color/color.js b/lib/commons/color/color.js index 15a33f1d60..d4f773f663 100644 --- a/lib/commons/color/color.js +++ b/lib/commons/color/color.js @@ -150,6 +150,54 @@ export default class Color { return 0.2126 * r + 0.7152 * g + 0.0722 * b; } + + /** + * Add the value to a color and return a new instance of the Color. The resulting color values are not clamped to valid values of the color space (must be done separately). + * @method add + * @memberof axe.commons.color.Color + * @instance + * @return {Color} + */ + add(value) { + return new Color( + this.red + value, + this.green + value, + this.blue + value, + this.alpha + ); + } + + /** + * Divide a color by the value and return a new instance of the Color + * @method divide + * @memberof axe.commons.color.Color + * @instance + * @return {Color} + */ + divide(value) { + return new Color( + this.red / value, + this.green / value, + this.blue / value, + this.alpha + ); + } + + /** + * Multiply a color by the value and return a new instance of the Color. The resulting color values are not clamped to valid values of the color space (must be done separately). + * @method multiply + * @memberof axe.commons.color.Color + * @instance + * @return {Color} + */ + multiply(value) { + return new Color( + this.red * value, + this.green * value, + this.blue * value, + this.alpha + ); + } } // clamp a value between two numbers (inclusive) diff --git a/lib/commons/color/flatten-colors.js b/lib/commons/color/flatten-colors.js index 9ac19f0d11..34c96c82e4 100644 --- a/lib/commons/color/flatten-colors.js +++ b/lib/commons/color/flatten-colors.js @@ -1,9 +1,7 @@ import Color from './color'; -// clamp a value between two numbers (inclusive) -function clamp(value, min, max) { - return Math.min(Math.max(min, value), max); -} +// @see https://www.w3.org/TR/compositing-1/#blendingnonseparable +const nonSeparableBlendModes = ['hue', 'saturation', 'color', 'luminosity']; // how to combine background and foreground colors together when using // the CSS property `mix-blend-mode`. Defaults to `normal` @@ -61,28 +59,28 @@ const blendFunctions = { exclusion(Cb, Cs) { // @see https://www.w3.org/TR/compositing-1/#blendingexclusion return Cb + Cs - 2 * Cb * Cs; + }, + + // non-separate color function take the entire color object + // an not individual color components (red, green, blue) + hue(Cb, Cs) { + // @see https://www.w3.org/TR/compositing-1/#blendinghue + return setLuminosity(setSaturation(Cs, saturation(Cb)), luminosity(Cb)); + }, + saturation(Cb, Cs) { + // @see https://www.w3.org/TR/compositing-1/#blendingsaturation + return setLuminosity(setSaturation(Cb, saturation(Cs)), luminosity(Cb)); + }, + color(Cb, Cs) { + // @see https://www.w3.org/TR/compositing-1/#blendingcolor + return setLuminosity(Cs, luminosity(Cb)); + }, + luminosity(Cb, Cs) { + // @see https://www.w3.org/TR/compositing-1/#blendingluminosity + return setLuminosity(Cb, luminosity(Cs)); } }; -// Simple Alpha Compositing written as non-premultiplied. -// formula: Rrgb × Ra = Srgb × Sa + Drgb × Da × (1 − Sa) -// Cs: the source color -// αs: the source alpha -// Cb: the backdrop color -// αb: the backdrop alpha -// @see https://www.w3.org/TR/compositing-1/#simplealphacompositing -// @see https://www.w3.org/TR/compositing-1/#blending -// @see https://ciechanow.ski/alpha-compositing/ -function simpleAlphaCompositing(Cs, αs, Cb, αb, blendMode) { - return ( - αs * (1 - αb) * Cs + - // Note: Cs and Cb values need to be between 0 and 1 inclusive for the blend function - // @see https://www.w3.org/TR/compositing-1/#simplealphacompositing - αs * αb * blendFunctions[blendMode](Cb / 255, Cs / 255) * 255 + - (1 - αs) * αb * Cb - ); -} - /** * Combine the two given color according to alpha blending. * @method flattenColors @@ -92,28 +90,45 @@ function simpleAlphaCompositing(Cs, αs, Cb, αb, blendMode) { * @param {Color} backdrop Background color * @return {Color} Blended color */ -function flattenColors(sourceColor, backdrop, blendMode = 'normal') { +export default function flattenColors( + sourceColor, + backdrop, + blendMode = 'normal' +) { + let blendingResult; + if (nonSeparableBlendModes.includes(blendMode)) { + // Note: Cs and Cb values need to be between 0 and 1 inclusive for the blend function + // @see https://www.w3.org/TR/compositing-1/#simplealphacompositing + const Cs = sourceColor.divide(255); + const Cb = backdrop.divide(255); + + blendingResult = blendFunctions[blendMode](Cb, Cs).multiply(255); + } + // foreground is the "source" color and background is the "backdrop" color const r = simpleAlphaCompositing( sourceColor.red, sourceColor.alpha, backdrop.red, backdrop.alpha, - blendMode + blendMode, + blendingResult?.red ); const g = simpleAlphaCompositing( sourceColor.green, sourceColor.alpha, backdrop.green, backdrop.alpha, - blendMode + blendMode, + blendingResult?.green ); const b = simpleAlphaCompositing( sourceColor.blue, sourceColor.alpha, backdrop.blue, backdrop.alpha, - blendMode + blendMode, + blendingResult?.blue ); // formula: αo = αs + αb x (1 - αs) @@ -136,11 +151,108 @@ function flattenColors(sourceColor, backdrop, blendMode = 'normal') { // // RGB color space doesn't have decimal values so we will follow what browsers do and round // e.g. rgb(255.2, 127.5, 127.8) === rgb(255, 128, 128) - const Cr = Math.round(r / αo); - const Cg = Math.round(g / αo); - const Cb = Math.round(b / αo); + const Cred = Math.round(r / αo); + const Cgreen = Math.round(g / αo); + const Cblue = Math.round(b / αo); + + return new Color(Cred, Cgreen, Cblue, αo); +} + +// Simple Alpha Compositing written as non-premultiplied. +// formula: Rrgb × Ra = Srgb × Sa + Drgb × Da × (1 − Sa) +// Cs: the source color +// αs: the source alpha +// Cb: the backdrop color +// αb: the backdrop alpha +// @see https://www.w3.org/TR/compositing-1/#simplealphacompositing +// @see https://www.w3.org/TR/compositing-1/#blending +// @see https://ciechanow.ski/alpha-compositing/ +function simpleAlphaCompositing(Cs, αs, Cb, αb, blendMode, blendingResult) { + return ( + αs * (1 - αb) * Cs + + αs * + αb * + (blendingResult ?? + // Note: Cs and Cb values need to be between 0 and 1 inclusive for the blend function + // @see https://www.w3.org/TR/compositing-1/#simplealphacompositing + blendFunctions[blendMode](Cb / 255, Cs / 255) * 255) + + (1 - αs) * αb * Cb + ); +} + +// clamp a value between two numbers (inclusive) +function clamp(value, min, max) { + return Math.min(Math.max(min, value), max); +} + +// following functions taken from the spec +// @see https://www.w3.org/TR/compositing-1/#blendingnonseparable +function luminosity(color) { + return 0.3 * color.red + 0.59 * color.green + 0.11 * color.blue; +} + +function clipColor(color) { + const L = luminosity(color); + const n = Math.min(color.red, color.green, color.blue); + const x = Math.max(color.red, color.green, color.blue); + + if (n < 0) { + return new Color( + L + ((color.red - L) * L) / (L - n), + L + ((color.green - L) * L) / (L - n), + L + ((color.blue - L) * L) / (L - n), + color.alpha + ); + } + + if (x > 1) { + return new Color( + L + ((color.red - L) * (1 - L)) / (x - L), + L + ((color.green - L) * (1 - L)) / (x - L), + L + ((color.blue - L) * (1 - L)) / (x - L), + color.alpha + ); + } + + return color; +} + +function setLuminosity(color, L) { + const d = L - luminosity(color); + return clipColor(color.add(d)); +} - return new Color(Cr, Cg, Cb, αo); +function saturation(color) { + return ( + Math.max(color.red, color.green, color.blue) - + Math.min(color.red, color.green, color.blue) + ); } -export default flattenColors; +function setSaturation(color, s) { + const C = new Color(color.red, color.green, color.blue, color.alpha); + const colorEntires = Object.entries(C) + .filter(([prop]) => prop !== 'alpha') + .map(([name, value]) => { + return { name, value }; + }); + + // find the min, mid, and max values of the color components + const [Cmin, Cmid, Cmax] = colorEntires.sort((a, b) => { + return a.value - b.value; + }); + + if (Cmax.value > Cmin.value) { + Cmid.value = ((Cmid.value - Cmin.value) * s) / (Cmax.value - Cmin.value); + Cmax.value = s; + } else { + Cmid.value = Cmax.value = 0; + } + + Cmin.value = 0; + + C[Cmax.name] = Cmax.value; + C[Cmin.name] = Cmin.value; + C[Cmid.name] = Cmid.value; + return C; +} diff --git a/test/commons/color/color.js b/test/commons/color/color.js index be349aa27f..f5e0215794 100644 --- a/test/commons/color/color.js +++ b/test/commons/color/color.js @@ -413,4 +413,82 @@ describe('color.Color', () => { assert.isTrue(lBlue > lBlack); }); }); + + describe('add', () => { + it('adds the value to the color', () => { + const color = new Color(0, 0, 0, 1); + const actual = color.add(144); + assert.equal(actual.red, 144); + assert.equal(actual.green, 144); + assert.equal(actual.blue, 144); + assert.equal(actual.alpha, 1); + }); + + it('does not modify the original color', () => { + const color = new Color(0, 0, 0, 1); + color.add(144); + assert.equal(color.red, 0); + assert.equal(color.green, 0); + assert.equal(color.blue, 0); + assert.equal(color.alpha, 1); + }); + + it('does not clamp the value', () => { + const color = new Color(0, 0, 0, 1); + const actual = color.add(3000); + assert.equal(actual.red, 3000); + assert.equal(actual.green, 3000); + assert.equal(actual.blue, 3000); + assert.equal(actual.alpha, 1); + }); + }); + + describe('divide', () => { + it('divides the color by the value', () => { + const color = new Color(144, 144, 144, 1); + const actual = color.divide(2); + assert.equal(actual.red, 72); + assert.equal(actual.green, 72); + assert.equal(actual.blue, 72); + assert.equal(actual.alpha, 1); + }); + + it('does not modify the original color', () => { + const color = new Color(144, 144, 144, 1); + color.divide(2); + assert.equal(color.red, 144); + assert.equal(color.green, 144); + assert.equal(color.blue, 144); + assert.equal(color.alpha, 1); + }); + }); + + describe('multiply', () => { + it('multiplies the color by the value', () => { + const color = new Color(72, 72, 72, 1); + const actual = color.multiply(2); + assert.equal(actual.red, 144); + assert.equal(actual.green, 144); + assert.equal(actual.blue, 144); + assert.equal(actual.alpha, 1); + }); + + it('does not modify the original color', () => { + const color = new Color(72, 72, 72, 1); + color.multiply(2); + assert.equal(color.red, 72); + assert.equal(color.green, 72); + assert.equal(color.blue, 72); + assert.equal(color.alpha, 1); + }); + + it('does not clamp the value', () => { + const color = new Color(30, 30, 30, 1); + const actual = color.multiply(100); + assert.equal(actual.red, 3000); + assert.equal(actual.green, 3000); + assert.equal(actual.blue, 3000); + assert.equal(actual.alpha, 1); + }); + }); }); diff --git a/test/commons/color/flatten-colors.js b/test/commons/color/flatten-colors.js index 45c575f59a..0d152616bc 100644 --- a/test/commons/color/flatten-colors.js +++ b/test/commons/color/flatten-colors.js @@ -297,4 +297,88 @@ describe('color.flattenColors ', function () { assert.equal(flattenTwo.blue, 180); assert.equal(flattenTwo.alpha, 1); }); + + it('should flatten colors correctly using blend mode: hue', function () { + var flatten = axe.commons.color.flattenColors(colourTwo, colourOne, 'hue'); + assert.equal(flatten.red, 162); + assert.equal(flatten.green, 50); + assert.equal(flatten.blue, 17); + assert.equal(flatten.alpha, 1); + + var flattenTwo = axe.commons.color.flattenColors( + colourFour, + colourThree, + 'hue' + ); + assert.equal(flattenTwo.red, 188); + assert.equal(flattenTwo.green, 177); + assert.equal(flattenTwo.blue, 162); + assert.equal(flattenTwo.alpha, 1); + }); + + it('should flatten colors correctly using blend mode: saturation', function () { + var flatten = axe.commons.color.flattenColors( + colourTwo, + colourOne, + 'saturation' + ); + assert.equal(flatten.red, 185); + assert.equal(flatten.green, 35); + assert.equal(flatten.blue, 35); + assert.equal(flatten.alpha, 1); + + var flattenTwo = axe.commons.color.flattenColors( + colourFour, + colourThree, + 'saturation' + ); + assert.equal(flattenTwo.red, 233); + assert.equal(flattenTwo.green, 151); + assert.equal(flattenTwo.blue, 181); + assert.equal(flattenTwo.alpha, 1); + }); + + it('should flatten colors correctly using blend mode: color', function () { + var flatten = axe.commons.color.flattenColors( + colourTwo, + colourOne, + 'color' + ); + assert.equal(flatten.red, 180); + assert.equal(flatten.green, 38); + assert.equal(flatten.blue, 34); + assert.equal(flatten.alpha, 1); + + var flattenTwo = axe.commons.color.flattenColors( + colourFour, + colourThree, + 'color' + ); + assert.equal(flattenTwo.red, 161); + assert.equal(flattenTwo.green, 204); + assert.equal(flattenTwo.blue, 90); + assert.equal(flattenTwo.alpha, 1); + }); + + it('should flatten colors correctly using blend mode: luminosity', function () { + var flatten = axe.commons.color.flattenColors( + colourTwo, + colourOne, + 'luminosity' + ); + assert.equal(flatten.red, 226); + assert.equal(flatten.green, 33); + assert.equal(flatten.blue, 33); + assert.equal(flatten.alpha, 1); + + var flattenTwo = axe.commons.color.flattenColors( + colourFour, + colourThree, + 'luminosity' + ); + assert.equal(flattenTwo.red, 214); + assert.equal(flattenTwo.green, 165); + assert.equal(flattenTwo.blue, 183); + assert.equal(flattenTwo.alpha, 1); + }); }); diff --git a/test/integration/full/contrast/blending.js b/test/integration/full/contrast/blending.js index 1a75035a56..b103be987f 100644 --- a/test/integration/full/contrast/blending.js +++ b/test/integration/full/contrast/blending.js @@ -121,7 +121,47 @@ describe('color-contrast blending test', () => { 'rgb(255, 0, 77)', 'rgb(165, 176, 81)', 'rgb(150, 157, 119)', - 'rgb(198, 198, 198)' + 'rgb(198, 198, 198)', + // hue + 'rgb(212, 212, 196)', + 'rgb(255, 255, 255)', + 'rgb(255, 255, 255)', + 'rgb(125, 32, 54)', + 'rgb(179, 39, 0)', + 'rgb(195, 16, 77)', + 'rgb(147, 180, 84)', + 'rgb(150, 156, 117)', + 'rgb(221, 221, 221)', + // saturation + 'rgb(168, 239, 168)', + 'rgb(255, 255, 255)', + 'rgb(255, 255, 255)', + 'rgb(169, 5, 76)', + 'rgb(228, 11, 11)', + 'rgb(255, 0, 0)', + 'rgb(165, 171, 81)', + 'rgb(150, 157, 112)', + 'rgb(221, 221, 221)', + // color + 'rgb(223, 207, 191)', + 'rgb(255, 255, 255)', + 'rgb(255, 255, 255)', + 'rgb(125, 32, 54)', + 'rgb(179, 39, 0)', + 'rgb(195, 16, 77)', + 'rgb(144, 182, 81)', + 'rgb(150, 156, 120)', + 'rgb(221, 221, 221)', + // luminosity + 'rgb(124, 156, 124)', + 'rgb(166, 166, 166)', + 'rgb(210, 210, 210)', + 'rgb(183, 4, 81)', + 'rgb(254, 0, 0)', + 'rgb(207, 0, 0)', + 'rgb(171, 177, 87)', + 'rgb(148, 154, 112)', + 'rgb(221, 221, 221)' ]; const fixture = document.querySelector('#fixture'); @@ -137,7 +177,11 @@ describe('color-contrast blending test', () => { 'hard-light', 'soft-light', 'difference', - 'exclusion' + 'exclusion', + 'hue', + 'saturation', + 'color', + 'luminosity' ].forEach(blendMode => { const nodes = testGroup.cloneNode(true); const group = testGroup.cloneNode(); From a0b8822881221449117d194a53628b39a6f660b2 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:21:22 -0600 Subject: [PATCH 2/3] apply changes --- lib/commons/color/flatten-colors.js | 125 ++++++---------------------- 1 file changed, 25 insertions(+), 100 deletions(-) diff --git a/lib/commons/color/flatten-colors.js b/lib/commons/color/flatten-colors.js index 34c96c82e4..9d17e5f228 100644 --- a/lib/commons/color/flatten-colors.js +++ b/lib/commons/color/flatten-colors.js @@ -65,19 +65,23 @@ const blendFunctions = { // an not individual color components (red, green, blue) hue(Cb, Cs) { // @see https://www.w3.org/TR/compositing-1/#blendinghue - return setLuminosity(setSaturation(Cs, saturation(Cb)), luminosity(Cb)); + return Cs.setSaturation(Cb.getSaturation()).setLuminosity( + Cb.getLuminosity() + ); }, saturation(Cb, Cs) { // @see https://www.w3.org/TR/compositing-1/#blendingsaturation - return setLuminosity(setSaturation(Cb, saturation(Cs)), luminosity(Cb)); + return Cb.setSaturation(Cs.getSaturation()).setLuminosity( + Cb.getLuminosity() + ); }, color(Cb, Cs) { // @see https://www.w3.org/TR/compositing-1/#blendingcolor - return setLuminosity(Cs, luminosity(Cb)); + return Cs.setLuminosity(Cb.getLuminosity()); }, luminosity(Cb, Cs) { // @see https://www.w3.org/TR/compositing-1/#blendingluminosity - return setLuminosity(Cb, luminosity(Cs)); + return Cb.setLuminosity(Cs.getLuminosity()); } }; @@ -95,15 +99,7 @@ export default function flattenColors( backdrop, blendMode = 'normal' ) { - let blendingResult; - if (nonSeparableBlendModes.includes(blendMode)) { - // Note: Cs and Cb values need to be between 0 and 1 inclusive for the blend function - // @see https://www.w3.org/TR/compositing-1/#simplealphacompositing - const Cs = sourceColor.divide(255); - const Cb = backdrop.divide(255); - - blendingResult = blendFunctions[blendMode](Cb, Cs).multiply(255); - } + const blendingResult = blend(backdrop, sourceColor, blendMode); // foreground is the "source" color and background is the "backdrop" color const r = simpleAlphaCompositing( @@ -111,24 +107,22 @@ export default function flattenColors( sourceColor.alpha, backdrop.red, backdrop.alpha, - blendMode, - blendingResult?.red + // we don't want to round the blended value + blendingResult.r * 255 ); const g = simpleAlphaCompositing( sourceColor.green, sourceColor.alpha, backdrop.green, backdrop.alpha, - blendMode, - blendingResult?.green + blendingResult.g * 255 ); const b = simpleAlphaCompositing( sourceColor.blue, sourceColor.alpha, backdrop.blue, backdrop.alpha, - blendMode, - blendingResult?.blue + blendingResult.b * 255 ); // formula: αo = αs + αb x (1 - αs) @@ -151,11 +145,11 @@ export default function flattenColors( // // RGB color space doesn't have decimal values so we will follow what browsers do and round // e.g. rgb(255.2, 127.5, 127.8) === rgb(255, 128, 128) - const Cred = Math.round(r / αo); - const Cgreen = Math.round(g / αo); - const Cblue = Math.round(b / αo); + const Cr = Math.round(r / αo); + const Cg = Math.round(g / αo); + const Cb = Math.round(b / αo); - return new Color(Cred, Cgreen, Cblue, αo); + return new Color(Cr, Cg, Cb, αo); } // Simple Alpha Compositing written as non-premultiplied. @@ -167,17 +161,8 @@ export default function flattenColors( // @see https://www.w3.org/TR/compositing-1/#simplealphacompositing // @see https://www.w3.org/TR/compositing-1/#blending // @see https://ciechanow.ski/alpha-compositing/ -function simpleAlphaCompositing(Cs, αs, Cb, αb, blendMode, blendingResult) { - return ( - αs * (1 - αb) * Cs + - αs * - αb * - (blendingResult ?? - // Note: Cs and Cb values need to be between 0 and 1 inclusive for the blend function - // @see https://www.w3.org/TR/compositing-1/#simplealphacompositing - blendFunctions[blendMode](Cb / 255, Cs / 255) * 255) + - (1 - αs) * αb * Cb - ); +function simpleAlphaCompositing(Cs, αs, Cb, αb, blendingResult) { + return αs * (1 - αb) * Cs + αs * αb * blendingResult + (1 - αs) * αb * Cb; } // clamp a value between two numbers (inclusive) @@ -185,74 +170,14 @@ function clamp(value, min, max) { return Math.min(Math.max(min, value), max); } -// following functions taken from the spec -// @see https://www.w3.org/TR/compositing-1/#blendingnonseparable -function luminosity(color) { - return 0.3 * color.red + 0.59 * color.green + 0.11 * color.blue; -} - -function clipColor(color) { - const L = luminosity(color); - const n = Math.min(color.red, color.green, color.blue); - const x = Math.max(color.red, color.green, color.blue); - - if (n < 0) { - return new Color( - L + ((color.red - L) * L) / (L - n), - L + ((color.green - L) * L) / (L - n), - L + ((color.blue - L) * L) / (L - n), - color.alpha - ); - } - - if (x > 1) { - return new Color( - L + ((color.red - L) * (1 - L)) / (x - L), - L + ((color.green - L) * (1 - L)) / (x - L), - L + ((color.blue - L) * (1 - L)) / (x - L), - color.alpha - ); +function blend(Cb, Cs, blendMode) { + if (nonSeparableBlendModes.includes(blendMode)) { + return blendFunctions[blendMode](Cb, Cs); } - return color; -} - -function setLuminosity(color, L) { - const d = L - luminosity(color); - return clipColor(color.add(d)); -} - -function saturation(color) { - return ( - Math.max(color.red, color.green, color.blue) - - Math.min(color.red, color.green, color.blue) - ); -} - -function setSaturation(color, s) { - const C = new Color(color.red, color.green, color.blue, color.alpha); - const colorEntires = Object.entries(C) - .filter(([prop]) => prop !== 'alpha') - .map(([name, value]) => { - return { name, value }; - }); - - // find the min, mid, and max values of the color components - const [Cmin, Cmid, Cmax] = colorEntires.sort((a, b) => { - return a.value - b.value; + const C = new Color(); + ['r', 'g', 'b'].forEach(channel => { + C[channel] = blendFunctions[blendMode](Cb[channel], Cs[channel]); }); - - if (Cmax.value > Cmin.value) { - Cmid.value = ((Cmid.value - Cmin.value) * s) / (Cmax.value - Cmin.value); - Cmax.value = s; - } else { - Cmid.value = Cmax.value = 0; - } - - Cmin.value = 0; - - C[Cmax.name] = Cmax.value; - C[Cmin.name] = Cmin.value; - C[Cmid.name] = Cmid.value; return C; } From afa6ce5c747b9ee74e1b8c796ea4b346b03e1a71 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:30:57 -0600 Subject: [PATCH 3/3] typo --- lib/commons/color/flatten-colors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commons/color/flatten-colors.js b/lib/commons/color/flatten-colors.js index 9d17e5f228..e70ceafd33 100644 --- a/lib/commons/color/flatten-colors.js +++ b/lib/commons/color/flatten-colors.js @@ -62,7 +62,7 @@ const blendFunctions = { }, // non-separate color function take the entire color object - // an not individual color components (red, green, blue) + // and not individual color components (red, green, blue) hue(Cb, Cs) { // @see https://www.w3.org/TR/compositing-1/#blendinghue return Cs.setSaturation(Cb.getSaturation()).setLuminosity(