UNPKG

ccctool-lib

Version:

367 lines (309 loc) 13.9 kB
import { isNumber, isMathMatrix, isColorJSON } from "../helper/guardClauses.js"; import { mXv, invert3x3, atan2_360Degree, degree360ToRad } from "../helper/math.js"; import { cccToolProperties } from "../properties.js"; export const hsv2rgb = (_colorJSON) => { checkInput(_colorJSON, "hsv", "hsv2rgb"); let r, g, b = undefined; let i = Math.floor(_colorJSON.c1 * 6); // h value of hsv let f = _colorJSON.c1 * 6 - i; let p = _colorJSON.c3 * (1 - _colorJSON.c2); let q = _colorJSON.c3 * (1 - f * _colorJSON.c2); let t = _colorJSON.c3 * (1 - (1 - f) * _colorJSON.c2); switch (i % 6) { case 0: (r = _colorJSON.c3), (g = t), (b = p); break; case 1: (r = q), (g = _colorJSON.c3), (b = p); break; case 2: (r = p), (g = _colorJSON.c3), (b = t); break; case 3: (r = p), (g = q), (b = _colorJSON.c3); break; case 4: (r = t), (g = p), (b = _colorJSON.c3); break; case 5: (r = _colorJSON.c3), (g = p), (b = q); break; } return { space: "rgb", c1: r, c2: g, c3: b }; }; export const rgb2hsv = (_colorJSON) => { checkInput(_colorJSON, "rgb", "rgb2hsv"); let max = Math.max(_colorJSON.c1, _colorJSON.c2, _colorJSON.c3), min = Math.min(_colorJSON.c1, _colorJSON.c2, _colorJSON.c3); let h, s, v = max; let d = max - min; s = max == 0 ? 0 : d / max; if (max == min) { h = 0; // achromatic } else { switch (max) { case _colorJSON.c1: h = (_colorJSON.c2 - _colorJSON.c3) / d + (_colorJSON.c2 < _colorJSON.c3 ? 6 : 0); break; case _colorJSON.c2: h = (_colorJSON.c3 - _colorJSON.c1) / d + 2; break; case _colorJSON.c3: h = (_colorJSON.c1 - _colorJSON.c2) / d + 4; break; } h /= 6; } return { space: "hsv", c1: h, c2: s, c3: v }; }; export const rgb2xyz = (_colorJSON, _tm) => { checkInput(_colorJSON, "rgb", "rgb2xyz"); let tmxyz_Selected = undefined; // the user can transfer a transfer matrix, a name of a existing transfermatrix or he can let it undefined (use the default) try { if (isMathMatrix(_tm, true)) { // is Matrix with number and has equal rows and columns if (_tm.length != 3) throw new TypeError('Error (colorConverter) :: Function "rgb2xyz" :: Incorrect format. No RGB color.'); } tmxyz_Selected = _tm; // the matrix is correct and can be used as tm } catch (error) { tmxyz_Selected = getTransferMatrix("rgb2xyz", _tm); // check if tm is a exisiting tm-name or not (=> use default) } let var_R = _colorJSON.c1; let var_G = _colorJSON.c2; let var_B = _colorJSON.c3; // remove standard gamma correction if (var_R > 0.04045) var_R = Math.pow((var_R + 0.055) / 1.055, 2.4); else var_R = var_R / 12.92; if (var_G > 0.04045) var_G = Math.pow((var_G + 0.055) / 1.055, 2.4); else var_G = var_G / 12.92; if (var_B > 0.04045) var_B = Math.pow((var_B + 0.055) / 1.055, 2.4); else var_B = var_B / 12.92; var_R = var_R * 100; var_G = var_G * 100; var_B = var_B * 100; let var_xyz = mXv(tmxyz_Selected, [var_R, var_G, var_B]); return { space: "xyz", c1: var_xyz[0], c2: var_xyz[1], c3: var_xyz[2] }; }; export const xyz2rgb = (_colorJSON, _tm) => { checkInput(_colorJSON, "xyz", "xyz2rgb"); let tmxyz_Selected_Inv = undefined; // the user can transfer a transfer matrix, a name of a existing transfermatrix or he can let it undefined (use the default) try { if (isMathMatrix(_tm, true)) { // is Matrix with number and has equal rows and columns if (_tm.length != 3) throw new TypeError('Error (colorConverter) :: Function "rgb2xyz" :: Incorrect format. No RGB color.'); } tmxyz_Selected_Inv = _tm; // the matrix is correct and can be used as tm } catch (error) { tmxyz_Selected_Inv = invert3x3(getTransferMatrix("xyz2rgb", _tm)); // check if tm is a exisiting tm-name or not (=> use default) } let var_X = _colorJSON.c1 / 100.0; let var_Y = _colorJSON.c2 / 100.0; let var_Z = _colorJSON.c3 / 100.0; let var_rgb = mXv(tmxyz_Selected_Inv, [var_X, var_Y, var_Z]); //apply standard gamma correction if (var_rgb[0] > 0.0031308) var_rgb[0] = 1.055 * Math.pow(var_rgb[0], 1.0 / 2.4) - 0.055; else var_rgb[0] = 12.92 * var_rgb[0]; if (var_rgb[1] > 0.0031308) var_rgb[1] = 1.055 * Math.pow(var_rgb[1], 1.0 / 2.4) - 0.055; else var_rgb[1] = 12.92 * var_rgb[1]; if (var_rgb[2] > 0.0031308) var_rgb[2] = 1.055 * Math.pow(var_rgb[2], 1.0 / 2.4) - 0.055; else var_rgb[2] = 12.92 * var_rgb[2]; // rgb - Clipping if (var_rgb[0] > 1.0 || var_rgb[1] > 1.0 || var_rgb[2] > 1.0 || var_rgb[0] < 0.0 || var_rgb[1] < 0.0 || var_rgb[2] < 0.0) { // Wrong rgb -Values if (var_rgb[0] > 1.0) { var_rgb[0] = 1.0; } if (var_rgb[1] > 1.0) { var_rgb[1] = 1.0; } if (var_rgb[2] > 1.0) { var_rgb[2] = 1.0; } if (var_rgb[0] < 0.0) { var_rgb[0] = 0.0; } if (var_rgb[1] < 0.0) { var_rgb[1] = 0.0; } if (var_rgb[2] < 0.0) { var_rgb[2] = 0.0; } } return { space: "rgb", c1: var_rgb[0], c2: var_rgb[1], c3: var_rgb[2] }; }; export const xyz2lms = (_colorJSON, _tm) => { checkInput(_colorJSON, "xyz", "xyz2lms"); let tmlms_Selected = undefined; // the user can transfer a transfer matrix, a name of a existing transfermatrix or he can let it undefined (use the default) try { if (isMathMatrix(_tm, true)) { // is Matrix with number and has equal rows and columns if (_tm.length != 3) throw new TypeError('Error (colorConverter) :: Function "xyz2lms" :: Incorrect format. No RGB color.'); } tmlms_Selected = _tm; // the matrix is correct and can be used as tm } catch (error) { tmlms_Selected = getTransferMatrix("xyz2lms", _tm); // check if tm is a exisiting tm-name or not (=> use default) } let var_lms = mXv(tmlms_Selected, [_colorJSON.c1, _colorJSON.c2, _colorJSON.c3]); return { space: "lms", c1: var_lms[0], c2: var_lms[1], c3: var_lms[2] }; }; export const lms2xyz = (_colorJSON, _tm) => { checkInput(_colorJSON, "lms", "lms2xyz"); let tmlms_Selected_Inv = undefined; // the user can transfer a transfer matrix, a name of a existing transfermatrix or he can let it undefined (use the default) try { if (isMathMatrix(_tm, true)) { // is Matrix with number and has equal rows and columns if (_tm.length != 3) throw new TypeError('Error (colorConverter) :: Function "xyz2lms" :: Incorrect format. No RGB color.'); } tmlms_Selected_Inv = _tm; // the matrix is correct and can be used as tm } catch (error) { tmlms_Selected_Inv = invert3x3(getTransferMatrix("lms2xyz", _tm)); // check if tm is a exisiting tm-name or not (=> use default) } let var_xyz = mXv(tmlms_Selected_Inv, [_colorJSON.c1, _colorJSON.c2, _colorJSON.c3]); return { space: "xyz", c1: var_xyz[0], c2: var_xyz[1], c3: var_xyz[2] }; }; export const lms2lms_CB = (_colorJSON, _tm) => { checkInput(_colorJSON, "lms", "lms2lms_CB"); if (isMathMatrix(_tm, true)) { // is Matrix with number and has equal rows and columns if (_tm.length != 3) throw new TypeError('Error (colorConverter) :: Function "xyz2lms" :: Incorrect format. No RGB color.'); } let var_lms_cb = mXv(_tm, [_colorJSON.c1, _colorJSON.c2, _colorJSON.c3]); return { space: "lms", c1: var_lms_cb[0], c2: var_lms_cb[1], c3: var_lms_cb[2] }; }; export const xyz2lab = (_colorJSON, _REFX, _REFY, _REFZ) => { if (!isNumber(_REFX)) _REFX = cccToolProperties.REFX; // if undefined use default if (!isNumber(_REFY)) _REFY = cccToolProperties.REFY; // if undefined use default if (!isNumber(_REFZ)) _REFZ = cccToolProperties.REFZ; // if undefined use default checkInput(_colorJSON, "xyz", "xyz2lab"); let var_X = _colorJSON.c1 / _REFX; let var_Y = _colorJSON.c2 / _REFY; let var_Z = _colorJSON.c3 / _REFZ; if (var_X > 0.008856) var_X = Math.pow(var_X, 1 / 3); else var_X = 7.787 * var_X + 16 / 116; if (var_Y > 0.008856) var_Y = Math.pow(var_Y, 1 / 3); else var_Y = 7.787 * var_Y + 16 / 116; if (var_Z > 0.008856) var_Z = Math.pow(var_Z, 1 / 3); else var_Z = 7.787 * var_Z + 16 / 116; let var_L = 116 * var_Y - 16; let var_a = 500 * (var_X - var_Y); let var_b = 200 * (var_Y - var_Z); return { space: "lab", c1: var_L, c2: var_a, c3: var_b }; }; export const lab2xyz = (_colorJSON, _REFX, _REFY, _REFZ) => { if (!isNumber(_REFX)) _REFX = cccToolProperties.REFX; // if undefined use default if (!isNumber(_REFY)) _REFY = cccToolProperties.REFY; // if undefined use default if (!isNumber(_REFZ)) _REFZ = cccToolProperties.REFZ; // if undefined use default checkInput(_colorJSON, "lab", "lab2xyz"); let var_Y = (_colorJSON.c1 + 16.0) / 116.0; let var_X = _colorJSON.c2 / 500.0 + var_Y; let var_Z = var_Y - _colorJSON.c3 / 200.0; if (Math.pow(var_X, 3.0) > 0.008856) { var_X = Math.pow(var_X, 3.0); } else { var_X = (var_X - 16.0 / 116.0) / 7.787; } if (Math.pow(var_Y, 3.0) > 0.008856) { var_Y = Math.pow(var_Y, 3.0); } else { var_Y = (var_Y - 16.0 / 116.0) / 7.787; } if (Math.pow(var_Z, 3.0) > 0.008856) { var_Z = Math.pow(var_Z, 3.0); } else { var_Z = (var_Z - 16.0 / 116.0) / 7.787; } var_X = var_X * _REFX; var_Y = var_Y * _REFY; var_Z = var_Z * _REFZ; return { space: "xyz", c1: var_X, c2: var_Y, c3: var_Z }; }; export const lab2lch = (_colorJSON) => { checkInput(_colorJSON, "lab", "lab2lch"); let var_l = _colorJSON.c1 / 100; let normAVal = _colorJSON.c2 / 128.0; let normBVal = _colorJSON.c3 / 128.0; let var_c = Math.sqrt(Math.pow(normAVal, 2) + Math.pow(normBVal, 2)); let var_h = atan2_360Degree(normAVal, normBVal) / 360; // values 0-1 return { space: "lch", c1: var_l, c2: var_c, c3: var_h }; }; export const lch2lab = (_colorJSON) => { checkInput(_colorJSON, "lch", "lch2lab"); let var_l = _colorJSON.c1 * 100; let tmpRad = degree360ToRad(_colorJSON.c3 * 360); let var_a = Math.cos(tmpRad) * _colorJSON.c2 * 128; let var_b = Math.sin(tmpRad) * _colorJSON.c2 * 128; return { space: "lab", c1: var_l, c2: var_a, c3: var_b }; }; export const lab2din99 = (_colorJSON, _DIN99KE, _DIN99KCH) => { if (!isNumber(_DIN99KE)) _DIN99KE = cccToolProperties.DIN99KE; // if undefined use default if (!isNumber(_DIN99KCH)) _DIN99KCH = cccToolProperties.DIN99KCH; // if undefined use default checkInput(_colorJSON, "lab", "lab2din99"); let valueL99, valueA99, valueB99; let lScale = 100 / Math.log(139 / 100.0); // = 303.67 valueL99 = (lScale / _DIN99KE) * Math.log(1 + 0.0039 * _colorJSON.c1); if (_colorJSON.c2 == 0.0 && _colorJSON.c3 == 0.0) { valueA99 = 0.0; valueB99 = 0.0; } else { let angle = ((2 * Math.PI) / 360) * 26; let e = _colorJSON.c2 * Math.cos(angle) + _colorJSON.c3 * Math.sin(angle); let f = 0.83 * (_colorJSON.c3 * Math.cos(angle) - _colorJSON.c2 * Math.sin(angle)); let G = Math.sqrt(Math.pow(e, 2) + Math.pow(f, 2)); let C99 = Math.log(1 + 0.075 * G) / (0.0435 * _DIN99KCH * _DIN99KE); let hef = Math.atan2(f, e); let h99 = hef + angle; valueA99 = C99 * Math.cos(h99); valueB99 = C99 * Math.sin(h99); } return { space: "din99", c1: valueL99, c2: valueA99, c3: valueB99 }; }; export const din992lab = (_colorJSON, _DIN99KE, _DIN99KCH) => { if (!isNumber(_DIN99KE)) _DIN99KE = cccToolProperties.DIN99KE; // if undefined use default if (!isNumber(_DIN99KCH)) _DIN99KCH = cccToolProperties.DIN99KCH; // if undefined use default checkInput(_colorJSON, "din99", "din992lab"); let angle = ((2 * Math.PI) / 360) * 26; let lScale = 100 / Math.log(139 / 100.0); // = 303.67 let var_l = (Math.exp((_colorJSON.c1 * _DIN99KE) / lScale) - 1.0) / 0.0039; let hef = Math.atan2(_colorJSON.c3, _colorJSON.c2); let h99 = hef - angle; let C99 = Math.sqrt(Math.pow(_colorJSON.c2, 2) + Math.pow(_colorJSON.c3, 2)); let G = (Math.exp(0.0435 * C99 * _DIN99KCH * _DIN99KE) - 1) / 0.075; let e = G * Math.cos(h99); let f = G * Math.sin(h99); let var_a = e * Math.cos(angle) - (f / 0.83) * Math.sin(angle); let var_b = e * Math.sin(angle) + (f / 0.83) * Math.cos(angle); return { space: "lab", c1: var_l, c2: var_a, c3: var_b }; }; ////////////////////////////////////////////////// /////////////////// Private //////////////////// ////////////////////////////////////////////////// function checkInput(_colorJSON, _space, _fctName) { if (!isColorJSON(_colorJSON)) throw new TypeError('Error (colorConverter) :: Function "' + _fctName + '" :: Incorrect colorJSON.'); if (_colorJSON.space !== _space) throw new TypeError('Error (colorConverter) :: Function "' + _fctName + '" :: Incorrect format. No "' + _space + '" color.'); } // getTransferMatrix checks if the TransferMatrix exists in the properties and return it or the default. function getTransferMatrix(_fctName, _tmName) { if (typeof _tmName !== "string") _tmName = "use_default"; if (!cccToolProperties.hasOwnProperty(_tmName)) _tmName = "use_default"; // check if transfer matrix exist; if not => use default switch (_fctName) { case "rgb2xyz": case "xyz2rgb": if (_tmName.substring(0, 9) !== "TMRGB2XYZ") _tmName = falseTM("TMRGB2XYZ"); break; case "xyz2lms": case "lms2xyz": if (_tmName.substring(0, 9) !== "TMXYZ2LMS") _tmName = falseTM("TMXYZ2LMS"); break; default: throw new TypeError('Error (colorConverter) :: Function "' + _fctName + '" :: Incorrect format. Could not find a TM in getTransferMatrix'); } return cccToolProperties[_tmName]; } function falseTM(_type) { //console.info('Info (colorConverter) :: Function "' + _type + '" :: Undefined, Unknown, or Incorrect Transfer Matrix => Use of the default TM.'); return cccToolProperties[_type + "::Default"]; // the default properties only include the key name of the default _tm. So this return the real TM name and not e.g, "TMRGB2XYZ::Default" }