Merge pull request #1045 from Tyriar/more_rendering_improvements

More rendering improvements
This commit is contained in:
Daniel Imms
2017-10-14 23:55:33 -07:00
committed by GitHub
8 changed files with 97 additions and 58 deletions
+2
View File
@@ -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);
+1
View File
@@ -139,6 +139,7 @@ export interface ITerminalOptions {
fontFamily?: string;
geometry?: [number, number];
handler?: (data: string) => void;
letterSpacing?: number;
lineHeight?: number;
rows?: number;
screenKeys?: boolean;
+2
View File
@@ -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
View File
@@ -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();
}
}
+63 -45
View File
@@ -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();
}
}
+4 -2
View File
@@ -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;
+16 -7
View File
@@ -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
+7 -2
View File
@@ -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.