Skip to content

Commit

Permalink
feat(Color) Regex and color code cleanup and new color format support (
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur authored May 18, 2023
1 parent f2090db commit 23999dc
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 335 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [next]

- feat(Color) Improve regex for new standards, more documentation and code cleanup [#8916](https://github.com/fabricjs/fabric.js/pull/8916)
- fix(TS): extending canvas and object event types (`type` => `interface`) [#8926](https://github.com/fabricjs/fabric.js/pull/8926)
- chore(build) simple deps update [#8929](https://github.com/fabricjs/fabric.js/pull/8929)
- fix(Canvas): sync cleanup of dom elements in dispose [#8903](https://github.com/fabricjs/fabric.js/pull/8903)
Expand Down
170 changes: 51 additions & 119 deletions src/color/Color.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { ColorNameMap } from './color_map';
import { reHSLa, reHex, reRGBa } from './constants';
import type { TRGBAColorSource, TColorArg, TRGBColorSource } from './typedefs';
import { hue2rgb, hexify } from './util';
import type { TRGBAColorSource, TColorArg } from './typedefs';
import {
hue2rgb,
hexify,
rgb2Hsl,
fromAlphaToFloat,
greyAverage,
} from './util';

/**
* @class Color common color operations
Expand Down Expand Up @@ -47,46 +53,6 @@ export class Color {
([0, 0, 0, 1] as TRGBAColorSource);
}

/**
* Adapted from {@link https://gist.github.com/mjackson/5311256 https://gist.github.com/mjackson}
* @private
* @param {Number} r Red color value
* @param {Number} g Green color value
* @param {Number} b Blue color value
* @return {TRGBColorSource} Hsl color
*/
_rgbToHsl(r: number, g: number, b: number): TRGBColorSource {
r /= 255;
g /= 255;
b /= 255;
const maxValue = Math.max(r, g, b),
minValue = Math.min(r, g, b);

let h!: number, s: number;
const l = (maxValue + minValue) / 2;

if (maxValue === minValue) {
h = s = 0; // achromatic
} else {
const d = maxValue - minValue;
s = l > 0.5 ? d / (2 - maxValue - minValue) : d / (maxValue + minValue);
switch (maxValue) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}

return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
}

/**
* Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
* @return {TRGBAColorSource}
Expand All @@ -108,57 +74,52 @@ export class Color {
* @return {String} ex: rgb(0-255,0-255,0-255)
*/
toRgb() {
const source = this.getSource();
return `rgb(${source[0]},${source[1]},${source[2]})`;
const [r, g, b] = this.getSource();
return `rgb(${r},${g},${b})`;
}

/**
* Returns color representation in RGBA format
* @return {String} ex: rgba(0-255,0-255,0-255,0-1)
*/
toRgba() {
const source = this.getSource();
return `rgba(${source[0]},${source[1]},${source[2]},${source[3]})`;
return `rgba(${this.getSource().join(',')})`;
}

/**
* Returns color representation in HSL format
* @return {String} ex: hsl(0-360,0%-100%,0%-100%)
*/
toHsl() {
const source = this.getSource(),
hsl = this._rgbToHsl(source[0], source[1], source[2]);

return `hsl(${hsl[0]},${hsl[1]}%,${hsl[2]}%)`;
const [h, s, l] = rgb2Hsl(...this.getSource());
return `hsl(${h},${s}%,${l}%)`;
}

/**
* Returns color representation in HSLA format
* @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
*/
toHsla() {
const source = this.getSource(),
hsl = this._rgbToHsl(source[0], source[1], source[2]);

return `hsla(${hsl[0]},${hsl[1]}%,${hsl[2]}%,${source[3]})`;
const [h, s, l, a] = rgb2Hsl(...this.getSource());
return `hsla(${h},${s}%,${l}%,${a})`;
}

/**
* Returns color representation in HEX format
* @return {String} ex: FF5555
*/
toHex() {
const [r, g, b] = this.getSource();
return `${hexify(r)}${hexify(g)}${hexify(b)}`;
const fullHex = this.toHexa();
return fullHex.slice(0, 6);
}

/**
* Returns color representation in HEXA format
* @return {String} ex: FF5555CC
*/
toHexa() {
const source = this.getSource();
return `${this.toHex()}${hexify(Math.round(source[3] * 255))}`;
const [r, g, b, a] = this.getSource();
return `${hexify(r)}${hexify(g)}${hexify(b)}${hexify(Math.round(a * 255))}`;
}

/**
Expand All @@ -175,9 +136,7 @@ export class Color {
* @return {Color} thisArg
*/
setAlpha(alpha: number) {
const source = this.getSource();
source[3] = alpha;
this.setSource(source);
this._source[3] = alpha;
return this;
}

Expand All @@ -186,13 +145,7 @@ export class Color {
* @return {Color} thisArg
*/
toGrayscale() {
const source = this.getSource(),
average = parseInt(
(source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0),
10
),
currentAlpha = source[3];
this.setSource([average, average, average, currentAlpha]);
this.setSource(greyAverage(this.getSource()));
return this;
}

Expand All @@ -202,14 +155,9 @@ export class Color {
* @return {Color} thisArg
*/
toBlackWhite(threshold: number) {
const source = this.getSource(),
currentAlpha = source[3];
let average = Math.round(
source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11
);

average = average < (threshold || 127) ? 0 : 255;
this.setSource([average, average, average, currentAlpha]);
const [average, , , a] = greyAverage(this.getSource()),
bOrW = average < (threshold || 127) ? 0 : 255;
this.setSource([bOrW, bOrW, bOrW, a]);
return this;
}

Expand All @@ -223,14 +171,14 @@ export class Color {
otherColor = new Color(otherColor);
}

const [r, g, b, alpha] = this.getSource(),
const source = this.getSource(),
otherAlpha = 0.5,
otherSource = otherColor.getSource(),
[R, G, B] = [r, g, b].map((value, index) =>
[R, G, B] = source.map((value, index) =>
Math.round(value * (1 - otherAlpha) + otherSource[index] * otherAlpha)
);

this.setSource([R, G, B, alpha]);
this.setSource([R, G, B, source[3]]);
return this;
}

Expand Down Expand Up @@ -263,19 +211,15 @@ export class Color {
* @return {TRGBAColorSource | undefined} source
*/
static sourceFromRgb(color: string): TRGBAColorSource | undefined {
const match = color.match(reRGBa);
const match = color.match(reRGBa());
if (match) {
const r =
(parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1)) *
(/%$/.test(match[1]) ? 255 : 1),
g =
(parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1)) *
(/%$/.test(match[2]) ? 255 : 1),
b =
(parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1)) *
(/%$/.test(match[3]) ? 255 : 1);

return [r, g, b, match[4] ? parseFloat(match[4]) : 1];
const [r, g, b] = match.slice(1, 4).map((value) => {
const parsedValue = parseFloat(value);
return value.endsWith('%')
? Math.round(parsedValue * 2.55)
: parsedValue;
});
return [r, g, b, fromAlphaToFloat(match[4])];
}
}

Expand Down Expand Up @@ -310,14 +254,14 @@ export class Color {
* @see http://http://www.w3.org/TR/css3-color/#hsl-color
*/
static sourceFromHsl(color: string): TRGBAColorSource | undefined {
const match = color.match(reHSLa);
const match = color.match(reHSLa());
if (!match) {
return;
}

const h = (((parseFloat(match[1]) % 360) + 360) % 360) / 360,
s = parseFloat(match[2]) / (/%$/.test(match[2]) ? 100 : 1),
l = parseFloat(match[3]) / (/%$/.test(match[3]) ? 100 : 1);
s = parseFloat(match[2]) / 100,
l = parseFloat(match[3]) / 100;
let r: number, g: number, b: number;

if (s === 0) {
Expand All @@ -335,7 +279,7 @@ export class Color {
Math.round(r * 255),
Math.round(g * 255),
Math.round(b * 255),
match[4] ? parseFloat(match[4]) : 1,
fromAlphaToFloat(match[4]),
];
}

Expand All @@ -358,31 +302,19 @@ export class Color {
* @return {TRGBAColorSource | undefined} source
*/
static sourceFromHex(color: string): TRGBAColorSource | undefined {
if (color.match(reHex)) {
if (color.match(reHex())) {
const value = color.slice(color.indexOf('#') + 1),
isShortNotation = value.length === 3 || value.length === 4,
isRGBa = value.length === 8 || value.length === 4,
r = isShortNotation
? value.charAt(0) + value.charAt(0)
: value.substring(0, 2),
g = isShortNotation
? value.charAt(1) + value.charAt(1)
: value.substring(2, 4),
b = isShortNotation
? value.charAt(2) + value.charAt(2)
: value.substring(4, 6),
a = isRGBa
? isShortNotation
? value.charAt(3) + value.charAt(3)
: value.substring(6, 8)
: 'FF';

return [
parseInt(r, 16),
parseInt(g, 16),
parseInt(b, 16),
parseFloat((parseInt(a, 16) / 255).toFixed(2)),
];
isShortNotation = value.length <= 4;
let expandedValue: string[];
if (isShortNotation) {
expandedValue = value.split('').map((hex) => hex + hex);
} else {
expandedValue = value.match(/.{2}/g)!;
}
const [r, g, b, a = 255] = expandedValue.map((hexCouple) =>
parseInt(hexCouple, 16)
);
return [r, g, b, a / 255];
}
}
}
26 changes: 13 additions & 13 deletions src/color/color_map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
export const ColorNameMap = {
aliceblue: '#F0F8FF',
antiquewhite: '#FAEBD7',
aqua: '#00FFFF',
aqua: '#0FF',
aquamarine: '#7FFFD4',
azure: '#F0FFFF',
beige: '#F5F5DC',
bisque: '#FFE4C4',
black: '#000000',
black: '#000',
blanchedalmond: '#FFEBCD',
blue: '#0000FF',
blue: '#00F',
blueviolet: '#8A2BE2',
brown: '#A52A2A',
burlywood: '#DEB887',
Expand All @@ -23,7 +23,7 @@ export const ColorNameMap = {
cornflowerblue: '#6495ED',
cornsilk: '#FFF8DC',
crimson: '#DC143C',
cyan: '#00FFFF',
cyan: '#0FF',
darkblue: '#00008B',
darkcyan: '#008B8B',
darkgoldenrod: '#B8860B',
Expand Down Expand Up @@ -51,7 +51,7 @@ export const ColorNameMap = {
firebrick: '#B22222',
floralwhite: '#FFFAF0',
forestgreen: '#228B22',
fuchsia: '#FF00FF',
fuchsia: '#F0F',
gainsboro: '#DCDCDC',
ghostwhite: '#F8F8FF',
gold: '#FFD700',
Expand Down Expand Up @@ -81,14 +81,14 @@ export const ColorNameMap = {
lightsalmon: '#FFA07A',
lightseagreen: '#20B2AA',
lightskyblue: '#87CEFA',
lightslategray: '#778899',
lightslategrey: '#778899',
lightslategray: '#789',
lightslategrey: '#789',
lightsteelblue: '#B0C4DE',
lightyellow: '#FFFFE0',
lime: '#00FF00',
lime: '#0F0',
limegreen: '#32CD32',
linen: '#FAF0E6',
magenta: '#FF00FF',
magenta: '#F0F',
maroon: '#800000',
mediumaquamarine: '#66CDAA',
mediumblue: '#0000CD',
Expand Down Expand Up @@ -122,8 +122,8 @@ export const ColorNameMap = {
plum: '#DDA0DD',
powderblue: '#B0E0E6',
purple: '#800080',
rebeccapurple: '#663399',
red: '#FF0000',
rebeccapurple: '#639',
red: '#F00',
rosybrown: '#BC8F8F',
royalblue: '#4169E1',
saddlebrown: '#8B4513',
Expand All @@ -147,8 +147,8 @@ export const ColorNameMap = {
turquoise: '#40E0D0',
violet: '#EE82EE',
wheat: '#F5DEB3',
white: '#FFFFFF',
white: '#FFF',
whitesmoke: '#F5F5F5',
yellow: '#FFFF00',
yellow: '#FF0',
yellowgreen: '#9ACD32',
};
Loading

0 comments on commit 23999dc

Please sign in to comment.