mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Provide convenience draw methods that deal with cells
This commit is contained in:
@@ -19,9 +19,9 @@ export class BackgroundRenderLayer extends BaseRenderLayer implements IDataRende
|
||||
this._state.resize(terminal.cols, terminal.rows);
|
||||
}
|
||||
|
||||
public clear(terminal: ITerminal): void {
|
||||
public reset(terminal: ITerminal): void {
|
||||
this._state.clear();
|
||||
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
this.clearAll();
|
||||
}
|
||||
|
||||
public render(terminal: ITerminal, startRow: number, endRow: number): void {
|
||||
@@ -48,11 +48,11 @@ export class BackgroundRenderLayer extends BaseRenderLayer implements IDataRende
|
||||
if (bg < 256) {
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = COLORS[bg];
|
||||
this._ctx.fillRect(x * this.scaledCharWidth, y * this.scaledCharHeight, this.scaledCharWidth, this.scaledCharHeight);
|
||||
this.fillCells(x, y, 1, 1);
|
||||
this._ctx.restore();
|
||||
this._state.cache[x][y] = bg;
|
||||
} else {
|
||||
this._ctx.clearRect(x * this.scaledCharWidth, y * this.scaledCharHeight, this.scaledCharWidth, this.scaledCharHeight);
|
||||
this.clearCells(x, y, 1, 1);
|
||||
this._state.cache[x][y] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ import { ITerminal } from '../Interfaces';
|
||||
import { COLORS } from './Color';
|
||||
|
||||
export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
protected _canvas: HTMLCanvasElement;
|
||||
private _canvas: HTMLCanvasElement;
|
||||
protected _ctx: CanvasRenderingContext2D;
|
||||
protected scaledCharWidth: number;
|
||||
protected scaledCharHeight: number;
|
||||
private scaledCharWidth: number;
|
||||
private scaledCharHeight: number;
|
||||
|
||||
// TODO: This will apply to all terminals, should it be per-terminal?
|
||||
protected static _charAtlas: ImageBitmap;
|
||||
private static _charAtlas: ImageBitmap;
|
||||
private static _charAtlasCharWidth: number;
|
||||
private static _charAtlasCharHeight: number;
|
||||
private static _charAtlasGenerator: CharAtlasGenerator;
|
||||
@@ -50,9 +50,21 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract clear(terminal: ITerminal): void;
|
||||
public abstract reset(terminal: ITerminal): void;
|
||||
|
||||
protected drawChar(terminal: ITerminal, char: string, code: number, fg: number, x: number, y: number, scaledCharWidth: number, scaledCharHeight: number): void {
|
||||
protected fillCells(startCol: number, startRow: number, colWidth: number, colHeight: number): void {
|
||||
this._ctx.fillRect(startCol * this.scaledCharWidth, startRow * this.scaledCharHeight, colWidth * this.scaledCharWidth, colHeight * this.scaledCharHeight);
|
||||
}
|
||||
|
||||
protected clearAll(): void {
|
||||
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
}
|
||||
|
||||
protected clearCells(startCol: number, startRow: number, colWidth: number, colHeight: number): void {
|
||||
this._ctx.clearRect(startCol * this.scaledCharWidth, startRow * this.scaledCharHeight, colWidth * this.scaledCharWidth, colHeight * this.scaledCharHeight);
|
||||
}
|
||||
|
||||
protected drawChar(terminal: ITerminal, char: string, code: number, fg: number, x: number, y: number): void {
|
||||
let colorIndex = 0;
|
||||
if (fg < 256) {
|
||||
colorIndex = fg + 1;
|
||||
@@ -60,10 +72,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
if (code < 256 && (colorIndex > 0 || fg > 255)) {
|
||||
// ImageBitmap's draw about twice as fast as from a canvas
|
||||
this._ctx.drawImage(BaseRenderLayer._charAtlas,
|
||||
code * scaledCharWidth, colorIndex * scaledCharHeight, scaledCharWidth, scaledCharHeight,
|
||||
x * scaledCharWidth, y * scaledCharHeight, scaledCharWidth, scaledCharHeight);
|
||||
code * this.scaledCharWidth, colorIndex * this.scaledCharHeight, this.scaledCharWidth, this.scaledCharHeight,
|
||||
x * this.scaledCharWidth, y * this.scaledCharHeight, this.scaledCharWidth, this.scaledCharHeight);
|
||||
} else {
|
||||
this._drawUncachedChar(terminal, char, fg, x, y, scaledCharWidth, scaledCharHeight);
|
||||
this._drawUncachedChar(terminal, char, fg, x, y, this.scaledCharWidth, this.scaledCharHeight);
|
||||
}
|
||||
// This draws the atlas (for debugging purposes)
|
||||
// this._ctx.drawImage(BaseRenderLayer._charAtlas, 0, 0);
|
||||
@@ -100,14 +112,12 @@ class CharAtlasGenerator {
|
||||
public generate(terminal: ITerminal, charWidth: number, charHeight: number): Promise<ImageBitmap> {
|
||||
const scaledCharWidth = Math.ceil(charWidth) * window.devicePixelRatio;
|
||||
const scaledCharHeight = Math.ceil(charHeight) * window.devicePixelRatio;
|
||||
console.log('generate');
|
||||
this._canvas.width = 255 * scaledCharWidth;
|
||||
this._canvas.height = (/*default*/1 + /*0-15*/16) * scaledCharHeight;
|
||||
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = '#ffffff';
|
||||
this._ctx.font = `${terminal.options.fontSize * window.devicePixelRatio}px ${terminal.options.fontFamily}`;
|
||||
console.log(this._ctx.font, scaledCharWidth, scaledCharHeight);
|
||||
this._ctx.textBaseline = 'top';
|
||||
|
||||
// Default color
|
||||
|
||||
@@ -14,7 +14,7 @@ export class CursorRenderLayer extends BaseRenderLayer implements IDataRenderLay
|
||||
this._state = null;
|
||||
}
|
||||
|
||||
public clear(terminal: ITerminal): void {
|
||||
public reset(terminal: ITerminal): void {
|
||||
this._clearCursor();
|
||||
}
|
||||
|
||||
@@ -46,18 +46,18 @@ export class CursorRenderLayer extends BaseRenderLayer implements IDataRenderLay
|
||||
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = COLORS[COLOR_CODES.WHITE];
|
||||
this._ctx.fillRect(terminal.buffer.x * this.scaledCharWidth, viewportRelativeCursorY * this.scaledCharHeight, this.scaledCharWidth, this.scaledCharHeight);
|
||||
this.fillCells(terminal.buffer.x, viewportRelativeCursorY, 1, 1);
|
||||
this._ctx.restore();
|
||||
|
||||
const charData = terminal.buffer.lines.get(cursorY)[terminal.buffer.x];
|
||||
this.drawChar(terminal, charData[CHAR_DATA_CHAR_INDEX], <number>charData[CHAR_DATA_CODE_INDEX], COLOR_CODES.BLACK, terminal.buffer.x, viewportRelativeCursorY, this.scaledCharWidth, this.scaledCharHeight);
|
||||
this.drawChar(terminal, charData[CHAR_DATA_CHAR_INDEX], <number>charData[CHAR_DATA_CODE_INDEX], COLOR_CODES.BLACK, terminal.buffer.x, viewportRelativeCursorY);
|
||||
|
||||
this._state = [terminal.buffer.x, viewportRelativeCursorY];
|
||||
}
|
||||
|
||||
private _clearCursor(): void {
|
||||
if (this._state) {
|
||||
this._ctx.clearRect(this._state[0] * this.scaledCharWidth, this._state[1] * this.scaledCharHeight, this.scaledCharWidth, this.scaledCharHeight);
|
||||
this.clearCells(this._state[0], this._state[1], 1, 1);
|
||||
this._state = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,18 +20,18 @@ export class ForegroundRenderLayer extends BaseRenderLayer implements IDataRende
|
||||
this._state.resize(terminal.cols, terminal.rows);
|
||||
}
|
||||
|
||||
public clear(terminal: ITerminal): void {
|
||||
public reset(terminal: ITerminal): void {
|
||||
this._state.clear();
|
||||
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
this.clearAll();
|
||||
}
|
||||
|
||||
public render(terminal: ITerminal, startRow: number, endRow: number): void {
|
||||
// TODO: Ensure that the render is eventually performed
|
||||
// Don't bother render until the atlas bitmap is ready
|
||||
// TODO: Move this to BaseRenderLayer?
|
||||
if (!BaseRenderLayer._charAtlas) {
|
||||
return;
|
||||
}
|
||||
// if (!BaseRenderLayer._charAtlas) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
for (let y = startRow; y <= endRow; y++) {
|
||||
const row = y + terminal.buffer.ydisp;
|
||||
@@ -53,7 +53,7 @@ export class ForegroundRenderLayer extends BaseRenderLayer implements IDataRende
|
||||
this._state.cache[x][y] = charData;
|
||||
|
||||
// Clear the old character
|
||||
this._ctx.clearRect(x * this.scaledCharWidth, y * this.scaledCharHeight, this.scaledCharWidth, this.scaledCharHeight);
|
||||
this.clearCells(x, y, 1, 1);
|
||||
|
||||
// Skip rendering if the character is invisible
|
||||
if (!code || code === 32 /*' '*/) {
|
||||
@@ -72,6 +72,7 @@ export class ForegroundRenderLayer extends BaseRenderLayer implements IDataRende
|
||||
}
|
||||
}
|
||||
|
||||
this._ctx.save();
|
||||
if (flags & FLAGS.BOLD) {
|
||||
this._ctx.font = `bold ${this._ctx.font}`;
|
||||
// Convert the FG color to the bold variant
|
||||
@@ -80,7 +81,8 @@ export class ForegroundRenderLayer extends BaseRenderLayer implements IDataRende
|
||||
}
|
||||
}
|
||||
|
||||
this.drawChar(terminal, char, code, fg, x, y, this.scaledCharWidth, this.scaledCharHeight);
|
||||
this.drawChar(terminal, char, code, fg, x, y);
|
||||
this._ctx.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export interface IRenderLayer {
|
||||
/**
|
||||
* Clear the state of the render layer.
|
||||
*/
|
||||
clear(terminal: ITerminal): void;
|
||||
reset(terminal: ITerminal): void;
|
||||
}
|
||||
|
||||
export interface IDataRenderLayer extends IRenderLayer {
|
||||
|
||||
@@ -59,10 +59,10 @@ export class Renderer {
|
||||
|
||||
public clear(): void {
|
||||
for (let i = 0; i < this._dataRenderLayers.length; i++) {
|
||||
this._dataRenderLayers[i].clear(this._terminal);
|
||||
this._dataRenderLayers[i].reset(this._terminal);
|
||||
}
|
||||
for (let i = 0; i < this._selectionRenderLayers.length; i++) {
|
||||
this._selectionRenderLayers[i].clear(this._terminal);
|
||||
this._selectionRenderLayers[i].reset(this._terminal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ export class SelectionRenderLayer extends BaseRenderLayer implements ISelectionR
|
||||
};
|
||||
}
|
||||
|
||||
public clear(terminal: ITerminal): void {
|
||||
public reset(terminal: ITerminal): void {
|
||||
if (this._state.start && this._state.end) {
|
||||
this._state = {
|
||||
start: null,
|
||||
end: null
|
||||
};
|
||||
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
this.clearAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export class SelectionRenderLayer extends BaseRenderLayer implements ISelectionR
|
||||
}
|
||||
|
||||
// Remove all selections
|
||||
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
this.clearAll();
|
||||
|
||||
// Selection does not exist
|
||||
if (!start || !end) {
|
||||
@@ -55,17 +55,17 @@ export class SelectionRenderLayer extends BaseRenderLayer implements ISelectionR
|
||||
const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
|
||||
const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : terminal.cols;
|
||||
this._ctx.fillStyle = 'rgba(255,255,255,0.3)';
|
||||
this._ctx.fillRect(startCol * this.scaledCharWidth, viewportCappedStartRow * this.scaledCharHeight, (startRowEndCol - startCol) * this.scaledCharWidth, this.scaledCharHeight);
|
||||
this.fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);
|
||||
|
||||
// Draw middle rows
|
||||
const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);
|
||||
this._ctx.fillRect(0, (viewportCappedStartRow + 1) * this.scaledCharHeight, terminal.cols * this.scaledCharWidth, middleRowsCount * this.scaledCharHeight);
|
||||
this.fillCells(0, viewportCappedStartRow + 1, terminal.cols, middleRowsCount);
|
||||
|
||||
// Draw final row
|
||||
if (viewportCappedStartRow !== viewportCappedEndRow) {
|
||||
// Only draw viewportEndRow if it's not the same as viewporttartRow
|
||||
const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : terminal.cols;
|
||||
this._ctx.fillRect(0, viewportCappedEndRow * this.scaledCharHeight, endCol * this.scaledCharWidth, this.scaledCharHeight);
|
||||
this.fillCells(0, viewportCappedEndRow, endCol, 1);
|
||||
}
|
||||
|
||||
// Save state for next render
|
||||
|
||||
Reference in New Issue
Block a user