mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Allow theming with arbitrary CSS color strings
This mostly worked before, but clearColor made the assumption that the colors were always in #RRBBGG format. See the discussion here: https://github.com/xtermjs/xterm.js/pull/1327#issuecomment-374812842 This changes the internal representation of a color so that we hold onto into an object containing the original css string along with an RGBA value encoded as a 32-bit uint. clearColor can then use that RGBA representation. Additionally, this deduplicates some code between ColorManager and Terminal. Terminal was generating the 256 ansi colors the same way ColorManager was, so this just makes Terminal use the same constant.
This commit is contained in:
+8
-39
@@ -622,7 +622,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
|
||||
// Create main element container
|
||||
this.element = this._document.createElement('div');
|
||||
this.element.dir = 'ltr'; //xterm.css assumes LTR
|
||||
this.element.dir = 'ltr'; // xterm.css assumes LTR
|
||||
this.element.classList.add('terminal');
|
||||
this.element.classList.add('xterm');
|
||||
this.element.setAttribute('tabindex', '0');
|
||||
@@ -2235,38 +2235,6 @@ function wasMondifierKeyOnlyEvent(ev: KeyboardEvent): boolean {
|
||||
* ANSI color code.
|
||||
*/
|
||||
|
||||
// Colors 0-15 + 16-255
|
||||
// Much thanks to TooTallNate for writing this.
|
||||
const vcolors: number[][] = (function(): number[][] {
|
||||
const result = DEFAULT_ANSI_COLORS.map(c => {
|
||||
c = c.substring(1);
|
||||
return [
|
||||
parseInt(c.substring(0, 2), 16),
|
||||
parseInt(c.substring(2, 4), 16),
|
||||
parseInt(c.substring(4, 6), 16)
|
||||
];
|
||||
});
|
||||
const r = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];
|
||||
|
||||
// 16-231
|
||||
for (let i = 0; i < 216; i++) {
|
||||
result.push([
|
||||
r[(i / 36) % 6 | 0],
|
||||
r[(i / 6) % 6 | 0],
|
||||
r[i % 6]
|
||||
]);
|
||||
}
|
||||
|
||||
// 232-255 (grey)
|
||||
let c: number;
|
||||
for (let i = 0; i < 24; i++) {
|
||||
c = 8 + i * 10;
|
||||
result.push([c, c, c]);
|
||||
}
|
||||
|
||||
return result;
|
||||
})();
|
||||
|
||||
const matchColorCache: {[colorRGBHash: number]: number} = {};
|
||||
|
||||
// http://stackoverflow.com/questions/1633828
|
||||
@@ -2287,17 +2255,18 @@ function matchColor_(r1: number, g1: number, b1: number): number {
|
||||
let ldiff = Infinity;
|
||||
let li = -1;
|
||||
let i = 0;
|
||||
let c: number[];
|
||||
let c: number;
|
||||
let r2: number;
|
||||
let g2: number;
|
||||
let b2: number;
|
||||
let diff: number;
|
||||
|
||||
for (; i < vcolors.length; i++) {
|
||||
c = vcolors[i];
|
||||
r2 = c[0];
|
||||
g2 = c[1];
|
||||
b2 = c[2];
|
||||
for (; i < DEFAULT_ANSI_COLORS.length; i++) {
|
||||
c = DEFAULT_ANSI_COLORS[i].rgba;
|
||||
r2 = c >>> 24;
|
||||
g2 = c >>> 16 & 0xFF;
|
||||
b2 = c >>> 8 & 0xFF;
|
||||
// assume that alpha is 0xFF
|
||||
|
||||
diff = matchColorDistance(r1, g1, b1, r2, g2, b2);
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ export class Viewport implements IViewport {
|
||||
}
|
||||
|
||||
public onThemeChanged(colors: IColorSet): void {
|
||||
this._viewportElement.style.backgroundColor = colors.background;
|
||||
this._viewportElement.style.backgroundColor = colors.background.css;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -179,7 +179,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
if (this._alpha) {
|
||||
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
} else {
|
||||
this._ctx.fillStyle = this._colors.background;
|
||||
this._ctx.fillStyle = this._colors.background.css;
|
||||
this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
width * this._scaledCellWidth,
|
||||
height * this._scaledCellHeight);
|
||||
} else {
|
||||
this._ctx.fillStyle = this._colors.background;
|
||||
this._ctx.fillStyle = this._colors.background.css;
|
||||
this._ctx.fillRect(
|
||||
x * this._scaledCellWidth,
|
||||
y * this._scaledCellHeight,
|
||||
@@ -311,12 +311,12 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._ctx.textBaseline = 'top';
|
||||
|
||||
if (fg === INVERTED_DEFAULT_COLOR) {
|
||||
this._ctx.fillStyle = this._colors.background;
|
||||
this._ctx.fillStyle = this._colors.background.css;
|
||||
} else if (fg < 256) {
|
||||
// 256 color support
|
||||
this._ctx.fillStyle = this._colors.ansi[fg];
|
||||
this._ctx.fillStyle = this._colors.ansi[fg].css;
|
||||
} else {
|
||||
this._ctx.fillStyle = this._colors.foreground;
|
||||
this._ctx.fillStyle = this._colors.foreground.css;
|
||||
}
|
||||
|
||||
this._clipRow(terminal, y);
|
||||
|
||||
+260
-249
File diff suppressed because it is too large
Load Diff
+101
-82
@@ -3,58 +3,75 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { IColorSet, IColorManager } from './Types';
|
||||
import { IColorManager } from './Types';
|
||||
import { IColor, IColorSet } from '../shared/Types';
|
||||
import { ITheme } from 'xterm';
|
||||
|
||||
const DEFAULT_FOREGROUND = '#ffffff';
|
||||
const DEFAULT_BACKGROUND = '#000000';
|
||||
const DEFAULT_CURSOR = '#ffffff';
|
||||
const DEFAULT_CURSOR_ACCENT = '#000000';
|
||||
const DEFAULT_SELECTION = 'rgba(255, 255, 255, 0.3)';
|
||||
export const DEFAULT_ANSI_COLORS = [
|
||||
// dark:
|
||||
'#2e3436',
|
||||
'#cc0000',
|
||||
'#4e9a06',
|
||||
'#c4a000',
|
||||
'#3465a4',
|
||||
'#75507b',
|
||||
'#06989a',
|
||||
'#d3d7cf',
|
||||
// bright:
|
||||
'#555753',
|
||||
'#ef2929',
|
||||
'#8ae234',
|
||||
'#fce94f',
|
||||
'#729fcf',
|
||||
'#ad7fa8',
|
||||
'#34e2e2',
|
||||
'#eeeeec'
|
||||
];
|
||||
const DEFAULT_FOREGROUND = fromHex('#ffffff');
|
||||
const DEFAULT_BACKGROUND = fromHex('#000000');
|
||||
const DEFAULT_CURSOR = fromHex('#ffffff');
|
||||
const DEFAULT_CURSOR_ACCENT = fromHex('#000000');
|
||||
const DEFAULT_SELECTION = {
|
||||
css: 'rgba(255, 255, 255, 0.3)',
|
||||
rgba: 0xFFFFFF77
|
||||
};
|
||||
|
||||
/**
|
||||
* Fills an existing 16 length string with the remaining 240 ANSI colors.
|
||||
* @param first16Colors The first 16 ANSI colors.
|
||||
*/
|
||||
function generate256Colors(first16Colors: string[]): string[] {
|
||||
let colors = first16Colors.slice();
|
||||
// An IIFE to generate DEFAULT_ANSI_COLORS. Do not mutate DEFAULT_ANSI_COLORS, instead make a copy
|
||||
// and mutate that.
|
||||
export const DEFAULT_ANSI_COLORS = (() => {
|
||||
const colors = [
|
||||
// dark:
|
||||
fromHex('#2e3436'),
|
||||
fromHex('#cc0000'),
|
||||
fromHex('#4e9a06'),
|
||||
fromHex('#c4a000'),
|
||||
fromHex('#3465a4'),
|
||||
fromHex('#75507b'),
|
||||
fromHex('#06989a'),
|
||||
fromHex('#d3d7cf'),
|
||||
// bright:
|
||||
fromHex('#555753'),
|
||||
fromHex('#ef2929'),
|
||||
fromHex('#8ae234'),
|
||||
fromHex('#fce94f'),
|
||||
fromHex('#729fcf'),
|
||||
fromHex('#ad7fa8'),
|
||||
fromHex('#34e2e2'),
|
||||
fromHex('#eeeeec')
|
||||
];
|
||||
|
||||
// Fill in the remaining 240 ANSI colors.
|
||||
// Generate colors (16-231)
|
||||
let v = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff];
|
||||
for (let i = 0; i < 216; i++) {
|
||||
const r = toPaddedHex(v[(i / 36) % 6 | 0]);
|
||||
const g = toPaddedHex(v[(i / 6) % 6 | 0]);
|
||||
const b = toPaddedHex(v[i % 6]);
|
||||
colors.push(`#${r}${g}${b}`);
|
||||
const r = v[(i / 36) % 6 | 0];
|
||||
const g = v[(i / 6) % 6 | 0];
|
||||
const b = v[i % 6];
|
||||
colors.push({
|
||||
css: `#${toPaddedHex(r)}${toPaddedHex(g)}${toPaddedHex(b)}`,
|
||||
// Use >>> 0 to force a conversion to an unsigned int
|
||||
rgba: ((r << 24) | (g << 16) | (b << 8) | 0xFF) >>> 0
|
||||
});
|
||||
}
|
||||
|
||||
// Generate greys (232-255)
|
||||
for (let i = 0; i < 24; i++) {
|
||||
const c = toPaddedHex(8 + i * 10);
|
||||
colors.push(`#${c}${c}${c}`);
|
||||
const c = 8 + i * 10;
|
||||
const ch = toPaddedHex(c);
|
||||
colors.push({
|
||||
css: `#${ch}${ch}${ch}`,
|
||||
rgba: ((c << 24) | (c << 16) | (c << 8) | 0xFF) >>> 0
|
||||
});
|
||||
}
|
||||
|
||||
return colors;
|
||||
})();
|
||||
|
||||
function fromHex(css: string): IColor {
|
||||
return {
|
||||
css,
|
||||
rgba: parseInt(css.slice(1), 16) << 8 | 0xFF
|
||||
};
|
||||
}
|
||||
|
||||
function toPaddedHex(c: number): string {
|
||||
@@ -67,17 +84,23 @@ function toPaddedHex(c: number): string {
|
||||
*/
|
||||
export class ColorManager implements IColorManager {
|
||||
public colors: IColorSet;
|
||||
private _document: Document;
|
||||
private _ctx: CanvasRenderingContext2D;
|
||||
private _litmusColor: CanvasGradient;
|
||||
|
||||
constructor(document: Document) {
|
||||
this._document = document;
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
this._ctx = canvas.getContext('2d');
|
||||
this._ctx.globalCompositeOperation = 'copy';
|
||||
this._litmusColor = this._ctx.createLinearGradient(0, 0, 1, 1);
|
||||
this.colors = {
|
||||
foreground: DEFAULT_FOREGROUND,
|
||||
background: DEFAULT_BACKGROUND,
|
||||
cursor: DEFAULT_CURSOR,
|
||||
cursorAccent: DEFAULT_CURSOR_ACCENT,
|
||||
selection: DEFAULT_SELECTION,
|
||||
ansi: generate256Colors(DEFAULT_ANSI_COLORS)
|
||||
ansi: DEFAULT_ANSI_COLORS.slice()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,54 +110,50 @@ export class ColorManager implements IColorManager {
|
||||
* colors will be used where colors are not defined.
|
||||
*/
|
||||
public setTheme(theme: ITheme): void {
|
||||
this.colors.foreground = this._validateColor(theme.foreground, DEFAULT_FOREGROUND);
|
||||
this.colors.background = this._validateColor(theme.background, DEFAULT_BACKGROUND);
|
||||
this.colors.cursor = this._validateColor(theme.cursor, DEFAULT_CURSOR);
|
||||
this.colors.cursorAccent = this._validateColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT);
|
||||
this.colors.selection = this._validateColor(theme.selection, DEFAULT_SELECTION);
|
||||
this.colors.ansi[0] = this._validateColor(theme.black, DEFAULT_ANSI_COLORS[0]);
|
||||
this.colors.ansi[1] = this._validateColor(theme.red, DEFAULT_ANSI_COLORS[1]);
|
||||
this.colors.ansi[2] = this._validateColor(theme.green, DEFAULT_ANSI_COLORS[2]);
|
||||
this.colors.ansi[3] = this._validateColor(theme.yellow, DEFAULT_ANSI_COLORS[3]);
|
||||
this.colors.ansi[4] = this._validateColor(theme.blue, DEFAULT_ANSI_COLORS[4]);
|
||||
this.colors.ansi[5] = this._validateColor(theme.magenta, DEFAULT_ANSI_COLORS[5]);
|
||||
this.colors.ansi[6] = this._validateColor(theme.cyan, DEFAULT_ANSI_COLORS[6]);
|
||||
this.colors.ansi[7] = this._validateColor(theme.white, DEFAULT_ANSI_COLORS[7]);
|
||||
this.colors.ansi[8] = this._validateColor(theme.brightBlack, DEFAULT_ANSI_COLORS[8]);
|
||||
this.colors.ansi[9] = this._validateColor(theme.brightRed, DEFAULT_ANSI_COLORS[9]);
|
||||
this.colors.ansi[10] = this._validateColor(theme.brightGreen, DEFAULT_ANSI_COLORS[10]);
|
||||
this.colors.ansi[11] = this._validateColor(theme.brightYellow, DEFAULT_ANSI_COLORS[11]);
|
||||
this.colors.ansi[12] = this._validateColor(theme.brightBlue, DEFAULT_ANSI_COLORS[12]);
|
||||
this.colors.ansi[13] = this._validateColor(theme.brightMagenta, DEFAULT_ANSI_COLORS[13]);
|
||||
this.colors.ansi[14] = this._validateColor(theme.brightCyan, DEFAULT_ANSI_COLORS[14]);
|
||||
this.colors.ansi[15] = this._validateColor(theme.brightWhite, DEFAULT_ANSI_COLORS[15]);
|
||||
this.colors.foreground = this._parseColor(theme.foreground, DEFAULT_FOREGROUND);
|
||||
this.colors.background = this._parseColor(theme.background, DEFAULT_BACKGROUND);
|
||||
this.colors.cursor = this._parseColor(theme.cursor, DEFAULT_CURSOR);
|
||||
this.colors.cursorAccent = this._parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT);
|
||||
this.colors.selection = this._parseColor(theme.selection, DEFAULT_SELECTION);
|
||||
this.colors.ansi[0] = this._parseColor(theme.black, DEFAULT_ANSI_COLORS[0]);
|
||||
this.colors.ansi[1] = this._parseColor(theme.red, DEFAULT_ANSI_COLORS[1]);
|
||||
this.colors.ansi[2] = this._parseColor(theme.green, DEFAULT_ANSI_COLORS[2]);
|
||||
this.colors.ansi[3] = this._parseColor(theme.yellow, DEFAULT_ANSI_COLORS[3]);
|
||||
this.colors.ansi[4] = this._parseColor(theme.blue, DEFAULT_ANSI_COLORS[4]);
|
||||
this.colors.ansi[5] = this._parseColor(theme.magenta, DEFAULT_ANSI_COLORS[5]);
|
||||
this.colors.ansi[6] = this._parseColor(theme.cyan, DEFAULT_ANSI_COLORS[6]);
|
||||
this.colors.ansi[7] = this._parseColor(theme.white, DEFAULT_ANSI_COLORS[7]);
|
||||
this.colors.ansi[8] = this._parseColor(theme.brightBlack, DEFAULT_ANSI_COLORS[8]);
|
||||
this.colors.ansi[9] = this._parseColor(theme.brightRed, DEFAULT_ANSI_COLORS[9]);
|
||||
this.colors.ansi[10] = this._parseColor(theme.brightGreen, DEFAULT_ANSI_COLORS[10]);
|
||||
this.colors.ansi[11] = this._parseColor(theme.brightYellow, DEFAULT_ANSI_COLORS[11]);
|
||||
this.colors.ansi[12] = this._parseColor(theme.brightBlue, DEFAULT_ANSI_COLORS[12]);
|
||||
this.colors.ansi[13] = this._parseColor(theme.brightMagenta, DEFAULT_ANSI_COLORS[13]);
|
||||
this.colors.ansi[14] = this._parseColor(theme.brightCyan, DEFAULT_ANSI_COLORS[14]);
|
||||
this.colors.ansi[15] = this._parseColor(theme.brightWhite, DEFAULT_ANSI_COLORS[15]);
|
||||
}
|
||||
|
||||
private _validateColor(color: string, fallback: string): string {
|
||||
if (!color) {
|
||||
private _parseColor(css: string, fallback: IColor): IColor {
|
||||
if (!css) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const isColorValid = this._isColorValid(color);
|
||||
|
||||
if (!isColorValid) {
|
||||
console.warn(`Color: ${color} is invalid using fallback ${fallback}`);
|
||||
// If parsing the value results in failure, then it must be ignored, and the attribute must
|
||||
// retain its previous value.
|
||||
// -- https://html.spec.whatwg.org/multipage/canvas.html#fill-and-stroke-styles
|
||||
this._ctx.fillStyle = this._litmusColor;
|
||||
this._ctx.fillStyle = css;
|
||||
if (typeof this._ctx.fillStyle !== 'string') {
|
||||
console.warn(`Color: ${css} is invalid using fallback ${fallback.css}`);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return isColorValid ? color : fallback;
|
||||
}
|
||||
this._ctx.fillRect(0, 0, 1, 1);
|
||||
const data = this._ctx.getImageData(0, 0, 1, 1).data;
|
||||
|
||||
private _isColorValid(color: string): boolean {
|
||||
const litmus = 'red';
|
||||
const d = this._document.createElement('div');
|
||||
d.style.color = litmus;
|
||||
d.style.color = color;
|
||||
|
||||
// Element's style.color will be reverted to litmus or set to '' if an invalid color is given
|
||||
if (color !== litmus && (d.style.color === litmus || d.style.color === '')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return {
|
||||
css,
|
||||
rgba: data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
if (!terminal.isFocused) {
|
||||
this._clearCursor();
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this._colors.cursor;
|
||||
this._ctx.fillStyle = this._colors.cursor.css;
|
||||
this._renderBlurCursor(terminal, terminal.buffer.x, viewportRelativeCursorY, charData);
|
||||
this._ctx.restore();
|
||||
this._state.x = terminal.buffer.x;
|
||||
@@ -190,30 +190,30 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
|
||||
private _renderBarCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this._colors.cursor;
|
||||
this._ctx.fillStyle = this._colors.cursor.css;
|
||||
this.fillLeftLineAtCell(x, y);
|
||||
this._ctx.restore();
|
||||
}
|
||||
|
||||
private _renderBlockCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this._colors.cursor;
|
||||
this._ctx.fillStyle = this._colors.cursor.css;
|
||||
this.fillCells(x, y, charData[CHAR_DATA_WIDTH_INDEX], 1);
|
||||
this._ctx.fillStyle = this._colors.cursorAccent;
|
||||
this._ctx.fillStyle = this._colors.cursorAccent.css;
|
||||
this.fillCharTrueColor(terminal, charData, x, y);
|
||||
this._ctx.restore();
|
||||
}
|
||||
|
||||
private _renderUnderlineCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this._colors.cursor;
|
||||
this._ctx.fillStyle = this._colors.cursor.css;
|
||||
this.fillBottomLineAtCells(x, y);
|
||||
this._ctx.restore();
|
||||
}
|
||||
|
||||
private _renderBlurCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {
|
||||
this._ctx.save();
|
||||
this._ctx.strokeStyle = this._colors.cursor;
|
||||
this._ctx.strokeStyle = this._colors.cursor.css;
|
||||
this.strokeRectAtCell(x, y, charData[CHAR_DATA_WIDTH_INDEX], 1);
|
||||
this._ctx.restore();
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export class LinkRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
|
||||
private _onLinkHover(e: ILinkHoverEvent): void {
|
||||
this._ctx.fillStyle = this._colors.foreground;
|
||||
this._ctx.fillStyle = this._colors.foreground.css;
|
||||
if (e.y1 === e.y2) {
|
||||
// Single line link
|
||||
this.fillBottomLineAtCells(e.x1, e.y1, e.x2 - e.x1);
|
||||
|
||||
@@ -65,7 +65,7 @@ export class SelectionRenderLayer extends BaseRenderLayer {
|
||||
// Draw first row
|
||||
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
|
||||
const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols;
|
||||
this._ctx.fillStyle = this._colors.selection;
|
||||
this._ctx.fillStyle = this._colors.selection.css;
|
||||
this.fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);
|
||||
|
||||
// Draw middle rows
|
||||
|
||||
@@ -158,7 +158,7 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
// Draw background
|
||||
if (bg < 256) {
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = (bg === INVERTED_DEFAULT_COLOR ? this._colors.foreground : this._colors.ansi[bg]);
|
||||
this._ctx.fillStyle = (bg === INVERTED_DEFAULT_COLOR ? this._colors.foreground.css : this._colors.ansi[bg].css);
|
||||
this.fillCells(x, y, width, 1);
|
||||
this._ctx.restore();
|
||||
}
|
||||
@@ -174,12 +174,12 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
|
||||
if (flags & FLAGS.UNDERLINE) {
|
||||
if (fg === INVERTED_DEFAULT_COLOR) {
|
||||
this._ctx.fillStyle = this._colors.background;
|
||||
this._ctx.fillStyle = this._colors.background.css;
|
||||
} else if (fg < 256) {
|
||||
// 256 color support
|
||||
this._ctx.fillStyle = this._colors.ansi[fg];
|
||||
this._ctx.fillStyle = this._colors.ansi[fg].css;
|
||||
} else {
|
||||
this._ctx.fillStyle = this._colors.foreground;
|
||||
this._ctx.fillStyle = this._colors.foreground.css;
|
||||
}
|
||||
this.fillBottomLineAtCells(x, y);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export function generateConfig(scaledCharWidth: number, scaledCharHeight: number
|
||||
|
||||
export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean {
|
||||
for (let i = 0; i < a.colors.ansi.length; i++) {
|
||||
if (a.colors.ansi[i] !== b.colors.ansi[i]) {
|
||||
if (a.colors.ansi[i].rgba !== b.colors.ansi[i].rgba) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-6
@@ -3,11 +3,16 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
export interface IColor {
|
||||
css: string;
|
||||
rgba: number; // 32-bit int with rgba in each byte
|
||||
}
|
||||
|
||||
export interface IColorSet {
|
||||
foreground: string;
|
||||
background: string;
|
||||
cursor: string;
|
||||
cursorAccent: string;
|
||||
selection: string;
|
||||
ansi: string[];
|
||||
foreground: IColor;
|
||||
background: IColor;
|
||||
cursor: IColor;
|
||||
cursorAccent: IColor;
|
||||
selection: IColor;
|
||||
ansi: IColor[];
|
||||
}
|
||||
|
||||
@@ -31,11 +31,11 @@ export function generateCharAtlas(context: Window, canvasFactory: (width: number
|
||||
);
|
||||
const ctx = canvas.getContext('2d', {alpha: config.allowTransparency});
|
||||
|
||||
ctx.fillStyle = config.colors.background;
|
||||
ctx.fillStyle = config.colors.background.css;
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.save();
|
||||
ctx.fillStyle = config.colors.foreground;
|
||||
ctx.fillStyle = config.colors.foreground.css;
|
||||
ctx.font = getFont(config.fontWeight, config);
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
@@ -75,7 +75,7 @@ export function generateCharAtlas(context: Window, canvasFactory: (width: number
|
||||
ctx.beginPath();
|
||||
ctx.rect(i * cellWidth, y, cellWidth, cellHeight);
|
||||
ctx.clip();
|
||||
ctx.fillStyle = config.colors.ansi[colorIndex];
|
||||
ctx.fillStyle = config.colors.ansi[colorIndex].css;
|
||||
ctx.fillText(String.fromCharCode(i), i * cellWidth, y);
|
||||
ctx.restore();
|
||||
}
|
||||
@@ -100,9 +100,9 @@ export function generateCharAtlas(context: Window, canvasFactory: (width: number
|
||||
const charAtlasImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Remove the background color from the image so characters may overlap
|
||||
const r = parseInt(config.colors.background.substr(1, 2), 16);
|
||||
const g = parseInt(config.colors.background.substr(3, 2), 16);
|
||||
const b = parseInt(config.colors.background.substr(5, 2), 16);
|
||||
const r = config.colors.background.rgba >>> 24;
|
||||
const g = config.colors.background.rgba >>> 16 & 0xFF;
|
||||
const b = config.colors.background.rgba >>> 8 & 0xFF;
|
||||
clearColor(charAtlasImageData, r, g, b);
|
||||
|
||||
return context.createImageBitmap(charAtlasImageData);
|
||||
|
||||
Reference in New Issue
Block a user