UNPKG

@inst/vscode-bin-darwin

Version:

BINARY ONLY - VSCode binary deployment for macOS

297 lines (270 loc) 11.6 kB
/** * Copyright (c) 2017 The xterm.js authors. All rights reserved. * @license MIT */ import { IRenderLayer, IColorSet, IRenderDimensions } from './Interfaces'; import { ITerminal, ITerminalOptions } from '../Interfaces'; import { acquireCharAtlas, CHAR_ATLAS_CELL_SPACING } from './CharAtlas'; import { CharData } from '../Types'; import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from '../Buffer'; export const INVERTED_DEFAULT_COLOR = -1; export abstract class BaseRenderLayer implements IRenderLayer { private _canvas: HTMLCanvasElement; protected _ctx: CanvasRenderingContext2D; private _scaledCharWidth: number; private _scaledCharHeight: number; private _scaledLineHeight: number; private _scaledLineDrawY: number; private _charAtlas: HTMLCanvasElement | ImageBitmap; constructor( container: HTMLElement, id: string, zIndex: number, private _alpha: boolean, protected _colors: IColorSet ) { this._canvas = document.createElement('canvas'); this._canvas.id = `xterm-${id}-layer`; this._canvas.style.zIndex = zIndex.toString(); this._ctx = this._canvas.getContext('2d', {alpha: _alpha}); this._ctx.scale(window.devicePixelRatio, window.devicePixelRatio); // Draw the background if this is an opaque layer if (!_alpha) { this.clearAll(); } container.appendChild(this._canvas); } public onOptionsChanged(terminal: ITerminal): void {} public onBlur(terminal: ITerminal): void {} public onFocus(terminal: ITerminal): void {} public onCursorMove(terminal: ITerminal): void {} public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {} public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number]): void {} public onThemeChanged(terminal: ITerminal, colorSet: IColorSet): void { this._refreshCharAtlas(terminal, colorSet); } /** * Refreshes the char atlas, aquiring a new one if necessary. * @param terminal The terminal. * @param colorSet The color set to use for the char atlas. */ private _refreshCharAtlas(terminal: ITerminal, colorSet: IColorSet): void { if (this._scaledCharWidth <= 0 && this._scaledCharHeight <= 0) { return; } this._charAtlas = null; const result = acquireCharAtlas(terminal, this._colors, this._scaledCharWidth, this._scaledCharHeight); if (result instanceof HTMLCanvasElement) { this._charAtlas = result; } else { result.then(bitmap => this._charAtlas = bitmap); } } public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void { this._scaledCharWidth = dim.scaledCharWidth; this._scaledCharHeight = dim.scaledCharHeight; this._scaledLineHeight = dim.scaledLineHeight; this._scaledLineDrawY = dim.scaledLineDrawY; this._canvas.width = dim.scaledCanvasWidth; this._canvas.height = dim.scaledCanvasHeight; this._canvas.style.width = `${dim.canvasWidth}px`; this._canvas.style.height = `${dim.canvasHeight}px`; // Draw the background if this is an opaque layer if (!this._alpha) { this.clearAll(); } if (charSizeChanged) { this._refreshCharAtlas(terminal, this._colors); } } public abstract reset(terminal: ITerminal): void; /** * Fills 1+ cells completely. This uses the existing fillStyle on the context. * @param x The column to start at. * @param y The row to start at * @param width The number of columns to fill. * @param height The number of rows to fill. */ protected fillCells(x: number, y: number, width: number, height: number): void { this._ctx.fillRect( x * this._scaledCharWidth, y * this._scaledLineHeight, width * this._scaledCharWidth, height * this._scaledLineHeight); } /** * Fills a 1px line (2px on HDPI) at the bottom of the cell. This uses the * existing fillStyle on the context. * @param x The column to fill. * @param y The row to fill. */ protected fillBottomLineAtCells(x: number, y: number, width: number = 1): void { this._ctx.fillRect( x * this._scaledCharWidth, (y + 1) * this._scaledLineHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */, width * this._scaledCharWidth, window.devicePixelRatio); } /** * Fills a 1px line (2px on HDPI) at the left of the cell. This uses the * existing fillStyle on the context. * @param x The column to fill. * @param y The row to fill. */ protected fillLeftLineAtCell(x: number, y: number): void { this._ctx.fillRect( x * this._scaledCharWidth, y * this._scaledLineHeight, window.devicePixelRatio, this._scaledLineHeight); } /** * Strokes a 1px rectangle (2px on HDPI) around a cell. This uses the existing * strokeStyle on the context. * @param x The column to fill. * @param y The row to fill. */ protected strokeRectAtCell(x: number, y: number, width: number, height: number): void { this._ctx.lineWidth = window.devicePixelRatio; this._ctx.strokeRect( x * this._scaledCharWidth + window.devicePixelRatio / 2, y * this._scaledLineHeight + (window.devicePixelRatio / 2), width * this._scaledCharWidth - window.devicePixelRatio, (height * this._scaledLineHeight) - window.devicePixelRatio); } /** * Clears the entire canvas. */ protected clearAll(): void { if (this._alpha) { this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); } else { this._ctx.fillStyle = this._colors.background; this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height); } } /** * Clears 1+ cells completely. * @param x The column to start at. * @param y The row to start at. * @param width The number of columns to clear. * @param height The number of rows to clear. */ protected clearCells(x: number, y: number, width: number, height: number): void { if (this._alpha) { this._ctx.clearRect( x * this._scaledCharWidth, y * this._scaledLineHeight, width * this._scaledCharWidth, height * this._scaledLineHeight); } else { this._ctx.fillStyle = this._colors.background; this._ctx.fillRect( x * this._scaledCharWidth, y * this._scaledLineHeight, width * this._scaledCharWidth, height * this._scaledLineHeight); } } /** * Draws a truecolor character at the cell. The character will be clipped to * ensure that it fits with the cell, including the cell to the right if it's * a wide character. This uses the existing fillStyle on the context. * @param terminal The terminal. * @param charData The char data for the character to draw. * @param x The column to draw at. * @param y The row to draw at. * @param color The color of the character. */ protected fillCharTrueColor(terminal: ITerminal, charData: CharData, x: number, y: number): void { this._ctx.font = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`; this._ctx.textBaseline = 'top'; // Since uncached characters are not coming off the char atlas with source // coordinates, it means that text drawn to the canvas (particularly '_') // can bleed into other cells. This code will clip the following fillText, // ensuring that its contents don't go beyond the cell bounds. this._ctx.beginPath(); this._ctx.rect(x * this._scaledCharWidth, y * this._scaledLineHeight + this._scaledLineDrawY, charData[CHAR_DATA_WIDTH_INDEX] * this._scaledCharWidth, this._scaledCharHeight); this._ctx.clip(); this._ctx.fillText(charData[CHAR_DATA_CHAR_INDEX], x * this._scaledCharWidth, y * this._scaledCharHeight); } /** * Draws a character at a cell. If possible this will draw using the character * atlas to reduce draw time. * @param terminal The terminal. * @param char The character. * @param code The character code. * @param width The width of the character. * @param x The column to draw at. * @param y The row to draw at. * @param fg The foreground color, in the format stored within the attributes. * @param bg The background color, in the format stored within the attributes. * This is used to validate whether a cached image can be used. * @param bold Whether the text is bold. */ protected drawChar(terminal: ITerminal, char: string, code: number, width: number, x: number, y: number, fg: number, bg: number, bold: boolean): void { let colorIndex = 0; if (fg < 256) { colorIndex = fg + 2; } else { // If default color and bold if (bold) { colorIndex = 1; } } const isAscii = code < 256; const isBasicColor = (colorIndex > 1 && fg < 16); const isDefaultColor = fg >= 256; const isDefaultBackground = bg >= 256; if (this._charAtlas && isAscii && (isBasicColor || isDefaultColor) && isDefaultBackground) { // ImageBitmap's draw about twice as fast as from a canvas const charAtlasCellWidth = this._scaledCharWidth + CHAR_ATLAS_CELL_SPACING; const charAtlasCellHeight = this._scaledCharHeight + CHAR_ATLAS_CELL_SPACING; this._ctx.drawImage(this._charAtlas, code * charAtlasCellWidth, colorIndex * charAtlasCellHeight, charAtlasCellWidth, this._scaledCharHeight, x * this._scaledCharWidth, y * this._scaledLineHeight + this._scaledLineDrawY, charAtlasCellWidth, this._scaledCharHeight); } else { this._drawUncachedChar(terminal, char, width, fg, x, y, bold); } // This draws the atlas (for debugging purposes) // this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); // this._ctx.drawImage(this._charAtlas, 0, 0); } /** * Draws a character at a cell. The character will be clipped to * ensure that it fits with the cell, including the cell to the right if it's * a wide character. * @param terminal The terminal. * @param char The character. * @param width The width of the character. * @param fg The foreground color, in the format stored within the attributes. * @param x The column to draw at. * @param y The row to draw at. */ private _drawUncachedChar(terminal: ITerminal, char: string, width: number, fg: number, x: number, y: number, bold: boolean): void { this._ctx.save(); this._ctx.font = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`; if (bold) { this._ctx.font = `bold ${this._ctx.font}`; } this._ctx.textBaseline = 'top'; if (fg === INVERTED_DEFAULT_COLOR) { this._ctx.fillStyle = this._colors.background; } else if (fg < 256) { // 256 color support this._ctx.fillStyle = this._colors.ansi[fg]; } else { this._ctx.fillStyle = this._colors.foreground; } // Since uncached characters are not coming off the char atlas with source // coordinates, it means that text drawn to the canvas (particularly '_') // can bleed into other cells. This code will clip the following fillText, // ensuring that its contents don't go beyond the cell bounds. this._ctx.beginPath(); this._ctx.rect(0, y * this._scaledLineHeight + this._scaledLineDrawY, terminal.cols * this._scaledCharWidth, this._scaledCharHeight); this._ctx.clip(); // Draw the character this._ctx.fillText(char, x * this._scaledCharWidth, y * this._scaledLineHeight + this._scaledLineDrawY); this._ctx.restore(); } }