1/* MIT license */ 2/* eslint-disable no-mixed-operators */ 3const cssKeywords = require('color-name'); 4 5// NOTE: conversions should only return primitive values (i.e. arrays, or 6// values that give correct `typeof` results). 7// do not use box values types (i.e. Number(), String(), etc.) 8 9const reverseKeywords = {}; 10for (const key of Object.keys(cssKeywords)) { 11 reverseKeywords[cssKeywords[key]] = key; 12} 13 14const convert = { 15 rgb: {channels: 3, labels: 'rgb'}, 16 hsl: {channels: 3, labels: 'hsl'}, 17 hsv: {channels: 3, labels: 'hsv'}, 18 hwb: {channels: 3, labels: 'hwb'}, 19 cmyk: {channels: 4, labels: 'cmyk'}, 20 xyz: {channels: 3, labels: 'xyz'}, 21 lab: {channels: 3, labels: 'lab'}, 22 lch: {channels: 3, labels: 'lch'}, 23 hex: {channels: 1, labels: ['hex']}, 24 keyword: {channels: 1, labels: ['keyword']}, 25 ansi16: {channels: 1, labels: ['ansi16']}, 26 ansi256: {channels: 1, labels: ['ansi256']}, 27 hcg: {channels: 3, labels: ['h', 'c', 'g']}, 28 apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, 29 gray: {channels: 1, labels: ['gray']} 30}; 31 32module.exports = convert; 33 34// Hide .channels and .labels properties 35for (const model of Object.keys(convert)) { 36 if (!('channels' in convert[model])) { 37 throw new Error('missing channels property: ' + model); 38 } 39 40 if (!('labels' in convert[model])) { 41 throw new Error('missing channel labels property: ' + model); 42 } 43 44 if (convert[model].labels.length !== convert[model].channels) { 45 throw new Error('channel and label counts mismatch: ' + model); 46 } 47 48 const {channels, labels} = convert[model]; 49 delete convert[model].channels; 50 delete convert[model].labels; 51 Object.defineProperty(convert[model], 'channels', {value: channels}); 52 Object.defineProperty(convert[model], 'labels', {value: labels}); 53} 54 55convert.rgb.hsl = function (rgb) { 56 const r = rgb[0] / 255; 57 const g = rgb[1] / 255; 58 const b = rgb[2] / 255; 59 const min = Math.min(r, g, b); 60 const max = Math.max(r, g, b); 61 const delta = max - min; 62 let h; 63 let s; 64 65 if (max === min) { 66 h = 0; 67 } else if (r === max) { 68 h = (g - b) / delta; 69 } else if (g === max) { 70 h = 2 + (b - r) / delta; 71 } else if (b === max) { 72 h = 4 + (r - g) / delta; 73 } 74 75 h = Math.min(h * 60, 360); 76 77 if (h < 0) { 78 h += 360; 79 } 80 81 const l = (min + max) / 2; 82 83 if (max === min) { 84 s = 0; 85 } else if (l <= 0.5) { 86 s = delta / (max + min); 87 } else { 88 s = delta / (2 - max - min); 89 } 90 91 return [h, s * 100, l * 100]; 92}; 93 94convert.rgb.hsv = function (rgb) { 95 let rdif; 96 let gdif; 97 let bdif; 98 let h; 99 let s; 100 101 const r = rgb[0] / 255; 102 const g = rgb[1] / 255; 103 const b = rgb[2] / 255; 104 const v = Math.max(r, g, b); 105 const diff = v - Math.min(r, g, b); 106 const diffc = function (c) { 107 return (v - c) / 6 / diff + 1 / 2; 108 }; 109 110 if (diff === 0) { 111 h = 0; 112 s = 0; 113 } else { 114 s = diff / v; 115 rdif = diffc(r); 116 gdif = diffc(g); 117 bdif = diffc(b); 118 119 if (r === v) { 120 h = bdif - gdif; 121 } else if (g === v) { 122 h = (1 / 3) + rdif - bdif; 123 } else if (b === v) { 124 h = (2 / 3) + gdif - rdif; 125 } 126 127 if (h < 0) { 128 h += 1; 129 } else if (h > 1) { 130 h -= 1; 131 } 132 } 133 134 return [ 135 h * 360, 136 s * 100, 137 v * 100 138 ]; 139}; 140 141convert.rgb.hwb = function (rgb) { 142 const r = rgb[0]; 143 const g = rgb[1]; 144 let b = rgb[2]; 145 const h = convert.rgb.hsl(rgb)[0]; 146 const w = 1 / 255 * Math.min(r, Math.min(g, b)); 147 148 b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); 149 150 return [h, w * 100, b * 100]; 151}; 152 153convert.rgb.cmyk = function (rgb) { 154 const r = rgb[0] / 255; 155 const g = rgb[1] / 255; 156 const b = rgb[2] / 255; 157 158 const k = Math.min(1 - r, 1 - g, 1 - b); 159 const c = (1 - r - k) / (1 - k) || 0; 160 const m = (1 - g - k) / (1 - k) || 0; 161 const y = (1 - b - k) / (1 - k) || 0; 162 163 return [c * 100, m * 100, y * 100, k * 100]; 164}; 165 166function comparativeDistance(x, y) { 167 /* 168 See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance 169 */ 170 return ( 171 ((x[0] - y[0]) ** 2) + 172 ((x[1] - y[1]) ** 2) + 173 ((x[2] - y[2]) ** 2) 174 ); 175} 176 177convert.rgb.keyword = function (rgb) { 178 const reversed = reverseKeywords[rgb]; 179 if (reversed) { 180 return reversed; 181 } 182 183 let currentClosestDistance = Infinity; 184 let currentClosestKeyword; 185 186 for (const keyword of Object.keys(cssKeywords)) { 187 const value = cssKeywords[keyword]; 188 189 // Compute comparative distance 190 const distance = comparativeDistance(rgb, value); 191 192 // Check if its less, if so set as closest 193 if (distance < currentClosestDistance) { 194 currentClosestDistance = distance; 195 currentClosestKeyword = keyword; 196 } 197 } 198 199 return currentClosestKeyword; 200}; 201 202convert.keyword.rgb = function (keyword) { 203 return cssKeywords[keyword]; 204}; 205 206convert.rgb.xyz = function (rgb) { 207 let r = rgb[0] / 255; 208 let g = rgb[1] / 255; 209 let b = rgb[2] / 255; 210 211 // Assume sRGB 212 r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92); 213 g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92); 214 b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92); 215 216 const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); 217 const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); 218 const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); 219 220 return [x * 100, y * 100, z * 100]; 221}; 222 223convert.rgb.lab = function (rgb) { 224 const xyz = convert.rgb.xyz(rgb); 225 let x = xyz[0]; 226 let y = xyz[1]; 227 let z = xyz[2]; 228 229 x /= 95.047; 230 y /= 100; 231 z /= 108.883; 232 233 x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); 234 y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); 235 z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); 236 237 const l = (116 * y) - 16; 238 const a = 500 * (x - y); 239 const b = 200 * (y - z); 240 241 return [l, a, b]; 242}; 243 244convert.hsl.rgb = function (hsl) { 245 const h = hsl[0] / 360; 246 const s = hsl[1] / 100; 247 const l = hsl[2] / 100; 248 let t2; 249 let t3; 250 let val; 251 252 if (s === 0) { 253 val = l * 255; 254 return [val, val, val]; 255 } 256 257 if (l < 0.5) { 258 t2 = l * (1 + s); 259 } else { 260 t2 = l + s - l * s; 261 } 262 263 const t1 = 2 * l - t2; 264 265 const rgb = [0, 0, 0]; 266 for (let i = 0; i < 3; i++) { 267 t3 = h + 1 / 3 * -(i - 1); 268 if (t3 < 0) { 269 t3++; 270 } 271 272 if (t3 > 1) { 273 t3--; 274 } 275 276 if (6 * t3 < 1) { 277 val = t1 + (t2 - t1) * 6 * t3; 278 } else if (2 * t3 < 1) { 279 val = t2; 280 } else if (3 * t3 < 2) { 281 val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; 282 } else { 283 val = t1; 284 } 285 286 rgb[i] = val * 255; 287 } 288 289 return rgb; 290}; 291 292convert.hsl.hsv = function (hsl) { 293 const h = hsl[0]; 294 let s = hsl[1] / 100; 295 let l = hsl[2] / 100; 296 let smin = s; 297 const lmin = Math.max(l, 0.01); 298 299 l *= 2; 300 s *= (l <= 1) ? l : 2 - l; 301 smin *= lmin <= 1 ? lmin : 2 - lmin; 302 const v = (l + s) / 2; 303 const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); 304 305 return [h, sv * 100, v * 100]; 306}; 307 308convert.hsv.rgb = function (hsv) { 309 const h = hsv[0] / 60; 310 const s = hsv[1] / 100; 311 let v = hsv[2] / 100; 312 const hi = Math.floor(h) % 6; 313 314 const f = h - Math.floor(h); 315 const p = 255 * v * (1 - s); 316 const q = 255 * v * (1 - (s * f)); 317 const t = 255 * v * (1 - (s * (1 - f))); 318 v *= 255; 319 320 switch (hi) { 321 case 0: 322 return [v, t, p]; 323 case 1: 324 return [q, v, p]; 325 case 2: 326 return [p, v, t]; 327 case 3: 328 return [p, q, v]; 329 case 4: 330 return [t, p, v]; 331 case 5: 332 return [v, p, q]; 333 } 334}; 335 336convert.hsv.hsl = function (hsv) { 337 const h = hsv[0]; 338 const s = hsv[1] / 100; 339 const v = hsv[2] / 100; 340 const vmin = Math.max(v, 0.01); 341 let sl; 342 let l; 343 344 l = (2 - s) * v; 345 const lmin = (2 - s) * vmin; 346 sl = s * vmin; 347 sl /= (lmin <= 1) ? lmin : 2 - lmin; 348 sl = sl || 0; 349 l /= 2; 350 351 return [h, sl * 100, l * 100]; 352}; 353 354// http://dev.w3.org/csswg/css-color/#hwb-to-rgb 355convert.hwb.rgb = function (hwb) { 356 const h = hwb[0] / 360; 357 let wh = hwb[1] / 100; 358 let bl = hwb[2] / 100; 359 const ratio = wh + bl; 360 let f; 361 362 // Wh + bl cant be > 1 363 if (ratio > 1) { 364 wh /= ratio; 365 bl /= ratio; 366 } 367 368 const i = Math.floor(6 * h); 369 const v = 1 - bl; 370 f = 6 * h - i; 371 372 if ((i & 0x01) !== 0) { 373 f = 1 - f; 374 } 375 376 const n = wh + f * (v - wh); // Linear interpolation 377 378 let r; 379 let g; 380 let b; 381 /* eslint-disable max-statements-per-line,no-multi-spaces */ 382 switch (i) { 383 default: 384 case 6: 385 case 0: r = v; g = n; b = wh; break; 386 case 1: r = n; g = v; b = wh; break; 387 case 2: r = wh; g = v; b = n; break; 388 case 3: r = wh; g = n; b = v; break; 389 case 4: r = n; g = wh; b = v; break; 390 case 5: r = v; g = wh; b = n; break; 391 } 392 /* eslint-enable max-statements-per-line,no-multi-spaces */ 393 394 return [r * 255, g * 255, b * 255]; 395}; 396 397convert.cmyk.rgb = function (cmyk) { 398 const c = cmyk[0] / 100; 399 const m = cmyk[1] / 100; 400 const y = cmyk[2] / 100; 401 const k = cmyk[3] / 100; 402 403 const r = 1 - Math.min(1, c * (1 - k) + k); 404 const g = 1 - Math.min(1, m * (1 - k) + k); 405 const b = 1 - Math.min(1, y * (1 - k) + k); 406 407 return [r * 255, g * 255, b * 255]; 408}; 409 410convert.xyz.rgb = function (xyz) { 411 const x = xyz[0] / 100; 412 const y = xyz[1] / 100; 413 const z = xyz[2] / 100; 414 let r; 415 let g; 416 let b; 417 418 r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); 419 g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); 420 b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); 421 422 // Assume sRGB 423 r = r > 0.0031308 424 ? ((1.055 * (r ** (1.0 / 2.4))) - 0.055) 425 : r * 12.92; 426 427 g = g > 0.0031308 428 ? ((1.055 * (g ** (1.0 / 2.4))) - 0.055) 429 : g * 12.92; 430 431 b = b > 0.0031308 432 ? ((1.055 * (b ** (1.0 / 2.4))) - 0.055) 433 : b * 12.92; 434 435 r = Math.min(Math.max(0, r), 1); 436 g = Math.min(Math.max(0, g), 1); 437 b = Math.min(Math.max(0, b), 1); 438 439 return [r * 255, g * 255, b * 255]; 440}; 441 442convert.xyz.lab = function (xyz) { 443 let x = xyz[0]; 444 let y = xyz[1]; 445 let z = xyz[2]; 446 447 x /= 95.047; 448 y /= 100; 449 z /= 108.883; 450 451 x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); 452 y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); 453 z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); 454 455 const l = (116 * y) - 16; 456 const a = 500 * (x - y); 457 const b = 200 * (y - z); 458 459 return [l, a, b]; 460}; 461 462convert.lab.xyz = function (lab) { 463 const l = lab[0]; 464 const a = lab[1]; 465 const b = lab[2]; 466 let x; 467 let y; 468 let z; 469 470 y = (l + 16) / 116; 471 x = a / 500 + y; 472 z = y - b / 200; 473 474 const y2 = y ** 3; 475 const x2 = x ** 3; 476 const z2 = z ** 3; 477 y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; 478 x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; 479 z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; 480 481 x *= 95.047; 482 y *= 100; 483 z *= 108.883; 484 485 return [x, y, z]; 486}; 487 488convert.lab.lch = function (lab) { 489 const l = lab[0]; 490 const a = lab[1]; 491 const b = lab[2]; 492 let h; 493 494 const hr = Math.atan2(b, a); 495 h = hr * 360 / 2 / Math.PI; 496 497 if (h < 0) { 498 h += 360; 499 } 500 501 const c = Math.sqrt(a * a + b * b); 502 503 return [l, c, h]; 504}; 505 506convert.lch.lab = function (lch) { 507 const l = lch[0]; 508 const c = lch[1]; 509 const h = lch[2]; 510 511 const hr = h / 360 * 2 * Math.PI; 512 const a = c * Math.cos(hr); 513 const b = c * Math.sin(hr); 514 515 return [l, a, b]; 516}; 517 518convert.rgb.ansi16 = function (args, saturation = null) { 519 const [r, g, b] = args; 520 let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization 521 522 value = Math.round(value / 50); 523 524 if (value === 0) { 525 return 30; 526 } 527 528 let ansi = 30 529 + ((Math.round(b / 255) << 2) 530 | (Math.round(g / 255) << 1) 531 | Math.round(r / 255)); 532 533 if (value === 2) { 534 ansi += 60; 535 } 536 537 return ansi; 538}; 539 540convert.hsv.ansi16 = function (args) { 541 // Optimization here; we already know the value and don't need to get 542 // it converted for us. 543 return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); 544}; 545 546convert.rgb.ansi256 = function (args) { 547 const r = args[0]; 548 const g = args[1]; 549 const b = args[2]; 550 551 // We use the extended greyscale palette here, with the exception of 552 // black and white. normal palette only has 4 greyscale shades. 553 if (r === g && g === b) { 554 if (r < 8) { 555 return 16; 556 } 557 558 if (r > 248) { 559 return 231; 560 } 561 562 return Math.round(((r - 8) / 247) * 24) + 232; 563 } 564 565 const ansi = 16 566 + (36 * Math.round(r / 255 * 5)) 567 + (6 * Math.round(g / 255 * 5)) 568 + Math.round(b / 255 * 5); 569 570 return ansi; 571}; 572 573convert.ansi16.rgb = function (args) { 574 let color = args % 10; 575 576 // Handle greyscale 577 if (color === 0 || color === 7) { 578 if (args > 50) { 579 color += 3.5; 580 } 581 582 color = color / 10.5 * 255; 583 584 return [color, color, color]; 585 } 586 587 const mult = (~~(args > 50) + 1) * 0.5; 588 const r = ((color & 1) * mult) * 255; 589 const g = (((color >> 1) & 1) * mult) * 255; 590 const b = (((color >> 2) & 1) * mult) * 255; 591 592 return [r, g, b]; 593}; 594 595convert.ansi256.rgb = function (args) { 596 // Handle greyscale 597 if (args >= 232) { 598 const c = (args - 232) * 10 + 8; 599 return [c, c, c]; 600 } 601 602 args -= 16; 603 604 let rem; 605 const r = Math.floor(args / 36) / 5 * 255; 606 const g = Math.floor((rem = args % 36) / 6) / 5 * 255; 607 const b = (rem % 6) / 5 * 255; 608 609 return [r, g, b]; 610}; 611 612convert.rgb.hex = function (args) { 613 const integer = ((Math.round(args[0]) & 0xFF) << 16) 614 + ((Math.round(args[1]) & 0xFF) << 8) 615 + (Math.round(args[2]) & 0xFF); 616 617 const string = integer.toString(16).toUpperCase(); 618 return '000000'.substring(string.length) + string; 619}; 620 621convert.hex.rgb = function (args) { 622 const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); 623 if (!match) { 624 return [0, 0, 0]; 625 } 626 627 let colorString = match[0]; 628 629 if (match[0].length === 3) { 630 colorString = colorString.split('').map(char => { 631 return char + char; 632 }).join(''); 633 } 634 635 const integer = parseInt(colorString, 16); 636 const r = (integer >> 16) & 0xFF; 637 const g = (integer >> 8) & 0xFF; 638 const b = integer & 0xFF; 639 640 return [r, g, b]; 641}; 642 643convert.rgb.hcg = function (rgb) { 644 const r = rgb[0] / 255; 645 const g = rgb[1] / 255; 646 const b = rgb[2] / 255; 647 const max = Math.max(Math.max(r, g), b); 648 const min = Math.min(Math.min(r, g), b); 649 const chroma = (max - min); 650 let grayscale; 651 let hue; 652 653 if (chroma < 1) { 654 grayscale = min / (1 - chroma); 655 } else { 656 grayscale = 0; 657 } 658 659 if (chroma <= 0) { 660 hue = 0; 661 } else 662 if (max === r) { 663 hue = ((g - b) / chroma) % 6; 664 } else 665 if (max === g) { 666 hue = 2 + (b - r) / chroma; 667 } else { 668 hue = 4 + (r - g) / chroma; 669 } 670 671 hue /= 6; 672 hue %= 1; 673 674 return [hue * 360, chroma * 100, grayscale * 100]; 675}; 676 677convert.hsl.hcg = function (hsl) { 678 const s = hsl[1] / 100; 679 const l = hsl[2] / 100; 680 681 const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l)); 682 683 let f = 0; 684 if (c < 1.0) { 685 f = (l - 0.5 * c) / (1.0 - c); 686 } 687 688 return [hsl[0], c * 100, f * 100]; 689}; 690 691convert.hsv.hcg = function (hsv) { 692 const s = hsv[1] / 100; 693 const v = hsv[2] / 100; 694 695 const c = s * v; 696 let f = 0; 697 698 if (c < 1.0) { 699 f = (v - c) / (1 - c); 700 } 701 702 return [hsv[0], c * 100, f * 100]; 703}; 704 705convert.hcg.rgb = function (hcg) { 706 const h = hcg[0] / 360; 707 const c = hcg[1] / 100; 708 const g = hcg[2] / 100; 709 710 if (c === 0.0) { 711 return [g * 255, g * 255, g * 255]; 712 } 713 714 const pure = [0, 0, 0]; 715 const hi = (h % 1) * 6; 716 const v = hi % 1; 717 const w = 1 - v; 718 let mg = 0; 719 720 /* eslint-disable max-statements-per-line */ 721 switch (Math.floor(hi)) { 722 case 0: 723 pure[0] = 1; pure[1] = v; pure[2] = 0; break; 724 case 1: 725 pure[0] = w; pure[1] = 1; pure[2] = 0; break; 726 case 2: 727 pure[0] = 0; pure[1] = 1; pure[2] = v; break; 728 case 3: 729 pure[0] = 0; pure[1] = w; pure[2] = 1; break; 730 case 4: 731 pure[0] = v; pure[1] = 0; pure[2] = 1; break; 732 default: 733 pure[0] = 1; pure[1] = 0; pure[2] = w; 734 } 735 /* eslint-enable max-statements-per-line */ 736 737 mg = (1.0 - c) * g; 738 739 return [ 740 (c * pure[0] + mg) * 255, 741 (c * pure[1] + mg) * 255, 742 (c * pure[2] + mg) * 255 743 ]; 744}; 745 746convert.hcg.hsv = function (hcg) { 747 const c = hcg[1] / 100; 748 const g = hcg[2] / 100; 749 750 const v = c + g * (1.0 - c); 751 let f = 0; 752 753 if (v > 0.0) { 754 f = c / v; 755 } 756 757 return [hcg[0], f * 100, v * 100]; 758}; 759 760convert.hcg.hsl = function (hcg) { 761 const c = hcg[1] / 100; 762 const g = hcg[2] / 100; 763 764 const l = g * (1.0 - c) + 0.5 * c; 765 let s = 0; 766 767 if (l > 0.0 && l < 0.5) { 768 s = c / (2 * l); 769 } else 770 if (l >= 0.5 && l < 1.0) { 771 s = c / (2 * (1 - l)); 772 } 773 774 return [hcg[0], s * 100, l * 100]; 775}; 776 777convert.hcg.hwb = function (hcg) { 778 const c = hcg[1] / 100; 779 const g = hcg[2] / 100; 780 const v = c + g * (1.0 - c); 781 return [hcg[0], (v - c) * 100, (1 - v) * 100]; 782}; 783 784convert.hwb.hcg = function (hwb) { 785 const w = hwb[1] / 100; 786 const b = hwb[2] / 100; 787 const v = 1 - b; 788 const c = v - w; 789 let g = 0; 790 791 if (c < 1) { 792 g = (v - c) / (1 - c); 793 } 794 795 return [hwb[0], c * 100, g * 100]; 796}; 797 798convert.apple.rgb = function (apple) { 799 return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; 800}; 801 802convert.rgb.apple = function (rgb) { 803 return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; 804}; 805 806convert.gray.rgb = function (args) { 807 return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; 808}; 809 810convert.gray.hsl = function (args) { 811 return [0, 0, args[0]]; 812}; 813 814convert.gray.hsv = convert.gray.hsl; 815 816convert.gray.hwb = function (gray) { 817 return [0, 100, gray[0]]; 818}; 819 820convert.gray.cmyk = function (gray) { 821 return [0, 0, 0, gray[0]]; 822}; 823 824convert.gray.lab = function (gray) { 825 return [gray[0], 0, 0]; 826}; 827 828convert.gray.hex = function (gray) { 829 const val = Math.round(gray[0] / 100 * 255) & 0xFF; 830 const integer = (val << 16) + (val << 8) + val; 831 832 const string = integer.toString(16).toUpperCase(); 833 return '000000'.substring(string.length) + string; 834}; 835 836convert.rgb.gray = function (rgb) { 837 const val = (rgb[0] + rgb[1] + rgb[2]) / 3; 838 return [val / 255 * 100]; 839}; 840