| import LRU from '../core/LRU'; |
| var kCSSColorTable = { |
| 'transparent': [0, 0, 0, 0], |
| 'aliceblue': [240, 248, 255, 1], |
| 'antiquewhite': [250, 235, 215, 1], |
| 'aqua': [0, 255, 255, 1], |
| 'aquamarine': [127, 255, 212, 1], |
| 'azure': [240, 255, 255, 1], |
| 'beige': [245, 245, 220, 1], |
| 'bisque': [255, 228, 196, 1], |
| 'black': [0, 0, 0, 1], |
| 'blanchedalmond': [255, 235, 205, 1], |
| 'blue': [0, 0, 255, 1], |
| 'blueviolet': [138, 43, 226, 1], |
| 'brown': [165, 42, 42, 1], |
| 'burlywood': [222, 184, 135, 1], |
| 'cadetblue': [95, 158, 160, 1], |
| 'chartreuse': [127, 255, 0, 1], |
| 'chocolate': [210, 105, 30, 1], |
| 'coral': [255, 127, 80, 1], |
| 'cornflowerblue': [100, 149, 237, 1], |
| 'cornsilk': [255, 248, 220, 1], |
| 'crimson': [220, 20, 60, 1], |
| 'cyan': [0, 255, 255, 1], |
| 'darkblue': [0, 0, 139, 1], |
| 'darkcyan': [0, 139, 139, 1], |
| 'darkgoldenrod': [184, 134, 11, 1], |
| 'darkgray': [169, 169, 169, 1], |
| 'darkgreen': [0, 100, 0, 1], |
| 'darkgrey': [169, 169, 169, 1], |
| 'darkkhaki': [189, 183, 107, 1], |
| 'darkmagenta': [139, 0, 139, 1], |
| 'darkolivegreen': [85, 107, 47, 1], |
| 'darkorange': [255, 140, 0, 1], |
| 'darkorchid': [153, 50, 204, 1], |
| 'darkred': [139, 0, 0, 1], |
| 'darksalmon': [233, 150, 122, 1], |
| 'darkseagreen': [143, 188, 143, 1], |
| 'darkslateblue': [72, 61, 139, 1], |
| 'darkslategray': [47, 79, 79, 1], |
| 'darkslategrey': [47, 79, 79, 1], |
| 'darkturquoise': [0, 206, 209, 1], |
| 'darkviolet': [148, 0, 211, 1], |
| 'deeppink': [255, 20, 147, 1], |
| 'deepskyblue': [0, 191, 255, 1], |
| 'dimgray': [105, 105, 105, 1], |
| 'dimgrey': [105, 105, 105, 1], |
| 'dodgerblue': [30, 144, 255, 1], |
| 'firebrick': [178, 34, 34, 1], |
| 'floralwhite': [255, 250, 240, 1], |
| 'forestgreen': [34, 139, 34, 1], |
| 'fuchsia': [255, 0, 255, 1], |
| 'gainsboro': [220, 220, 220, 1], |
| 'ghostwhite': [248, 248, 255, 1], |
| 'gold': [255, 215, 0, 1], |
| 'goldenrod': [218, 165, 32, 1], |
| 'gray': [128, 128, 128, 1], |
| 'green': [0, 128, 0, 1], |
| 'greenyellow': [173, 255, 47, 1], |
| 'grey': [128, 128, 128, 1], |
| 'honeydew': [240, 255, 240, 1], |
| 'hotpink': [255, 105, 180, 1], |
| 'indianred': [205, 92, 92, 1], |
| 'indigo': [75, 0, 130, 1], |
| 'ivory': [255, 255, 240, 1], |
| 'khaki': [240, 230, 140, 1], |
| 'lavender': [230, 230, 250, 1], |
| 'lavenderblush': [255, 240, 245, 1], |
| 'lawngreen': [124, 252, 0, 1], |
| 'lemonchiffon': [255, 250, 205, 1], |
| 'lightblue': [173, 216, 230, 1], |
| 'lightcoral': [240, 128, 128, 1], |
| 'lightcyan': [224, 255, 255, 1], |
| 'lightgoldenrodyellow': [250, 250, 210, 1], |
| 'lightgray': [211, 211, 211, 1], |
| 'lightgreen': [144, 238, 144, 1], |
| 'lightgrey': [211, 211, 211, 1], |
| 'lightpink': [255, 182, 193, 1], |
| 'lightsalmon': [255, 160, 122, 1], |
| 'lightseagreen': [32, 178, 170, 1], |
| 'lightskyblue': [135, 206, 250, 1], |
| 'lightslategray': [119, 136, 153, 1], |
| 'lightslategrey': [119, 136, 153, 1], |
| 'lightsteelblue': [176, 196, 222, 1], |
| 'lightyellow': [255, 255, 224, 1], |
| 'lime': [0, 255, 0, 1], |
| 'limegreen': [50, 205, 50, 1], |
| 'linen': [250, 240, 230, 1], |
| 'magenta': [255, 0, 255, 1], |
| 'maroon': [128, 0, 0, 1], |
| 'mediumaquamarine': [102, 205, 170, 1], |
| 'mediumblue': [0, 0, 205, 1], |
| 'mediumorchid': [186, 85, 211, 1], |
| 'mediumpurple': [147, 112, 219, 1], |
| 'mediumseagreen': [60, 179, 113, 1], |
| 'mediumslateblue': [123, 104, 238, 1], |
| 'mediumspringgreen': [0, 250, 154, 1], |
| 'mediumturquoise': [72, 209, 204, 1], |
| 'mediumvioletred': [199, 21, 133, 1], |
| 'midnightblue': [25, 25, 112, 1], |
| 'mintcream': [245, 255, 250, 1], |
| 'mistyrose': [255, 228, 225, 1], |
| 'moccasin': [255, 228, 181, 1], |
| 'navajowhite': [255, 222, 173, 1], |
| 'navy': [0, 0, 128, 1], |
| 'oldlace': [253, 245, 230, 1], |
| 'olive': [128, 128, 0, 1], |
| 'olivedrab': [107, 142, 35, 1], |
| 'orange': [255, 165, 0, 1], |
| 'orangered': [255, 69, 0, 1], |
| 'orchid': [218, 112, 214, 1], |
| 'palegoldenrod': [238, 232, 170, 1], |
| 'palegreen': [152, 251, 152, 1], |
| 'paleturquoise': [175, 238, 238, 1], |
| 'palevioletred': [219, 112, 147, 1], |
| 'papayawhip': [255, 239, 213, 1], |
| 'peachpuff': [255, 218, 185, 1], |
| 'peru': [205, 133, 63, 1], |
| 'pink': [255, 192, 203, 1], |
| 'plum': [221, 160, 221, 1], |
| 'powderblue': [176, 224, 230, 1], |
| 'purple': [128, 0, 128, 1], |
| 'red': [255, 0, 0, 1], |
| 'rosybrown': [188, 143, 143, 1], |
| 'royalblue': [65, 105, 225, 1], |
| 'saddlebrown': [139, 69, 19, 1], |
| 'salmon': [250, 128, 114, 1], |
| 'sandybrown': [244, 164, 96, 1], |
| 'seagreen': [46, 139, 87, 1], |
| 'seashell': [255, 245, 238, 1], |
| 'sienna': [160, 82, 45, 1], |
| 'silver': [192, 192, 192, 1], |
| 'skyblue': [135, 206, 235, 1], |
| 'slateblue': [106, 90, 205, 1], |
| 'slategray': [112, 128, 144, 1], |
| 'slategrey': [112, 128, 144, 1], |
| 'snow': [255, 250, 250, 1], |
| 'springgreen': [0, 255, 127, 1], |
| 'steelblue': [70, 130, 180, 1], |
| 'tan': [210, 180, 140, 1], |
| 'teal': [0, 128, 128, 1], |
| 'thistle': [216, 191, 216, 1], |
| 'tomato': [255, 99, 71, 1], |
| 'turquoise': [64, 224, 208, 1], |
| 'violet': [238, 130, 238, 1], |
| 'wheat': [245, 222, 179, 1], |
| 'white': [255, 255, 255, 1], |
| 'whitesmoke': [245, 245, 245, 1], |
| 'yellow': [255, 255, 0, 1], |
| 'yellowgreen': [154, 205, 50, 1] |
| }; |
| |
| function clampCssByte(i) { |
| // Clamp to integer 0 .. 255. |
| i = Math.round(i); // Seems to be what Chrome does (vs truncation). |
| |
| return i < 0 ? 0 : i > 255 ? 255 : i; |
| } |
| |
| function clampCssAngle(i) { |
| // Clamp to integer 0 .. 360. |
| i = Math.round(i); // Seems to be what Chrome does (vs truncation). |
| |
| return i < 0 ? 0 : i > 360 ? 360 : i; |
| } |
| |
| function clampCssFloat(f) { |
| // Clamp to float 0.0 .. 1.0. |
| return f < 0 ? 0 : f > 1 ? 1 : f; |
| } |
| |
| function parseCssInt(str) { |
| // int or percentage. |
| if (str.length && str.charAt(str.length - 1) === '%') { |
| return clampCssByte(parseFloat(str) / 100 * 255); |
| } |
| |
| return clampCssByte(parseInt(str, 10)); |
| } |
| |
| function parseCssFloat(str) { |
| // float or percentage. |
| if (str.length && str.charAt(str.length - 1) === '%') { |
| return clampCssFloat(parseFloat(str) / 100); |
| } |
| |
| return clampCssFloat(parseFloat(str)); |
| } |
| |
| function cssHueToRgb(m1, m2, h) { |
| if (h < 0) { |
| h += 1; |
| } else if (h > 1) { |
| h -= 1; |
| } |
| |
| if (h * 6 < 1) { |
| return m1 + (m2 - m1) * h * 6; |
| } |
| |
| if (h * 2 < 1) { |
| return m2; |
| } |
| |
| if (h * 3 < 2) { |
| return m1 + (m2 - m1) * (2 / 3 - h) * 6; |
| } |
| |
| return m1; |
| } |
| |
| function lerpNumber(a, b, p) { |
| return a + (b - a) * p; |
| } |
| |
| function setRgba(out, r, g, b, a) { |
| out[0] = r; |
| out[1] = g; |
| out[2] = b; |
| out[3] = a; |
| return out; |
| } |
| |
| function copyRgba(out, a) { |
| out[0] = a[0]; |
| out[1] = a[1]; |
| out[2] = a[2]; |
| out[3] = a[3]; |
| return out; |
| } |
| |
| var colorCache = new LRU(20); |
| var lastRemovedArr = null; |
| |
| function putToCache(colorStr, rgbaArr) { |
| // Reuse removed array |
| if (lastRemovedArr) { |
| copyRgba(lastRemovedArr, rgbaArr); |
| } |
| |
| lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || rgbaArr.slice()); |
| } |
| /** |
| * @param {string} colorStr |
| * @param {Array.<number>} out |
| * @return {Array.<number>} |
| * @memberOf module:zrender/util/color |
| */ |
| |
| |
| export function parse(colorStr, rgbaArr) { |
| if (!colorStr) { |
| return; |
| } |
| |
| rgbaArr = rgbaArr || []; |
| var cached = colorCache.get(colorStr); |
| |
| if (cached) { |
| return copyRgba(rgbaArr, cached); |
| } // colorStr may be not string |
| |
| |
| colorStr = colorStr + ''; // Remove all whitespace, not compliant, but should just be more accepting. |
| |
| var str = colorStr.replace(/ /g, '').toLowerCase(); // Color keywords (and transparent) lookup. |
| |
| if (str in kCSSColorTable) { |
| copyRgba(rgbaArr, kCSSColorTable[str]); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| } // #abc and #abc123 syntax. |
| |
| |
| if (str.charAt(0) === '#') { |
| if (str.length === 4) { |
| var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. |
| |
| if (!(iv >= 0 && iv <= 0xfff)) { |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; // Covers NaN. |
| } |
| |
| setRgba(rgbaArr, (iv & 0xf00) >> 4 | (iv & 0xf00) >> 8, iv & 0xf0 | (iv & 0xf0) >> 4, iv & 0xf | (iv & 0xf) << 4, 1); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| } else if (str.length === 7) { |
| var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. |
| |
| if (!(iv >= 0 && iv <= 0xffffff)) { |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; // Covers NaN. |
| } |
| |
| setRgba(rgbaArr, (iv & 0xff0000) >> 16, (iv & 0xff00) >> 8, iv & 0xff, 1); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| } |
| |
| return; |
| } |
| |
| var op = str.indexOf('('); |
| var ep = str.indexOf(')'); |
| |
| if (op !== -1 && ep + 1 === str.length) { |
| var fname = str.substr(0, op); |
| var params = str.substr(op + 1, ep - (op + 1)).split(','); |
| var alpha = 1; // To allow case fallthrough. |
| |
| switch (fname) { |
| case 'rgba': |
| if (params.length !== 4) { |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; |
| } |
| |
| alpha = parseCssFloat(params.pop()); |
| // jshint ignore:line |
| // Fall through. |
| |
| case 'rgb': |
| if (params.length !== 3) { |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; |
| } |
| |
| setRgba(rgbaArr, parseCssInt(params[0]), parseCssInt(params[1]), parseCssInt(params[2]), alpha); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| |
| case 'hsla': |
| if (params.length !== 4) { |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; |
| } |
| |
| params[3] = parseCssFloat(params[3]); |
| hsla2rgba(params, rgbaArr); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| |
| case 'hsl': |
| if (params.length !== 3) { |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; |
| } |
| |
| hsla2rgba(params, rgbaArr); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| |
| default: |
| return; |
| } |
| } |
| |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; |
| } |
| /** |
| * @param {Array.<number>} hsla |
| * @param {Array.<number>} rgba |
| * @return {Array.<number>} rgba |
| */ |
| |
| function hsla2rgba(hsla, rgba) { |
| var h = (parseFloat(hsla[0]) % 360 + 360) % 360 / 360; // 0 .. 1 |
| // NOTE(deanm): According to the CSS spec s/l should only be |
| // percentages, but we don't bother and let float or percentage. |
| |
| var s = parseCssFloat(hsla[1]); |
| var l = parseCssFloat(hsla[2]); |
| var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; |
| var m1 = l * 2 - m2; |
| rgba = rgba || []; |
| setRgba(rgba, clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), clampCssByte(cssHueToRgb(m1, m2, h) * 255), clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), 1); |
| |
| if (hsla.length === 4) { |
| rgba[3] = hsla[3]; |
| } |
| |
| return rgba; |
| } |
| /** |
| * @param {Array.<number>} rgba |
| * @return {Array.<number>} hsla |
| */ |
| |
| |
| function rgba2hsla(rgba) { |
| if (!rgba) { |
| return; |
| } // RGB from 0 to 255 |
| |
| |
| var R = rgba[0] / 255; |
| var G = rgba[1] / 255; |
| var B = rgba[2] / 255; |
| var vMin = Math.min(R, G, B); // Min. value of RGB |
| |
| var vMax = Math.max(R, G, B); // Max. value of RGB |
| |
| var delta = vMax - vMin; // Delta RGB value |
| |
| var L = (vMax + vMin) / 2; |
| var H; |
| var S; // HSL results from 0 to 1 |
| |
| if (delta === 0) { |
| H = 0; |
| S = 0; |
| } else { |
| if (L < 0.5) { |
| S = delta / (vMax + vMin); |
| } else { |
| S = delta / (2 - vMax - vMin); |
| } |
| |
| var deltaR = ((vMax - R) / 6 + delta / 2) / delta; |
| var deltaG = ((vMax - G) / 6 + delta / 2) / delta; |
| var deltaB = ((vMax - B) / 6 + delta / 2) / delta; |
| |
| if (R === vMax) { |
| H = deltaB - deltaG; |
| } else if (G === vMax) { |
| H = 1 / 3 + deltaR - deltaB; |
| } else if (B === vMax) { |
| H = 2 / 3 + deltaG - deltaR; |
| } |
| |
| if (H < 0) { |
| H += 1; |
| } |
| |
| if (H > 1) { |
| H -= 1; |
| } |
| } |
| |
| var hsla = [H * 360, S, L]; |
| |
| if (rgba[3] != null) { |
| hsla.push(rgba[3]); |
| } |
| |
| return hsla; |
| } |
| /** |
| * @param {string} color |
| * @param {number} level |
| * @return {string} |
| * @memberOf module:zrender/util/color |
| */ |
| |
| |
| export function lift(color, level) { |
| var colorArr = parse(color); |
| |
| if (colorArr) { |
| for (var i = 0; i < 3; i++) { |
| if (level < 0) { |
| colorArr[i] = colorArr[i] * (1 - level) | 0; |
| } else { |
| colorArr[i] = (255 - colorArr[i]) * level + colorArr[i] | 0; |
| } |
| |
| if (colorArr[i] > 255) { |
| colorArr[i] = 255; |
| } else if (color[i] < 0) { |
| colorArr[i] = 0; |
| } |
| } |
| |
| return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); |
| } |
| } |
| /** |
| * @param {string} color |
| * @return {string} |
| * @memberOf module:zrender/util/color |
| */ |
| |
| export function toHex(color) { |
| var colorArr = parse(color); |
| |
| if (colorArr) { |
| return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + +colorArr[2]).toString(16).slice(1); |
| } |
| } |
| /** |
| * Map value to color. Faster than lerp methods because color is represented by rgba array. |
| * @param {number} normalizedValue A float between 0 and 1. |
| * @param {Array.<Array.<number>>} colors List of rgba color array |
| * @param {Array.<number>} [out] Mapped gba color array |
| * @return {Array.<number>} will be null/undefined if input illegal. |
| */ |
| |
| export function fastLerp(normalizedValue, colors, out) { |
| if (!(colors && colors.length) || !(normalizedValue >= 0 && normalizedValue <= 1)) { |
| return; |
| } |
| |
| out = out || []; |
| var value = normalizedValue * (colors.length - 1); |
| var leftIndex = Math.floor(value); |
| var rightIndex = Math.ceil(value); |
| var leftColor = colors[leftIndex]; |
| var rightColor = colors[rightIndex]; |
| var dv = value - leftIndex; |
| out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)); |
| out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)); |
| out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)); |
| out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)); |
| return out; |
| } |
| /** |
| * @deprecated |
| */ |
| |
| export var fastMapToColor = fastLerp; |
| /** |
| * @param {number} normalizedValue A float between 0 and 1. |
| * @param {Array.<string>} colors Color list. |
| * @param {boolean=} fullOutput Default false. |
| * @return {(string|Object)} Result color. If fullOutput, |
| * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...}, |
| * @memberOf module:zrender/util/color |
| */ |
| |
| export function lerp(normalizedValue, colors, fullOutput) { |
| if (!(colors && colors.length) || !(normalizedValue >= 0 && normalizedValue <= 1)) { |
| return; |
| } |
| |
| var value = normalizedValue * (colors.length - 1); |
| var leftIndex = Math.floor(value); |
| var rightIndex = Math.ceil(value); |
| var leftColor = parse(colors[leftIndex]); |
| var rightColor = parse(colors[rightIndex]); |
| var dv = value - leftIndex; |
| var color = stringify([clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)), clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))], 'rgba'); |
| return fullOutput ? { |
| color: color, |
| leftIndex: leftIndex, |
| rightIndex: rightIndex, |
| value: value |
| } : color; |
| } |
| /** |
| * @deprecated |
| */ |
| |
| export var mapToColor = lerp; |
| /** |
| * @param {string} color |
| * @param {number=} h 0 ~ 360, ignore when null. |
| * @param {number=} s 0 ~ 1, ignore when null. |
| * @param {number=} l 0 ~ 1, ignore when null. |
| * @return {string} Color string in rgba format. |
| * @memberOf module:zrender/util/color |
| */ |
| |
| export function modifyHSL(color, h, s, l) { |
| color = parse(color); |
| |
| if (color) { |
| color = rgba2hsla(color); |
| h != null && (color[0] = clampCssAngle(h)); |
| s != null && (color[1] = parseCssFloat(s)); |
| l != null && (color[2] = parseCssFloat(l)); |
| return stringify(hsla2rgba(color), 'rgba'); |
| } |
| } |
| /** |
| * @param {string} color |
| * @param {number=} alpha 0 ~ 1 |
| * @return {string} Color string in rgba format. |
| * @memberOf module:zrender/util/color |
| */ |
| |
| export function modifyAlpha(color, alpha) { |
| color = parse(color); |
| |
| if (color && alpha != null) { |
| color[3] = clampCssFloat(alpha); |
| return stringify(color, 'rgba'); |
| } |
| } |
| /** |
| * @param {Array.<number>} arrColor like [12,33,44,0.4] |
| * @param {string} type 'rgba', 'hsva', ... |
| * @return {string} Result color. (If input illegal, return undefined). |
| */ |
| |
| export function stringify(arrColor, type) { |
| if (!arrColor || !arrColor.length) { |
| return; |
| } |
| |
| var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; |
| |
| if (type === 'rgba' || type === 'hsva' || type === 'hsla') { |
| colorStr += ',' + arrColor[3]; |
| } |
| |
| return type + '(' + colorStr + ')'; |
| } |