From 510940389c2714ba985475c9f140b02705aa4646 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 31 Aug 2017 23:01:15 -0700 Subject: [PATCH] Provide convenience draw methods that deal with cells --- src/renderer/BackgroundRenderLayer.ts | 8 +++---- src/renderer/BaseRenderLayer.ts | 32 ++++++++++++++++++--------- src/renderer/CursorRenderLayer.ts | 8 +++---- src/renderer/ForegroundRenderLayer.ts | 16 ++++++++------ src/renderer/Interfaces.ts | 2 +- src/renderer/Renderer.ts | 4 ++-- src/renderer/SelectionRenderLayer.ts | 12 +++++----- 7 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/renderer/BackgroundRenderLayer.ts b/src/renderer/BackgroundRenderLayer.ts index 1e8169d7..a37c4387 100644 --- a/src/renderer/BackgroundRenderLayer.ts +++ b/src/renderer/BackgroundRenderLayer.ts @@ -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; } } diff --git a/src/renderer/BaseRenderLayer.ts b/src/renderer/BaseRenderLayer.ts index 7790e034..e286b220 100644 --- a/src/renderer/BaseRenderLayer.ts +++ b/src/renderer/BaseRenderLayer.ts @@ -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 { 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 diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts index bb0ff928..7b5be052 100644 --- a/src/renderer/CursorRenderLayer.ts +++ b/src/renderer/CursorRenderLayer.ts @@ -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], charData[CHAR_DATA_CODE_INDEX], COLOR_CODES.BLACK, terminal.buffer.x, viewportRelativeCursorY, this.scaledCharWidth, this.scaledCharHeight); + this.drawChar(terminal, charData[CHAR_DATA_CHAR_INDEX], 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; } } diff --git a/src/renderer/ForegroundRenderLayer.ts b/src/renderer/ForegroundRenderLayer.ts index 68d00857..e5b814ce 100644 --- a/src/renderer/ForegroundRenderLayer.ts +++ b/src/renderer/ForegroundRenderLayer.ts @@ -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(); } } } diff --git a/src/renderer/Interfaces.ts b/src/renderer/Interfaces.ts index 8eb3acdd..82b2e77a 100644 --- a/src/renderer/Interfaces.ts +++ b/src/renderer/Interfaces.ts @@ -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 { diff --git a/src/renderer/Renderer.ts b/src/renderer/Renderer.ts index 26b472cf..a512c983 100644 --- a/src/renderer/Renderer.ts +++ b/src/renderer/Renderer.ts @@ -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); } } diff --git a/src/renderer/SelectionRenderLayer.ts b/src/renderer/SelectionRenderLayer.ts index 602bb486..daaf160d 100644 --- a/src/renderer/SelectionRenderLayer.ts +++ b/src/renderer/SelectionRenderLayer.ts @@ -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