mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge pull request #1045 from Tyriar/more_rendering_improvements
More rendering improvements
This commit is contained in:
@@ -140,6 +140,7 @@ namespace methods_core {
|
||||
const r18: (data: string) => void = t.getOption('handler');
|
||||
const r19: string = t.getOption('bellSound');
|
||||
const r20: string = t.getOption('bellStyle');
|
||||
const r22: number = t.getOption('letterSpacing');
|
||||
}
|
||||
{
|
||||
const t: Terminal = new Terminal();
|
||||
@@ -157,6 +158,7 @@ namespace methods_core {
|
||||
t.setOption('useFlowControl', true);
|
||||
t.setOption('visualBell', true);
|
||||
t.setOption('colors', ['a', 'b']);
|
||||
t.setOption('letterSpacing', 1);
|
||||
t.setOption('cols', 1);
|
||||
t.setOption('rows', 1);
|
||||
t.setOption('tabStopWidth', 1);
|
||||
|
||||
@@ -139,6 +139,7 @@ export interface ITerminalOptions {
|
||||
fontFamily?: string;
|
||||
geometry?: [number, number];
|
||||
handler?: (data: string) => void;
|
||||
letterSpacing?: number;
|
||||
lineHeight?: number;
|
||||
rows?: number;
|
||||
screenKeys?: boolean;
|
||||
|
||||
@@ -80,6 +80,7 @@ const DEFAULT_OPTIONS: ITerminalOptions = {
|
||||
fontFamily: 'courier-new, courier, monospace',
|
||||
fontSize: 15,
|
||||
lineHeight: 1.0,
|
||||
letterSpacing: 0,
|
||||
scrollback: 1000,
|
||||
screenKeys: false,
|
||||
debug: false,
|
||||
@@ -410,6 +411,7 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
this.renderer.clear();
|
||||
this.charMeasure.measure(this.options);
|
||||
break;
|
||||
case 'letterSpacing':
|
||||
case 'lineHeight':
|
||||
// When the font changes the size of the cells may change which requires a renderer clear
|
||||
this.renderer.clear();
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ export class Viewport implements IViewport {
|
||||
*/
|
||||
private refresh(): void {
|
||||
if (this.charMeasure.height > 0) {
|
||||
this.currentRowHeight = this.terminal.renderer.dimensions.scaledLineHeight / window.devicePixelRatio;
|
||||
this.currentRowHeight = this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio;
|
||||
|
||||
if (this.lastRecordedViewportHeight !== this.terminal.renderer.dimensions.canvasHeight) {
|
||||
this.lastRecordedViewportHeight = this.terminal.renderer.dimensions.canvasHeight;
|
||||
@@ -75,7 +75,7 @@ export class Viewport implements IViewport {
|
||||
this.refresh();
|
||||
} else {
|
||||
// If size has changed, refresh viewport
|
||||
if (this.terminal.renderer.dimensions.scaledLineHeight / window.devicePixelRatio !== this.currentRowHeight) {
|
||||
if (this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this.currentRowHeight) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
protected _ctx: CanvasRenderingContext2D;
|
||||
private _scaledCharWidth: number;
|
||||
private _scaledCharHeight: number;
|
||||
private _scaledLineHeight: number;
|
||||
private _scaledLineDrawY: number;
|
||||
private _scaledCellWidth: number;
|
||||
private _scaledCellHeight: number;
|
||||
private _scaledCharLeft: number;
|
||||
private _scaledCharTop: number;
|
||||
|
||||
private _charAtlas: HTMLCanvasElement | ImageBitmap;
|
||||
|
||||
@@ -71,10 +73,12 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
}
|
||||
|
||||
public resize(terminal: ITerminal, dim: IRenderDimensions, charSizeChanged: boolean): void {
|
||||
this._scaledCellWidth = dim.scaledCellWidth;
|
||||
this._scaledCellHeight = dim.scaledCellHeight;
|
||||
this._scaledCharWidth = dim.scaledCharWidth;
|
||||
this._scaledCharHeight = dim.scaledCharHeight;
|
||||
this._scaledLineHeight = dim.scaledLineHeight;
|
||||
this._scaledLineDrawY = dim.scaledLineDrawY;
|
||||
this._scaledCharLeft = dim.scaledCharLeft;
|
||||
this._scaledCharTop = dim.scaledCharTop;
|
||||
this._canvas.width = dim.scaledCanvasWidth;
|
||||
this._canvas.height = dim.scaledCanvasHeight;
|
||||
this._canvas.style.width = `${dim.canvasWidth}px`;
|
||||
@@ -101,10 +105,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
*/
|
||||
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);
|
||||
x * this._scaledCellWidth,
|
||||
y * this._scaledCellHeight,
|
||||
width * this._scaledCellWidth,
|
||||
height * this._scaledCellHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,9 +119,9 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
*/
|
||||
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,
|
||||
x * this._scaledCellWidth,
|
||||
(y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,
|
||||
width * this._scaledCellWidth,
|
||||
window.devicePixelRatio);
|
||||
}
|
||||
|
||||
@@ -129,10 +133,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
*/
|
||||
protected fillLeftLineAtCell(x: number, y: number): void {
|
||||
this._ctx.fillRect(
|
||||
x * this._scaledCharWidth,
|
||||
y * this._scaledLineHeight,
|
||||
x * this._scaledCellWidth,
|
||||
y * this._scaledCellHeight,
|
||||
window.devicePixelRatio,
|
||||
this._scaledLineHeight);
|
||||
this._scaledCellHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,10 +148,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
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);
|
||||
x * this._scaledCellWidth + window.devicePixelRatio / 2,
|
||||
y * this._scaledCellHeight + (window.devicePixelRatio / 2),
|
||||
width * this._scaledCellWidth - window.devicePixelRatio,
|
||||
(height * this._scaledCellHeight) - window.devicePixelRatio);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,17 +176,17 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
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);
|
||||
x * this._scaledCellWidth,
|
||||
y * this._scaledCellHeight,
|
||||
width * this._scaledCellWidth,
|
||||
height * this._scaledCellHeight);
|
||||
} else {
|
||||
this._ctx.fillStyle = this._colors.background;
|
||||
this._ctx.fillRect(
|
||||
x * this._scaledCharWidth,
|
||||
y * this._scaledLineHeight,
|
||||
width * this._scaledCharWidth,
|
||||
height * this._scaledLineHeight);
|
||||
x * this._scaledCellWidth,
|
||||
y * this._scaledCellHeight,
|
||||
width * this._scaledCellWidth,
|
||||
height * this._scaledCellHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,15 +203,11 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
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);
|
||||
this._clipRow(terminal, y);
|
||||
this._ctx.fillText(
|
||||
charData[CHAR_DATA_CHAR_INDEX],
|
||||
x * this._scaledCellWidth + this._scaledCharLeft,
|
||||
y * this._scaledCellHeight + this._scaledCharTop);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,8 +250,14 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._ctx.globalAlpha = DIM_OPACITY;
|
||||
}
|
||||
this._ctx.drawImage(this._charAtlas,
|
||||
code * charAtlasCellWidth, colorIndex * charAtlasCellHeight, charAtlasCellWidth, this._scaledCharHeight,
|
||||
x * this._scaledCharWidth, y * this._scaledLineHeight + this._scaledLineDrawY, charAtlasCellWidth, this._scaledCharHeight);
|
||||
code * charAtlasCellWidth,
|
||||
colorIndex * charAtlasCellHeight,
|
||||
charAtlasCellWidth,
|
||||
this._scaledCharHeight,
|
||||
x * this._scaledCellWidth + this._scaledCharLeft,
|
||||
y * this._scaledCellHeight + this._scaledCharTop,
|
||||
charAtlasCellWidth,
|
||||
this._scaledCharHeight);
|
||||
} else {
|
||||
this._drawUncachedChar(terminal, char, width, fg, x, y, bold, dim);
|
||||
}
|
||||
@@ -288,21 +294,33 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
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();
|
||||
this._clipRow(terminal, y);
|
||||
|
||||
// Apply alpha to dim the character
|
||||
if (dim) {
|
||||
this._ctx.globalAlpha = DIM_OPACITY;
|
||||
}
|
||||
// Draw the character
|
||||
this._ctx.fillText(char, x * this._scaledCharWidth, y * this._scaledLineHeight + this._scaledLineDrawY);
|
||||
this._ctx.fillText(
|
||||
char,
|
||||
x * this._scaledCellWidth + this._scaledCharLeft,
|
||||
y * this._scaledCellHeight + this._scaledCharTop);
|
||||
this._ctx.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clips a row to ensure no pixels will be drawn outside the cells in the row.
|
||||
* @param terminal The terminal.
|
||||
* @param y The row to clip.
|
||||
*/
|
||||
private _clipRow(terminal: ITerminal, y: number): void {
|
||||
this._ctx.beginPath();
|
||||
this._ctx.rect(
|
||||
0,
|
||||
y * this._scaledCellHeight,
|
||||
terminal.cols * this._scaledCellWidth,
|
||||
this._scaledCellHeight);
|
||||
this._ctx.clip();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,8 +86,10 @@ export interface IColorSet {
|
||||
export interface IRenderDimensions {
|
||||
scaledCharWidth: number;
|
||||
scaledCharHeight: number;
|
||||
scaledLineHeight: number;
|
||||
scaledLineDrawY: number;
|
||||
scaledCellWidth: number;
|
||||
scaledCellHeight: number;
|
||||
scaledCharLeft: number;
|
||||
scaledCharTop: number;
|
||||
scaledCanvasWidth: number;
|
||||
scaledCanvasHeight: number;
|
||||
canvasWidth: number;
|
||||
|
||||
@@ -40,8 +40,10 @@ export class Renderer extends EventEmitter implements IRenderer {
|
||||
this.dimensions = {
|
||||
scaledCharWidth: null,
|
||||
scaledCharHeight: null,
|
||||
scaledLineHeight: null,
|
||||
scaledLineDrawY: null,
|
||||
scaledCellWidth: null,
|
||||
scaledCellHeight: null,
|
||||
scaledCharLeft: null,
|
||||
scaledCharTop: null,
|
||||
scaledCanvasWidth: null,
|
||||
scaledCanvasHeight: null,
|
||||
canvasWidth: null,
|
||||
@@ -91,20 +93,27 @@ export class Renderer extends EventEmitter implements IRenderer {
|
||||
// enough space to draw the character to the cell.
|
||||
this.dimensions.scaledCharHeight = Math.ceil(this._terminal.charMeasure.height * window.devicePixelRatio);
|
||||
|
||||
// Calculate the scaled line height, if lineHeight is not 1 then the value
|
||||
// Calculate the scaled cell height, if lineHeight is not 1 then the value
|
||||
// will be floored because since lineHeight can never be lower then 1, there
|
||||
// is a guarentee that the scaled line height will always be larger than
|
||||
// scaled char height.
|
||||
this.dimensions.scaledLineHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight);
|
||||
this.dimensions.scaledCellHeight = Math.floor(this.dimensions.scaledCharHeight * this._terminal.options.lineHeight);
|
||||
|
||||
// Calculate the y coordinate within a cell that text should draw from in
|
||||
// order to draw in the center of a cell.
|
||||
this.dimensions.scaledLineDrawY = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledLineHeight - this.dimensions.scaledCharHeight) / 2);
|
||||
this.dimensions.scaledCharTop = this._terminal.options.lineHeight === 1 ? 0 : Math.round((this.dimensions.scaledCellHeight - this.dimensions.scaledCharHeight) / 2);
|
||||
|
||||
// Calculate the scaled cell width, taking the letterSpacing into account.
|
||||
this.dimensions.scaledCellWidth = this.dimensions.scaledCharWidth + Math.round(this._terminal.options.letterSpacing);
|
||||
|
||||
// Calculate the x coordinate with a cell that text should draw from in
|
||||
// order to draw in the center of a cell.
|
||||
this.dimensions.scaledCharLeft = Math.floor(this._terminal.options.letterSpacing / 2);
|
||||
|
||||
// Recalculate the canvas dimensions; scaled* define the actual number of
|
||||
// pixel in the canvas
|
||||
this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledLineHeight;
|
||||
this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCharWidth;
|
||||
this.dimensions.scaledCanvasHeight = this._terminal.rows * this.dimensions.scaledCellHeight;
|
||||
this.dimensions.scaledCanvasWidth = this._terminal.cols * this.dimensions.scaledCellWidth;
|
||||
|
||||
// The the size of the canvas on the page. It's very important that this
|
||||
// rounds to nearest integer and not ceils as browsers often set
|
||||
|
||||
Vendored
+7
-2
@@ -51,6 +51,11 @@ interface ITerminalOptions {
|
||||
*/
|
||||
fontFamily?: string;
|
||||
|
||||
/**
|
||||
* The spacing in whole pixels between characters..
|
||||
*/
|
||||
letterSpacing?: number;
|
||||
|
||||
/**
|
||||
* The line height used to render text.
|
||||
*/
|
||||
@@ -407,7 +412,7 @@ declare module 'xterm' {
|
||||
* Retrieves an option's value from the terminal.
|
||||
* @param key The option key.
|
||||
*/
|
||||
getOption(key: 'cols' | 'fontSize' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number;
|
||||
getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number;
|
||||
/**
|
||||
* Retrieves an option's value from the terminal.
|
||||
* @param key The option key.
|
||||
@@ -459,7 +464,7 @@ declare module 'xterm' {
|
||||
* @param key The option key.
|
||||
* @param value The option value.
|
||||
*/
|
||||
setOption(key: 'cols' | 'fontSize' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback', value: number): void;
|
||||
setOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback', value: number): void;
|
||||
/**
|
||||
* Sets an option on the terminal.
|
||||
* @param key The option key.
|
||||
|
||||
Reference in New Issue
Block a user