From 71637c372bbbbab0498ba374e097254254dd23b3 Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sat, 5 Nov 2022 19:30:41 -0700 Subject: [PATCH 01/12] Added conversion functions for LAB to RGB for the preview --- src/languageFacts/colors.ts | 105 ++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 4 deletions(-) diff --git a/src/languageFacts/colors.ts b/src/languageFacts/colors.ts index 28f517cd..5efd3e27 100644 --- a/src/languageFacts/colors.ts +++ b/src/languageFacts/colors.ts @@ -15,7 +15,8 @@ export const colorFunctions = [ { func: 'rgba($red, $green, $blue, $alpha)', desc: localize('css.builtin.rgba', 'Creates a Color from red, green, blue, and alpha values.') }, { func: 'hsl($hue, $saturation, $lightness)', desc: localize('css.builtin.hsl', 'Creates a Color from hue, saturation, and lightness values.') }, { func: 'hsla($hue, $saturation, $lightness, $alpha)', desc: localize('css.builtin.hsla', 'Creates a Color from hue, saturation, lightness, and alpha values.') }, - { func: 'hwb($hue $white $black)', desc: localize('css.builtin.hwb', 'Creates a Color from hue, white and black.') } + { func: 'hwb($hue $white $black)', desc: localize('css.builtin.hwb', 'Creates a Color from hue, white and black.') }, + { func: 'lab($lightness $a $b $alpha)', desc: localize('css.builtin.lab', 'Creates a Color from Lightness, a, b and alpha values.') } ]; export const colors: { [name: string]: string } = { @@ -174,7 +175,7 @@ export const colorKeywords: { [name: string]: string } = { 'transparent': 'Fully transparent. This keyword can be considered a shorthand for rgba(0,0,0,0) which is its computed value.', }; -function getNumericValue(node: nodes.Node, factor: number) { +function getNumericValue(node: nodes.Node, factor: number, lowerLimit: number = 0, upperLimit: number = 1) { const val = node.getText(); const m = val.match(/^([-+]?[0-9]*\.?[0-9]+)(%?)$/); if (m) { @@ -182,7 +183,7 @@ function getNumericValue(node: nodes.Node, factor: number) { factor = 100.0; } const result = parseFloat(m[1]) / factor; - if (result >= 0 && result <= 1) { + if (result >= lowerLimit && result <= upperLimit) { return result; } } @@ -217,7 +218,7 @@ export function isColorConstructor(node: nodes.Function): boolean { if (!name) { return false; } - return /^(rgb|rgba|hsl|hsla|hwb)$/gi.test(name); + return /^(rgb|rgba|hsl|hsla|hwb|lab)$/gi.test(name); } /** @@ -406,6 +407,96 @@ export function hwbFromColor(rgba: Color): HWBA { }; } +export interface XYZ { x: number; y: number; z: number; alpha: number; } + +export interface RGB { r: number; g: number; b: number; alpha: number; } + +export function xyzFromLAB(lab : LAB) : XYZ { + let xyz: XYZ = { + x: 0, + y: 0, + z: 0, + alpha: lab.alpha ?? 1 + } + xyz["y"] = (lab.l + 16.0) / 116.0; + xyz["x"] = (lab.a / 500.0) + xyz["y"]; + xyz["z"] = xyz["y"] - (lab.b / 200.0); + let key: keyof XYZ; + + for (key in xyz){ + let pow = xyz[key] * xyz[key] * xyz[key]; + if (pow > 0.008856){ + xyz[key] = pow; + } else { + xyz[key] = (xyz[key]- 16.0 / 116.0) / 7.787; + } + } + + xyz["x"] = xyz["x"] * 95.047; + xyz["y"] = xyz["y"] * 100.0; + xyz["z"] = xyz["z"] * 108.883; + return xyz; +} + +export function xyzToRGB(xyz: XYZ) : Color{ + let rgb: RGB = { + r: 0, + g: 0, + b: 0, + alpha: xyz.alpha + }; + + let new_xyz: XYZ = { + x: xyz.x / 100, + y: xyz.y / 100, + z: xyz.z / 100, + alpha: xyz.alpha ?? 1 + } + + rgb.r = (new_xyz.x * 3.240479) + (new_xyz.y * -1.537150) + (new_xyz.z * -0.498535); + rgb.g = (new_xyz.x * -0.969256) + (new_xyz.y * 1.875992) + (new_xyz.z * 0.041556); + rgb.b = (new_xyz.x * 0.055648) + (new_xyz.y * -0.204043) + (new_xyz.z * 1.057311); + let key: keyof RGB; + + for(key in rgb) { + if (rgb[key] > 0.0031308) { + rgb[key] = (1.055 * Math.pow(rgb[key], (1.0 / 2.4))) - 0.055; + } else { + rgb[key] = rgb[key] * 12.92; + } + } + rgb.r = Math.round(rgb.r * 255.0); + rgb.g = Math.round(rgb.g * 255.0); + rgb.b = Math.round(rgb.b * 255.0); + + return { + red: rgb.r, + blue: rgb.b, + green: rgb.g, + alpha: rgb.alpha + } +} + +export function colorFromLAB(l: number, a: number, b: number, alpha: number = 1.0): Color { + const lab : LAB = { + l, + a, + b, + alpha + } + const xyz = xyzFromLAB(lab) + const rgb = xyzToRGB(xyz) + return { + red: (rgb.red >= 0 ? (rgb.red <= 255 ? rgb.red : 255) : 0) / 255.0, + green: (rgb.green >= 0 ? (rgb.green <= 255 ? rgb.green : 255) : 0) / 255.0, + blue: (rgb.blue >= 0 ? (rgb.blue <= 255 ? rgb.blue : 255) : 0) / 255.0, + alpha + }; +} + +export interface LAB { l: number; a: number; b: number; alpha?: number; } + + export function getColorValue(node: nodes.Node): Color | null { if (node.type === nodes.NodeType.HexColorValue) { const text = node.getText(); @@ -451,6 +542,12 @@ export function getColorValue(node: nodes.Node): Color | null { const w = getNumericValue(colorValues[1], 100.0); const b = getNumericValue(colorValues[2], 100.0); return colorFromHWB(h, w, b, alpha); + } else if (name === 'lab') { + const l = getNumericValue(colorValues[0], 100.0); + // Since these two values can be negative, a lower limit of -1 has been added + const a = getNumericValue(colorValues[1], 125.0, -1); + const b = getNumericValue(colorValues[2], 125.0, -1); + return colorFromLAB(l*100, a*125, b*125, alpha); } } catch (e) { // parse error on numeric value From 605afed8521bd026d55a9f8cdb674442466f0e38 Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sat, 5 Nov 2022 19:31:01 -0700 Subject: [PATCH 02/12] Added the ability to parse negative numbers since LAB requires it --- src/parser/cssScanner.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/parser/cssScanner.ts b/src/parser/cssScanner.ts index eb129ac4..5bb916c4 100644 --- a/src/parser/cssScanner.ts +++ b/src/parser/cssScanner.ts @@ -440,12 +440,13 @@ export class Scanner { } private _number(): boolean { - let npeek = 0, ch: number; + let npeek = 0, ch: number, next_ch: number; if (this.stream.peekChar() === _DOT) { npeek = 1; } ch = this.stream.peekChar(npeek); - if (ch >= _0 && ch <= _9) { + next_ch = this.stream.peekChar(npeek + 1); + if ((ch >= _0 && ch <= _9) || (ch === _MIN && next_ch >= _0 && next_ch <= _9)) { this.stream.advance(npeek + 1); this.stream.advanceWhileChar((ch) => { return ch >= _0 && ch <= _9 || npeek === 0 && ch === _DOT; From d3674b6719e940ec5f3e6c74add6aeb988eae0ec Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sat, 5 Nov 2022 19:31:19 -0700 Subject: [PATCH 03/12] Added basic tests for LAB, need to add more cases yet --- src/test/css/languageFacts.test.ts | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/test/css/languageFacts.test.ts b/src/test/css/languageFacts.test.ts index 33b9f773..282e4949 100644 --- a/src/test/css/languageFacts.test.ts +++ b/src/test/css/languageFacts.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import { isColorValue, getColorValue, getBrowserLabel, colorFrom256RGB, colorFromHex, hexDigit, hslFromColor, HSLA, hwbFromColor, HWBA, colorFromHWB, colorFromHSL } from '../../languageFacts/facts'; +import { isColorValue, getColorValue, getBrowserLabel, colorFrom256RGB, colorFromHex, hexDigit, hslFromColor, HSLA, XYZ, xyzToRGB, xyzFromLAB, hwbFromColor, HWBA, colorFromHWB, colorFromHSL, colorFromLAB } from '../../languageFacts/facts'; import { Parser } from '../../parser/cssParser'; import * as nodes from '../../parser/cssNodes'; import { TextDocument, Color } from '../../cssLanguageTypes'; @@ -69,6 +69,18 @@ function assertHWBValue(actual: HWBA, expected: HWBA) { assert.deepEqual(actual, expected); } +function assertXYZValue(actual: XYZ, expected: XYZ) { + if (actual && expected) { + let hDiff = Math.abs(actual.x - expected.x); + let wDiff = Math.abs(actual.y - expected.y); + let bDiff = Math.abs(actual.z - expected.z); + let aDiff = Math.abs((actual.alpha - expected.alpha) * 100); + if (hDiff < 1 && wDiff < 1 && bDiff < 1 && aDiff < 1) { + return; + } + } + assert.deepEqual(actual, expected); +} suite('CSS - Language Facts', () => { @@ -128,6 +140,9 @@ suite('CSS - Language Facts', () => { assertColor(parser, '#main { color: hsla(240 100% 50% / .05) }', 'hsl', colorFrom256RGB(0, 0, 255, 0.05)); assertColor(parser, '#main { color: hwb(120 0% 0% / .05) }', 'hwb', colorFrom256RGB(0, 255, 0, 0.05)); assertColor(parser, '#main { color: hwb(36 33% 35%) }', 'hwb', colorFrom256RGB(166, 133, 84)); + assertColor(parser, '#main { color: lab(90 100 100) }', 'lab', colorFrom256RGB(255, 112, 0)); + assertColor(parser, '#main { color: lab(46.41 39.24 33.51) }', 'lab', colorFrom256RGB(180, 79, 56)); + assertColor(parser, '#main { color: lab(46.41 -39.24 33.51) }', 'lab', colorFrom256RGB(50, 125, 50)); }); test('hexDigit', function () { @@ -229,4 +244,15 @@ suite('CSS - Language Facts', () => { assertColorValue(colorFromHSL(118, 0.98, 0.5), colorFrom256RGB(11, 252, 3), 'hsl(118, 98%, 50%)'); assertColorValue(colorFromHSL(120, 0.83, 0.95), colorFrom256RGB(232, 253, 232), 'hsl(120, 83%, 95%)'); }); + + test('xyzFromLAB', function () { + assertXYZValue(xyzFromLAB({l: 46.41, a: -39.24, b: 33.51}), { x: 9.22, y: 15.58, z: 5.54, alpha: 1 }); + }); + + test('xyzToRGB', function () { + assertColorValue(xyzToRGB({ x: 9.22, y: 15.58, z: 5.54, alpha: 1 }), { red: 50, green: 125, blue: 50, alpha: 1 }, 'xyz(9.22, 15.58, 5.54)'); + }); + test('LABToRGB', function () { + assertColorValue(colorFromLAB(46.41, -39.24, 33.51), colorFrom256RGB(50, 125, 50), 'lab(46.41, -39.24, 33.51)'); + }); }); From 3ebcdf7afab986b724e2a597c5f0fa723e436e3b Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sat, 5 Nov 2022 20:29:03 -0700 Subject: [PATCH 04/12] Added lch() conversion functions --- src/languageFacts/colors.ts | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/languageFacts/colors.ts b/src/languageFacts/colors.ts index 5efd3e27..49af4333 100644 --- a/src/languageFacts/colors.ts +++ b/src/languageFacts/colors.ts @@ -16,7 +16,8 @@ export const colorFunctions = [ { func: 'hsl($hue, $saturation, $lightness)', desc: localize('css.builtin.hsl', 'Creates a Color from hue, saturation, and lightness values.') }, { func: 'hsla($hue, $saturation, $lightness, $alpha)', desc: localize('css.builtin.hsla', 'Creates a Color from hue, saturation, lightness, and alpha values.') }, { func: 'hwb($hue $white $black)', desc: localize('css.builtin.hwb', 'Creates a Color from hue, white and black.') }, - { func: 'lab($lightness $a $b $alpha)', desc: localize('css.builtin.lab', 'Creates a Color from Lightness, a, b and alpha values.') } + { func: 'lab($lightness $channel_a $channel_b $alpha)', desc: localize('css.builtin.lab', 'Creates a Color from Lightness, Channel a, Channel b and alpha values.') }, + { func: 'lch($lightness $chrome $hue $alpha)', desc: localize('css.builtin.lab', 'Creates a Color from Lightness, Chroma, Hue and alpha values.') } ]; export const colors: { [name: string]: string } = { @@ -218,7 +219,7 @@ export function isColorConstructor(node: nodes.Function): boolean { if (!name) { return false; } - return /^(rgb|rgba|hsl|hsla|hwb|lab)$/gi.test(name); + return /^(rgb|rgba|hsl|hsla|hwb|lab|lch)$/gi.test(name); } /** @@ -484,8 +485,9 @@ export function colorFromLAB(l: number, a: number, b: number, alpha: number = 1. b, alpha } - const xyz = xyzFromLAB(lab) - const rgb = xyzToRGB(xyz) + console.log(lab, "LABBB"); + const xyz = xyzFromLAB(lab); + const rgb = xyzToRGB(xyz); return { red: (rgb.red >= 0 ? (rgb.red <= 255 ? rgb.red : 255) : 0) / 255.0, green: (rgb.green >= 0 ? (rgb.green <= 255 ? rgb.green : 255) : 0) / 255.0, @@ -496,6 +498,21 @@ export function colorFromLAB(l: number, a: number, b: number, alpha: number = 1. export interface LAB { l: number; a: number; b: number; alpha?: number; } +export function labFromLCH(l: number, c: number, h: number, alpha: number = 1.0) : LAB { + return { + l: l, + a: c * Math.cos(h * (Math.PI / 180)), + b: c * Math.sin(h * ( Math.PI / 180)), + alpha: alpha + } +} + +export function colorFromLCH(l: number, c: number, h: number, alpha: number = 1.0): Color { + const lab: LAB = labFromLCH(l, c, h, alpha); + return colorFromLAB(lab.l, lab.a, lab.b, alpha); +} + +export interface LCH { l: number; c: number; h: number; alpha?: number; } export function getColorValue(node: nodes.Node): Color | null { if (node.type === nodes.NodeType.HexColorValue) { @@ -548,6 +565,12 @@ export function getColorValue(node: nodes.Node): Color | null { const a = getNumericValue(colorValues[1], 125.0, -1); const b = getNumericValue(colorValues[2], 125.0, -1); return colorFromLAB(l*100, a*125, b*125, alpha); + } else if (name === 'lch') { + const l = getNumericValue(colorValues[0], 100.0); + const c = getNumericValue(colorValues[1], 230.0); + const h = getAngle(colorValues[2]); + console.log(l, c, h, "LCCCH") + return colorFromLCH(l*100, c * 230, h, alpha); } } catch (e) { // parse error on numeric value From 3b3145a899f9e8ffdb1a99e41890c87b6fa8368c Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sat, 5 Nov 2022 20:29:13 -0700 Subject: [PATCH 05/12] Added basic tests for lch(), need to add more --- src/test/css/languageFacts.test.ts | 32 +++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/test/css/languageFacts.test.ts b/src/test/css/languageFacts.test.ts index 282e4949..7ad1b622 100644 --- a/src/test/css/languageFacts.test.ts +++ b/src/test/css/languageFacts.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import { isColorValue, getColorValue, getBrowserLabel, colorFrom256RGB, colorFromHex, hexDigit, hslFromColor, HSLA, XYZ, xyzToRGB, xyzFromLAB, hwbFromColor, HWBA, colorFromHWB, colorFromHSL, colorFromLAB } from '../../languageFacts/facts'; +import { isColorValue, getColorValue, getBrowserLabel, colorFrom256RGB, colorFromHex, hexDigit, hslFromColor, HSLA, XYZ, LAB, xyzToRGB, xyzFromLAB, hwbFromColor, HWBA, colorFromHWB, colorFromHSL, colorFromLAB, labFromLCH, colorFromLCH } from '../../languageFacts/facts'; import { Parser } from '../../parser/cssParser'; import * as nodes from '../../parser/cssNodes'; import { TextDocument, Color } from '../../cssLanguageTypes'; @@ -71,17 +71,32 @@ function assertHWBValue(actual: HWBA, expected: HWBA) { function assertXYZValue(actual: XYZ, expected: XYZ) { if (actual && expected) { - let hDiff = Math.abs(actual.x - expected.x); - let wDiff = Math.abs(actual.y - expected.y); - let bDiff = Math.abs(actual.z - expected.z); + let xDiff = Math.abs(actual.x - expected.x); + let yDiff = Math.abs(actual.y - expected.y); + let zDiff = Math.abs(actual.z - expected.z); let aDiff = Math.abs((actual.alpha - expected.alpha) * 100); - if (hDiff < 1 && wDiff < 1 && bDiff < 1 && aDiff < 1) { + if (xDiff < 1 && yDiff < 1 && zDiff < 1 && aDiff < 1) { return; } } assert.deepEqual(actual, expected); } +function assertLABValue(actual: LAB, expected: LAB) { + if (actual && expected) { + let lDiff = Math.abs(actual.l - expected.l); + let aDiff = Math.abs(actual.a - expected.a); + let bDiff = Math.abs(actual.b - expected.b); + let alphaDiff = 0; + if(actual.alpha && expected.alpha) { + alphaDiff = Math.abs((actual.alpha - expected.alpha) * 100); + } + if (lDiff < 1 && aDiff < 1 && bDiff < 1 && alphaDiff < 1) { + return; + } + } + assert.deepEqual(actual, expected); +} suite('CSS - Language Facts', () => { const cssDataManager = new CSSDataManager({ useDefaultDataProvider: true }); @@ -143,6 +158,7 @@ suite('CSS - Language Facts', () => { assertColor(parser, '#main { color: lab(90 100 100) }', 'lab', colorFrom256RGB(255, 112, 0)); assertColor(parser, '#main { color: lab(46.41 39.24 33.51) }', 'lab', colorFrom256RGB(180, 79, 56)); assertColor(parser, '#main { color: lab(46.41 -39.24 33.51) }', 'lab', colorFrom256RGB(50, 125, 50)); + assertColor(parser, '#main { color: lch(46.41, 51.60, 139.50) }', 'lch', colorFrom256RGB(50, 125, 50)); }); test('hexDigit', function () { @@ -255,4 +271,10 @@ suite('CSS - Language Facts', () => { test('LABToRGB', function () { assertColorValue(colorFromLAB(46.41, -39.24, 33.51), colorFrom256RGB(50, 125, 50), 'lab(46.41, -39.24, 33.51)'); }); + test('labFromLCH', function () { + assertLABValue(labFromLCH(46.41, 51.60, 139.50), {l: 46.41, a: -39.24, b: 33.51, alpha: 1}); + }); + test('LCHtoRGB', function () { + assertColorValue(colorFromLCH(46.41, 51.60, 139.50), colorFrom256RGB(50, 125, 50), 'lch(46.41, 51.60, 139.50)'); + }); }); From 780733f6eaee1db76f51c69e68589398d28df900 Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sat, 5 Nov 2022 20:30:07 -0700 Subject: [PATCH 06/12] Added lch() conversion functions --- src/languageFacts/colors.ts | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/languageFacts/colors.ts b/src/languageFacts/colors.ts index 5efd3e27..1142a0b2 100644 --- a/src/languageFacts/colors.ts +++ b/src/languageFacts/colors.ts @@ -16,7 +16,8 @@ export const colorFunctions = [ { func: 'hsl($hue, $saturation, $lightness)', desc: localize('css.builtin.hsl', 'Creates a Color from hue, saturation, and lightness values.') }, { func: 'hsla($hue, $saturation, $lightness, $alpha)', desc: localize('css.builtin.hsla', 'Creates a Color from hue, saturation, lightness, and alpha values.') }, { func: 'hwb($hue $white $black)', desc: localize('css.builtin.hwb', 'Creates a Color from hue, white and black.') }, - { func: 'lab($lightness $a $b $alpha)', desc: localize('css.builtin.lab', 'Creates a Color from Lightness, a, b and alpha values.') } + { func: 'lab($lightness $channel_a $channel_b $alpha)', desc: localize('css.builtin.lab', 'Creates a Color from Lightness, Channel a, Channel b and alpha values.') }, + { func: 'lch($lightness $chrome $hue $alpha)', desc: localize('css.builtin.lab', 'Creates a Color from Lightness, Chroma, Hue and alpha values.') } ]; export const colors: { [name: string]: string } = { @@ -218,7 +219,7 @@ export function isColorConstructor(node: nodes.Function): boolean { if (!name) { return false; } - return /^(rgb|rgba|hsl|hsla|hwb|lab)$/gi.test(name); + return /^(rgb|rgba|hsl|hsla|hwb|lab|lch)$/gi.test(name); } /** @@ -484,8 +485,8 @@ export function colorFromLAB(l: number, a: number, b: number, alpha: number = 1. b, alpha } - const xyz = xyzFromLAB(lab) - const rgb = xyzToRGB(xyz) + const xyz = xyzFromLAB(lab); + const rgb = xyzToRGB(xyz); return { red: (rgb.red >= 0 ? (rgb.red <= 255 ? rgb.red : 255) : 0) / 255.0, green: (rgb.green >= 0 ? (rgb.green <= 255 ? rgb.green : 255) : 0) / 255.0, @@ -496,6 +497,21 @@ export function colorFromLAB(l: number, a: number, b: number, alpha: number = 1. export interface LAB { l: number; a: number; b: number; alpha?: number; } +export function labFromLCH(l: number, c: number, h: number, alpha: number = 1.0) : LAB { + return { + l: l, + a: c * Math.cos(h * (Math.PI / 180)), + b: c * Math.sin(h * ( Math.PI / 180)), + alpha: alpha + } +} + +export function colorFromLCH(l: number, c: number, h: number, alpha: number = 1.0): Color { + const lab: LAB = labFromLCH(l, c, h, alpha); + return colorFromLAB(lab.l, lab.a, lab.b, alpha); +} + +export interface LCH { l: number; c: number; h: number; alpha?: number; } export function getColorValue(node: nodes.Node): Color | null { if (node.type === nodes.NodeType.HexColorValue) { @@ -548,6 +564,11 @@ export function getColorValue(node: nodes.Node): Color | null { const a = getNumericValue(colorValues[1], 125.0, -1); const b = getNumericValue(colorValues[2], 125.0, -1); return colorFromLAB(l*100, a*125, b*125, alpha); + } else if (name === 'lch') { + const l = getNumericValue(colorValues[0], 100.0); + const c = getNumericValue(colorValues[1], 230.0); + const h = getAngle(colorValues[2]); + return colorFromLCH(l*100, c * 230, h, alpha); } } catch (e) { // parse error on numeric value From 736a01fdfc460a76392cf1edc7a1f272e115f2ab Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sat, 5 Nov 2022 20:30:24 -0700 Subject: [PATCH 07/12] Added basic tests for lch(), need to add more --- src/test/css/languageFacts.test.ts | 32 +++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/test/css/languageFacts.test.ts b/src/test/css/languageFacts.test.ts index 282e4949..7ad1b622 100644 --- a/src/test/css/languageFacts.test.ts +++ b/src/test/css/languageFacts.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import { isColorValue, getColorValue, getBrowserLabel, colorFrom256RGB, colorFromHex, hexDigit, hslFromColor, HSLA, XYZ, xyzToRGB, xyzFromLAB, hwbFromColor, HWBA, colorFromHWB, colorFromHSL, colorFromLAB } from '../../languageFacts/facts'; +import { isColorValue, getColorValue, getBrowserLabel, colorFrom256RGB, colorFromHex, hexDigit, hslFromColor, HSLA, XYZ, LAB, xyzToRGB, xyzFromLAB, hwbFromColor, HWBA, colorFromHWB, colorFromHSL, colorFromLAB, labFromLCH, colorFromLCH } from '../../languageFacts/facts'; import { Parser } from '../../parser/cssParser'; import * as nodes from '../../parser/cssNodes'; import { TextDocument, Color } from '../../cssLanguageTypes'; @@ -71,17 +71,32 @@ function assertHWBValue(actual: HWBA, expected: HWBA) { function assertXYZValue(actual: XYZ, expected: XYZ) { if (actual && expected) { - let hDiff = Math.abs(actual.x - expected.x); - let wDiff = Math.abs(actual.y - expected.y); - let bDiff = Math.abs(actual.z - expected.z); + let xDiff = Math.abs(actual.x - expected.x); + let yDiff = Math.abs(actual.y - expected.y); + let zDiff = Math.abs(actual.z - expected.z); let aDiff = Math.abs((actual.alpha - expected.alpha) * 100); - if (hDiff < 1 && wDiff < 1 && bDiff < 1 && aDiff < 1) { + if (xDiff < 1 && yDiff < 1 && zDiff < 1 && aDiff < 1) { return; } } assert.deepEqual(actual, expected); } +function assertLABValue(actual: LAB, expected: LAB) { + if (actual && expected) { + let lDiff = Math.abs(actual.l - expected.l); + let aDiff = Math.abs(actual.a - expected.a); + let bDiff = Math.abs(actual.b - expected.b); + let alphaDiff = 0; + if(actual.alpha && expected.alpha) { + alphaDiff = Math.abs((actual.alpha - expected.alpha) * 100); + } + if (lDiff < 1 && aDiff < 1 && bDiff < 1 && alphaDiff < 1) { + return; + } + } + assert.deepEqual(actual, expected); +} suite('CSS - Language Facts', () => { const cssDataManager = new CSSDataManager({ useDefaultDataProvider: true }); @@ -143,6 +158,7 @@ suite('CSS - Language Facts', () => { assertColor(parser, '#main { color: lab(90 100 100) }', 'lab', colorFrom256RGB(255, 112, 0)); assertColor(parser, '#main { color: lab(46.41 39.24 33.51) }', 'lab', colorFrom256RGB(180, 79, 56)); assertColor(parser, '#main { color: lab(46.41 -39.24 33.51) }', 'lab', colorFrom256RGB(50, 125, 50)); + assertColor(parser, '#main { color: lch(46.41, 51.60, 139.50) }', 'lch', colorFrom256RGB(50, 125, 50)); }); test('hexDigit', function () { @@ -255,4 +271,10 @@ suite('CSS - Language Facts', () => { test('LABToRGB', function () { assertColorValue(colorFromLAB(46.41, -39.24, 33.51), colorFrom256RGB(50, 125, 50), 'lab(46.41, -39.24, 33.51)'); }); + test('labFromLCH', function () { + assertLABValue(labFromLCH(46.41, 51.60, 139.50), {l: 46.41, a: -39.24, b: 33.51, alpha: 1}); + }); + test('LCHtoRGB', function () { + assertColorValue(colorFromLCH(46.41, 51.60, 139.50), colorFrom256RGB(50, 125, 50), 'lch(46.41, 51.60, 139.50)'); + }); }); From 2c80fe391f59cd70a1238c00c468650d926a579d Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sat, 5 Nov 2022 21:28:34 -0700 Subject: [PATCH 08/12] Removed merge conflict --- src/languageFacts/colors.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/languageFacts/colors.ts b/src/languageFacts/colors.ts index 97aeae2b..1142a0b2 100644 --- a/src/languageFacts/colors.ts +++ b/src/languageFacts/colors.ts @@ -568,10 +568,6 @@ export function getColorValue(node: nodes.Node): Color | null { const l = getNumericValue(colorValues[0], 100.0); const c = getNumericValue(colorValues[1], 230.0); const h = getAngle(colorValues[2]); -<<<<<<< HEAD -======= - console.log(l, c, h, "LCCCH") ->>>>>>> 3b3145a899f9e8ffdb1a99e41890c87b6fa8368c return colorFromLCH(l*100, c * 230, h, alpha); } } catch (e) { From f814a01d8f44393192b85cfd64d9901a4ae9fa52 Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sun, 6 Nov 2022 01:25:38 -0700 Subject: [PATCH 09/12] Removed warnings and added reverse conversion functions --- src/languageFacts/colors.ts | 100 ++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 4 deletions(-) diff --git a/src/languageFacts/colors.ts b/src/languageFacts/colors.ts index 1142a0b2..ea785b40 100644 --- a/src/languageFacts/colors.ts +++ b/src/languageFacts/colors.ts @@ -418,7 +418,7 @@ export function xyzFromLAB(lab : LAB) : XYZ { y: 0, z: 0, alpha: lab.alpha ?? 1 - } + }; xyz["y"] = (lab.l + 16.0) / 116.0; xyz["x"] = (lab.a / 500.0) + xyz["y"]; xyz["z"] = xyz["y"] - (lab.b / 200.0); @@ -452,7 +452,7 @@ export function xyzToRGB(xyz: XYZ) : Color{ y: xyz.y / 100, z: xyz.z / 100, alpha: xyz.alpha ?? 1 - } + }; rgb.r = (new_xyz.x * 3.240479) + (new_xyz.y * -1.537150) + (new_xyz.z * -0.498535); rgb.g = (new_xyz.x * -0.969256) + (new_xyz.y * 1.875992) + (new_xyz.z * 0.041556); @@ -475,7 +475,98 @@ export function xyzToRGB(xyz: XYZ) : Color{ blue: rgb.b, green: rgb.g, alpha: rgb.alpha + }; +} + +export function RGBtoXYZ(rgba: Color): XYZ { + let r: number = rgba.red, + g: number = rgba.green, + b: number = rgba.blue; + + if (r > 0.04045) { + r = Math.pow((r + 0.055)/ 1.055, 2.4); + } else { + r = r / 12.92; } + if (g > 0.04045) { + g = Math.pow((g + 0.055) / 1.055 , 2.4); + } else { + g = g / 12.92; + } + if (b > 0.04045) { + b = Math.pow(( b + 0.055 ) / 1.055, 2.4); + } else { + b = b / 12.92; + } + r = r * 100; + g = g * 100; + b = b * 100; + + //Observer = 2°, Illuminant = D65 + const x = r * 0.4124 + g * 0.3576 + b * 0.1805; + const y = r * 0.2126 + g * 0.7152 + b * 0.0722; + const z = r * 0.0193 + g * 0.1192 + b * 0.9505; + return {x, y, z, alpha: rgba.alpha}; +} + +export function XYZtoLAB(xyz: XYZ, round: Boolean = true): LAB{ + const ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883; + + let x: number = xyz.x / ref_X, + y: number = xyz.y / ref_Y, + z: number = xyz.z / ref_Z; + + if (x > 0.008856) { + x = Math.pow(x, 1/3); + } else { + x = (7.787 * x) + (16/116); + } + if (y > 0.008856) { + y = Math.pow(y, 1/3); + } else { + y = (7.787 * y) + (16/116); + } + if (z > 0.008856) { + z = Math.pow(z, 1/3); + } else { + z = (7.787 * z) + (16/116); + } + const l: number = (116 * y) - 16, + a: number = 500 * (x - y), + b: number = 200 * (y - z); + if(round) { + return { + l: Math.round((l + Number.EPSILON) * 100) / 100, + a: Math.round((a + Number.EPSILON) * 100) / 100, + b: Math.round((b + Number.EPSILON) * 100) / 100, + alpha: xyz.alpha + }; + } else { + return { + l, a, b, + alpha: xyz.alpha + }; + } +} + +export function labFromColor(rgba: Color, round: Boolean = true): LAB{ + const xyz: XYZ = RGBtoXYZ(rgba); + const lab: LAB = XYZtoLAB(xyz, round); + return lab; +} +export function lchFromColor(rgba: Color): LCH{ + const lab: LAB = labFromColor(rgba, false); + const c: number = Math.sqrt(Math.pow(lab.a, 2) + Math.pow(lab.b, 2)); + let h:number = Math.atan2(lab.b, lab.a) * (180 / Math.PI); + while(h < 0) { + h = h + 360; + } + return { + l: Math.round((lab.l + Number.EPSILON) * 100) / 100, + c: Math.round((c + Number.EPSILON) * 100) / 100, + h: Math.round((h + Number.EPSILON) * 100) / 100, + alpha: lab.alpha + }; } export function colorFromLAB(l: number, a: number, b: number, alpha: number = 1.0): Color { @@ -484,7 +575,7 @@ export function colorFromLAB(l: number, a: number, b: number, alpha: number = 1. a, b, alpha - } + }; const xyz = xyzFromLAB(lab); const rgb = xyzToRGB(xyz); return { @@ -503,7 +594,7 @@ export function labFromLCH(l: number, c: number, h: number, alpha: number = 1.0) a: c * Math.cos(h * (Math.PI / 180)), b: c * Math.sin(h * ( Math.PI / 180)), alpha: alpha - } + }; } export function colorFromLCH(l: number, c: number, h: number, alpha: number = 1.0): Color { @@ -559,6 +650,7 @@ export function getColorValue(node: nodes.Node): Color | null { const b = getNumericValue(colorValues[2], 100.0); return colorFromHWB(h, w, b, alpha); } else if (name === 'lab') { + // Reference: https://mina86.com/2021/srgb-lab-lchab-conversions/ const l = getNumericValue(colorValues[0], 100.0); // Since these two values can be negative, a lower limit of -1 has been added const a = getNumericValue(colorValues[1], 125.0, -1); From d466e2141737497c879abad3a7b3ef37edd5c671 Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sun, 6 Nov 2022 01:26:12 -0700 Subject: [PATCH 10/12] Added color presentations for lab and lch to make picker work --- src/services/cssNavigation.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/services/cssNavigation.ts b/src/services/cssNavigation.ts index d0c64ae2..55417499 100644 --- a/src/services/cssNavigation.ts +++ b/src/services/cssNavigation.ts @@ -11,7 +11,7 @@ import { import * as nls from 'vscode-nls'; import * as nodes from '../parser/cssNodes'; import { Symbols } from '../parser/cssSymbolScope'; -import { getColorValue, hslFromColor, hwbFromColor } from '../languageFacts/facts'; +import { getColorValue, hslFromColor, hwbFromColor, labFromColor, lchFromColor } from '../languageFacts/facts'; import { startsWith } from '../utils/strings'; import { dirname, joinPath } from '../utils/resources'; @@ -327,6 +327,21 @@ export class CSSNavigation { } result.push({ label: label, textEdit: TextEdit.replace(range, label) }); + const lab = labFromColor(color); + if (lab.alpha === 1) { + label = `lab(${lab.l}% ${lab.a} ${lab.b})`; + } else { + label = `lab(${lab.l}% ${lab.a} ${lab.b} / ${lab.alpha})`; + } + result.push({ label: label, textEdit: TextEdit.replace(range, label) }); + + const lch = lchFromColor(color); + if (lab.alpha === 1) { + label = `lch(${lch.l}% ${lch.c} ${lch.h})`; + } else { + label = `lch(${lch.l}% ${lch.c} ${lch.h} / ${lch.alpha})`; + } + result.push({ label: label, textEdit: TextEdit.replace(range, label) }); return result; } From 8d98ec7733346a43b98421a545ce7e2b8b4bd703 Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Sun, 6 Nov 2022 01:26:27 -0700 Subject: [PATCH 11/12] Added new tests for lab and lch functions --- src/test/css/languageFacts.test.ts | 27 ++++++++++++++++++++++++++- src/test/css/navigation.test.ts | 4 ++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/test/css/languageFacts.test.ts b/src/test/css/languageFacts.test.ts index 7ad1b622..75f947ed 100644 --- a/src/test/css/languageFacts.test.ts +++ b/src/test/css/languageFacts.test.ts @@ -5,7 +5,7 @@ 'use strict'; import * as assert from 'assert'; -import { isColorValue, getColorValue, getBrowserLabel, colorFrom256RGB, colorFromHex, hexDigit, hslFromColor, HSLA, XYZ, LAB, xyzToRGB, xyzFromLAB, hwbFromColor, HWBA, colorFromHWB, colorFromHSL, colorFromLAB, labFromLCH, colorFromLCH } from '../../languageFacts/facts'; +import { isColorValue, getColorValue, getBrowserLabel, colorFrom256RGB, colorFromHex, hexDigit, hslFromColor, HSLA, XYZ, LAB, xyzToRGB, xyzFromLAB, hwbFromColor, HWBA, colorFromHWB, colorFromHSL, colorFromLAB, labFromLCH, colorFromLCH, labFromColor, RGBtoXYZ, lchFromColor, LCH } from '../../languageFacts/facts'; import { Parser } from '../../parser/cssParser'; import * as nodes from '../../parser/cssNodes'; import { TextDocument, Color } from '../../cssLanguageTypes'; @@ -97,6 +97,22 @@ function assertLABValue(actual: LAB, expected: LAB) { } assert.deepEqual(actual, expected); } + +function assertLCHValue(actual: LCH, expected: LCH) { + if (actual && expected) { + let lDiff = Math.abs(actual.l - expected.l); + let cDiff = Math.abs(actual.c - expected.c); + let hDiff = Math.abs(actual.h - expected.h); + let alphaDiff = 0; + if(actual.alpha && expected.alpha) { + alphaDiff = Math.abs((actual.alpha - expected.alpha) * 100); + } + if (lDiff < 1 && cDiff < 1 && hDiff < 1 && alphaDiff < 1) { + return; + } + } + assert.deepEqual(actual, expected); +} suite('CSS - Language Facts', () => { const cssDataManager = new CSSDataManager({ useDefaultDataProvider: true }); @@ -277,4 +293,13 @@ suite('CSS - Language Facts', () => { test('LCHtoRGB', function () { assertColorValue(colorFromLCH(46.41, 51.60, 139.50), colorFrom256RGB(50, 125, 50), 'lch(46.41, 51.60, 139.50)'); }); + test('labFromColor', function () { + assertLABValue(labFromColor(colorFrom256RGB(50, 125, 50)), {l: 46.41, a: -39.24, b: 33.51, alpha: 1}); + }); + test('RGBToXYZ', function () { + assertXYZValue(RGBtoXYZ(colorFrom256RGB(50, 125, 50)), { x: 9.22, y: 15.58, z: 5.54, alpha: 1 }); + }); + test('RGBToLCH', function () { + assertLCHValue(lchFromColor(colorFrom256RGB(50, 125, 50)), {l: 46.41, c: 51.60, h: 139.50}); + }); }); diff --git a/src/test/css/navigation.test.ts b/src/test/css/navigation.test.ts index 1fe95d72..c5ef26a3 100644 --- a/src/test/css/navigation.test.ts +++ b/src/test/css/navigation.test.ts @@ -459,8 +459,8 @@ suite('CSS - Navigation', () => { test('color presentations', function () { let ls = getCSSLS(); - assertColorPresentations(ls, colorFrom256RGB(255, 0, 0), 'rgb(255, 0, 0)', '#ff0000', 'hsl(0, 100%, 50%)', 'hwb(0 0% 0%)'); - assertColorPresentations(ls, colorFrom256RGB(77, 33, 111, 0.5), 'rgba(77, 33, 111, 0.5)', '#4d216f80', 'hsla(274, 54%, 28%, 0.5)', 'hwb(274 13% 56% / 0.5)'); + assertColorPresentations(ls, colorFrom256RGB(255, 0, 0), 'rgb(255, 0, 0)', '#ff0000', 'hsl(0, 100%, 50%)', 'hwb(0 0% 0%)', 'lab(53.23% 80.11 67.22)', 'lch(53.23% 104.58 40)'); + assertColorPresentations(ls, colorFrom256RGB(77, 33, 111, 0.5), 'rgba(77, 33, 111, 0.5)', '#4d216f80', 'hsla(274, 54%, 28%, 0.5)', 'hwb(274 13% 56% / 0.5)', 'lab(23.04% 35.9 -36.96 / 0.5)', 'lch(23.04% 51.53 314.16 / 0.5)'); }); }); }); From 13566d131b9ffc7c03815229a01629f1b525f2ce Mon Sep 17 00:00:00 2001 From: Gaurav Bhagchandani Date: Wed, 9 Nov 2022 09:24:33 -0800 Subject: [PATCH 12/12] Ran the built-in formatter and replaced const where appropriate --- src/languageFacts/colors.ts | 130 ++++++++++++++--------------- src/parser/cssScanner.ts | 4 +- src/test/css/languageFacts.test.ts | 34 ++++---- 3 files changed, 84 insertions(+), 84 deletions(-) diff --git a/src/languageFacts/colors.ts b/src/languageFacts/colors.ts index 0ccc2bf9..2de2fa29 100644 --- a/src/languageFacts/colors.ts +++ b/src/languageFacts/colors.ts @@ -15,8 +15,8 @@ export const colorFunctions = [ { func: 'hsl($hue, $saturation, $lightness)', desc: l10n.t('Creates a Color from hue, saturation, and lightness values.') }, { func: 'hsla($hue, $saturation, $lightness, $alpha)', desc: l10n.t('Creates a Color from hue, saturation, lightness, and alpha values.') }, { func: 'hwb($hue $white $black)', desc: l10n.t('Creates a Color from hue, white and black.') }, - { func: 'lab($lightness $channel_a $channel_b $alpha)', desc: l10n.t('css.builtin.lab', 'Creates a Color from Lightness, Channel a, Channel b and alpha values.') }, - { func: 'lch($lightness $chrome $hue $alpha)', desc: l10n.t('css.builtin.lab', 'Creates a Color from Lightness, Chroma, Hue and alpha values.') + { func: 'lab($lightness $channel_a $channel_b $alpha)', desc: l10n.t('css.builtin.lab', 'Creates a Color from Lightness, Channel a, Channel b and alpha values.') }, + { func: 'lch($lightness $chrome $hue $alpha)', desc: l10n.t('css.builtin.lab', 'Creates a Color from Lightness, Chroma, Hue and alpha values.') } ]; export const colors: { [name: string]: string } = { @@ -368,7 +368,7 @@ export function hslFromColor(rgba: Color): HSLA { export function colorFromHWB(hue: number, white: number, black: number, alpha: number = 1.0): Color { if (white + black >= 1) { const gray = white / (white + black); - return {red: gray, green: gray, blue: gray, alpha}; + return { red: gray, green: gray, blue: gray, alpha }; } const rgb = colorFromHSL(hue, 1, 0.5, alpha); @@ -411,8 +411,8 @@ export interface XYZ { x: number; y: number; z: number; alpha: number; } export interface RGB { r: number; g: number; b: number; alpha: number; } -export function xyzFromLAB(lab : LAB) : XYZ { - let xyz: XYZ = { +export function xyzFromLAB(lab: LAB): XYZ { + const xyz: XYZ = { x: 0, y: 0, z: 0, @@ -423,12 +423,12 @@ export function xyzFromLAB(lab : LAB) : XYZ { xyz["z"] = xyz["y"] - (lab.b / 200.0); let key: keyof XYZ; - for (key in xyz){ + for (key in xyz) { let pow = xyz[key] * xyz[key] * xyz[key]; - if (pow > 0.008856){ + if (pow > 0.008856) { xyz[key] = pow; } else { - xyz[key] = (xyz[key]- 16.0 / 116.0) / 7.787; + xyz[key] = (xyz[key] - 16.0 / 116.0) / 7.787; } } @@ -438,15 +438,15 @@ export function xyzFromLAB(lab : LAB) : XYZ { return xyz; } -export function xyzToRGB(xyz: XYZ) : Color{ - let rgb: RGB = { +export function xyzToRGB(xyz: XYZ): Color { + const rgb: RGB = { r: 0, g: 0, b: 0, alpha: xyz.alpha }; - let new_xyz: XYZ = { + const new_xyz: XYZ = { x: xyz.x / 100, y: xyz.y / 100, z: xyz.z / 100, @@ -454,11 +454,11 @@ export function xyzToRGB(xyz: XYZ) : Color{ }; rgb.r = (new_xyz.x * 3.240479) + (new_xyz.y * -1.537150) + (new_xyz.z * -0.498535); - rgb.g = (new_xyz.x * -0.969256) + (new_xyz.y * 1.875992) + (new_xyz.z * 0.041556); - rgb.b = (new_xyz.x * 0.055648) + (new_xyz.y * -0.204043) + (new_xyz.z * 1.057311); + rgb.g = (new_xyz.x * -0.969256) + (new_xyz.y * 1.875992) + (new_xyz.z * 0.041556); + rgb.b = (new_xyz.x * 0.055648) + (new_xyz.y * -0.204043) + (new_xyz.z * 1.057311); let key: keyof RGB; - for(key in rgb) { + for (key in rgb) { if (rgb[key] > 0.0031308) { rgb[key] = (1.055 * Math.pow(rgb[key], (1.0 / 2.4))) - 0.055; } else { @@ -468,7 +468,7 @@ export function xyzToRGB(xyz: XYZ) : Color{ rgb.r = Math.round(rgb.r * 255.0); rgb.g = Math.round(rgb.g * 255.0); rgb.b = Math.round(rgb.b * 255.0); - + return { red: rgb.r, blue: rgb.b, @@ -479,85 +479,85 @@ export function xyzToRGB(xyz: XYZ) : Color{ export function RGBtoXYZ(rgba: Color): XYZ { let r: number = rgba.red, - g: number = rgba.green, - b: number = rgba.blue; + g: number = rgba.green, + b: number = rgba.blue; - if (r > 0.04045) { - r = Math.pow((r + 0.055)/ 1.055, 2.4); + if (r > 0.04045) { + r = Math.pow((r + 0.055) / 1.055, 2.4); } else { r = r / 12.92; } - if (g > 0.04045) { - g = Math.pow((g + 0.055) / 1.055 , 2.4); + if (g > 0.04045) { + g = Math.pow((g + 0.055) / 1.055, 2.4); } else { g = g / 12.92; } - if (b > 0.04045) { - b = Math.pow(( b + 0.055 ) / 1.055, 2.4); + if (b > 0.04045) { + b = Math.pow((b + 0.055) / 1.055, 2.4); } else { b = b / 12.92; } - r = r * 100; - g = g * 100; - b = b * 100; - - //Observer = 2°, Illuminant = D65 - const x = r * 0.4124 + g * 0.3576 + b * 0.1805; - const y = r * 0.2126 + g * 0.7152 + b * 0.0722; - const z = r * 0.0193 + g * 0.1192 + b * 0.9505; - return {x, y, z, alpha: rgba.alpha}; + r = r * 100; + g = g * 100; + b = b * 100; + + //Observer = 2°, Illuminant = D65 + const x = r * 0.4124 + g * 0.3576 + b * 0.1805; + const y = r * 0.2126 + g * 0.7152 + b * 0.0722; + const z = r * 0.0193 + g * 0.1192 + b * 0.9505; + return { x, y, z, alpha: rgba.alpha }; } -export function XYZtoLAB(xyz: XYZ, round: Boolean = true): LAB{ - const ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883; +export function XYZtoLAB(xyz: XYZ, round: Boolean = true): LAB { + const ref_X = 95.047, ref_Y = 100.000, ref_Z = 108.883; - let x: number = xyz.x / ref_X, - y: number = xyz.y / ref_Y, - z: number = xyz.z / ref_Z; + let x: number = xyz.x / ref_X, + y: number = xyz.y / ref_Y, + z: number = xyz.z / ref_Z; - if (x > 0.008856) { - x = Math.pow(x, 1/3); + if (x > 0.008856) { + x = Math.pow(x, 1 / 3); } else { - x = (7.787 * x) + (16/116); + x = (7.787 * x) + (16 / 116); } - if (y > 0.008856) { - y = Math.pow(y, 1/3); + if (y > 0.008856) { + y = Math.pow(y, 1 / 3); } else { - y = (7.787 * y) + (16/116); + y = (7.787 * y) + (16 / 116); } - if (z > 0.008856) { - z = Math.pow(z, 1/3); + if (z > 0.008856) { + z = Math.pow(z, 1 / 3); } else { - z = (7.787 * z) + (16/116); + z = (7.787 * z) + (16 / 116); } - const l: number = (116 * y) - 16, - a: number = 500 * (x - y), - b: number = 200 * (y - z); - if(round) { + const l: number = (116 * y) - 16, + a: number = 500 * (x - y), + b: number = 200 * (y - z); + if (round) { return { - l: Math.round((l + Number.EPSILON) * 100) / 100, - a: Math.round((a + Number.EPSILON) * 100) / 100, - b: Math.round((b + Number.EPSILON) * 100) / 100, + l: Math.round((l + Number.EPSILON) * 100) / 100, + a: Math.round((a + Number.EPSILON) * 100) / 100, + b: Math.round((b + Number.EPSILON) * 100) / 100, alpha: xyz.alpha }; } else { return { - l, a, b, + l, a, b, alpha: xyz.alpha }; } } -export function labFromColor(rgba: Color, round: Boolean = true): LAB{ +export function labFromColor(rgba: Color, round: Boolean = true): LAB { const xyz: XYZ = RGBtoXYZ(rgba); const lab: LAB = XYZtoLAB(xyz, round); return lab; } -export function lchFromColor(rgba: Color): LCH{ +export function lchFromColor(rgba: Color): LCH { const lab: LAB = labFromColor(rgba, false); const c: number = Math.sqrt(Math.pow(lab.a, 2) + Math.pow(lab.b, 2)); - let h:number = Math.atan2(lab.b, lab.a) * (180 / Math.PI); - while(h < 0) { + let h: number = Math.atan2(lab.b, lab.a) * (180 / Math.PI); + while (h < 0) { h = h + 360; } return { @@ -569,7 +569,7 @@ export function lchFromColor(rgba: Color): LCH{ } export function colorFromLAB(l: number, a: number, b: number, alpha: number = 1.0): Color { - const lab : LAB = { + const lab: LAB = { l, a, b, @@ -587,11 +587,11 @@ export function colorFromLAB(l: number, a: number, b: number, alpha: number = 1. export interface LAB { l: number; a: number; b: number; alpha?: number; } -export function labFromLCH(l: number, c: number, h: number, alpha: number = 1.0) : LAB { +export function labFromLCH(l: number, c: number, h: number, alpha: number = 1.0): LAB { return { l: l, a: c * Math.cos(h * (Math.PI / 180)), - b: c * Math.sin(h * ( Math.PI / 180)), + b: c * Math.sin(h * (Math.PI / 180)), alpha: alpha }; } @@ -620,11 +620,11 @@ export function getColorValue(node: nodes.Node): Color | null { if (lastValue instanceof nodes.BinaryExpression) { const left = lastValue.getLeft(), right = lastValue.getRight(), operator = lastValue.getOperator(); if (left && right && operator && operator.matches('/')) { - colorValues = [ colorValues[0], colorValues[1], left, right ]; + colorValues = [colorValues[0], colorValues[1], left, right]; } } } - } + } } if (!name || colorValues.length < 3 || colorValues.length > 4) { return null; @@ -654,12 +654,12 @@ export function getColorValue(node: nodes.Node): Color | null { // Since these two values can be negative, a lower limit of -1 has been added const a = getNumericValue(colorValues[1], 125.0, -1); const b = getNumericValue(colorValues[2], 125.0, -1); - return colorFromLAB(l*100, a*125, b*125, alpha); + return colorFromLAB(l * 100, a * 125, b * 125, alpha); } else if (name === 'lch') { const l = getNumericValue(colorValues[0], 100.0); const c = getNumericValue(colorValues[1], 230.0); const h = getAngle(colorValues[2]); - return colorFromLCH(l*100, c * 230, h, alpha); + return colorFromLCH(l * 100, c * 230, h, alpha); } } catch (e) { // parse error on numeric value diff --git a/src/parser/cssScanner.ts b/src/parser/cssScanner.ts index 5bb916c4..be6b9c2e 100644 --- a/src/parser/cssScanner.ts +++ b/src/parser/cssScanner.ts @@ -440,12 +440,12 @@ export class Scanner { } private _number(): boolean { - let npeek = 0, ch: number, next_ch: number; + let npeek = 0, ch: number; if (this.stream.peekChar() === _DOT) { npeek = 1; } ch = this.stream.peekChar(npeek); - next_ch = this.stream.peekChar(npeek + 1); + const next_ch: number = this.stream.peekChar(npeek + 1); if ((ch >= _0 && ch <= _9) || (ch === _MIN && next_ch >= _0 && next_ch <= _9)) { this.stream.advance(npeek + 1); this.stream.advanceWhileChar((ch) => { diff --git a/src/test/css/languageFacts.test.ts b/src/test/css/languageFacts.test.ts index 75f947ed..5136da24 100644 --- a/src/test/css/languageFacts.test.ts +++ b/src/test/css/languageFacts.test.ts @@ -71,10 +71,10 @@ function assertHWBValue(actual: HWBA, expected: HWBA) { function assertXYZValue(actual: XYZ, expected: XYZ) { if (actual && expected) { - let xDiff = Math.abs(actual.x - expected.x); - let yDiff = Math.abs(actual.y - expected.y); - let zDiff = Math.abs(actual.z - expected.z); - let aDiff = Math.abs((actual.alpha - expected.alpha) * 100); + const xDiff = Math.abs(actual.x - expected.x); + const yDiff = Math.abs(actual.y - expected.y); + const zDiff = Math.abs(actual.z - expected.z); + const aDiff = Math.abs((actual.alpha - expected.alpha) * 100); if (xDiff < 1 && yDiff < 1 && zDiff < 1 && aDiff < 1) { return; } @@ -84,11 +84,11 @@ function assertXYZValue(actual: XYZ, expected: XYZ) { function assertLABValue(actual: LAB, expected: LAB) { if (actual && expected) { - let lDiff = Math.abs(actual.l - expected.l); - let aDiff = Math.abs(actual.a - expected.a); - let bDiff = Math.abs(actual.b - expected.b); + const lDiff = Math.abs(actual.l - expected.l); + const aDiff = Math.abs(actual.a - expected.a); + const bDiff = Math.abs(actual.b - expected.b); let alphaDiff = 0; - if(actual.alpha && expected.alpha) { + if (actual.alpha && expected.alpha) { alphaDiff = Math.abs((actual.alpha - expected.alpha) * 100); } if (lDiff < 1 && aDiff < 1 && bDiff < 1 && alphaDiff < 1) { @@ -100,11 +100,11 @@ function assertLABValue(actual: LAB, expected: LAB) { function assertLCHValue(actual: LCH, expected: LCH) { if (actual && expected) { - let lDiff = Math.abs(actual.l - expected.l); - let cDiff = Math.abs(actual.c - expected.c); - let hDiff = Math.abs(actual.h - expected.h); + const lDiff = Math.abs(actual.l - expected.l); + const cDiff = Math.abs(actual.c - expected.c); + const hDiff = Math.abs(actual.h - expected.h); let alphaDiff = 0; - if(actual.alpha && expected.alpha) { + if (actual.alpha && expected.alpha) { alphaDiff = Math.abs((actual.alpha - expected.alpha) * 100); } if (lDiff < 1 && cDiff < 1 && hDiff < 1 && alphaDiff < 1) { @@ -276,9 +276,9 @@ suite('CSS - Language Facts', () => { assertColorValue(colorFromHSL(118, 0.98, 0.5), colorFrom256RGB(11, 252, 3), 'hsl(118, 98%, 50%)'); assertColorValue(colorFromHSL(120, 0.83, 0.95), colorFrom256RGB(232, 253, 232), 'hsl(120, 83%, 95%)'); }); - + test('xyzFromLAB', function () { - assertXYZValue(xyzFromLAB({l: 46.41, a: -39.24, b: 33.51}), { x: 9.22, y: 15.58, z: 5.54, alpha: 1 }); + assertXYZValue(xyzFromLAB({ l: 46.41, a: -39.24, b: 33.51 }), { x: 9.22, y: 15.58, z: 5.54, alpha: 1 }); }); test('xyzToRGB', function () { @@ -288,18 +288,18 @@ suite('CSS - Language Facts', () => { assertColorValue(colorFromLAB(46.41, -39.24, 33.51), colorFrom256RGB(50, 125, 50), 'lab(46.41, -39.24, 33.51)'); }); test('labFromLCH', function () { - assertLABValue(labFromLCH(46.41, 51.60, 139.50), {l: 46.41, a: -39.24, b: 33.51, alpha: 1}); + assertLABValue(labFromLCH(46.41, 51.60, 139.50), { l: 46.41, a: -39.24, b: 33.51, alpha: 1 }); }); test('LCHtoRGB', function () { assertColorValue(colorFromLCH(46.41, 51.60, 139.50), colorFrom256RGB(50, 125, 50), 'lch(46.41, 51.60, 139.50)'); }); test('labFromColor', function () { - assertLABValue(labFromColor(colorFrom256RGB(50, 125, 50)), {l: 46.41, a: -39.24, b: 33.51, alpha: 1}); + assertLABValue(labFromColor(colorFrom256RGB(50, 125, 50)), { l: 46.41, a: -39.24, b: 33.51, alpha: 1 }); }); test('RGBToXYZ', function () { assertXYZValue(RGBtoXYZ(colorFrom256RGB(50, 125, 50)), { x: 9.22, y: 15.58, z: 5.54, alpha: 1 }); }); test('RGBToLCH', function () { - assertLCHValue(lchFromColor(colorFrom256RGB(50, 125, 50)), {l: 46.41, c: 51.60, h: 139.50}); + assertLCHValue(lchFromColor(colorFrom256RGB(50, 125, 50)), { l: 46.41, c: 51.60, h: 139.50 }); }); });