├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── conversions.js ├── index.d.ts ├── index.js ├── index.test-d.ts ├── package.json ├── route.js └── test ├── ansi-color-grid.js ├── ansi-colorize.js └── basic.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.coffee] 11 | indent_style = space 12 | 13 | [{package.json,*.yml}] 14 | indent_style = space 15 | indent_size = 2 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: 17 | - 22 18 | - 20 19 | - 18 20 | - 16 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm install 28 | - run: npm test 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw[a-p] 2 | /node_modules/ 3 | yarn-error.log 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.0 - 2016-01-07 2 | 3 | - Removed: unused speed test 4 | - Added: Automatic routing between previously unsupported conversions 5 | ([#27](https://github.com/Qix-/color-convert/pull/27)) 6 | - Removed: `xxx2xxx()` and `xxx2xxxRaw()` functions 7 | ([#27](https://github.com/Qix-/color-convert/pull/27)) 8 | - Removed: `convert()` class 9 | ([#27](https://github.com/Qix-/color-convert/pull/27)) 10 | - Changed: all functions to lookup dictionary 11 | ([#27](https://github.com/Qix-/color-convert/pull/27)) 12 | - Changed: `ansi` to `ansi256` 13 | ([#27](https://github.com/Qix-/color-convert/pull/27)) 14 | - Fixed: argument grouping for functions requiring only one argument 15 | ([#27](https://github.com/Qix-/color-convert/pull/27)) 16 | 17 | # 0.6.0 - 2015-07-23 18 | 19 | - Added: methods to handle 20 | [ANSI](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) 16/256 colors: 21 | - rgb2ansi16 22 | - rgb2ansi 23 | - hsl2ansi16 24 | - hsl2ansi 25 | - hsv2ansi16 26 | - hsv2ansi 27 | - hwb2ansi16 28 | - hwb2ansi 29 | - cmyk2ansi16 30 | - cmyk2ansi 31 | - keyword2ansi16 32 | - keyword2ansi 33 | - ansi162rgb 34 | - ansi162hsl 35 | - ansi162hsv 36 | - ansi162hwb 37 | - ansi162cmyk 38 | - ansi162keyword 39 | - ansi2rgb 40 | - ansi2hsl 41 | - ansi2hsv 42 | - ansi2hwb 43 | - ansi2cmyk 44 | - ansi2keyword 45 | ([#18](https://github.com/harthur/color-convert/pull/18)) 46 | 47 | # 0.5.3 - 2015-06-02 48 | 49 | - Fixed: hsl2hsv does not return `NaN` anymore when using `[0,0,0]` 50 | ([#15](https://github.com/harthur/color-convert/issues/15)) 51 | 52 | --- 53 | 54 | Check out commit logs for older releases 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2016 Heather Arthur . 2 | Copyright (c) 2016-2021 Josh Junon . 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # color-convert 2 | 3 | Color-convert is a color conversion library for JavaScript and node. 4 | It converts all ways between `rgb`, `hsl`, `hsv`, `hwb`, `cmyk`, `ansi`, `ansi16`, `hex` strings, and CSS `keyword`s (will round to closest): 5 | 6 | ```js 7 | import convert from 'color-convert'; 8 | 9 | convert.rgb.hsl(140, 200, 100); // [96, 48, 59] 10 | convert.keyword.rgb('blue'); // [0, 0, 255] 11 | 12 | const rgbChannels = convert.rgb.channels; // 3 13 | const cmykChannels = convert.cmyk.channels; // 4 14 | const ansiChannels = convert.ansi16.channels; // 1 15 | ``` 16 | 17 | # Install 18 | 19 | ```sh 20 | npm install color-convert 21 | ``` 22 | 23 | # API 24 | 25 | Simply get the property of the _from_ and _to_ conversion that you're looking for. 26 | 27 | All functions have a rounded and unrounded variant. By default, return values are rounded. To get the unrounded (raw) results, simply tack on `.raw` to the function. 28 | 29 | All 'from' functions have a hidden property called `.channels` that indicates the number of channels the function expects (not including alpha). 30 | 31 | ```js 32 | import convert from 'color-convert'; 33 | 34 | // Hex to LAB 35 | convert.hex.lab('DEADBF'); // [ 76, 21, -2 ] 36 | convert.hex.lab.raw('DEADBF'); // [ 75.56213190997677, 20.653827952644754, -2.290532499330533 ] 37 | 38 | // RGB to CMYK 39 | convert.rgb.cmyk(167, 255, 4); // [ 35, 0, 98, 0 ] 40 | convert.rgb.cmyk.raw(167, 255, 4); // [ 34.509803921568626, 0, 98.43137254901961, 0 ] 41 | ``` 42 | 43 | ### Arrays 44 | All functions that accept multiple arguments also support passing an array. 45 | 46 | Note that this does **not** apply to functions that convert from a color that only requires one value (e.g. `keyword`, `ansi256`, `hex`, etc.) 47 | 48 | ```js 49 | import convert from 'color-convert'; 50 | 51 | convert.rgb.hex(123, 45, 67); // '7B2D43' 52 | convert.rgb.hex([123, 45, 67]); // '7B2D43' 53 | ``` 54 | 55 | ## Routing 56 | 57 | Conversions that don't have an _explicitly_ defined conversion (in [conversions.js](conversions.js)), but can be converted by means of sub-conversions (e.g. XYZ -> **RGB** -> CMYK), are automatically routed together. This allows just about any color model supported by `color-convert` to be converted to any other model, so long as a sub-conversion path exists. This is also true for conversions requiring more than one step in between (e.g. LCH -> **LAB** -> **XYZ** -> **RGB** -> Hex). 58 | 59 | Keep in mind that extensive conversions _may_ result in a loss of precision, and exist only to be complete. For a list of "direct" (single-step) conversions, see [conversions.js](conversions.js). 60 | 61 | ## Color Space Scales 62 | Conversions rely on an agreed upon 'full-scale' value for each of the channels. Listed here are those values for the most common color spaces 63 | 64 | ### rgb 65 | channel | full-scale value 66 | ---|--- 67 | r | 255 68 | g | 255 69 | b | 255 70 | 71 | ### hsl 72 | channel | full-scale value 73 | ---|--- 74 | h | 360 75 | s | 100 76 | l | 100 77 | 78 | ### hsv 79 | channel | full-scale value 80 | ---|--- 81 | h | 360 82 | s | 100 83 | v | 100 84 | 85 | ### hwb 86 | channel | full-scale value 87 | ---|--- 88 | h | 360 89 | w | 100 90 | b | 100 91 | 92 | ### xyz 93 | channel | full-scale value 94 | ---|--- 95 | x | 94 96 | y | 99 97 | z | 108 98 | 99 | ### lab 100 | channel | full-scale value 101 | ---|--- 102 | l | 100 103 | a | -86, 98 104 | b | -108, 94 105 | 106 | ### lch 107 | channel | full-scale value 108 | ---|--- 109 | l | 100 110 | c | 133 111 | h | 360 112 | 113 | ### oklab 114 | channel | full-scale value 115 | ---|--- 116 | l | 100 117 | a | -23, 28 118 | b | -31, 20 119 | 120 | ### oklch 121 | channel | full-scale value 122 | ---|--- 123 | l | 100 124 | c | 32 125 | h | 360 126 | 127 | ### cmyk 128 | channel | full-scale value 129 | ---|--- 130 | c | 100 131 | m | 100 132 | y | 100 133 | k | 100 134 | 135 | ### hex 136 | channel | full-scale value 137 | ---|--- 138 | hex | ```0xffffff``` 139 | 140 | ### keyword 141 | channel | value 142 | ---|--- 143 | name | any key from [color-name](https://github.com/colorjs/color-name/blob/master/index.js) 144 | 145 | ### apple 146 | channel | full-scale value 147 | ---|--- 148 | 0 | 65535 149 | 1 | 65535 150 | 2 | 65535 151 | 152 | ### gray 153 | channel | full-scale value 154 | ---|--- 155 | gray | 100 156 | 157 | # Contribute 158 | 159 | If there is a new model you would like to support, or want to add a direct conversion between two existing models, please send us a pull request. 160 | 161 | # License 162 | Copyright © 2011-2016, Heather Arthur. 163 | Copyright © 2016-2021, Josh Junon. 164 | 165 | Licensed under the [MIT License](LICENSE). 166 | -------------------------------------------------------------------------------- /conversions.js: -------------------------------------------------------------------------------- 1 | /* MIT license */ 2 | /* eslint-disable no-mixed-operators */ 3 | import cssKeywords from '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 | 9 | const reverseKeywords = {}; 10 | for (const key of Object.keys(cssKeywords)) { 11 | reverseKeywords[cssKeywords[key]] = key; 12 | } 13 | 14 | const 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 | oklab: {channels: 3, labels: ['okl', 'oka', 'okb']}, 23 | lch: {channels: 3, labels: 'lch'}, 24 | oklch: {channels: 3, labels: ['okl', 'okc', 'okh']}, 25 | hex: {channels: 1, labels: ['hex']}, 26 | keyword: {channels: 1, labels: ['keyword']}, 27 | ansi16: {channels: 1, labels: ['ansi16']}, 28 | ansi256: {channels: 1, labels: ['ansi256']}, 29 | hcg: {channels: 3, labels: ['h', 'c', 'g']}, 30 | apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, 31 | gray: {channels: 1, labels: ['gray']}, 32 | }; 33 | 34 | export default convert; 35 | 36 | // LAB f(t) constant 37 | const LAB_FT = (6 / 29) ** 3; 38 | 39 | // SRGB non-linear transform functions 40 | function srgbNonlinearTransform(c) { 41 | const cc = c > 0.003_130_8 42 | ? ((1.055 * (c ** (1 / 2.4))) - 0.055) 43 | : c * 12.92; 44 | return Math.min(Math.max(0, cc), 1); 45 | } 46 | 47 | function srgbNonlinearTransformInv(c) { 48 | return c > 0.040_45 ? (((c + 0.055) / 1.055) ** 2.4) : (c / 12.92); 49 | } 50 | 51 | // Hide .channels and .labels properties 52 | for (const model of Object.keys(convert)) { 53 | if (!('channels' in convert[model])) { 54 | throw new Error('missing channels property: ' + model); 55 | } 56 | 57 | if (!('labels' in convert[model])) { 58 | throw new Error('missing channel labels property: ' + model); 59 | } 60 | 61 | if (convert[model].labels.length !== convert[model].channels) { 62 | throw new Error('channel and label counts mismatch: ' + model); 63 | } 64 | 65 | const {channels, labels} = convert[model]; 66 | delete convert[model].channels; 67 | delete convert[model].labels; 68 | Object.defineProperty(convert[model], 'channels', {value: channels}); 69 | Object.defineProperty(convert[model], 'labels', {value: labels}); 70 | } 71 | 72 | convert.rgb.hsl = function (rgb) { 73 | const r = rgb[0] / 255; 74 | const g = rgb[1] / 255; 75 | const b = rgb[2] / 255; 76 | const min = Math.min(r, g, b); 77 | const max = Math.max(r, g, b); 78 | const delta = max - min; 79 | let h; 80 | let s; 81 | 82 | switch (max) { 83 | case min: { 84 | h = 0; 85 | 86 | break; 87 | } 88 | 89 | case r: { 90 | h = (g - b) / delta; 91 | 92 | break; 93 | } 94 | 95 | case g: { 96 | h = 2 + (b - r) / delta; 97 | 98 | break; 99 | } 100 | 101 | case b: { 102 | h = 4 + (r - g) / delta; 103 | 104 | break; 105 | } 106 | // No default 107 | } 108 | 109 | h = Math.min(h * 60, 360); 110 | 111 | if (h < 0) { 112 | h += 360; 113 | } 114 | 115 | const l = (min + max) / 2; 116 | 117 | if (max === min) { 118 | s = 0; 119 | } else if (l <= 0.5) { 120 | s = delta / (max + min); 121 | } else { 122 | s = delta / (2 - max - min); 123 | } 124 | 125 | return [h, s * 100, l * 100]; 126 | }; 127 | 128 | convert.rgb.hsv = function (rgb) { 129 | let rdif; 130 | let gdif; 131 | let bdif; 132 | let h; 133 | let s; 134 | 135 | const r = rgb[0] / 255; 136 | const g = rgb[1] / 255; 137 | const b = rgb[2] / 255; 138 | const v = Math.max(r, g, b); 139 | const diff = v - Math.min(r, g, b); 140 | const diffc = function (c) { 141 | return (v - c) / 6 / diff + 1 / 2; 142 | }; 143 | 144 | if (diff === 0) { 145 | h = 0; 146 | s = 0; 147 | } else { 148 | s = diff / v; 149 | rdif = diffc(r); 150 | gdif = diffc(g); 151 | bdif = diffc(b); 152 | 153 | switch (v) { 154 | case r: { 155 | h = bdif - gdif; 156 | 157 | break; 158 | } 159 | 160 | case g: { 161 | h = (1 / 3) + rdif - bdif; 162 | 163 | break; 164 | } 165 | 166 | case b: { 167 | h = (2 / 3) + gdif - rdif; 168 | 169 | break; 170 | } 171 | // No default 172 | } 173 | 174 | if (h < 0) { 175 | h += 1; 176 | } else if (h > 1) { 177 | h -= 1; 178 | } 179 | } 180 | 181 | return [ 182 | h * 360, 183 | s * 100, 184 | v * 100, 185 | ]; 186 | }; 187 | 188 | convert.rgb.hwb = function (rgb) { 189 | const r = rgb[0]; 190 | const g = rgb[1]; 191 | let b = rgb[2]; 192 | const h = convert.rgb.hsl(rgb)[0]; 193 | const w = 1 / 255 * Math.min(r, Math.min(g, b)); 194 | 195 | b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); 196 | 197 | return [h, w * 100, b * 100]; 198 | }; 199 | 200 | convert.rgb.oklab = function (rgb) { 201 | // Assume sRGB 202 | const r = srgbNonlinearTransformInv(rgb[0] / 255); 203 | const g = srgbNonlinearTransformInv(rgb[1] / 255); 204 | const b = srgbNonlinearTransformInv(rgb[2] / 255); 205 | 206 | const lp = Math.cbrt(0.412_221_470_8 * r + 0.536_332_536_3 * g + 0.051_445_992_9 * b); 207 | const mp = Math.cbrt(0.211_903_498_2 * r + 0.680_699_545_1 * g + 0.107_396_956_6 * b); 208 | const sp = Math.cbrt(0.088_302_461_9 * r + 0.281_718_837_6 * g + 0.629_978_700_5 * b); 209 | 210 | const l = 0.210_454_255_3 * lp + 0.793_617_785 * mp - 0.004_072_046_8 * sp; 211 | const aa = 1.977_998_495_1 * lp - 2.428_592_205 * mp + 0.450_593_709_9 * sp; 212 | const bb = 0.025_904_037_1 * lp + 0.782_771_766_2 * mp - 0.808_675_766 * sp; 213 | 214 | return [l * 100, aa * 100, bb * 100]; 215 | }; 216 | 217 | convert.rgb.cmyk = function (rgb) { 218 | const r = rgb[0] / 255; 219 | const g = rgb[1] / 255; 220 | const b = rgb[2] / 255; 221 | 222 | const k = Math.min(1 - r, 1 - g, 1 - b); 223 | const c = (1 - r - k) / (1 - k) || 0; 224 | const m = (1 - g - k) / (1 - k) || 0; 225 | const y = (1 - b - k) / (1 - k) || 0; 226 | 227 | return [c * 100, m * 100, y * 100, k * 100]; 228 | }; 229 | 230 | function comparativeDistance(x, y) { 231 | /* 232 | See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance 233 | */ 234 | return ( 235 | ((x[0] - y[0]) ** 2) + 236 | ((x[1] - y[1]) ** 2) + 237 | ((x[2] - y[2]) ** 2) 238 | ); 239 | } 240 | 241 | convert.rgb.keyword = function (rgb) { 242 | const reversed = reverseKeywords[rgb]; 243 | if (reversed) { 244 | return reversed; 245 | } 246 | 247 | let currentClosestDistance = Number.POSITIVE_INFINITY; 248 | let currentClosestKeyword; 249 | 250 | for (const keyword of Object.keys(cssKeywords)) { 251 | const value = cssKeywords[keyword]; 252 | 253 | // Compute comparative distance 254 | const distance = comparativeDistance(rgb, value); 255 | 256 | // Check if its less, if so set as closest 257 | if (distance < currentClosestDistance) { 258 | currentClosestDistance = distance; 259 | currentClosestKeyword = keyword; 260 | } 261 | } 262 | 263 | return currentClosestKeyword; 264 | }; 265 | 266 | convert.keyword.rgb = function (keyword) { 267 | return cssKeywords[keyword]; 268 | }; 269 | 270 | convert.rgb.xyz = function (rgb) { 271 | // Assume sRGB 272 | const r = srgbNonlinearTransformInv(rgb[0] / 255); 273 | const g = srgbNonlinearTransformInv(rgb[1] / 255); 274 | const b = srgbNonlinearTransformInv(rgb[2] / 255); 275 | 276 | const x = (r * 0.412_456_4) + (g * 0.357_576_1) + (b * 0.180_437_5); 277 | const y = (r * 0.212_672_9) + (g * 0.715_152_2) + (b * 0.072_175); 278 | const z = (r * 0.019_333_9) + (g * 0.119_192) + (b * 0.950_304_1); 279 | 280 | return [x * 100, y * 100, z * 100]; 281 | }; 282 | 283 | convert.rgb.lab = function (rgb) { 284 | const xyz = convert.rgb.xyz(rgb); 285 | let x = xyz[0]; 286 | let y = xyz[1]; 287 | let z = xyz[2]; 288 | 289 | x /= 95.047; 290 | y /= 100; 291 | z /= 108.883; 292 | 293 | x = x > LAB_FT ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); 294 | y = y > LAB_FT ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); 295 | z = z > LAB_FT ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); 296 | 297 | const l = (116 * y) - 16; 298 | const a = 500 * (x - y); 299 | const b = 200 * (y - z); 300 | 301 | return [l, a, b]; 302 | }; 303 | 304 | convert.hsl.rgb = function (hsl) { 305 | const h = hsl[0] / 360; 306 | const s = hsl[1] / 100; 307 | const l = hsl[2] / 100; 308 | let t3; 309 | let value; 310 | 311 | if (s === 0) { 312 | value = l * 255; 313 | return [value, value, value]; 314 | } 315 | 316 | const t2 = l < 0.5 ? l * (1 + s) : l + s - l * s; 317 | 318 | const t1 = 2 * l - t2; 319 | 320 | const rgb = [0, 0, 0]; 321 | for (let i = 0; i < 3; i++) { 322 | t3 = h + 1 / 3 * -(i - 1); 323 | if (t3 < 0) { 324 | t3++; 325 | } 326 | 327 | if (t3 > 1) { 328 | t3--; 329 | } 330 | 331 | if (6 * t3 < 1) { 332 | value = t1 + (t2 - t1) * 6 * t3; 333 | } else if (2 * t3 < 1) { 334 | value = t2; 335 | } else if (3 * t3 < 2) { 336 | value = t1 + (t2 - t1) * (2 / 3 - t3) * 6; 337 | } else { 338 | value = t1; 339 | } 340 | 341 | rgb[i] = value * 255; 342 | } 343 | 344 | return rgb; 345 | }; 346 | 347 | convert.hsl.hsv = function (hsl) { 348 | const h = hsl[0]; 349 | let s = hsl[1] / 100; 350 | let l = hsl[2] / 100; 351 | let smin = s; 352 | const lmin = Math.max(l, 0.01); 353 | 354 | l *= 2; 355 | s *= (l <= 1) ? l : 2 - l; 356 | smin *= lmin <= 1 ? lmin : 2 - lmin; 357 | const v = (l + s) / 2; 358 | const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); 359 | 360 | return [h, sv * 100, v * 100]; 361 | }; 362 | 363 | convert.hsv.rgb = function (hsv) { 364 | const h = hsv[0] / 60; 365 | const s = hsv[1] / 100; 366 | let v = hsv[2] / 100; 367 | const hi = Math.floor(h) % 6; 368 | 369 | const f = h - Math.floor(h); 370 | const p = 255 * v * (1 - s); 371 | const q = 255 * v * (1 - (s * f)); 372 | const t = 255 * v * (1 - (s * (1 - f))); 373 | v *= 255; 374 | 375 | switch (hi) { 376 | case 0: { 377 | return [v, t, p]; 378 | } 379 | 380 | case 1: { 381 | return [q, v, p]; 382 | } 383 | 384 | case 2: { 385 | return [p, v, t]; 386 | } 387 | 388 | case 3: { 389 | return [p, q, v]; 390 | } 391 | 392 | case 4: { 393 | return [t, p, v]; 394 | } 395 | 396 | case 5: { 397 | return [v, p, q]; 398 | } 399 | } 400 | }; 401 | 402 | convert.hsv.hsl = function (hsv) { 403 | const h = hsv[0]; 404 | const s = hsv[1] / 100; 405 | const v = hsv[2] / 100; 406 | const vmin = Math.max(v, 0.01); 407 | let sl; 408 | let l; 409 | 410 | l = (2 - s) * v; 411 | const lmin = (2 - s) * vmin; 412 | sl = s * vmin; 413 | sl /= (lmin <= 1) ? lmin : 2 - lmin; 414 | sl = sl || 0; 415 | l /= 2; 416 | 417 | return [h, sl * 100, l * 100]; 418 | }; 419 | 420 | // http://dev.w3.org/csswg/css-color/#hwb-to-rgb 421 | convert.hwb.rgb = function (hwb) { 422 | const h = hwb[0] / 360; 423 | let wh = hwb[1] / 100; 424 | let bl = hwb[2] / 100; 425 | const ratio = wh + bl; 426 | let f; 427 | 428 | // Wh + bl cant be > 1 429 | if (ratio > 1) { 430 | wh /= ratio; 431 | bl /= ratio; 432 | } 433 | 434 | const i = Math.floor(6 * h); 435 | const v = 1 - bl; 436 | f = 6 * h - i; 437 | 438 | // eslint-disable-next-line no-bitwise 439 | if ((i & 0x01) !== 0) { 440 | f = 1 - f; 441 | } 442 | 443 | const n = wh + f * (v - wh); // Linear interpolation 444 | 445 | let r; 446 | let g; 447 | let b; 448 | /* eslint-disable max-statements-per-line,no-multi-spaces, default-case-last */ 449 | switch (i) { 450 | default: 451 | case 6: 452 | case 0: { r = v; g = n; b = wh; break; 453 | } 454 | 455 | case 1: { r = n; g = v; b = wh; break; 456 | } 457 | 458 | case 2: { r = wh; g = v; b = n; break; 459 | } 460 | 461 | case 3: { r = wh; g = n; b = v; break; 462 | } 463 | 464 | case 4: { r = n; g = wh; b = v; break; 465 | } 466 | 467 | case 5: { r = v; g = wh; b = n; break; 468 | } 469 | } 470 | /* eslint-enable max-statements-per-line,no-multi-spaces, default-case-last */ 471 | 472 | return [r * 255, g * 255, b * 255]; 473 | }; 474 | 475 | convert.cmyk.rgb = function (cmyk) { 476 | const c = cmyk[0] / 100; 477 | const m = cmyk[1] / 100; 478 | const y = cmyk[2] / 100; 479 | const k = cmyk[3] / 100; 480 | 481 | const r = 1 - Math.min(1, c * (1 - k) + k); 482 | const g = 1 - Math.min(1, m * (1 - k) + k); 483 | const b = 1 - Math.min(1, y * (1 - k) + k); 484 | 485 | return [r * 255, g * 255, b * 255]; 486 | }; 487 | 488 | convert.xyz.rgb = function (xyz) { 489 | const x = xyz[0] / 100; 490 | const y = xyz[1] / 100; 491 | const z = xyz[2] / 100; 492 | let r; 493 | let g; 494 | let b; 495 | 496 | r = (x * 3.240_454_2) + (y * -1.537_138_5) + (z * -0.498_531_4); 497 | g = (x * -0.969_266) + (y * 1.876_010_8) + (z * 0.041_556); 498 | b = (x * 0.055_643_4) + (y * -0.204_025_9) + (z * 1.057_225_2); 499 | 500 | // Assume sRGB 501 | r = srgbNonlinearTransform(r); 502 | g = srgbNonlinearTransform(g); 503 | b = srgbNonlinearTransform(b); 504 | 505 | return [r * 255, g * 255, b * 255]; 506 | }; 507 | 508 | convert.xyz.lab = function (xyz) { 509 | let x = xyz[0]; 510 | let y = xyz[1]; 511 | let z = xyz[2]; 512 | 513 | x /= 95.047; 514 | y /= 100; 515 | z /= 108.883; 516 | 517 | x = x > LAB_FT ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); 518 | y = y > LAB_FT ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); 519 | z = z > LAB_FT ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); 520 | 521 | const l = (116 * y) - 16; 522 | const a = 500 * (x - y); 523 | const b = 200 * (y - z); 524 | 525 | return [l, a, b]; 526 | }; 527 | 528 | convert.xyz.oklab = function (xyz) { 529 | const x = xyz[0] / 100; 530 | const y = xyz[1] / 100; 531 | const z = xyz[2] / 100; 532 | 533 | const lp = Math.cbrt(0.818_933_010_1 * x + 0.361_866_742_4 * y - 0.128_859_713_7 * z); 534 | const mp = Math.cbrt(0.032_984_543_6 * x + 0.929_311_871_5 * y + 0.036_145_638_7 * z); 535 | const sp = Math.cbrt(0.048_200_301_8 * x + 0.264_366_269_1 * y + 0.633_851_707 * z); 536 | 537 | const l = 0.210_454_255_3 * lp + 0.793_617_785 * mp - 0.004_072_046_8 * sp; 538 | const a = 1.977_998_495_1 * lp - 2.428_592_205 * mp + 0.450_593_709_9 * sp; 539 | const b = 0.025_904_037_1 * lp + 0.782_771_766_2 * mp - 0.808_675_766 * sp; 540 | 541 | return [l * 100, a * 100, b * 100]; 542 | }; 543 | 544 | convert.oklab.oklch = function (oklab) { 545 | return convert.lab.lch(oklab); 546 | }; 547 | 548 | convert.oklab.xyz = function (oklab) { 549 | const ll = oklab[0] / 100; 550 | const a = oklab[1] / 100; 551 | const b = oklab[2] / 100; 552 | 553 | const l = (0.999_999_998 * ll + 0.396_337_792 * a + 0.215_803_758 * b) ** 3; 554 | const m = (1.000_000_008 * ll - 0.105_561_342 * a - 0.063_854_175 * b) ** 3; 555 | const s = (1.000_000_055 * ll - 0.089_484_182 * a - 1.291_485_538 * b) ** 3; 556 | 557 | const x = 1.227_013_851 * l - 0.557_799_98 * m + 0.281_256_149 * s; 558 | const y = -0.040_580_178 * l + 1.112_256_87 * m - 0.071_676_679 * s; 559 | const z = -0.076_381_285 * l - 0.421_481_978 * m + 1.586_163_22 * s; 560 | 561 | return [x * 100, y * 100, z * 100]; 562 | }; 563 | 564 | convert.oklab.rgb = function (oklab) { 565 | const ll = oklab[0] / 100; 566 | const aa = oklab[1] / 100; 567 | const bb = oklab[2] / 100; 568 | 569 | const l = (ll + 0.396_337_777_4 * aa + 0.215_803_757_3 * bb) ** 3; 570 | const m = (ll - 0.105_561_345_8 * aa - 0.063_854_172_8 * bb) ** 3; 571 | const s = (ll - 0.089_484_177_5 * aa - 1.291_485_548 * bb) ** 3; 572 | 573 | // Assume sRGB 574 | const r = srgbNonlinearTransform(4.076_741_662_1 * l - 3.307_711_591_3 * m + 0.230_969_929_2 * s); 575 | const g = srgbNonlinearTransform(-1.268_438_004_6 * l + 2.609_757_401_1 * m - 0.341_319_396_5 * s); 576 | const b = srgbNonlinearTransform(-0.004_196_086_3 * l - 0.703_418_614_7 * m + 1.707_614_701 * s); 577 | 578 | return [r * 255, g * 255, b * 255]; 579 | }; 580 | 581 | convert.oklch.oklab = function (oklch) { 582 | return convert.lch.lab(oklch); 583 | }; 584 | 585 | convert.lab.xyz = function (lab) { 586 | const l = lab[0]; 587 | const a = lab[1]; 588 | const b = lab[2]; 589 | let x; 590 | let y; 591 | let z; 592 | 593 | y = (l + 16) / 116; 594 | x = a / 500 + y; 595 | z = y - b / 200; 596 | 597 | const y2 = y ** 3; 598 | const x2 = x ** 3; 599 | const z2 = z ** 3; 600 | y = y2 > LAB_FT ? y2 : (y - 16 / 116) / 7.787; 601 | x = x2 > LAB_FT ? x2 : (x - 16 / 116) / 7.787; 602 | z = z2 > LAB_FT ? z2 : (z - 16 / 116) / 7.787; 603 | 604 | // Illuminant D65 XYZ Tristrimulus Values 605 | // https://en.wikipedia.org/wiki/CIE_1931_color_space 606 | x *= 95.047; 607 | y *= 100; 608 | z *= 108.883; 609 | 610 | return [x, y, z]; 611 | }; 612 | 613 | convert.lab.lch = function (lab) { 614 | const l = lab[0]; 615 | const a = lab[1]; 616 | const b = lab[2]; 617 | let h; 618 | 619 | const hr = Math.atan2(b, a); 620 | h = hr * 360 / 2 / Math.PI; 621 | 622 | if (h < 0) { 623 | h += 360; 624 | } 625 | 626 | const c = Math.sqrt(a * a + b * b); 627 | 628 | return [l, c, h]; 629 | }; 630 | 631 | convert.lch.lab = function (lch) { 632 | const l = lch[0]; 633 | const c = lch[1]; 634 | const h = lch[2]; 635 | 636 | const hr = h / 360 * 2 * Math.PI; 637 | const a = c * Math.cos(hr); 638 | const b = c * Math.sin(hr); 639 | 640 | return [l, a, b]; 641 | }; 642 | 643 | convert.rgb.ansi16 = function (args, saturation = null) { 644 | const [r, g, b] = args; 645 | let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization 646 | 647 | value = Math.round(value / 50); 648 | 649 | if (value === 0) { 650 | return 30; 651 | } 652 | 653 | let ansi = 30 654 | /* eslint-disable no-bitwise */ 655 | + ((Math.round(b / 255) << 2) 656 | | (Math.round(g / 255) << 1) 657 | | Math.round(r / 255)); 658 | /* eslint-enable no-bitwise */ 659 | 660 | if (value === 2) { 661 | ansi += 60; 662 | } 663 | 664 | return ansi; 665 | }; 666 | 667 | convert.hsv.ansi16 = function (args) { 668 | // Optimization here; we already know the value and don't need to get 669 | // it converted for us. 670 | return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); 671 | }; 672 | 673 | convert.rgb.ansi256 = function (args) { 674 | const r = args[0]; 675 | const g = args[1]; 676 | const b = args[2]; 677 | 678 | // We use the extended greyscale palette here, with the exception of 679 | // black and white. normal palette only has 4 greyscale shades. 680 | // eslint-disable-next-line no-bitwise 681 | if (r >> 4 === g >> 4 && g >> 4 === b >> 4) { 682 | if (r < 8) { 683 | return 16; 684 | } 685 | 686 | if (r > 248) { 687 | return 231; 688 | } 689 | 690 | return Math.round(((r - 8) / 247) * 24) + 232; 691 | } 692 | 693 | const ansi = 16 694 | + (36 * Math.round(r / 255 * 5)) 695 | + (6 * Math.round(g / 255 * 5)) 696 | + Math.round(b / 255 * 5); 697 | 698 | return ansi; 699 | }; 700 | 701 | convert.ansi16.rgb = function (args) { 702 | args = args[0]; 703 | 704 | let color = args % 10; 705 | 706 | // Handle greyscale 707 | if (color === 0 || color === 7) { 708 | if (args > 50) { 709 | color += 3.5; 710 | } 711 | 712 | color = color / 10.5 * 255; 713 | 714 | return [color, color, color]; 715 | } 716 | 717 | const mult = (Math.trunc(args > 50) + 1) * 0.5; 718 | /* eslint-disable no-bitwise */ 719 | const r = ((color & 1) * mult) * 255; 720 | const g = (((color >> 1) & 1) * mult) * 255; 721 | const b = (((color >> 2) & 1) * mult) * 255; 722 | /* eslint-enable no-bitwise */ 723 | 724 | return [r, g, b]; 725 | }; 726 | 727 | convert.ansi256.rgb = function (args) { 728 | args = args[0]; 729 | 730 | // Handle greyscale 731 | if (args >= 232) { 732 | const c = (args - 232) * 10 + 8; 733 | return [c, c, c]; 734 | } 735 | 736 | args -= 16; 737 | 738 | let rem; 739 | const r = Math.floor(args / 36) / 5 * 255; 740 | const g = Math.floor((rem = args % 36) / 6) / 5 * 255; 741 | const b = (rem % 6) / 5 * 255; 742 | 743 | return [r, g, b]; 744 | }; 745 | 746 | convert.rgb.hex = function (args) { 747 | /* eslint-disable no-bitwise */ 748 | const integer = ((Math.round(args[0]) & 0xFF) << 16) 749 | + ((Math.round(args[1]) & 0xFF) << 8) 750 | + (Math.round(args[2]) & 0xFF); 751 | /* eslint-enable no-bitwise */ 752 | 753 | const string = integer.toString(16).toUpperCase(); 754 | return '000000'.slice(string.length) + string; 755 | }; 756 | 757 | convert.hex.rgb = function (args) { 758 | const match = args.toString(16).match(/[a-f\d]{6}|[a-f\d]{3}/i); 759 | if (!match) { 760 | return [0, 0, 0]; 761 | } 762 | 763 | let colorString = match[0]; 764 | 765 | if (match[0].length === 3) { 766 | colorString = [...colorString].map(char => char + char).join(''); 767 | } 768 | 769 | const integer = Number.parseInt(colorString, 16); 770 | /* eslint-disable no-bitwise */ 771 | const r = (integer >> 16) & 0xFF; 772 | const g = (integer >> 8) & 0xFF; 773 | const b = integer & 0xFF; 774 | /* eslint-enable no-bitwise */ 775 | 776 | return [r, g, b]; 777 | }; 778 | 779 | convert.rgb.hcg = function (rgb) { 780 | const r = rgb[0] / 255; 781 | const g = rgb[1] / 255; 782 | const b = rgb[2] / 255; 783 | const max = Math.max(Math.max(r, g), b); 784 | const min = Math.min(Math.min(r, g), b); 785 | const chroma = (max - min); 786 | let hue; 787 | 788 | const grayscale = chroma < 1 ? min / (1 - chroma) : 0; 789 | 790 | if (chroma <= 0) { 791 | hue = 0; 792 | } else if (max === r) { 793 | hue = ((g - b) / chroma) % 6; 794 | } else if (max === g) { 795 | hue = 2 + (b - r) / chroma; 796 | } else { 797 | hue = 4 + (r - g) / chroma; 798 | } 799 | 800 | hue /= 6; 801 | hue %= 1; 802 | 803 | return [hue * 360, chroma * 100, grayscale * 100]; 804 | }; 805 | 806 | convert.hsl.hcg = function (hsl) { 807 | const s = hsl[1] / 100; 808 | const l = hsl[2] / 100; 809 | 810 | const c = l < 0.5 ? (2 * s * l) : (2 * s * (1 - l)); 811 | 812 | let f = 0; 813 | if (c < 1) { 814 | f = (l - 0.5 * c) / (1 - c); 815 | } 816 | 817 | return [hsl[0], c * 100, f * 100]; 818 | }; 819 | 820 | convert.hsv.hcg = function (hsv) { 821 | const s = hsv[1] / 100; 822 | const v = hsv[2] / 100; 823 | 824 | const c = s * v; 825 | let f = 0; 826 | 827 | if (c < 1) { 828 | f = (v - c) / (1 - c); 829 | } 830 | 831 | return [hsv[0], c * 100, f * 100]; 832 | }; 833 | 834 | convert.hcg.rgb = function (hcg) { 835 | const h = hcg[0] / 360; 836 | const c = hcg[1] / 100; 837 | const g = hcg[2] / 100; 838 | 839 | if (c === 0) { 840 | return [g * 255, g * 255, g * 255]; 841 | } 842 | 843 | const pure = [0, 0, 0]; 844 | const hi = (h % 1) * 6; 845 | const v = hi % 1; 846 | const w = 1 - v; 847 | let mg = 0; 848 | 849 | /* eslint-disable max-statements-per-line */ 850 | switch (Math.floor(hi)) { 851 | case 0: { 852 | pure[0] = 1; pure[1] = v; pure[2] = 0; break; 853 | } 854 | 855 | case 1: { 856 | pure[0] = w; pure[1] = 1; pure[2] = 0; break; 857 | } 858 | 859 | case 2: { 860 | pure[0] = 0; pure[1] = 1; pure[2] = v; break; 861 | } 862 | 863 | case 3: { 864 | pure[0] = 0; pure[1] = w; pure[2] = 1; break; 865 | } 866 | 867 | case 4: { 868 | pure[0] = v; pure[1] = 0; pure[2] = 1; break; 869 | } 870 | 871 | default: { 872 | pure[0] = 1; pure[1] = 0; pure[2] = w; 873 | } 874 | } 875 | /* eslint-enable max-statements-per-line */ 876 | 877 | mg = (1 - c) * g; 878 | 879 | return [ 880 | (c * pure[0] + mg) * 255, 881 | (c * pure[1] + mg) * 255, 882 | (c * pure[2] + mg) * 255, 883 | ]; 884 | }; 885 | 886 | convert.hcg.hsv = function (hcg) { 887 | const c = hcg[1] / 100; 888 | const g = hcg[2] / 100; 889 | 890 | const v = c + g * (1 - c); 891 | let f = 0; 892 | 893 | if (v > 0) { 894 | f = c / v; 895 | } 896 | 897 | return [hcg[0], f * 100, v * 100]; 898 | }; 899 | 900 | convert.hcg.hsl = function (hcg) { 901 | const c = hcg[1] / 100; 902 | const g = hcg[2] / 100; 903 | 904 | const l = g * (1 - c) + 0.5 * c; 905 | let s = 0; 906 | 907 | if (l > 0 && l < 0.5) { 908 | s = c / (2 * l); 909 | } else if (l >= 0.5 && l < 1) { 910 | s = c / (2 * (1 - l)); 911 | } 912 | 913 | return [hcg[0], s * 100, l * 100]; 914 | }; 915 | 916 | convert.hcg.hwb = function (hcg) { 917 | const c = hcg[1] / 100; 918 | const g = hcg[2] / 100; 919 | const v = c + g * (1 - c); 920 | return [hcg[0], (v - c) * 100, (1 - v) * 100]; 921 | }; 922 | 923 | convert.hwb.hcg = function (hwb) { 924 | const w = hwb[1] / 100; 925 | const b = hwb[2] / 100; 926 | const v = 1 - b; 927 | const c = v - w; 928 | let g = 0; 929 | 930 | if (c < 1) { 931 | g = (v - c) / (1 - c); 932 | } 933 | 934 | return [hwb[0], c * 100, g * 100]; 935 | }; 936 | 937 | convert.apple.rgb = function (apple) { 938 | return [(apple[0] / 65_535) * 255, (apple[1] / 65_535) * 255, (apple[2] / 65_535) * 255]; 939 | }; 940 | 941 | convert.rgb.apple = function (rgb) { 942 | return [(rgb[0] / 255) * 65_535, (rgb[1] / 255) * 65_535, (rgb[2] / 255) * 65_535]; 943 | }; 944 | 945 | convert.gray.rgb = function (args) { 946 | return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; 947 | }; 948 | 949 | convert.gray.hsl = function (args) { 950 | return [0, 0, args[0]]; 951 | }; 952 | 953 | convert.gray.hsv = convert.gray.hsl; 954 | 955 | convert.gray.hwb = function (gray) { 956 | return [0, 100, gray[0]]; 957 | }; 958 | 959 | convert.gray.cmyk = function (gray) { 960 | return [0, 0, 0, gray[0]]; 961 | }; 962 | 963 | convert.gray.lab = function (gray) { 964 | return [gray[0], 0, 0]; 965 | }; 966 | 967 | convert.gray.hex = function (gray) { 968 | /* eslint-disable no-bitwise */ 969 | const value = Math.round(gray[0] / 100 * 255) & 0xFF; 970 | const integer = (value << 16) + (value << 8) + value; 971 | /* eslint-enable no-bitwise */ 972 | 973 | const string = integer.toString(16).toUpperCase(); 974 | return '000000'.slice(string.length) + string; 975 | }; 976 | 977 | convert.rgb.gray = function (rgb) { 978 | const value = (rgb[0] + rgb[1] + rgb[2]) / 3; 979 | return [value / 255 * 100]; 980 | }; 981 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export type Channels = number; 2 | export type RGB = [r: number, g: number, b: number]; 3 | export type HSL = [h: number, s: number, l: number]; 4 | export type HSV = [h: number, s: number, v: number]; 5 | export type CMYK = [c: number, m: number, y: number, k: number]; 6 | export type LAB = [l: number, a: number, b: number]; 7 | export type LCH = [l: number, c: number, h: number]; 8 | export type HCG = [h: number, c: number, g: number]; 9 | export type HWB = [h: number, w: number, b: number]; 10 | export type XYZ = [x: number, y: number, z: number]; 11 | export type Apple = [r16: number, g16: number, b16: number]; 12 | export type Gray = [gray: number]; 13 | export type ANSI16 = number; 14 | export type ANSI256 = number; 15 | export type Keyword = string; 16 | export type HEX = string; 17 | 18 | declare namespace route { 19 | type rgb = { 20 | hsl(from: RGB): HSL; 21 | hsl(...from: RGB): HSL; 22 | hsl(from: RGB): HSL; 23 | hsl(...from: RGB): HSL; 24 | hsv(from: RGB): HSV; 25 | hsv(...from: RGB): HSV; 26 | hwb(from: RGB): HWB; 27 | hwb(...from: RGB): HWB; 28 | cmyk(from: RGB): CMYK; 29 | cmyk(...from: RGB): CMYK; 30 | xyz(from: RGB): XYZ; 31 | xyz(...from: RGB): XYZ; 32 | lab(from: RGB): LAB; 33 | lab(...from: RGB): LAB; 34 | lch(from: RGB): LCH; 35 | lch(...from: RGB): LCH; 36 | hex(from: RGB): HEX; 37 | hex(...from: RGB): HEX; 38 | keyword(from: RGB): Keyword; 39 | keyword(...from: RGB): Keyword; 40 | ansi16(from: RGB): ANSI16; 41 | ansi16(...from: RGB): ANSI16; 42 | ansi256(from: RGB): ANSI256; 43 | ansi256(...from: RGB): ANSI256; 44 | hcg(from: RGB): HCG; 45 | hcg(...from: RGB): HCG; 46 | apple(from: RGB): Apple; 47 | apple(...from: RGB): Apple; 48 | gray(from: RGB): Gray; 49 | gray(...from: RGB): Gray; 50 | }; 51 | 52 | type hsl = { 53 | rgb(from: HSL): RGB; 54 | rgb(...from: HSL): RGB; 55 | hsv(from: HSL): HSV; 56 | hsv(...from: HSL): HSV; 57 | hwb(from: HSL): HWB; 58 | hwb(...from: HSL): HWB; 59 | cmyk(from: HSL): CMYK; 60 | cmyk(...from: HSL): CMYK; 61 | xyz(from: HSL): XYZ; 62 | xyz(...from: HSL): XYZ; 63 | lab(from: HSL): LAB; 64 | lab(...from: HSL): LAB; 65 | lch(from: HSL): LCH; 66 | lch(...from: HSL): LCH; 67 | hex(from: HSL): HEX; 68 | hex(...from: HSL): HEX; 69 | keyword(from: HSL): Keyword; 70 | keyword(...from: HSL): Keyword; 71 | ansi16(from: HSL): ANSI16; 72 | ansi16(...from: HSL): ANSI16; 73 | ansi256(from: HSL): ANSI256; 74 | ansi256(...from: HSL): ANSI256; 75 | hcg(from: HSL): HCG; 76 | hcg(...from: HSL): HCG; 77 | apple(from: HSL): Apple; 78 | apple(...from: HSL): Apple; 79 | gray(from: HSL): Gray; 80 | gray(...from: HSL): Gray; 81 | }; 82 | 83 | type hsv = { 84 | rgb(from: HSV): RGB; 85 | rgb(...from: HSV): RGB; 86 | hsl(from: HSV): HSL; 87 | hsl(...from: HSV): HSL; 88 | hwb(from: HSV): HWB; 89 | hwb(...from: HSV): HWB; 90 | cmyk(from: HSV): CMYK; 91 | cmyk(...from: HSV): CMYK; 92 | xyz(from: HSV): XYZ; 93 | xyz(...from: HSV): XYZ; 94 | lab(from: HSV): LAB; 95 | lab(...from: HSV): LAB; 96 | lch(from: HSV): LCH; 97 | lch(...from: HSV): LCH; 98 | hex(from: HSV): HEX; 99 | hex(...from: HSV): HEX; 100 | keyword(from: HSV): Keyword; 101 | keyword(...from: HSV): Keyword; 102 | ansi16(from: HSV): ANSI16; 103 | ansi16(...from: HSV): ANSI16; 104 | ansi256(from: HSV): ANSI256; 105 | ansi256(...from: HSV): ANSI256; 106 | hcg(from: HSV): HCG; 107 | hcg(...from: HSV): HCG; 108 | apple(from: HSV): Apple; 109 | apple(...from: HSV): Apple; 110 | gray(from: HSV): Gray; 111 | gray(...from: HSV): Gray; 112 | }; 113 | 114 | type hwb = { 115 | rgb(from: HWB): RGB; 116 | rgb(...from: HWB): RGB; 117 | hsl(from: HWB): HSL; 118 | hsl(...from: HWB): HSL; 119 | hsv(from: HWB): HSV; 120 | hsv(...from: HWB): HSV; 121 | cmyk(from: HWB): CMYK; 122 | cmyk(...from: HWB): CMYK; 123 | xyz(from: HWB): XYZ; 124 | xyz(...from: HWB): XYZ; 125 | lab(from: HWB): LAB; 126 | lab(...from: HWB): LAB; 127 | lch(from: HWB): LCH; 128 | lch(...from: HWB): LCH; 129 | hex(from: HWB): HEX; 130 | hex(...from: HWB): HEX; 131 | keyword(from: HWB): Keyword; 132 | keyword(...from: HWB): Keyword; 133 | ansi16(from: HWB): ANSI16; 134 | ansi16(...from: HWB): ANSI16; 135 | ansi256(from: HWB): ANSI256; 136 | ansi256(...from: HWB): ANSI256; 137 | hcg(from: HWB): HCG; 138 | hcg(...from: HWB): HCG; 139 | apple(from: HWB): Apple; 140 | apple(...from: HWB): Apple; 141 | gray(from: HWB): Gray; 142 | gray(...from: HWB): Gray; 143 | 144 | }; 145 | 146 | type cmyk = { 147 | rgb(from: CMYK): RGB; 148 | rgb(...from: CMYK): RGB; 149 | hsl(from: CMYK): HSL; 150 | hsl(...from: CMYK): HSL; 151 | hsv(from: CMYK): HSV; 152 | hsv(...from: CMYK): HSV; 153 | hwb(from: CMYK): HWB; 154 | hwb(...from: CMYK): HWB; 155 | xyz(from: CMYK): XYZ; 156 | xyz(...from: CMYK): XYZ; 157 | lab(from: CMYK): LAB; 158 | lab(...from: CMYK): LAB; 159 | lch(from: CMYK): LCH; 160 | lch(...from: CMYK): LCH; 161 | hex(from: CMYK): HEX; 162 | hex(...from: CMYK): HEX; 163 | keyword(from: CMYK): Keyword; 164 | keyword(...from: CMYK): Keyword; 165 | ansi16(from: CMYK): ANSI16; 166 | ansi16(...from: CMYK): ANSI16; 167 | ansi256(from: CMYK): ANSI256; 168 | ansi256(...from: CMYK): ANSI256; 169 | hcg(from: CMYK): HCG; 170 | hcg(...from: CMYK): HCG; 171 | apple(from: CMYK): Apple; 172 | apple(...from: CMYK): Apple; 173 | gray(from: CMYK): Gray; 174 | gray(...from: CMYK): Gray; 175 | }; 176 | 177 | type xyz = { 178 | rgb(from: XYZ): RGB; 179 | rgb(...from: XYZ): RGB; 180 | hsl(from: XYZ): HSL; 181 | hsl(...from: XYZ): HSL; 182 | hsv(from: XYZ): HSV; 183 | hsv(...from: XYZ): HSV; 184 | hwb(from: XYZ): HWB; 185 | hwb(...from: XYZ): HWB; 186 | cmyk(from: XYZ): CMYK; 187 | cmyk(...from: XYZ): CMYK; 188 | lab(from: XYZ): LAB; 189 | lab(...from: XYZ): LAB; 190 | lch(from: XYZ): LCH; 191 | lch(...from: XYZ): LCH; 192 | hex(from: XYZ): HEX; 193 | hex(...from: XYZ): HEX; 194 | keyword(from: XYZ): Keyword; 195 | keyword(...from: XYZ): Keyword; 196 | ansi16(from: XYZ): ANSI16; 197 | ansi16(...from: XYZ): ANSI16; 198 | ansi256(from: XYZ): ANSI256; 199 | ansi256(...from: XYZ): ANSI256; 200 | hcg(from: XYZ): HCG; 201 | hcg(...from: XYZ): HCG; 202 | apple(from: XYZ): Apple; 203 | apple(...from: XYZ): Apple; 204 | gray(from: XYZ): Gray; 205 | gray(...from: XYZ): Gray; 206 | }; 207 | 208 | type lab = { 209 | rgb(from: LAB): RGB; 210 | rgb(...from: LAB): RGB; 211 | hsl(from: LAB): HSL; 212 | hsl(...from: LAB): HSL; 213 | hsv(from: LAB): HSV; 214 | hsv(...from: LAB): HSV; 215 | hwb(from: LAB): HWB; 216 | hwb(...from: LAB): HWB; 217 | cmyk(from: LAB): CMYK; 218 | cmyk(...from: LAB): CMYK; 219 | xyz(from: LAB): XYZ; 220 | xyz(...from: LAB): XYZ; 221 | lch(from: LAB): LCH; 222 | lch(...from: LAB): LCH; 223 | hex(from: LAB): HEX; 224 | hex(...from: LAB): HEX; 225 | keyword(from: LAB): Keyword; 226 | keyword(...from: LAB): Keyword; 227 | ansi16(from: LAB): ANSI16; 228 | ansi16(...from: LAB): ANSI16; 229 | ansi256(from: LAB): ANSI256; 230 | ansi256(...from: LAB): ANSI256; 231 | hcg(from: LAB): HCG; 232 | hcg(...from: LAB): HCG; 233 | apple(from: LAB): Apple; 234 | apple(...from: LAB): Apple; 235 | gray(from: LAB): Gray; 236 | gray(...from: LAB): Gray; 237 | }; 238 | 239 | type lch = { 240 | rgb(from: LCH): RGB; 241 | rgb(...from: LCH): RGB; 242 | hsl(from: LCH): HSL; 243 | hsl(...from: LCH): HSL; 244 | hsv(from: LCH): HSV; 245 | hsv(...from: LCH): HSV; 246 | hwb(from: LCH): HWB; 247 | hwb(...from: LCH): HWB; 248 | cmyk(from: LCH): CMYK; 249 | cmyk(...from: LCH): CMYK; 250 | xyz(from: LCH): XYZ; 251 | xyz(...from: LCH): XYZ; 252 | lab(from: LCH): LAB; 253 | lab(...from: LCH): LAB; 254 | hex(from: LCH): HEX; 255 | hex(...from: LCH): HEX; 256 | keyword(from: LCH): Keyword; 257 | keyword(...from: LCH): Keyword; 258 | ansi16(from: LCH): ANSI16; 259 | ansi16(...from: LCH): ANSI16; 260 | ansi256(from: LCH): ANSI256; 261 | ansi256(...from: LCH): ANSI256; 262 | hcg(from: LCH): HCG; 263 | hcg(...from: LCH): HCG; 264 | apple(from: LCH): Apple; 265 | apple(...from: LCH): Apple; 266 | gray(from: LCH): Gray; 267 | gray(...from: LCH): Gray; 268 | }; 269 | 270 | type hex = { 271 | rgb(from: HEX): RGB; 272 | hsl(from: HEX): HSL; 273 | hsv(from: HEX): HSV; 274 | hwb(from: HEX): HWB; 275 | cmyk(from: HEX): CMYK; 276 | xyz(from: HEX): XYZ; 277 | lab(from: HEX): LAB; 278 | lch(from: HEX): LCH; 279 | keyword(from: HEX): Keyword; 280 | ansi16(from: HEX): ANSI16; 281 | ansi256(from: HEX): ANSI256; 282 | hcg(from: HEX): HCG; 283 | apple(from: HEX): Apple; 284 | gray(from: HEX): Gray; 285 | }; 286 | 287 | type keyword = { 288 | rgb(from: Keyword): RGB; 289 | hsl(from: Keyword): HSL; 290 | hsv(from: Keyword): HSV; 291 | hwb(from: Keyword): HWB; 292 | cmyk(from: Keyword): CMYK; 293 | xyz(from: Keyword): XYZ; 294 | lab(from: Keyword): LAB; 295 | lch(from: Keyword): LCH; 296 | hex(from: Keyword): HEX; 297 | ansi16(from: Keyword): ANSI16; 298 | ansi256(from: Keyword): ANSI256; 299 | hcg(from: Keyword): HCG; 300 | apple(from: Keyword): Apple; 301 | gray(from: Keyword): Gray; 302 | }; 303 | 304 | type ansi16 = { 305 | rgb(from: ANSI16): RGB; 306 | hsl(from: ANSI16): HSL; 307 | hsv(from: ANSI16): HSV; 308 | hwb(from: ANSI16): HWB; 309 | cmyk(from: ANSI16): CMYK; 310 | xyz(from: ANSI16): XYZ; 311 | lab(from: ANSI16): LAB; 312 | lch(from: ANSI16): LCH; 313 | hex(from: ANSI16): HEX; 314 | keyword(from: ANSI16): Keyword; 315 | ansi256(from: ANSI16): ANSI256; 316 | hcg(from: ANSI16): HCG; 317 | apple(from: ANSI16): Apple; 318 | gray(from: ANSI16): Gray; 319 | }; 320 | 321 | type ansi256 = { 322 | rgb(from: ANSI256): RGB; 323 | hsl(from: ANSI256): HSL; 324 | hsv(from: ANSI256): HSV; 325 | hwb(from: ANSI256): HWB; 326 | cmyk(from: ANSI256): CMYK; 327 | xyz(from: ANSI256): XYZ; 328 | lab(from: ANSI256): LAB; 329 | lch(from: ANSI256): LCH; 330 | hex(from: ANSI256): HEX; 331 | keyword(from: ANSI256): Keyword; 332 | ansi16(from: ANSI256): ANSI16; 333 | hcg(from: ANSI256): HCG; 334 | apple(from: ANSI256): Apple; 335 | gray(from: ANSI256): Gray; 336 | }; 337 | 338 | type hcg = { 339 | rgb(from: HCG): RGB; 340 | rgb(...from: HCG): RGB; 341 | hsl(from: HCG): HSL; 342 | hsl(...from: HCG): HSL; 343 | hsv(from: HCG): HSV; 344 | hsv(...from: HCG): HSV; 345 | hwb(from: HCG): HWB; 346 | hwb(...from: HCG): HWB; 347 | cmyk(from: HCG): CMYK; 348 | cmyk(...from: HCG): CMYK; 349 | xyz(from: HCG): XYZ; 350 | xyz(...from: HCG): XYZ; 351 | lab(from: HCG): LAB; 352 | lab(...from: HCG): LAB; 353 | lch(from: HCG): LCH; 354 | lch(...from: HCG): LCH; 355 | hex(from: HCG): HEX; 356 | hex(...from: HCG): HEX; 357 | keyword(from: HCG): Keyword; 358 | keyword(...from: HCG): Keyword; 359 | ansi16(from: HCG): ANSI16; 360 | ansi16(...from: HCG): ANSI16; 361 | ansi256(from: HCG): ANSI256; 362 | ansi256(...from: HCG): ANSI256; 363 | apple(from: HCG): Apple; 364 | apple(...from: HCG): Apple; 365 | gray(from: HCG): Gray; 366 | gray(...from: HCG): Gray; 367 | 368 | }; 369 | 370 | type apple = { 371 | rgb(from: Apple): RGB; 372 | rgb(...from: Apple): RGB; 373 | hsl(from: Apple): HSL; 374 | hsl(...from: Apple): HSL; 375 | hsv(from: Apple): HSV; 376 | hsv(...from: Apple): HSV; 377 | hwb(from: Apple): HWB; 378 | hwb(...from: Apple): HWB; 379 | cmyk(from: Apple): CMYK; 380 | cmyk(...from: Apple): CMYK; 381 | xyz(from: Apple): XYZ; 382 | xyz(...from: Apple): XYZ; 383 | lab(from: Apple): LAB; 384 | lab(...from: Apple): LAB; 385 | lch(from: Apple): LCH; 386 | lch(...from: Apple): LCH; 387 | hex(from: Apple): HEX; 388 | hex(...from: Apple): HEX; 389 | keyword(from: Apple): Keyword; 390 | keyword(...from: Apple): Keyword; 391 | ansi16(from: Apple): ANSI16; 392 | ansi16(...from: Apple): ANSI16; 393 | ansi256(from: Apple): ANSI256; 394 | ansi256(...from: Apple): ANSI256; 395 | hcg(from: Apple): HCG; 396 | hcg(...from: Apple): HCG; 397 | gray(from: Apple): Gray; 398 | gray(...from: Apple): Gray; 399 | }; 400 | 401 | type gray = { 402 | rgb(from: Gray): RGB; 403 | rgb(...from: Gray): RGB; 404 | hsl(from: Gray): HSL; 405 | hsl(...from: Gray): HSL; 406 | hsv(from: Gray): HSV; 407 | hsv(...from: Gray): HSV; 408 | hwb(from: Gray): HWB; 409 | hwb(...from: Gray): HWB; 410 | cmyk(from: Gray): CMYK; 411 | cmyk(...from: Gray): CMYK; 412 | xyz(from: Gray): XYZ; 413 | xyz(...from: Gray): XYZ; 414 | lab(from: Gray): LAB; 415 | lab(...from: Gray): LAB; 416 | lch(from: Gray): LCH; 417 | lch(...from: Gray): LCH; 418 | hex(from: Gray): HEX; 419 | hex(...from: Gray): HEX; 420 | keyword(from: Gray): Keyword; 421 | keyword(...from: Gray): Keyword; 422 | ansi16(from: Gray): ANSI16; 423 | ansi16(...from: Gray): ANSI16; 424 | ansi256(from: Gray): ANSI256; 425 | ansi256(...from: Gray): ANSI256; 426 | hcg(from: Gray): HCG; 427 | hcg(...from: Gray): HCG; 428 | apple(from: Gray): Apple; 429 | apple(...from: Gray): Apple; 430 | }; 431 | } 432 | 433 | declare function route(fromModel: 'rgb'): route.rgb; 434 | declare function route(fromModel: 'hsl'): route.hsl; 435 | declare function route(fromModel: 'hsv'): route.hsv; 436 | declare function route(fromModel: 'hwb'): route.hwb; 437 | declare function route(fromModel: 'cmyk'): route.cmyk; 438 | declare function route(fromModel: 'xyz'): route.xyz; 439 | declare function route(fromModel: 'lab'): route.lab; 440 | declare function route(fromModel: 'lch'): route.lch; 441 | declare function route(fromModel: 'hex'): route.hex; 442 | declare function route(fromModel: 'keyword'): route.keyword; 443 | declare function route(fromModel: 'ansi16'): route.ansi16; 444 | declare function route(fromModel: 'ansi256'): route.ansi256; 445 | declare function route(fromModel: 'hcg'): route.hcg; 446 | declare function route(fromModel: 'apple'): route.apple; 447 | declare function route(fromModel: 'gray'): route.gray; 448 | 449 | export type Convert = { 450 | rgb: { 451 | channels: Channels; 452 | labels: 'rgb'; 453 | hsl: { 454 | (...rgb: RGB): HSL; 455 | raw: (...rgb: RGB) => HSL; 456 | }; 457 | hsv: { 458 | (...rgb: RGB): HSV; 459 | raw: (...rgb: RGB) => HSV; 460 | }; 461 | hwb: { 462 | (...rgb: RGB): HWB; 463 | raw: (...rgb: RGB) => HWB; 464 | }; 465 | hcg: { 466 | (...rgb: RGB): HCG; 467 | raw: (...rgb: RGB) => HCG; 468 | }; 469 | cmyk: { 470 | (...rgb: RGB): CMYK; 471 | raw: (...rgb: RGB) => CMYK; 472 | }; 473 | keyword: { 474 | (...rgb: RGB): Keyword; 475 | raw: (...rgb: RGB) => Keyword; 476 | }; 477 | ansi16: { 478 | (...rgb: RGB): ANSI16; 479 | raw: (...rgb: RGB) => ANSI16; 480 | }; 481 | ansi256: { 482 | (...rgb: RGB): ANSI256; 483 | raw: (...rgb: RGB) => ANSI256; 484 | }; 485 | apple: { 486 | (...rgb: RGB): Apple; 487 | raw: (...rgb: RGB) => Apple; 488 | }; 489 | hex: { 490 | (...rgb: RGB): HEX; 491 | raw: (...rgb: RGB) => HEX; 492 | }; 493 | gray: { 494 | (...rgb: RGB): Gray; 495 | raw: (...rgb: RGB) => Gray; 496 | }; 497 | } & route.rgb & { 498 | [F in keyof route.rgb]: { 499 | raw: route.rgb[F]; 500 | }; 501 | }; 502 | keyword: { 503 | channels: Channels; 504 | rgb: { 505 | (keyword: Keyword): RGB; 506 | raw: (keyword: Keyword) => RGB; 507 | }; 508 | } & route.keyword & { 509 | [F in keyof route.keyword]: { 510 | raw: route.keyword[F]; 511 | }; 512 | }; 513 | hsl: { 514 | channels: Channels; 515 | labels: 'hsl'; 516 | rgb: { 517 | (...hsl: HSL): RGB; 518 | raw: (...hsl: HSL) => RGB; 519 | }; 520 | hsv: { 521 | (...hsl: HSL): HSV; 522 | raw: (...hsl: HSL) => HSV; 523 | }; 524 | hcg: { 525 | (...hsl: HSL): HCG; 526 | raw: (...hsl: HSL) => HCG; 527 | }; 528 | } & route.hsl & { 529 | [F in keyof route.hsl]: { 530 | raw: route.hsl[F]; 531 | }; 532 | }; 533 | hsv: { 534 | channels: Channels; 535 | labels: 'hsv'; 536 | hcg: { 537 | (...hsv: HSV): HCG; 538 | raw: (...hsv: HSV) => HCG; 539 | }; 540 | rgb: { 541 | (...hsv: HSV): RGB; 542 | raw: (...hsv: HSV) => RGB; 543 | }; 544 | hsv: { 545 | (...hsv: HSV): HSV; 546 | raw: (...hsv: HSV) => HSV; 547 | }; 548 | hsl: { 549 | (...hsv: HSV): HSL; 550 | raw: (...hsv: HSV) => HSL; 551 | }; 552 | hwb: { 553 | (...hsv: HSV): HWB; 554 | raw: (...hsv: HSV) => HWB; 555 | }; 556 | ansi16: { 557 | (...hsv: HSV): ANSI16; 558 | raw: (...hsv: HSV) => ANSI16; 559 | }; 560 | } & route.hsv & { 561 | [F in keyof route.hsv]: { 562 | raw: route.hsv[F]; 563 | }; 564 | }; 565 | hwb: { 566 | channels: Channels; 567 | labels: 'hwb'; 568 | hcg: { 569 | (...hwb: HWB): HCG; 570 | raw: (...hwb: HWB) => HCG; 571 | }; 572 | rgb: { 573 | (...hwb: HWB): RGB; 574 | raw: (...hwb: HWB) => RGB; 575 | }; 576 | } & route.hwb & { 577 | [F in keyof route.hwb]: { 578 | raw: route.hwb[F]; 579 | }; 580 | }; 581 | cmyk: { 582 | channels: Channels; 583 | labels: 'cmyk'; 584 | rgb: { 585 | (...cmyk: CMYK): RGB; 586 | raw: (...cmyk: CMYK) => RGB; 587 | }; 588 | } & route.cmyk & { 589 | [F in keyof route.cmyk]: { 590 | raw: route.cmyk[F]; 591 | }; 592 | }; 593 | xyz: { 594 | channels: Channels; 595 | labels: 'xyz'; 596 | rgb: { 597 | (...xyz: XYZ): RGB; 598 | raw: (...xyz: XYZ) => RGB; 599 | }; 600 | lab: { 601 | (...xyz: XYZ): LAB; 602 | raw: (...xyz: XYZ) => LAB; 603 | }; 604 | } & route.xyz & { 605 | [F in keyof route.xyz]: { 606 | raw: route.xyz[F]; 607 | }; 608 | }; 609 | lab: { 610 | channels: Channels; 611 | labels: 'lab'; 612 | xyz: { 613 | (...lab: LAB): XYZ; 614 | raw: (...lab: LAB) => XYZ; 615 | }; 616 | lch: { 617 | (...lab: LAB): LCH; 618 | raw: (...lab: LAB) => LCH; 619 | }; 620 | } & route.lab & { 621 | [F in keyof route.lab]: { 622 | raw: route.lab[F]; 623 | }; 624 | }; 625 | lch: { 626 | channels: Channels; 627 | labels: 'lch'; 628 | lab: { 629 | (...lch: LCH): LAB; 630 | raw: (...lch: LCH) => LAB; 631 | }; 632 | } & route.lch & { 633 | [F in keyof route.lch]: { 634 | raw: route.lch[F]; 635 | }; 636 | }; 637 | hex: { 638 | channels: Channels; 639 | labels: ['hex']; 640 | rgb: { 641 | (hex: HEX): RGB; 642 | raw: (hex: HEX) => RGB; 643 | }; 644 | } & route.hex & { 645 | [F in keyof route.hex]: { 646 | raw: route.hex[F]; 647 | }; 648 | }; 649 | ansi16: { 650 | channels: Channels; 651 | labels: ['ansi16']; 652 | rgb: { 653 | (ansi16: ANSI16): RGB; 654 | raw: (ansi16: ANSI16) => RGB; 655 | }; 656 | } & route.ansi16 & { 657 | [F in keyof route.ansi16]: { 658 | raw: route.ansi16[F]; 659 | }; 660 | }; 661 | ansi256: { 662 | channels: Channels; 663 | labels: ['ansi256']; 664 | rgb: { 665 | (ansi256: ANSI256): RGB; 666 | raw: (ansi256: ANSI256) => RGB; 667 | }; 668 | } & route.ansi256 & { 669 | [F in keyof route.ansi256]: { 670 | raw: route.ansi256[F]; 671 | }; 672 | }; 673 | hcg: { 674 | channels: Channels; 675 | labels: ['h', 'c', 'g']; 676 | rgb: { 677 | (...hcg: HCG): RGB; 678 | raw: (...hcg: HCG) => RGB; 679 | }; 680 | hsv: { 681 | (...hcg: HCG): HSV; 682 | raw: (...hcg: HCG) => HSV; 683 | }; 684 | hwb: { 685 | (...hcg: HCG): HWB; 686 | raw: (...hcg: HCG) => HWB; 687 | }; 688 | } & route.hcg & { 689 | [F in keyof route.hcg]: { 690 | raw: route.hcg[F]; 691 | }; 692 | }; 693 | apple: { 694 | channels: Channels; 695 | labels: ['r16', 'g16', 'b16']; 696 | rgb: { 697 | (...apple: Apple): RGB; 698 | raw: (...apple: Apple) => RGB; 699 | }; 700 | } & route.apple & { 701 | [F in keyof route.apple]: { 702 | raw: route.apple[F]; 703 | }; 704 | }; 705 | gray: { 706 | channels: Channels; 707 | labels: ['gray']; 708 | rgb: { 709 | (...gray: Gray): RGB; 710 | raw: (...gray: Gray) => RGB; 711 | }; 712 | hsl: { 713 | (...gray: Gray): HSL; 714 | raw: (...gray: Gray) => HSL; 715 | }; 716 | hsv: { 717 | (...gray: Gray): HSV; 718 | raw: (...gray: Gray) => HSV; 719 | }; 720 | hwb: { 721 | (...gray: Gray): HWB; 722 | raw: (...gray: Gray) => HWB; 723 | }; 724 | cmyk: { 725 | (...gray: Gray): CMYK; 726 | raw: (...gray: Gray) => CMYK; 727 | }; 728 | lab: { 729 | (...gray: Gray): LAB; 730 | raw: (...gray: Gray) => LAB; 731 | }; 732 | hex: { 733 | (...gray: Gray): HEX; 734 | raw: (...gray: Gray) => HEX; 735 | }; 736 | } & route.gray & { 737 | [F in keyof route.gray]: { 738 | raw: route.gray[F]; 739 | }; 740 | }; 741 | }; 742 | 743 | declare const convert: Convert; 744 | export default convert; 745 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import conversions from './conversions.js'; 2 | import route from './route.js'; 3 | 4 | const convert = {}; 5 | 6 | const models = Object.keys(conversions); 7 | 8 | function wrapRaw(fn) { 9 | const wrappedFn = function (...args) { 10 | const arg0 = args[0]; 11 | if (arg0 === undefined || arg0 === null) { 12 | return arg0; 13 | } 14 | 15 | if (arg0.length > 1) { 16 | args = arg0; 17 | } 18 | 19 | return fn(args); 20 | }; 21 | 22 | // Preserve .conversion property if there is one 23 | if ('conversion' in fn) { 24 | wrappedFn.conversion = fn.conversion; 25 | } 26 | 27 | return wrappedFn; 28 | } 29 | 30 | function wrapRounded(fn) { 31 | const wrappedFn = function (...args) { 32 | const arg0 = args[0]; 33 | 34 | if (arg0 === undefined || arg0 === null) { 35 | return arg0; 36 | } 37 | 38 | if (arg0.length > 1) { 39 | args = arg0; 40 | } 41 | 42 | const result = fn(args); 43 | 44 | // We're assuming the result is an array here. 45 | // see notice in conversions.js; don't use box types 46 | // in conversion functions. 47 | if (typeof result === 'object') { 48 | for (let {length} = result, i = 0; i < length; i++) { 49 | result[i] = Math.round(result[i]); 50 | } 51 | } 52 | 53 | return result; 54 | }; 55 | 56 | // Preserve .conversion property if there is one 57 | if ('conversion' in fn) { 58 | wrappedFn.conversion = fn.conversion; 59 | } 60 | 61 | return wrappedFn; 62 | } 63 | 64 | for (const fromModel of models) { 65 | convert[fromModel] = {}; 66 | 67 | Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); 68 | Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); 69 | 70 | const routes = route(fromModel); 71 | const routeModels = Object.keys(routes); 72 | 73 | for (const toModel of routeModels) { 74 | const fn = routes[toModel]; 75 | 76 | convert[fromModel][toModel] = wrapRounded(fn); 77 | convert[fromModel][toModel].raw = wrapRaw(fn); 78 | } 79 | } 80 | 81 | export default convert; 82 | -------------------------------------------------------------------------------- /index.test-d.ts: -------------------------------------------------------------------------------- 1 | import {expectType} from 'tsd'; 2 | import convert, {type Channels, type RGB, type HSL, type HSV, type CMYK, type LAB, type LCH, type HCG, type HWB, type XYZ, type Apple, type Gray, type ANSI16, type ANSI256, type Keyword, type HEX} from './index.js'; 3 | 4 | // RGB 5 | expectType(convert.rgb.channels); 6 | expectType(convert.rgb.hsl(0, 0, 0)); 7 | expectType(convert.rgb.hsl.raw(0, 0, 0)); 8 | expectType(convert.rgb.hsv(0, 0, 0)); 9 | expectType(convert.rgb.hsv.raw(0, 0, 0)); 10 | expectType(convert.rgb.hwb(0, 0, 0)); 11 | expectType(convert.rgb.hwb.raw(0, 0, 0)); 12 | expectType(convert.rgb.hcg(0, 0, 0)); 13 | expectType(convert.rgb.hcg.raw(0, 0, 0)); 14 | expectType(convert.rgb.cmyk(0, 0, 0)); 15 | expectType(convert.rgb.cmyk.raw(0, 0, 0)); 16 | expectType(convert.rgb.keyword(0, 0, 0)); 17 | expectType(convert.rgb.keyword.raw(0, 0, 0)); 18 | expectType(convert.rgb.ansi16(0, 0, 0)); 19 | expectType(convert.rgb.ansi16.raw(0, 0, 0)); 20 | expectType(convert.rgb.ansi256(0, 0, 0)); 21 | expectType(convert.rgb.ansi256.raw(0, 0, 0)); 22 | expectType(convert.rgb.apple(0, 0, 0)); 23 | expectType(convert.rgb.apple.raw(0, 0, 0)); 24 | expectType(convert.rgb.hex(0, 0, 0)); 25 | expectType(convert.rgb.hex.raw(0, 0, 0)); 26 | expectType(convert.rgb.gray(0, 0, 0)); 27 | expectType(convert.rgb.gray.raw(0, 0, 0)); 28 | // - automatic conversions 29 | expectType(convert.rgb.xyz(0, 0, 0)); 30 | expectType(convert.rgb.xyz.raw(0, 0, 0)); 31 | expectType(convert.rgb.lab(0, 0, 0)); 32 | expectType(convert.rgb.lab.raw(0, 0, 0)); 33 | expectType(convert.rgb.lch(0, 0, 0)); 34 | expectType(convert.rgb.lch.raw(0, 0, 0)); 35 | 36 | // Keyword 37 | expectType(convert.keyword.channels); 38 | expectType(convert.keyword.rgb('blue')); 39 | expectType(convert.keyword.rgb.raw('blue')); 40 | // - automatic conversions 41 | expectType(convert.keyword.hsl('blue')); 42 | expectType(convert.keyword.hsl.raw('blue')); 43 | expectType(convert.keyword.hsv('blue')); 44 | expectType(convert.keyword.hsv.raw('blue')); 45 | expectType(convert.keyword.hwb('blue')); 46 | expectType(convert.keyword.hwb.raw('blue')); 47 | expectType(convert.keyword.cmyk('blue')); 48 | expectType(convert.keyword.cmyk.raw('blue')); 49 | expectType(convert.keyword.xyz('blue')); 50 | expectType(convert.keyword.xyz.raw('blue')); 51 | expectType(convert.keyword.lab('blue')); 52 | expectType(convert.keyword.lab.raw('blue')); 53 | expectType(convert.keyword.lch('blue')); 54 | expectType(convert.keyword.lch.raw('blue')); 55 | expectType(convert.keyword.hex('blue')); 56 | expectType(convert.keyword.hex.raw('blue')); 57 | expectType(convert.keyword.ansi16('blue')); 58 | expectType(convert.keyword.ansi16.raw('blue')); 59 | expectType(convert.keyword.ansi256('blue')); 60 | expectType(convert.keyword.ansi256.raw('blue')); 61 | expectType(convert.keyword.hcg('blue')); 62 | expectType(convert.keyword.hcg.raw('blue')); 63 | expectType(convert.keyword.apple('blue')); 64 | expectType(convert.keyword.apple.raw('blue')); 65 | expectType(convert.keyword.gray('blue')); 66 | expectType(convert.keyword.gray.raw('blue')); 67 | 68 | // HSL 69 | expectType(convert.hsl.channels); 70 | expectType(convert.hsl.rgb(0, 0, 0)); 71 | expectType(convert.hsl.rgb.raw(0, 0, 0)); 72 | expectType(convert.hsl.hsv(0, 0, 0)); 73 | expectType(convert.hsl.hsv.raw(0, 0, 0)); 74 | expectType(convert.hsl.hcg(0, 0, 0)); 75 | expectType(convert.hsl.hcg.raw(0, 0, 0)); 76 | // - automatic conversions 77 | expectType(convert.hsl.hwb(0, 0, 0)); 78 | expectType(convert.hsl.hwb.raw(0, 0, 0)); 79 | expectType(convert.hsl.cmyk(0, 0, 0)); 80 | expectType(convert.hsl.cmyk.raw(0, 0, 0)); 81 | expectType(convert.hsl.xyz(0, 0, 0)); 82 | expectType(convert.hsl.xyz.raw(0, 0, 0)); 83 | expectType(convert.hsl.lab(0, 0, 0)); 84 | expectType(convert.hsl.lab.raw(0, 0, 0)); 85 | expectType(convert.hsl.lch(0, 0, 0)); 86 | expectType(convert.hsl.lch.raw(0, 0, 0)); 87 | expectType(convert.hsl.hex(0, 0, 0)); 88 | expectType(convert.hsl.hex.raw(0, 0, 0)); 89 | expectType(convert.hsl.keyword(0, 0, 0)); 90 | expectType(convert.hsl.keyword.raw(0, 0, 0)); 91 | expectType(convert.hsl.ansi16(0, 0, 0)); 92 | expectType(convert.hsl.ansi16.raw(0, 0, 0)); 93 | expectType(convert.hsl.ansi256(0, 0, 0)); 94 | expectType(convert.hsl.ansi256.raw(0, 0, 0)); 95 | expectType(convert.hsl.apple(0, 0, 0)); 96 | expectType(convert.hsl.apple.raw(0, 0, 0)); 97 | expectType(convert.hsl.gray(0, 0, 0)); 98 | expectType(convert.hsl.gray.raw(0, 0, 0)); 99 | 100 | // HSV 101 | expectType(convert.hsv.channels); 102 | expectType(convert.hsv.rgb(0, 0, 0)); 103 | expectType(convert.hsv.rgb.raw(0, 0, 0)); 104 | expectType(convert.hsv.hsl(0, 0, 0)); 105 | expectType(convert.hsv.hsl.raw(0, 0, 0)); 106 | expectType(convert.hsv.hcg(0, 0, 0)); 107 | expectType(convert.hsv.hcg.raw(0, 0, 0)); 108 | expectType(convert.hsv.hwb(0, 0, 0)); 109 | expectType(convert.hsv.hwb.raw(0, 0, 0)); 110 | expectType(convert.hsv.ansi16(0, 0, 0)); 111 | // - automatic conversions 112 | expectType(convert.hsv.ansi16.raw(0, 0, 0)); 113 | expectType(convert.hsv.ansi256(0, 0, 0)); 114 | expectType(convert.hsv.ansi256.raw(0, 0, 0)); 115 | expectType(convert.hsv.apple(0, 0, 0)); 116 | expectType(convert.hsv.apple.raw(0, 0, 0)); 117 | expectType(convert.hsv.gray(0, 0, 0)); 118 | expectType(convert.hsv.gray.raw(0, 0, 0)); 119 | expectType(convert.hsv.cmyk(0, 0, 0)); 120 | expectType(convert.hsv.cmyk.raw(0, 0, 0)); 121 | expectType(convert.hsv.xyz(0, 0, 0)); 122 | expectType(convert.hsv.xyz.raw(0, 0, 0)); 123 | expectType(convert.hsv.lab(0, 0, 0)); 124 | expectType(convert.hsv.lab.raw(0, 0, 0)); 125 | expectType(convert.hsv.lch(0, 0, 0)); 126 | expectType(convert.hsv.lch.raw(0, 0, 0)); 127 | expectType(convert.hsv.hex(0, 0, 0)); 128 | expectType(convert.hsv.hex.raw(0, 0, 0)); 129 | expectType(convert.hsv.keyword(0, 0, 0)); 130 | expectType(convert.hsv.keyword.raw(0, 0, 0)); 131 | 132 | // HWB 133 | expectType(convert.hwb.channels); 134 | expectType(convert.hwb.rgb(0, 0, 0)); 135 | expectType(convert.hwb.rgb.raw(0, 0, 0)); 136 | // - automatic conversions 137 | expectType(convert.hwb.hsl(0, 0, 0)); 138 | expectType(convert.hwb.hsl.raw(0, 0, 0)); 139 | expectType(convert.hwb.hsv(0, 0, 0)); 140 | expectType(convert.hwb.hsv.raw(0, 0, 0)); 141 | expectType(convert.hwb.cmyk(0, 0, 0)); 142 | expectType(convert.hwb.cmyk.raw(0, 0, 0)); 143 | expectType(convert.hwb.xyz(0, 0, 0)); 144 | expectType(convert.hwb.xyz.raw(0, 0, 0)); 145 | expectType(convert.hwb.lab(0, 0, 0)); 146 | expectType(convert.hwb.lab.raw(0, 0, 0)); 147 | expectType(convert.hwb.lch(0, 0, 0)); 148 | expectType(convert.hwb.lch.raw(0, 0, 0)); 149 | expectType(convert.hwb.hex(0, 0, 0)); 150 | expectType(convert.hwb.hex.raw(0, 0, 0)); 151 | expectType(convert.hwb.keyword(0, 0, 0)); 152 | expectType(convert.hwb.keyword.raw(0, 0, 0)); 153 | expectType(convert.hwb.ansi16(0, 0, 0)); 154 | expectType(convert.hwb.ansi16.raw(0, 0, 0)); 155 | expectType(convert.hwb.ansi256(0, 0, 0)); 156 | expectType(convert.hwb.ansi256.raw(0, 0, 0)); 157 | expectType(convert.hwb.hcg(0, 0, 0)); 158 | expectType(convert.hwb.hcg.raw(0, 0, 0)); 159 | expectType(convert.hwb.apple(0, 0, 0)); 160 | expectType(convert.hwb.apple.raw(0, 0, 0)); 161 | expectType(convert.hwb.gray(0, 0, 0)); 162 | expectType(convert.hwb.gray.raw(0, 0, 0)); 163 | 164 | // CMYK 165 | expectType(convert.cmyk.channels); 166 | expectType(convert.cmyk.rgb(0, 0, 0, 0)); 167 | expectType(convert.cmyk.rgb.raw(0, 0, 0, 0)); 168 | // - automatic conversions 169 | expectType(convert.cmyk.hsl(0, 0, 0, 0)); 170 | expectType(convert.cmyk.hsl.raw(0, 0, 0, 0)); 171 | expectType(convert.cmyk.hsv(0, 0, 0, 0)); 172 | expectType(convert.cmyk.hsv.raw(0, 0, 0, 0)); 173 | expectType(convert.cmyk.hwb(0, 0, 0, 0)); 174 | expectType(convert.cmyk.hwb.raw(0, 0, 0, 0)); 175 | expectType(convert.cmyk.xyz(0, 0, 0, 0)); 176 | expectType(convert.cmyk.xyz.raw(0, 0, 0, 0)); 177 | expectType(convert.cmyk.lab(0, 0, 0, 0)); 178 | expectType(convert.cmyk.lab.raw(0, 0, 0, 0)); 179 | expectType(convert.cmyk.lch(0, 0, 0, 0)); 180 | expectType(convert.cmyk.lch.raw(0, 0, 0, 0)); 181 | expectType(convert.cmyk.hex(0, 0, 0, 0)); 182 | expectType(convert.cmyk.hex.raw(0, 0, 0, 0)); 183 | expectType(convert.cmyk.keyword(0, 0, 0, 0)); 184 | expectType(convert.cmyk.keyword.raw(0, 0, 0, 0)); 185 | expectType(convert.cmyk.ansi16(0, 0, 0, 0)); 186 | expectType(convert.cmyk.ansi16.raw(0, 0, 0, 0)); 187 | expectType(convert.cmyk.ansi256(0, 0, 0, 0)); 188 | expectType(convert.cmyk.ansi256.raw(0, 0, 0, 0)); 189 | expectType(convert.cmyk.hcg(0, 0, 0, 0)); 190 | expectType(convert.cmyk.hcg.raw(0, 0, 0, 0)); 191 | expectType(convert.cmyk.apple(0, 0, 0, 0)); 192 | expectType(convert.cmyk.apple.raw(0, 0, 0, 0)); 193 | expectType(convert.cmyk.gray(0, 0, 0, 0)); 194 | expectType(convert.cmyk.gray.raw(0, 0, 0, 0)); 195 | 196 | // XYZ 197 | expectType(convert.xyz.channels); 198 | expectType(convert.xyz.rgb(0, 0, 0)); 199 | expectType(convert.xyz.rgb.raw(0, 0, 0)); 200 | expectType(convert.xyz.lab(0, 0, 0)); 201 | expectType(convert.xyz.lab.raw(0, 0, 0)); 202 | // - automatic conversions 203 | expectType(convert.xyz.hsl(0, 0, 0)); 204 | expectType(convert.xyz.hsl.raw(0, 0, 0)); 205 | expectType(convert.xyz.hsv(0, 0, 0)); 206 | expectType(convert.xyz.hsv.raw(0, 0, 0)); 207 | expectType(convert.xyz.hwb(0, 0, 0)); 208 | expectType(convert.xyz.hwb.raw(0, 0, 0)); 209 | expectType(convert.xyz.cmyk(0, 0, 0)); 210 | expectType(convert.xyz.cmyk.raw(0, 0, 0)); 211 | expectType(convert.xyz.lch(0, 0, 0)); 212 | expectType(convert.xyz.lch.raw(0, 0, 0)); 213 | expectType(convert.xyz.hex(0, 0, 0)); 214 | expectType(convert.xyz.hex.raw(0, 0, 0)); 215 | expectType(convert.xyz.keyword(0, 0, 0)); 216 | expectType(convert.xyz.keyword.raw(0, 0, 0)); 217 | expectType(convert.xyz.ansi16(0, 0, 0)); 218 | expectType(convert.xyz.ansi16.raw(0, 0, 0)); 219 | expectType(convert.xyz.ansi256(0, 0, 0)); 220 | expectType(convert.xyz.ansi256.raw(0, 0, 0)); 221 | expectType(convert.xyz.hcg(0, 0, 0)); 222 | expectType(convert.xyz.hcg.raw(0, 0, 0)); 223 | expectType(convert.xyz.apple(0, 0, 0)); 224 | expectType(convert.xyz.apple.raw(0, 0, 0)); 225 | expectType(convert.xyz.gray(0, 0, 0)); 226 | expectType(convert.xyz.gray.raw(0, 0, 0)); 227 | 228 | // LAB 229 | expectType(convert.lab.channels); 230 | expectType(convert.lab.xyz(0, 0, 0)); 231 | expectType(convert.lab.xyz.raw(0, 0, 0)); 232 | expectType(convert.lab.lch(0, 0, 0)); 233 | expectType(convert.lab.lch.raw(0, 0, 0)); 234 | // - automatic conversions 235 | expectType(convert.lab.rgb(0, 0, 0)); 236 | expectType(convert.lab.rgb.raw(0, 0, 0)); 237 | expectType(convert.lab.hsl(0, 0, 0)); 238 | expectType(convert.lab.hsl.raw(0, 0, 0)); 239 | expectType(convert.lab.hsv(0, 0, 0)); 240 | expectType(convert.lab.hsv.raw(0, 0, 0)); 241 | expectType(convert.lab.hwb(0, 0, 0)); 242 | expectType(convert.lab.hwb.raw(0, 0, 0)); 243 | expectType(convert.lab.cmyk(0, 0, 0)); 244 | expectType(convert.lab.cmyk.raw(0, 0, 0)); 245 | expectType(convert.lab.hex(0, 0, 0)); 246 | expectType(convert.lab.hex.raw(0, 0, 0)); 247 | expectType(convert.lab.keyword(0, 0, 0)); 248 | expectType(convert.lab.keyword.raw(0, 0, 0)); 249 | expectType(convert.lab.ansi16(0, 0, 0)); 250 | expectType(convert.lab.ansi16.raw(0, 0, 0)); 251 | expectType(convert.lab.ansi256(0, 0, 0)); 252 | expectType(convert.lab.ansi256.raw(0, 0, 0)); 253 | expectType(convert.lab.hcg(0, 0, 0)); 254 | expectType(convert.lab.hcg.raw(0, 0, 0)); 255 | expectType(convert.lab.apple(0, 0, 0)); 256 | expectType(convert.lab.apple.raw(0, 0, 0)); 257 | expectType(convert.lab.gray(0, 0, 0)); 258 | expectType(convert.lab.gray.raw(0, 0, 0)); 259 | 260 | // LCH 261 | expectType(convert.lch.channels); 262 | expectType(convert.lch.lab(0, 0, 0)); 263 | expectType(convert.lch.lab.raw(0, 0, 0)); 264 | // - automatic conversions 265 | expectType(convert.lch.rgb(0, 0, 0)); 266 | expectType(convert.lch.rgb.raw(0, 0, 0)); 267 | expectType(convert.lch.hsl(0, 0, 0)); 268 | expectType(convert.lch.hsl.raw(0, 0, 0)); 269 | expectType(convert.lch.hsv(0, 0, 0)); 270 | expectType(convert.lch.hsv.raw(0, 0, 0)); 271 | expectType(convert.lch.hwb(0, 0, 0)); 272 | expectType(convert.lch.hwb.raw(0, 0, 0)); 273 | expectType(convert.lch.cmyk(0, 0, 0)); 274 | expectType(convert.lch.cmyk.raw(0, 0, 0)); 275 | expectType(convert.lch.xyz(0, 0, 0)); 276 | expectType(convert.lch.xyz.raw(0, 0, 0)); 277 | expectType(convert.lch.hex(0, 0, 0)); 278 | expectType(convert.lch.hex.raw(0, 0, 0)); 279 | expectType(convert.lch.keyword(0, 0, 0)); 280 | expectType(convert.lch.keyword.raw(0, 0, 0)); 281 | expectType(convert.lch.ansi16(0, 0, 0)); 282 | expectType(convert.lch.ansi16.raw(0, 0, 0)); 283 | expectType(convert.lch.ansi256(0, 0, 0)); 284 | expectType(convert.lch.ansi256.raw(0, 0, 0)); 285 | expectType(convert.lch.hcg(0, 0, 0)); 286 | expectType(convert.lch.hcg.raw(0, 0, 0)); 287 | expectType(convert.lch.apple(0, 0, 0)); 288 | expectType(convert.lch.apple.raw(0, 0, 0)); 289 | expectType(convert.lch.gray(0, 0, 0)); 290 | expectType(convert.lch.gray.raw(0, 0, 0)); 291 | 292 | // HCG 293 | expectType(convert.hcg.channels); 294 | expectType(convert.hcg.rgb(0, 0, 0)); 295 | expectType(convert.hcg.rgb.raw(0, 0, 0)); 296 | expectType(convert.hcg.hsv(0, 0, 0)); 297 | expectType(convert.hcg.hsv.raw(0, 0, 0)); 298 | expectType(convert.hcg.hwb(0, 0, 0)); 299 | expectType(convert.hcg.hwb.raw(0, 0, 0)); 300 | // - automatic conversions 301 | expectType(convert.hcg.hsl(0, 0, 0)); 302 | expectType(convert.hcg.hsl.raw(0, 0, 0)); 303 | expectType(convert.hcg.cmyk(0, 0, 0)); 304 | expectType(convert.hcg.cmyk.raw(0, 0, 0)); 305 | expectType(convert.hcg.xyz(0, 0, 0)); 306 | expectType(convert.hcg.xyz.raw(0, 0, 0)); 307 | expectType(convert.hcg.lab(0, 0, 0)); 308 | expectType(convert.hcg.lab.raw(0, 0, 0)); 309 | expectType(convert.hcg.lch(0, 0, 0)); 310 | expectType(convert.hcg.lch.raw(0, 0, 0)); 311 | expectType(convert.hcg.hex(0, 0, 0)); 312 | expectType(convert.hcg.hex.raw(0, 0, 0)); 313 | expectType(convert.hcg.keyword(0, 0, 0)); 314 | expectType(convert.hcg.keyword.raw(0, 0, 0)); 315 | expectType(convert.hcg.ansi16(0, 0, 0)); 316 | expectType(convert.hcg.ansi16.raw(0, 0, 0)); 317 | expectType(convert.hcg.ansi256(0, 0, 0)); 318 | expectType(convert.hcg.ansi256.raw(0, 0, 0)); 319 | expectType(convert.hcg.apple(0, 0, 0)); 320 | expectType(convert.hcg.apple.raw(0, 0, 0)); 321 | expectType(convert.hcg.gray(0, 0, 0)); 322 | expectType(convert.hcg.gray.raw(0, 0, 0)); 323 | 324 | // Apple 325 | expectType(convert.apple.channels); 326 | expectType(convert.apple.rgb(0, 0, 0)); 327 | expectType(convert.apple.rgb.raw(0, 0, 0)); 328 | // - automatic conversions 329 | expectType(convert.apple.hsl(0, 0, 0)); 330 | expectType(convert.apple.hsl.raw(0, 0, 0)); 331 | expectType(convert.apple.hsv(0, 0, 0)); 332 | expectType(convert.apple.hsv.raw(0, 0, 0)); 333 | expectType(convert.apple.hwb(0, 0, 0)); 334 | expectType(convert.apple.hwb.raw(0, 0, 0)); 335 | expectType(convert.apple.cmyk(0, 0, 0)); 336 | expectType(convert.apple.cmyk.raw(0, 0, 0)); 337 | expectType(convert.apple.xyz(0, 0, 0)); 338 | expectType(convert.apple.xyz.raw(0, 0, 0)); 339 | expectType(convert.apple.lab(0, 0, 0)); 340 | expectType(convert.apple.lab.raw(0, 0, 0)); 341 | expectType(convert.apple.lch(0, 0, 0)); 342 | expectType(convert.apple.lch.raw(0, 0, 0)); 343 | expectType(convert.apple.hex(0, 0, 0)); 344 | expectType(convert.apple.hex.raw(0, 0, 0)); 345 | expectType(convert.apple.keyword(0, 0, 0)); 346 | expectType(convert.apple.keyword.raw(0, 0, 0)); 347 | expectType(convert.apple.ansi16(0, 0, 0)); 348 | expectType(convert.apple.ansi16.raw(0, 0, 0)); 349 | expectType(convert.apple.ansi256(0, 0, 0)); 350 | expectType(convert.apple.ansi256.raw(0, 0, 0)); 351 | expectType(convert.apple.hcg(0, 0, 0)); 352 | expectType(convert.apple.hcg.raw(0, 0, 0)); 353 | expectType(convert.apple.gray(0, 0, 0)); 354 | expectType(convert.apple.gray.raw(0, 0, 0)); 355 | 356 | // Gray 357 | expectType(convert.gray.channels); 358 | expectType(convert.gray.rgb(0)); 359 | expectType(convert.gray.rgb.raw(0)); 360 | expectType(convert.gray.hsl(0)); 361 | expectType(convert.gray.hsl.raw(0)); 362 | expectType(convert.gray.hsv(0)); 363 | expectType(convert.gray.hsv.raw(0)); 364 | expectType(convert.gray.hwb(0)); 365 | expectType(convert.gray.hwb.raw(0)); 366 | expectType(convert.gray.cmyk(0)); 367 | expectType(convert.gray.cmyk.raw(0)); 368 | expectType(convert.gray.lab(0)); 369 | expectType(convert.gray.lab.raw(0)); 370 | expectType(convert.gray.hex(0)); 371 | expectType(convert.gray.hex.raw(0)); 372 | // - automatic conversions 373 | expectType(convert.gray.xyz(0)); 374 | expectType(convert.gray.xyz.raw(0)); 375 | expectType(convert.gray.lch(0)); 376 | expectType(convert.gray.lch.raw(0)); 377 | expectType(convert.gray.keyword(0)); 378 | expectType(convert.gray.keyword.raw(0)); 379 | expectType(convert.gray.ansi16(0)); 380 | expectType(convert.gray.ansi16.raw(0)); 381 | expectType(convert.gray.ansi256(0)); 382 | expectType(convert.gray.ansi256.raw(0)); 383 | expectType(convert.gray.hcg(0)); 384 | expectType(convert.gray.hcg.raw(0)); 385 | expectType(convert.gray.apple(0)); 386 | expectType(convert.gray.apple.raw(0)); 387 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "color-convert", 3 | "description": "Plain color conversion functions", 4 | "version": "3.1.0", 5 | "author": "Josh Junon ", 6 | "contributors": [ 7 | "Heather Arthur " 8 | ], 9 | "license": "MIT", 10 | "repository": "Qix-/color-convert", 11 | "type": "module", 12 | "exports": "./index.js", 13 | "types": "./index.d.ts", 14 | "engines": { 15 | "node": ">=14.6" 16 | }, 17 | "scripts": { 18 | "test": "xo && tsd && node test/basic.js" 19 | }, 20 | "files": [ 21 | "index.js", 22 | "index.d.ts", 23 | "conversions.js", 24 | "route.js" 25 | ], 26 | "keywords": [ 27 | "color", 28 | "colour", 29 | "convert", 30 | "converter", 31 | "conversion", 32 | "rgb", 33 | "hsl", 34 | "hsv", 35 | "hwb", 36 | "cmyk", 37 | "ansi", 38 | "ansi16" 39 | ], 40 | "xo": { 41 | "rules": { 42 | "default-case": 0, 43 | "no-inline-comments": 0, 44 | "operator-linebreak": 0, 45 | "unicorn/prefer-exponentiation-operator": 0, 46 | "@typescript-eslint/naming-convention": 0 47 | } 48 | }, 49 | "devDependencies": { 50 | "chalk": "^5.2.0", 51 | "jimp": "^0.22.8", 52 | "tsd": "^0.28.1", 53 | "xo": "^0.54.2" 54 | }, 55 | "dependencies": { 56 | "color-name": "^2.0.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /route.js: -------------------------------------------------------------------------------- 1 | import conversions from './conversions.js'; 2 | 3 | /* 4 | This function routes a model to all other models. 5 | 6 | all functions that are routed have a property `.conversion` attached 7 | to the returned synthetic function. This property is an array 8 | of strings, each with the steps in between the 'from' and 'to' 9 | color models (inclusive). 10 | 11 | conversions that are not possible simply are not included. 12 | */ 13 | 14 | function buildGraph() { 15 | const graph = {}; 16 | // https://jsperf.com/object-keys-vs-for-in-with-closure/3 17 | const models = Object.keys(conversions); 18 | 19 | for (let {length} = models, i = 0; i < length; i++) { 20 | graph[models[i]] = { 21 | // http://jsperf.com/1-vs-infinity 22 | // micro-opt, but this is simple. 23 | distance: -1, 24 | parent: null, 25 | }; 26 | } 27 | 28 | return graph; 29 | } 30 | 31 | // https://en.wikipedia.org/wiki/Breadth-first_search 32 | function deriveBFS(fromModel) { 33 | const graph = buildGraph(); 34 | const queue = [fromModel]; // Unshift -> queue -> pop 35 | 36 | graph[fromModel].distance = 0; 37 | 38 | while (queue.length > 0) { 39 | const current = queue.pop(); 40 | const adjacents = Object.keys(conversions[current]); 41 | 42 | for (let {length} = adjacents, i = 0; i < length; i++) { 43 | const adjacent = adjacents[i]; 44 | const node = graph[adjacent]; 45 | 46 | if (node.distance === -1) { 47 | node.distance = graph[current].distance + 1; 48 | node.parent = current; 49 | queue.unshift(adjacent); 50 | } 51 | } 52 | } 53 | 54 | return graph; 55 | } 56 | 57 | function link(from, to) { 58 | return function (args) { 59 | return to(from(args)); 60 | }; 61 | } 62 | 63 | function wrapConversion(toModel, graph) { 64 | const path = [graph[toModel].parent, toModel]; 65 | let fn = conversions[graph[toModel].parent][toModel]; 66 | 67 | let cur = graph[toModel].parent; 68 | while (graph[cur].parent) { 69 | path.unshift(graph[cur].parent); 70 | fn = link(conversions[graph[cur].parent][cur], fn); 71 | cur = graph[cur].parent; 72 | } 73 | 74 | fn.conversion = path; 75 | return fn; 76 | } 77 | 78 | function route(fromModel) { 79 | const graph = deriveBFS(fromModel); 80 | const conversion = {}; 81 | 82 | const models = Object.keys(graph); 83 | for (let {length} = models, i = 0; i < length; i++) { 84 | const toModel = models[i]; 85 | const node = graph[toModel]; 86 | 87 | if (node.parent === null) { 88 | // No possible conversion, or this node is the source model. 89 | continue; 90 | } 91 | 92 | conversion[toModel] = wrapConversion(toModel, graph); 93 | } 94 | 95 | return conversion; 96 | } 97 | 98 | export default route; 99 | -------------------------------------------------------------------------------- /test/ansi-color-grid.js: -------------------------------------------------------------------------------- 1 | import process from 'node:process'; 2 | import cc from '../index.js'; 3 | 4 | process.stdout.write('\n'); 5 | 6 | for (let i = 0; i < 256; i++) { 7 | if (i > 0 && (i % 8) === 0) { 8 | process.stdout.write('\n'); 9 | } 10 | 11 | const code16 = cc.ansi256.ansi16(i); 12 | 13 | process.stdout.write( 14 | ` [${ 15 | ' '.slice(Math.max(0, Math.floor(Math.log10(i)))) 16 | }${ 17 | i 18 | } \u001B[48;5;${i}m \u001B[0;${ 19 | code16 + 10 20 | }m \u001B[m ${ 21 | code16 22 | }${ 23 | ' '.slice(Math.max(0, Math.floor(Math.log10(code16)))) 24 | }]`, 25 | ); 26 | } 27 | 28 | process.stdout.write('\n\n'); 29 | -------------------------------------------------------------------------------- /test/ansi-colorize.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import process from 'node:process'; 3 | import jimp from 'jimp'; 4 | import cc from '../index.js'; 5 | 6 | async function main() { 7 | if (process.argv.length !== 4) { 8 | console.error('usage: '); 9 | process.exit(2); 10 | } 11 | 12 | const inputPath = process.argv[2]; 13 | const outputPath = process.argv[3]; 14 | 15 | const img = await jimp.read(inputPath); 16 | 17 | img.scan( 18 | 0, 0, 19 | img.bitmap.width, img.bitmap.height, 20 | (x, y, idx) => { 21 | let r = img.bitmap.data[idx]; 22 | let g = img.bitmap.data[idx + 1]; 23 | let b = img.bitmap.data[idx + 2]; 24 | 25 | [r, g, b] = cc.ansi256.rgb(cc.rgb.ansi256(r, g, b)); 26 | 27 | img.bitmap.data[idx] = r; 28 | img.bitmap.data[idx + 1] = g; 29 | img.bitmap.data[idx + 2] = b; 30 | }, 31 | ); 32 | 33 | await img.write(outputPath); 34 | } 35 | 36 | await main().catch(error => { 37 | console.error(error); 38 | process.exit(1); 39 | }); 40 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import chalk from 'chalk'; 3 | import keywords from 'color-name'; 4 | import conversions from '../conversions.js'; 5 | import convert from '../index.js'; 6 | 7 | const models = Object.keys(conversions); 8 | for (let {length} = models, i = 0; i < length; i++) { 9 | const toModel = models[i]; 10 | for (let j = 0; j < length; j++) { 11 | const fromModel = models[j]; 12 | 13 | if (toModel === fromModel) { 14 | continue; 15 | } 16 | 17 | const fn = convert[toModel][fromModel]; 18 | if (fn) { 19 | const path = [...(fn.conversion || [fromModel, toModel])]; 20 | path[0] = chalk.bold.cyan(path[0]); 21 | path[path.length - 1] = chalk.bold.cyan(path[path.length - 1]); 22 | 23 | console.log(path.join(chalk.bold.black('->'))); 24 | } else { 25 | console.log(chalk.red([toModel, fromModel].join('->')), chalk.red('(no conversion)')); 26 | } 27 | } 28 | 29 | // Should not expose channels 30 | assert.ok(convert[toModel].channels > 0); 31 | assert.ok(!Object.keys(convert[toModel]).includes('channels')); 32 | } 33 | 34 | // Labels should be unique 35 | const uniqued = {}; 36 | for (const model of models) { 37 | const hash = Array.prototype.slice.call(convert[model].labels).sort().join(''); 38 | if (hash in uniqued) { 39 | throw new Error('models ' + uniqued[hash] + ' and ' + model + ' have the same label set'); 40 | } 41 | 42 | uniqued[hash] = model; 43 | } 44 | 45 | assert.deepStrictEqual(convert.rgb.hsl([140, 200, 100]), [96, 48, 59]); 46 | assert.deepStrictEqual(convert.rgb.hsv([140, 200, 100]), [96, 50, 78]); 47 | assert.deepStrictEqual(convert.rgb.hwb([140, 200, 100]), [96, 39, 22]); 48 | assert.deepStrictEqual(convert.rgb.cmyk([140, 200, 100]), [30, 0, 50, 22]); 49 | assert.deepStrictEqual(convert.rgb.cmyk([0, 0, 0, 1]), [0, 0, 0, 100]); 50 | assert.deepStrictEqual(convert.rgb.keyword([255, 228, 196]), 'bisque'); 51 | assert.deepStrictEqual(convert.rgb.xyz([92, 191, 84]), [25, 40, 15]); 52 | assert.deepStrictEqual(convert.rgb.lab([92, 191, 84]), [70, -50, 45]); 53 | assert.deepStrictEqual(convert.rgb.oklab([153, 102, 255]), [64, 9, -20]); 54 | assert.deepStrictEqual(convert.rgb.lch([92, 191, 84]), [70, 67, 138]); 55 | assert.deepStrictEqual(convert.rgb.oklch([153, 102, 255]), [64, 22, 295]); 56 | assert.deepStrictEqual(convert.rgb.ansi16([92, 191, 84]), 32); 57 | assert.deepStrictEqual(convert.rgb.ansi256([92, 191, 84]), 114); 58 | assert.deepStrictEqual(convert.rgb.hex([92, 191, 84]), '5CBF54'); 59 | assert.deepStrictEqual(convert.rgb.hcg([140, 200, 100]), [96, 39, 65]); 60 | assert.deepStrictEqual(convert.rgb.apple([255, 127, 0]), [65_535, 32_639, 0]); 61 | 62 | assert.deepStrictEqual(convert.hsl.rgb([96, 48, 59]), [140, 201, 100]); 63 | assert.deepStrictEqual(convert.hsl.hsv([96, 48, 59]), [96, 50, 79]); // Colorpicker says [96,50,79] 64 | assert.deepStrictEqual(convert.hsl.hwb([96, 48, 59]), [96, 39, 21]); // Computer round to 21, should be 22 65 | assert.deepStrictEqual(convert.hsl.cmyk([96, 48, 59]), [30, 0, 50, 21]); 66 | assert.deepStrictEqual(convert.hsl.keyword([240, 100, 50]), 'blue'); 67 | assert.deepStrictEqual(convert.hsl.ansi16([240, 100, 50]), 94); 68 | assert.deepStrictEqual(convert.hsl.ansi256([240, 100, 50]), 21); 69 | assert.deepStrictEqual(convert.hsl.hex([240, 100, 50]), '0000FF'); 70 | assert.deepStrictEqual(convert.hsl.hcg([96, 48, 59]), [96, 39, 65]); 71 | 72 | assert.deepStrictEqual(convert.hsv.rgb([96, 50, 78]), [139, 199, 99]); 73 | assert.deepStrictEqual(convert.hsv.hsl([96, 50, 78]), [96, 47, 59]); 74 | assert.deepStrictEqual(convert.hsv.hsl([0, 0, 0]), [0, 0, 0]); 75 | assert.deepStrictEqual(convert.hsv.hwb([96, 50, 78]), [96, 39, 22]); 76 | assert.deepStrictEqual(convert.hsv.cmyk([96, 50, 78]), [30, 0, 50, 22]); 77 | assert.deepStrictEqual(convert.hsv.keyword([240, 100, 100]), 'blue'); 78 | assert.deepStrictEqual(convert.hsv.ansi16([240, 100, 100]), 94); 79 | assert.deepStrictEqual(convert.hsv.ansi256([240, 100, 100]), 21); 80 | assert.deepStrictEqual(convert.hsv.hex([251, 80, 42]), '25156B'); 81 | assert.deepStrictEqual(convert.hsv.hcg([96, 50, 78]), [96, 39, 64]); 82 | 83 | assert.deepStrictEqual(convert.cmyk.rgb([30, 0, 50, 22]), [139, 199, 99]); 84 | assert.deepStrictEqual(convert.cmyk.hsl([30, 0, 50, 22]), [96, 47, 59]); 85 | assert.deepStrictEqual(convert.cmyk.hsv([30, 0, 50, 22]), [96, 50, 78]); 86 | assert.deepStrictEqual(convert.cmyk.hwb([30, 0, 50, 22]), [96, 39, 22]); 87 | assert.deepStrictEqual(convert.cmyk.keyword([100, 100, 0, 0]), 'blue'); 88 | assert.deepStrictEqual(convert.cmyk.ansi16([30, 0, 50, 22]), 93); 89 | assert.deepStrictEqual(convert.cmyk.ansi256([30, 0, 50, 22]), 150); 90 | assert.deepStrictEqual(convert.cmyk.hex([30, 0, 50, 22]), '8BC763'); 91 | 92 | assert.deepStrictEqual(convert.keyword.rgb('blue'), [0, 0, 255]); 93 | assert.deepStrictEqual(convert.keyword.hsl('blue'), [240, 100, 50]); 94 | assert.deepStrictEqual(convert.keyword.hsv('blue'), [240, 100, 100]); 95 | assert.deepStrictEqual(convert.keyword.hwb('blue'), [240, 0, 0]); 96 | assert.deepStrictEqual(convert.keyword.cmyk('blue'), [100, 100, 0, 0]); 97 | assert.deepStrictEqual(convert.keyword.lab('blue'), [32, 79, -108]); 98 | assert.deepStrictEqual(convert.keyword.xyz('blue'), [18, 7, 95]); 99 | assert.deepStrictEqual(convert.keyword.ansi16('purple'), 35); 100 | assert.deepStrictEqual(convert.keyword.ansi256('purple'), 127); 101 | assert.deepStrictEqual(convert.keyword.hex('blue'), '0000FF'); 102 | 103 | assert.deepStrictEqual(convert.xyz.rgb([25, 40, 15]), [97, 190, 85]); 104 | assert.deepStrictEqual(convert.xyz.rgb([50, 100, 100]), [0, 255, 241]); 105 | assert.deepStrictEqual(convert.xyz.lab([25, 40, 15]), [69, -48, 44]); 106 | assert.deepStrictEqual(convert.xyz.oklab([95, 100, 108.9]), [100, -0, -0]); 107 | assert.deepStrictEqual(convert.xyz.oklab([100, 0, 0]), [45, 124, -2]); 108 | assert.deepStrictEqual(convert.xyz.oklab([0, 100, 0]), [92, -67, 26]); 109 | assert.deepStrictEqual(convert.xyz.oklab([0, 0, 100]), [15, -141, -45]); 110 | assert.deepStrictEqual(convert.xyz.lch([25, 40, 15]), [69, 65, 137]); 111 | 112 | assert.deepStrictEqual(convert.lab.xyz([69, -48, 44]), [25, 39, 15]); 113 | assert.deepStrictEqual(convert.lab.rgb([75, 20, -30]), [194, 175, 240]); 114 | assert.deepStrictEqual(convert.lab.lch([69, -48, 44]), [69, 65, 137]); 115 | 116 | assert.deepStrictEqual(convert.oklab.xyz([100, 0, 0]), [95, 100, 109]); 117 | assert.deepStrictEqual(convert.oklab.xyz([45, 123.6, -1.9]), [100, -0, -0]); 118 | assert.deepStrictEqual(convert.oklab.xyz([92.2, -67.1, 26.3]), [0, 100, 0]); 119 | assert.deepStrictEqual(convert.oklab.xyz([15.3, -141.5, -44.9]), [0, 0, 100]); 120 | assert.deepStrictEqual(convert.oklab.rgb([64, 9, -20]), [152, 102, 255]); 121 | 122 | assert.deepStrictEqual(convert.lch.lab([69, 65, 137]), [69, -48, 44]); 123 | assert.deepStrictEqual(convert.lch.xyz([69, 65, 137]), [25, 39, 15]); 124 | assert.deepStrictEqual(convert.lch.rgb([69, 65, 137]), [98, 188, 83]); 125 | 126 | assert.deepStrictEqual(convert.oklch.rgb([64, 22, 295]), [154, 101, 255]); 127 | 128 | assert.deepStrictEqual(convert.ansi16.rgb(103), [255, 255, 0]); 129 | assert.deepStrictEqual(convert.ansi256.rgb(175), [204, 102, 153]); 130 | 131 | assert.deepStrictEqual(convert.hex.rgb('ABCDEF'), [171, 205, 239]); 132 | assert.deepStrictEqual(convert.hex.rgb('AABBCC'), [170, 187, 204]); 133 | assert.deepStrictEqual(convert.hex.rgb('ABC'), [170, 187, 204]); 134 | 135 | assert.deepStrictEqual(convert.hcg.rgb([96, 39, 64]), [139, 199, 100]); 136 | assert.deepStrictEqual(convert.hcg.hsv([96, 39, 64]), [96, 50, 78]); 137 | assert.deepStrictEqual(convert.hcg.hsl([96, 39, 64]), [96, 47, 59]); 138 | 139 | // https://github.com/Qix-/color-convert/issues/73 140 | assert.deepStrictEqual(convert.rgb.hcg.raw([250, 0, 255]), [298.823_529_411_764_7, 100, 0]); 141 | 142 | // Non-array arguments 143 | assert.deepStrictEqual(convert.hsl.rgb(96, 48, 59), [140, 201, 100]); 144 | 145 | // Raw functions 146 | function round(vals) { 147 | for (let i = 0; i < vals.length; i++) { 148 | vals[i] = Number(vals[i].toFixed(1)); 149 | } 150 | 151 | return vals; 152 | } 153 | 154 | assert.deepStrictEqual(round(convert.hsl.rgb.raw([96, 48, 59])), [140.4, 200.6, 100.3]); 155 | assert.deepStrictEqual(round(convert.rgb.hsl.raw([140, 200, 100])), [96, 47.6, 58.8]); 156 | 157 | assert.deepStrictEqual(round(convert.hsv.rgb.raw([96, 50, 78])), [139.2, 198.9, 99.5]); 158 | assert.deepStrictEqual(round(convert.rgb.hsv.raw([140, 200, 100])), [96, 50, 78.4]); 159 | 160 | assert.deepStrictEqual(round(convert.hwb.rgb.raw([96, 39, 22])), [139.2, 198.9, 99.5]); 161 | assert.deepStrictEqual(round(convert.rgb.hwb.raw([140, 200, 100])), [96, 39.2, 21.6]); 162 | 163 | assert.deepStrictEqual(round(convert.cmyk.rgb.raw([30, 0, 50, 22])), [139.2, 198.9, 99.5]); 164 | assert.deepStrictEqual(round(convert.rgb.cmyk.raw([140, 200, 100])), [30, 0, 50, 21.6]); 165 | 166 | assert.deepStrictEqual(round(convert.keyword.rgb.raw('blue')), [0, 0, 255]); 167 | assert.deepStrictEqual(convert.rgb.keyword.raw([255, 228, 196]), 'bisque'); 168 | 169 | assert.deepStrictEqual(round(convert.hsv.hsl.raw([96, 50, 78])), [96, 47, 58.5]); 170 | assert.deepStrictEqual(round(convert.hsv.hsl.raw([302, 32, 55])), [302, 19, 46.2]); 171 | assert.deepStrictEqual(round(convert.hsv.hsl.raw([267, 19, 89])), [267, 43.5, 80.5]); 172 | assert.deepStrictEqual(round(convert.hsv.hsl.raw([267, 91, 95])), [267, 89.6, 51.8]); 173 | assert.deepStrictEqual(round(convert.hsv.hsl.raw([267, 91, 12])), [267, 83.5, 6.5]); 174 | assert.deepStrictEqual(round(convert.hsv.hsl.raw([180, 50, 0])), [180, 33.3, 0]); // Preserve saturation 175 | 176 | assert.deepStrictEqual(round(convert.hsl.hsv.raw([96, 48, 59])), [96, 50, 78.7]); 177 | assert.deepStrictEqual(round(convert.hsl.hsv.raw([120, 54, 61])), [120, 51.3, 82.1]); 178 | assert.deepStrictEqual(round(convert.hsl.hsv.raw([27, 51, 43])), [27, 67.5, 64.9]); 179 | assert.deepStrictEqual(round(convert.hsl.hsv.raw([241, 17, 79])), [241, 8.6, 82.6]); 180 | assert.deepStrictEqual(round(convert.hsl.hsv.raw([120, 50, 0])), [120, 66.7, 0]); // Preserve saturation 181 | 182 | assert.deepStrictEqual(round(convert.xyz.rgb.raw([25, 40, 15])), [97.4, 189.9, 85]); 183 | assert.deepStrictEqual(round(convert.rgb.xyz.raw([92, 191, 84])), [24.6, 40.2, 14.8]); 184 | 185 | assert.deepStrictEqual(round(convert.rgb.lab.raw([92, 191, 84])), [69.6, -50.1, 44.6]); 186 | 187 | // Hwb 188 | // http://dev.w3.org/csswg/css-color/#hwb-examples 189 | 190 | // all extreme value should give black, white or grey 191 | for (let angle = 0; angle <= 360; angle++) { 192 | assert.deepStrictEqual(convert.hwb.rgb([angle, 0, 100]), [0, 0, 0]); 193 | assert.deepStrictEqual(convert.hwb.rgb([angle, 100, 0]), [255, 255, 255]); 194 | assert.deepStrictEqual(convert.hwb.rgb([angle, 100, 100]), [128, 128, 128]); 195 | } 196 | 197 | assert.deepStrictEqual(convert.hwb.rgb([0, 0, 0]), [255, 0, 0]); 198 | assert.deepStrictEqual(convert.hwb.rgb([0, 20, 40]), [153, 51, 51]); 199 | assert.deepStrictEqual(convert.hwb.rgb([0, 40, 40]), [153, 102, 102]); 200 | assert.deepStrictEqual(convert.hwb.rgb([0, 40, 20]), [204, 102, 102]); 201 | 202 | assert.deepStrictEqual(convert.hwb.rgb([120, 0, 0]), [0, 255, 0]); 203 | assert.deepStrictEqual(convert.hwb.rgb([120, 20, 40]), [51, 153, 51]); 204 | assert.deepStrictEqual(convert.hwb.rgb([120, 40, 40]), [102, 153, 102]); 205 | assert.deepStrictEqual(convert.hwb.rgb([120, 40, 20]), [102, 204, 102]); 206 | 207 | assert.deepStrictEqual(convert.hwb.rgb([240, 0, 0]), [0, 0, 255]); 208 | assert.deepStrictEqual(convert.hwb.rgb([240, 20, 40]), [51, 51, 153]); 209 | assert.deepStrictEqual(convert.hwb.rgb([240, 40, 40]), [102, 102, 153]); 210 | assert.deepStrictEqual(convert.hwb.rgb([240, 40, 20]), [102, 102, 204]); 211 | 212 | // Black should always stay black 213 | const value = [0, 0, 0]; 214 | assert.deepStrictEqual(convert.hsl.hsv(value), value); 215 | assert.deepStrictEqual(convert.hsl.rgb(value), value); 216 | assert.deepStrictEqual(convert.hsl.hwb(value), [0, 0, 100]); 217 | assert.deepStrictEqual(convert.hsl.cmyk(value), [0, 0, 0, 100]); 218 | assert.deepStrictEqual(convert.hsl.hex(value), '000000'); 219 | 220 | // Test keyword rounding 221 | assert.deepStrictEqual(convert.rgb.keyword(255, 255, 0), 'yellow'); 222 | assert.deepStrictEqual(convert.rgb.keyword(255, 255, 1), 'yellow'); 223 | assert.deepStrictEqual(convert.rgb.keyword(250, 254, 1), 'yellow'); 224 | 225 | // Assure euclidean distance algorithm produces perfectly inverse results 226 | const keywordKeys = Object.keys(keywords); 227 | for (const k of keywordKeys) { 228 | // Why the roundabout testing method? certain css keywords have the same color values. 229 | const derived = convert.rgb.keyword(keywords[k]); 230 | assert.deepStrictEqual(keywords[derived], keywords[k]); 231 | } 232 | 233 | // Basic gray tests 234 | assert.deepStrictEqual(convert.gray.rgb([0]), [0, 0, 0]); 235 | assert.deepStrictEqual(convert.gray.rgb([50]), [128, 128, 128]); 236 | assert.deepStrictEqual(convert.gray.rgb([100]), [255, 255, 255]); 237 | assert.deepStrictEqual(convert.gray.hsl([50]), [0, 0, 50]); 238 | assert.deepStrictEqual(convert.gray.hsv([50]), [0, 0, 50]); 239 | assert.deepStrictEqual(convert.gray.hwb([50]), [0, 100, 50]); 240 | assert.deepStrictEqual(convert.gray.cmyk([50]), [0, 0, 0, 50]); 241 | assert.deepStrictEqual(convert.gray.lab([50]), [50, 0, 0]); 242 | assert.deepStrictEqual(convert.gray.hex([50]), '808080'); 243 | assert.deepStrictEqual(convert.gray.hex([100]), 'FFFFFF'); 244 | assert.deepStrictEqual(convert.gray.hex([0]), '000000'); 245 | 246 | assert.deepStrictEqual(convert.rgb.gray([0, 0, 0]), [0]); 247 | assert.deepStrictEqual(convert.rgb.gray([128, 128, 128]), [50]); 248 | assert.deepStrictEqual(convert.rgb.gray([255, 255, 255]), [100]); 249 | assert.deepStrictEqual(convert.rgb.gray([0, 128, 255]), [50]); 250 | 251 | // https://github.com/Qix-/color-convert/issues/74 252 | assert.deepStrictEqual(convert.rgb.ansi256(40, 38, 41), 235); 253 | --------------------------------------------------------------------------------