@ngageoint/color-js
Version:
Color Javascript
700 lines • 22.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ColorUtils = void 0;
/**
* Color utilities with support for hex, RBG, arithmetic RBG, HSL, and integer
* colors
*
* @author osbornb
*/
class ColorUtils {
/**
* Convert the hex color values to a hex color, shorthanded when possible
*
* @param red
* red hex color in format RR or R
* @param green
* green hex color in format GG or G
* @param blue
* blue hex color in format BB or B
*
* @return hex color in format #RGB or #RRGGBB
*/
static toColorShorthand(red, green, blue) {
return ColorUtils.shorthandHex(ColorUtils.toColor(red, green, blue));
}
/**
* Convert the hex color values to a hex color including an opaque alpha
* value of FF or F, shorthanded when possible
*
* @param red
* red hex color in format RR or R
* @param green
* green hex color in format GG or G
* @param blue
* blue hex color in format BB or B
*
* @return hex color in format #ARGB or #AARRGGBB
*/
static toColorShorthandWithDefaultAlpha(red, green, blue) {
return ColorUtils.shorthandHex(ColorUtils.toColorWithDefaultAlpha(red, green, blue));
}
/**
* Convert the hex color values to a hex color, shorthanded when possible
*
* @param red
* red hex color in format RR or R
* @param green
* green hex color in format GG or G
* @param blue
* blue hex color in format BB or B
* @param alpha
* alpha hex color in format AA or A, null to not include alpha
*
* @return hex color in format #ARGB, #RGB, #AARRGGBB, or #RRGGBB
*/
static toColorShorthandWithAlpha(red, green, blue, alpha) {
return ColorUtils.shorthandHex(ColorUtils.toColorWithAlpha(red, green, blue, alpha));
}
/**
* Convert the RBG values to a color integer or hex color values to a hex color
*
* @param red
* red integer color inclusively between 0 and 255 or red hex color in format RR or R
* @param green
* green integer color inclusively between 0 and 255 or green hex color in format GG or G
* @param blue
* blue integer color inclusively between 0 and 255 or blue hex color in format BB or B
*
* @return integer color or hex color in format #RRGGBB
*/
static toColor(red, green, blue) {
let color;
if (typeof red === 'number') {
color = ColorUtils.toColorWithAlpha(red, green, blue, -1);
}
else {
color = ColorUtils.toColorWithAlpha(red, green, blue, null);
}
return color;
}
/**
* Convert the RBG values to a color integer including an opaque alpha value
* of 255 or Convert the hex color values to a hex color including an opaque alpha
* value of FF
*
* @param red
* red integer color inclusively between 0 and 255 or red hex color in format RR or R
* @param green
* green integer color inclusively between 0 and 255 or green hex color in format GG or G
* @param blue
* blue integer color inclusively between 0 and 255 or blue hex color in format BB or B
*
* @return integer color or hex color in format #AARRGGBB
*/
static toColorWithDefaultAlpha(red, green, blue) {
let color;
if (typeof red === 'number') {
color = ColorUtils.toColorWithAlpha(red, green, blue, 255);
}
else {
let defaultAlpha = 'FF';
if (red !== null && red.length > 0 && red.charAt(0).toLowerCase() === red.charAt(0)) {
defaultAlpha = defaultAlpha.toLowerCase();
}
color = ColorUtils.toColorWithAlpha(red, green, blue, defaultAlpha);
}
return color;
}
/**
* Convert the RBGA values to a color integer or Convert the hex color values to a hex color
*
* @param red
* red integer color inclusively between 0 and 255 or red hex color in format RR or R
* @param green
* green integer color inclusively between 0 and 255 or green hex color in format GG or G
* @param blue
* blue integer color inclusively between 0 and 255 or blue hex color in format BB or B or alpha hex color in format AA or A, null to not include alpha
* @param alpha
* alpha integer color inclusively between 0 and 255, -1 to not
* include alpha
*
* @return integer color or hex color in format #AARRGGBB or #RRGGBB
*/
static toColorWithAlpha(red, green, blue, alpha) {
let color = '';
if (typeof red === 'number' && typeof green === 'number' && typeof blue === 'number') {
ColorUtils.validateRGB(red);
ColorUtils.validateRGB(green);
ColorUtils.validateRGB(blue);
color = ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff);
if (typeof alpha === 'number' && alpha !== -1) {
ColorUtils.validateRGB(alpha);
color = ((alpha & 0xff) << 24) | color;
}
}
else if (typeof red === 'string' && typeof green === 'string' && typeof blue === 'string') {
ColorUtils.validateHexSingle(red);
ColorUtils.validateHexSingle(green);
ColorUtils.validateHexSingle(blue);
color = '#';
if (alpha != null && typeof alpha === 'string') {
color += ColorUtils.expandShorthandHexSingle(alpha);
}
color += ColorUtils.expandShorthandHexSingle(red);
color += ColorUtils.expandShorthandHexSingle(green);
color += ColorUtils.expandShorthandHexSingle(blue);
}
return color;
}
/**
* Convert the RGB integer to a hex single color
*
* @param color
* integer color inclusively between 0 and 255 or float color inclusively between 0.0 and 1.0
* @return hex single color in format FF
*/
static toHex(color) {
let hex;
if (!Number.isInteger(color) || color === 1 || color === 0) {
color = ColorUtils.toRGB(color);
}
ColorUtils.validateRGB(color);
hex = color.toString(16).toUpperCase();
if (hex.length === 1) {
hex = '0' + hex;
}
return hex;
}
/**
* Convert red, green, and blue arithmetic values to HSL (hue, saturation,
* lightness) values
*
* @param red
* red color inclusively between 0.0 and 1.0 or between 0 and 255
* @param green
* green color inclusively between 0.0 and 1.0 or between 0 and 255
* @param blue
* blue color inclusively between 0.0 and 1.0 or between 0 and 255
* @return HSL array where: 0 = hue, 1 = saturation, 2 = lightness
*/
static toHSL(red, green, blue) {
if (Number.isInteger(red) && red !== 0 && red !== 1) {
red = ColorUtils.toArithmeticRGB(red);
}
if (Number.isInteger(green) && green !== 0 && green !== 1) {
green = ColorUtils.toArithmeticRGB(green);
}
if (Number.isInteger(blue) && blue !== 0 && blue !== 1) {
blue = ColorUtils.toArithmeticRGB(blue);
}
ColorUtils.validateArithmeticRGB(red);
ColorUtils.validateArithmeticRGB(green);
ColorUtils.validateArithmeticRGB(blue);
const min = Math.min(Math.min(red, green), blue);
const max = Math.max(Math.max(red, green), blue);
const range = max - min;
let hue = 0.0;
if (range > 0.0) {
if (red >= green && red >= blue) {
hue = (green - blue) / range;
}
else if (green >= blue) {
hue = 2 + (blue - red) / range;
}
else {
hue = 4 + (red - green) / range;
}
}
hue *= 60.0;
if (hue < 0) {
hue += 360.0;
}
const sum = min + max;
const lightness = sum / 2.0;
let saturation;
if (min === max) {
saturation = 0.0;
}
else {
if (lightness < 0.5) {
saturation = range / sum;
}
else {
saturation = range / (2.0 - max - min);
}
}
return [hue, saturation, lightness];
}
/**
* Convert the RGB integer to an arithmetic RBG float or Convert the hex single color to an arithmetic RBG float
*
* @param color
* integer color inclusively between 0 and 255 or hex single color in format FF or F
* @return float color inclusively between 0.0 and 1.0
*/
static toArithmeticRGB(color) {
if (typeof color === 'string') {
color = ColorUtils.toRGB(color);
}
if (Number.isInteger(color) && color !== 0 && color !== 1) {
ColorUtils.validateRGB(color);
color = color / 255.0;
}
return color;
}
/**
* Convert HSL (hue, saturation, and lightness) values to RGB arithmetic
* values
*
* @param hue
* hue value inclusively between 0.0 and 360.0
* @param saturation
* saturation inclusively between 0.0 and 1.0
* @param lightness
* lightness inclusively between 0.0 and 1.0
* @return arithmetic RGB array where: 0 = red, 1 = green, 2 = blue
*/
static toArithmeticRGBFromHSL(hue, saturation, lightness) {
ColorUtils.validateHue(hue);
ColorUtils.validateSaturation(saturation);
ColorUtils.validateLightness(lightness);
hue /= 60.0;
let t2;
if (lightness <= 0.5) {
t2 = lightness * (saturation + 1);
}
else {
t2 = lightness + saturation - lightness * saturation;
}
const t1 = lightness * 2.0 - t2;
const red = ColorUtils.hslConvert(t1, t2, hue + 2);
const green = ColorUtils.hslConvert(t1, t2, hue);
const blue = ColorUtils.hslConvert(t1, t2, hue - 2);
return [red, green, blue];
}
/**
* Convert the arithmetic RGB float to a RBG integer or Convert the hex single color to a RBG integer
*
* @param color
* float color inclusively between 0.0 and 1.0 or hex single color in format FF or F
*
* @return integer color inclusively between 0 and 255
*/
static toRGB(color) {
let colorNumber;
if (typeof color === 'number') {
ColorUtils.validateArithmeticRGB(color);
colorNumber = Math.round(255 * color);
}
else {
ColorUtils.validateHexSingle(color);
if (color.length === 1) {
color += color;
}
colorNumber = parseInt(color, 16);
}
return colorNumber;
}
/**
* Convert HSL (hue, saturation, and lightness) values to RGB integer values
*
* @param hue
* hue value inclusively between 0.0 and 360.0
* @param saturation
* saturation inclusively between 0.0 and 1.0
* @param lightness
* lightness inclusively between 0.0 and 1.0
* @return RGB integer array where: 0 = red, 1 = green, 2 = blue
*/
static toRGBFromHSL(hue, saturation, lightness) {
const arithmeticRGB = ColorUtils.toArithmeticRGBFromHSL(hue, saturation, lightness);
const rgb = [
ColorUtils.toRGB(arithmeticRGB[0]),
ColorUtils.toRGB(arithmeticRGB[1]),
ColorUtils.toRGB(arithmeticRGB[2]),
];
return rgb;
}
/**
* HSL convert helper method
*
* @param t1
* t1
* @param t2
* t2
* @param hue
* hue
* @return arithmetic RBG value
*/
static hslConvert(t1, t2, hue) {
let value;
if (hue < 0) {
hue += 6;
}
if (hue >= 6) {
hue -= 6;
}
if (hue < 1) {
value = (t2 - t1) * hue + t1;
}
else if (hue < 3) {
value = t2;
}
else if (hue < 4) {
value = (t2 - t1) * (4 - hue) + t1;
}
else {
value = t1;
}
return value;
}
/**
* Get the hex single color
*
* @param hex
* hex color
* @param colorIndex
* red=0, green=1, blue=2, alpha=-1
* @return hex single color in format FF or null
*/
static getHexSingle(hex, colorIndex) {
ColorUtils.validateHex(hex);
if (hex.startsWith('#')) {
hex = hex.substring(1);
}
let colorCharacters = 1;
let numColors = hex.length;
if (numColors > 4) {
colorCharacters++;
numColors /= 2;
}
let color = null;
if (colorIndex >= 0 || numColors > 3) {
if (numColors > 3) {
colorIndex++;
}
let startIndex = colorIndex;
if (colorCharacters > 1) {
startIndex *= 2;
}
color = hex.substring(startIndex, startIndex + colorCharacters);
color = ColorUtils.expandShorthandHexSingle(color);
}
return color;
}
/**
* Get the red color from color integer or Get the hex red color from the hex string
*
* @param color
* color integer or hex color
* @return red color or hex red color in format RR
*/
static getRed(color) {
let red;
if (typeof color === 'number') {
red = (color >> 16) & 0xff;
}
else {
red = ColorUtils.getHexSingle(color, 0);
}
return red;
}
/**
* Get the green color from color integer or Get the hex green color from the hex string
*
* @param color
* color integer or hex color
* @return green color or hex green color in format GG
*/
static getGreen(color) {
let green;
if (typeof color === 'number') {
green = (color >> 8) & 0xff;
}
else {
green = ColorUtils.getHexSingle(color, 1);
}
return green;
}
/**
* Get the blue color from color integer or Get the hex blue color from the hex string
*
* @param color
* color integer or hex color
* @return blue color or hex blue color in format BB
*/
static getBlue(color) {
let blue;
if (typeof color === 'number') {
blue = color & 0xff;
}
else {
blue = ColorUtils.getHexSingle(color, 2);
}
return blue;
}
/**
* Get the alpha color from color integer or Get the hex alpha color from the hex string if it exists
*
* @param color
* color integer or hex color
* @return alpha color or hex alpha color in format AA or null
*/
static getAlpha(color) {
let alpha = null;
if (typeof color === 'number') {
alpha = (color >> 24) & 0xff;
}
else {
alpha = ColorUtils.getHexSingle(color, -1);
}
return alpha;
}
/**
* Shorthand the hex color if possible
*
* @param color
* hex color
* @return shorthand hex color or original value
*/
static shorthandHex(color) {
ColorUtils.validateHex(color);
if (color.length > 5) {
let shorthandColor = '';
let startIndex = 0;
if (color.startsWith('#')) {
shorthandColor += '#';
startIndex++;
}
for (; startIndex < color.length; startIndex += 2) {
const shorthand = ColorUtils.shorthandHexSingle(color.substring(startIndex, startIndex + 2));
if (shorthand.length > 1) {
shorthandColor = null;
break;
}
shorthandColor += shorthand;
}
if (shorthandColor != null) {
color = shorthandColor.toString();
}
}
return color;
}
/**
* Expand the hex if it is in shorthand
*
* @param color
* hex color
* @return expanded hex color or original value
*/
static expandShorthandHex(color) {
ColorUtils.validateHex(color);
if (color.length < 6) {
let expandColor = '';
let startIndex = 0;
if (color.startsWith('#')) {
expandColor += '#';
startIndex++;
}
for (; startIndex < color.length; startIndex++) {
const expand = ColorUtils.expandShorthandHexSingle(color.substring(startIndex, startIndex + 1));
expandColor += expand;
}
color = expandColor.toString();
}
return color;
}
/**
* Shorthand the hex single color if possible
*
* @param color
* hex single color
* @return shorthand hex color or original value
*/
static shorthandHexSingle(color) {
ColorUtils.validateHexSingle(color);
if (color.length > 1 && color.charAt(0).toUpperCase() === color.charAt(1).toUpperCase()) {
color = color.substring(0, 1);
}
return color;
}
/**
* Expand the hex single if it is in shorthand
*
* @param color
* hex single color
* @return expanded hex color or original value
*/
static expandShorthandHexSingle(color) {
ColorUtils.validateHexSingle(color);
if (color.length === 1) {
color += color;
}
return color;
}
/**
* Check if the hex color value is valid
*
* @param color
* hex color
* @return true if valid
*/
static isValidHex(color) {
return color !== null && ColorUtils.hexColorPattern.test(color);
}
/**
* Validate the hex color value
*
* @param color
* hex color
*/
static validateHex(color) {
if (!ColorUtils.isValidHex(color)) {
throw new Error('Hex color must be in format #RRGGBB, #RGB, #AARRGGBB, #ARGB, RRGGBB, RGB, AARRGGBB, or ARGB, invalid value: ' +
color);
}
}
/**
* Check if the hex single color value is valid
*
* @param color
* hex single color
* @return true if valid
*/
static isValidHexSingle(color) {
return color !== null && ColorUtils.hexSingleColorPattern.test(color);
}
/**
* Validate the hex single color value
*
* @param color
* hex single color
*/
static validateHexSingle(color) {
if (!ColorUtils.isValidHexSingle(color)) {
throw new Error('Must be in format FF or F, invalid value: ' + color);
}
}
/**
* Check if the RBG integer color is valid, inclusively between 0 and 255
*
* @param color
* decimal color
* @return true if valid
*/
static isValidRGB(color) {
return color >= 0 && color <= 255;
}
/**
* Validate the RBG integer color is inclusively between 0 and 255
*
* @param color
* decimal color
*/
static validateRGB(color) {
if (!ColorUtils.isValidRGB(color)) {
throw new Error('Must be inclusively between 0 and 255, invalid value: ' + color);
}
}
/**
* Check if the arithmetic RGB float color is valid, inclusively between 0.0
* and 1.0
*
* @param color
* decimal color
* @return true if valid
*/
static isValidArithmeticRGB(color) {
return color >= 0.0 && color <= 1.0;
}
/**
* Validate the arithmetic RGB float color is inclusively between 0.0 and
* 1.0
*
* @param color
* decimal color
*/
static validateArithmeticRGB(color) {
if (!ColorUtils.isValidArithmeticRGB(color)) {
throw new Error('Must be inclusively between 0.0 and 1.0, invalid value: ' + color);
}
}
/**
* Check if the HSL hue float value is valid, inclusively between 0.0 and
* 360.0
*
* @param hue
* hue value
* @return true if valid
*/
static isValidHue(hue) {
return hue >= 0.0 && hue <= 360.0;
}
/**
* Validate the HSL hue float value is inclusively between 0.0 and 360.0
*
* @param hue
* hue value
*/
static validateHue(hue) {
if (!ColorUtils.isValidHue(hue)) {
throw new Error('Must be inclusively between 0.0 and 360.0, invalid value: ' + hue);
}
}
/**
* Check if the HSL saturation float value is valid, inclusively between 0.0
* and 1.0
*
* @param saturation
* saturation value
* @return true if valid
*/
static isValidSaturation(saturation) {
return saturation >= 0.0 && saturation <= 1.0;
}
/**
* Validate the HSL saturation float value is inclusively between 0.0 and
* 1.0
*
* @param saturation
* saturation value
*/
static validateSaturation(saturation) {
if (!ColorUtils.isValidSaturation(saturation)) {
throw new Error('Must be inclusively between 0.0 and 1.0, invalid value: ' + saturation);
}
}
/**
* Check if the HSL lightness float value is valid, inclusively between 0.0
* and 1.0
*
* @param lightness
* lightness value
* @return true if valid
*/
static isValidLightness(lightness) {
return lightness >= 0.0 && lightness <= 1.0;
}
/**
* Validate the HSL lightness float value is inclusively between 0.0 and 1.0
*
* @param lightness
* lightness value
*/
static validateLightness(lightness) {
if (!ColorUtils.isValidLightness(lightness)) {
throw new Error('Must be inclusively between 0.0 and 1.0, invalid value: ' + lightness);
}
}
}
exports.ColorUtils = ColorUtils;
/**
* Hex color pattern
*/
ColorUtils.hexColorPattern = new RegExp('^#?(([0-9a-fA-F]{3}){1,2}|([0-9a-fA-F]{4}){1,2})$');
/**
* Hex single color pattern
*/
ColorUtils.hexSingleColorPattern = new RegExp('^[0-9a-fA-F]{1,2}$');
//# sourceMappingURL=ColorUtils.js.map