color.ts ×55

Frontier kind: Code frontier

unlabeled · c_398fe88a5d5a

977 tests · 3686 LOC · 19 files · introduces 0 tests · 308 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
55 ranges308 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
535 ranges3686 lines · 19 files · Browse complete extent
All tests (intent)
977 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 308 introduced LOC across 55 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/base/common/color.ts 308 introduced LOC · 55 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- color.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CharCode } from './charCode.js';
7 >
8 > function roundFloat(number: number, decimalPoints: number): number {
9 > const decimal = Math.pow(10, decimalPoints);
10 > return Math.round(number * decimal) / decimal;
11 > }
12 >
13 > export class RGBA {
14 > _rgbaBrand: void = undefined;
15 >
16 > /**
17 > * Red: integer in [0-255]
18 > */
19 > readonly r: number;
20 >
21 > /**
22 > * Green: integer in [0-255]
23 > */
24 > readonly g: number;
25 >
26 > /**
27 > * Blue: integer in [0-255]
28 > */
29 > readonly b: number;
30 >
31 > /**
32 > * Alpha: float in [0-1]
33 > */
34 > readonly a: number;
35 >
36 > constructor(r: number, g: number, b: number, a: number = 1) {
37 > this.r = Math.min(255, Math.max(0, r)) | 0;
38 > this.g = Math.min(255, Math.max(0, g)) | 0;
39 > this.b = Math.min(255, Math.max(0, b)) | 0;
40 > this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
41 > }
42 >
43 > static equals(a: RGBA, b: RGBA): boolean {
44 return a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a;
45 }
46 > } color.ts
47 >
48 > export class HSLA {
49 >
50 > _hslaBrand: void = undefined;
51 >
52 > /**
53 > * Hue: integer in [0, 360]
54 > */
55 > readonly h: number;
56 >
57 > /**
58 > * Saturation: float in [0, 1]
59 > */
60 > readonly s: number;
61 >
62 > /**
63 > * Luminosity: float in [0, 1]
64 > */
65 > readonly l: number;
66 >
67 > /**
68 > * Alpha: float in [0, 1]
69 > */
70 > readonly a: number;
71 >
72 > constructor(h: number, s: number, l: number, a: number) {
73 this.h = Math.max(Math.min(360, h), 0) | 0;
74 this.s = roundFloat(Math.max(Math.min(1, s), 0), 3);
76 this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
77 }
78 > color.ts
79 > static equals(a: HSLA, b: HSLA): boolean {
80 return a.h === b.h && a.s === b.s && a.l === b.l && a.a === b.a;
81 }
82 > color.ts
83 > /**
84 > * Converts an RGB color value to HSL. Conversion formula
85 > * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
86 > * Assumes r, g, and b are contained in the set [0, 255] and
87 > * returns h in the set [0, 360], s, and l in the set [0, 1].
88 > */
89 > static fromRGBA(rgba: RGBA): HSLA {
90 const r = rgba.r / 255;
91 const g = rgba.g / 255;
114 return new HSLA(h, s, l, a);
115 }
116 > color.ts
117 > private static _hue2rgb(p: number, q: number, t: number): number {
118 if (t < 0) {
119 t += 1;
133 return p;
134 }
135 > color.ts
136 > /**
137 > * Converts an HSL color value to RGB. Conversion formula
138 > * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
139 > * Assumes h in the set [0, 360] s, and l are contained in the set [0, 1] and
140 > * returns r, g, and b in the set [0, 255].
141 > */
142 > static toRGBA(hsla: HSLA): RGBA {
143 const h = hsla.h / 360;
144 const { s, l, a } = hsla;
157 return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a);
158 }
159 > } color.ts
160 >
161 > export class HSVA {
162 >
163 > _hsvaBrand: void = undefined;
164 >
165 > /**
166 > * Hue: integer in [0, 360]
167 > */
168 > readonly h: number;
169 >
170 > /**
171 > * Saturation: float in [0, 1]
172 > */
173 > readonly s: number;
174 >
175 > /**
176 > * Value: float in [0, 1]
177 > */
178 > readonly v: number;
179 >
180 > /**
181 > * Alpha: float in [0, 1]
182 > */
183 > readonly a: number;
184 >
185 > constructor(h: number, s: number, v: number, a: number) {
186 this.h = Math.max(Math.min(360, h), 0) | 0;
187 this.s = roundFloat(Math.max(Math.min(1, s), 0), 3);
189 this.a = roundFloat(Math.max(Math.min(1, a), 0), 3);
190 }
191 > color.ts
192 > static equals(a: HSVA, b: HSVA): boolean {
193 return a.h === b.h && a.s === b.s && a.v === b.v && a.a === b.a;
194 }
195 > color.ts
196 > // from http://www.rapidtables.com/convert/color/rgb-to-hsv.htm
197 > static fromRGBA(rgba: RGBA): HSVA {
198 const r = rgba.r / 255;
199 const g = rgba.g / 255;
217 return new HSVA(Math.round(m * 60), s, cmax, rgba.a);
218 }
219 > color.ts
220 > // from http://www.rapidtables.com/convert/color/hsv-to-rgb.htm
221 > static toRGBA(hsva: HSVA): RGBA {
222 const { h, s, v, a } = hsva;
223 const c = v * s;
252 return new RGBA(r, g, b, a);
253 }
254 > } color.ts
255 >
256 > export class Color {
257 >
258 > static fromHex(hex: string): Color {
259 return Color.Format.CSS.parseHex(hex) || Color.red;
260 }
261 > color.ts
262 > static equals(a: Color | null, b: Color | null): boolean {
263 if (!a && !b) {
264 return true;
269 return a.equals(b);
270 }
271 > color.ts
272 > readonly rgba: RGBA;
273 > private _hsla?: HSLA;
274 > get hsla(): HSLA {
275 if (this._hsla) {
276 return this._hsla;
279 }
280 }
281 > color.ts
282 > private _hsva?: HSVA;
283 > get hsva(): HSVA {
284 if (this._hsva) {
285 return this._hsva;
287 return HSVA.fromRGBA(this.rgba);
288 }
289 > color.ts
290 > constructor(arg: RGBA | HSLA | HSVA) {
291 > if (!arg) {
292 throw new Error('Color needs a value');
293 > } else if (arg instanceof RGBA) { color.ts
294 > this.rgba = arg;
295 > } else if (arg instanceof HSLA) {
296 this._hsla = arg;
297 this.rgba = HSLA.toRGBA(arg);
302 throw new Error('Invalid color ctor argument');
303 }
304 > } color.ts
305 >
306 > equals(other: Color | null): boolean {
307 return !!other && RGBA.equals(this.rgba, other.rgba) && HSLA.equals(this.hsla, other.hsla) && HSVA.equals(this.hsva, other.hsva);
308 }
309 > color.ts
310 > /**
311 > * http://www.w3.org/TR/WCAG20/#relativeluminancedef
312 > * Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white.
313 > */
314 > getRelativeLuminance(): number {
315 const R = Color._relativeLuminanceForComponent(this.rgba.r);
316 const G = Color._relativeLuminanceForComponent(this.rgba.g);
320 return roundFloat(luminance, 4);
321 }
322 > color.ts
323 > /**
324 > * Reduces the "foreground" color on this "background" color unti it is
325 > * below the relative luminace ratio.
326 > * @returns the new foreground color
327 > * @see https://github.com/xtermjs/xterm.js/blob/44f9fa39ae03e2ca6d28354d88a399608686770e/src/common/Color.ts#L315
328 > */
329 > reduceRelativeLuminace(foreground: Color, ratio: number): Color {
330 // This is a naive but fast approach to reducing luminance as converting to
331 // HSL and back is expensive
343 return new Color(new RGBA(fgR, fgG, fgB));
344 }
345 > color.ts
346 > /**
347 > * Increases the "foreground" color on this "background" color unti it is
348 > * below the relative luminace ratio.
349 > * @returns the new foreground color
350 > * @see https://github.com/xtermjs/xterm.js/blob/44f9fa39ae03e2ca6d28354d88a399608686770e/src/common/Color.ts#L335
351 > */
352 > increaseRelativeLuminace(foreground: Color, ratio: number): Color {
353 // This is a naive but fast approach to reducing luminance as converting to
354 // HSL and back is expensive
364 return new Color(new RGBA(fgR, fgG, fgB));
365 }
366 > color.ts
367 > private static _relativeLuminanceForComponent(color: number): number {
368 const c = color / 255;
369 return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4);
370 }
371 > color.ts
372 > /**
373 > * http://www.w3.org/TR/WCAG20/#contrast-ratiodef
374 > * Returns the contrast ration number in the set [1, 21].
375 > */
376 > getContrastRatio(another: Color): number {
377 const lum1 = this.getRelativeLuminance();
378 const lum2 = another.getRelativeLuminance();
379 return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05);
380 }
381 > color.ts
382 > /**
383 > * http://24ways.org/2010/calculating-color-contrast
384 > * Return 'true' if darker color otherwise 'false'
385 > */
386 > isDarker(): boolean {
387 const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
388 return yiq < 128;
389 }
390 > color.ts
391 > /**
392 > * http://24ways.org/2010/calculating-color-contrast
393 > * Return 'true' if lighter color otherwise 'false'
394 > */
395 > isLighter(): boolean {
396 const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
397 return yiq >= 128;
398 }
399 > color.ts
400 > isLighterThan(another: Color): boolean {
401 const lum1 = this.getRelativeLuminance();
402 const lum2 = another.getRelativeLuminance();
403 return lum1 > lum2;
404 }
405 > color.ts
406 > isDarkerThan(another: Color): boolean {
407 const lum1 = this.getRelativeLuminance();
408 const lum2 = another.getRelativeLuminance();
409 return lum1 < lum2;
410 }
411 > color.ts
412 > /**
413 > * Based on xterm.js: https://github.com/xtermjs/xterm.js/blob/44f9fa39ae03e2ca6d28354d88a399608686770e/src/common/Color.ts#L288
414 > *
415 > * Given a foreground color and a background color, either increase or reduce the luminance of the
416 > * foreground color until the specified contrast ratio is met. If pure white or black is hit
417 > * without the contrast ratio being met, go the other direction using the background color as the
418 > * foreground color and take either the first or second result depending on which has the higher
419 > * contrast ratio.
420 > *
421 > * @param foreground The foreground color.
422 > * @param ratio The contrast ratio to achieve.
423 > * @returns The adjusted foreground color.
424 > */
425 > ensureConstrast(foreground: Color, ratio: number): Color {
426 const bgL = this.getRelativeLuminance();
427 const fgL = foreground.getRelativeLuminance();
450 return foreground;
451 }
452 > color.ts
453 > lighten(factor: number): Color {
454 return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l + this.hsla.l * factor, this.hsla.a));
455 }
456 > color.ts
457 > darken(factor: number): Color {
458 return new Color(new HSLA(this.hsla.h, this.hsla.s, this.hsla.l - this.hsla.l * factor, this.hsla.a));
459 }
460 > color.ts
461 > transparent(factor: number): Color {
462 const { r, g, b, a } = this.rgba;
463 return new Color(new RGBA(r, g, b, a * factor));
464 }
465 > color.ts
466 > isTransparent(): boolean {
467 return this.rgba.a === 0;
468 }
469 > color.ts
470 > isOpaque(): boolean {
471 return this.rgba.a === 1;
472 }
473 > color.ts
474 > opposite(): Color {
475 return new Color(new RGBA(255 - this.rgba.r, 255 - this.rgba.g, 255 - this.rgba.b, this.rgba.a));
476 }
477 > color.ts
478 > blend(c: Color): Color {
479 const rgba = c.rgba;
480
494 return new Color(new RGBA(r, g, b, a));
495 }
496 > color.ts
497 > /**
498 > * Mixes the current color with the provided color based on the given factor.
499 > * @param color The color to mix with
500 > * @param factor The factor of mixing (0 means this color, 1 means the input color, 0.5 means equal mix)
501 > * @returns A new color representing the mix
502 > */
503 > mix(color: Color, factor: number = 0.5): Color {
504 const normalize = Math.min(Math.max(factor, 0), 1);
505 const thisRGBA = this.rgba;
513 return new Color(new RGBA(r, g, b, a));
514 }
515 > color.ts
516 > makeOpaque(opaqueBackground: Color): Color {
517 if (this.isOpaque() || opaqueBackground.rgba.a !== 1) {
518 // only allow to blend onto a non-opaque color onto a opaque color
530 ));
531 }
532 > color.ts
533 > flatten(...backgrounds: Color[]): Color {
534 const background = backgrounds.reduceRight((accumulator, color) => {
535 return Color._flatten(color, accumulator);
537 return Color._flatten(this, background);
538 }
539 > color.ts
540 > private static _flatten(foreground: Color, background: Color) {
541 const backgroundAlpha = 1 - foreground.rgba.a;
542 return new Color(new RGBA(
546 ));
547 }
548 > color.ts
549 > private _toString?: string;
550 > toString(): string {
551 if (!this._toString) {
552 this._toString = Color.Format.CSS.format(this);
554 return this._toString;
555 }
556 > color.ts
557 > private _toNumber32Bit?: number;
558 > toNumber32Bit(): number {
559 if (!this._toNumber32Bit) {
560 this._toNumber32Bit = (
567 return this._toNumber32Bit;
568 }
569 > color.ts
570 > static getLighterColor(of: Color, relative: Color, factor?: number): Color {
571 if (of.isLighterThan(relative)) {
572 return of;
578 return of.lighten(factor);
579 }
580 > color.ts
581 > static getDarkerColor(of: Color, relative: Color, factor?: number): Color {
582 if (of.isDarkerThan(relative)) {
583 return of;
589 return of.darken(factor);
590 }
591 > color.ts
592 > static readonly white = new Color(new RGBA(255, 255, 255, 1));
593 > static readonly black = new Color(new RGBA(0, 0, 0, 1));
594 > static readonly red = new Color(new RGBA(255, 0, 0, 1));
595 > static readonly blue = new Color(new RGBA(0, 0, 255, 1));
596 > static readonly green = new Color(new RGBA(0, 255, 0, 1));
597 > static readonly cyan = new Color(new RGBA(0, 255, 255, 1));
598 > static readonly lightgrey = new Color(new RGBA(211, 211, 211, 1));
599 > static readonly transparent = new Color(new RGBA(0, 0, 0, 0));
600 > }
601 >
602 > export namespace Color {
603 > export namespace Format {
604 > export namespace CSS {
605 >
606 > export function formatRGB(color: Color): string {
607 if (color.rgba.a === 1) {
608 return `rgb(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b})`;
611 return Color.Format.CSS.formatRGBA(color);
612 }
613 > color.ts
614 > export function formatRGBA(color: Color): string {
615 return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a).toFixed(2)})`;
616 }
617 > color.ts
618 > export function formatHSL(color: Color): string {
619 if (color.hsla.a === 1) {
620 return `hsl(${color.hsla.h}, ${Math.round(color.hsla.s * 100)}%, ${Math.round(color.hsla.l * 100)}%)`;
623 return Color.Format.CSS.formatHSLA(color);
624 }
625 > color.ts
626 > export function formatHSLA(color: Color): string {
627 return `hsla(${color.hsla.h}, ${Math.round(color.hsla.s * 100)}%, ${Math.round(color.hsla.l * 100)}%, ${color.hsla.a.toFixed(2)})`;
628 }
629 > color.ts
630 > function _toTwoDigitHex(n: number): string {
631 const r = n.toString(16);
632 return r.length !== 2 ? '0' + r : r;
633 }
634 > color.ts
635 > /**
636 > * Formats the color as #RRGGBB
637 > */
638 > export function formatHex(color: Color): string {
639 return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}`;
640 }
641 > color.ts
642 > /**
643 > * Formats the color as #RRGGBBAA
644 > * If 'compact' is set, colors without transparancy will be printed as #RRGGBB
645 > */
646 > export function formatHexA(color: Color, compact = false): string {
647 if (compact && color.rgba.a === 1) {
648 return Color.Format.CSS.formatHex(color);
651 return `#${_toTwoDigitHex(color.rgba.r)}${_toTwoDigitHex(color.rgba.g)}${_toTwoDigitHex(color.rgba.b)}${_toTwoDigitHex(Math.round(color.rgba.a * 255))}`;
652 }
653 > color.ts
654 > /**
655 > * The default format will use HEX if opaque and RGBA otherwise.
656 > */
657 > export function format(color: Color): string {
658 if (color.isOpaque()) {
659 return Color.Format.CSS.formatHex(color);
662 return Color.Format.CSS.formatRGBA(color);
663 }
664 > color.ts
665 > /**
666 > * Parse a CSS color and return a {@link Color}.
667 > * @param css The CSS color to parse.
668 > * @see https://drafts.csswg.org/css-color/#typedef-color
669 > */
670 > export function parse(css: string): Color | null {
671 if (css === 'transparent') {
672 return Color.transparent;
699 return parseNamedKeyword(css);
700 }
701 > color.ts
702 > function parseNamedKeyword(css: string): Color | null {
703 // https://drafts.csswg.org/css-color/#named-colors
704 switch (css) {
854 }
855 }
856 > color.ts
857 > /**
858 > * Converts an Hex color value to a Color.
859 > * returns r, g, and b are contained in the set [0, 255]
860 > * @param hex string (#RGB, #RGBA, #RRGGBB or #RRGGBBAA).
861 > */
862 > export function parseHex(hex: string): Color | null {
863 const length = hex.length;
864
910 return null;
911 }
912 > color.ts
913 > function _parseHexDigit(charCode: CharCode): number {
914 switch (charCode) {
915 case CharCode.Digit0: return 0;
938 return 0;
939 }
940 > } color.ts
941 > }
942 > }