Skip to content

Commit

Permalink
Always draw entire line and allow overlapping
Browse files Browse the repository at this point in the history
State/diffing code is kept in comments as it may be reinstated soon.

Fixes xtermjs#1035
Fixes xtermjs#1036
Fixes xtermjs#1032
Fixes xtermjs#1037
  • Loading branch information
Tyriar committed Oct 6, 2017
1 parent 356a14b commit 06b9f1d
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 21 deletions.
4 changes: 2 additions & 2 deletions src/renderer/BaseRenderLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
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, this._scaledCharWidth, this._scaledCharHeight,
code * charAtlasCellWidth, colorIndex * charAtlasCellHeight, charAtlasCellWidth, this._scaledCharHeight,
x * this._scaledCharWidth, y * this._scaledLineHeight + this._scaledLineDrawY, this._scaledCharWidth, this._scaledCharHeight);
} else {
this._drawUncachedChar(terminal, char, width, fg, x, y, bold);
Expand Down Expand Up @@ -285,7 +285,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
// 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, width * this._scaledCharWidth, this._scaledCharHeight);
this._ctx.rect(0, y * this._scaledLineHeight + this._scaledLineDrawY, terminal.cols * this._scaledCharWidth, this._scaledCharHeight);
this._ctx.clip();

// Draw the character
Expand Down
35 changes: 33 additions & 2 deletions src/renderer/CharAtlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,23 @@ class CharAtlasGenerator {

// Default color
for (let i = 0; i < 256; i++) {
this._ctx.save();
this._ctx.beginPath();
this._ctx.rect(i * cellWidth, 0, cellWidth, cellHeight);
this._ctx.clip();
this._ctx.fillText(String.fromCharCode(i), i * cellWidth, 0);
this._ctx.restore();
}
// Default color bold
this._ctx.save();
this._ctx.font = `bold ${this._ctx.font}`;
for (let i = 0; i < 256; i++) {
this._ctx.save();
this._ctx.beginPath();
this._ctx.rect(i * cellWidth, cellHeight, cellWidth, cellHeight);
this._ctx.clip();
this._ctx.fillText(String.fromCharCode(i), i * cellWidth, cellHeight);
this._ctx.restore();
}
this._ctx.restore();

Expand All @@ -162,14 +172,17 @@ class CharAtlasGenerator {
const y = (colorIndex + 2) * cellHeight;
// Draw ascii characters
for (let i = 0; i < 256; i++) {
this._ctx.save();
this._ctx.beginPath();
this._ctx.rect(i * cellWidth, y, cellWidth, cellHeight);
this._ctx.clip();
this._ctx.fillStyle = ansiColors[colorIndex];
this._ctx.fillText(String.fromCharCode(i), i * cellWidth, y);
this._ctx.restore();
}
}
this._ctx.restore();

const charAtlasImageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);

// Support is patchy for createImageBitmap at the moment, pass a canvas back
// if support is lacking as drawImage works there too. Firefox is also
// included here as ImageBitmap appears both buggy and has horrible
Expand All @@ -183,9 +196,27 @@ class CharAtlasGenerator {
return result;
}

const charAtlasImageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height);

// Remove the background color from the image so characters may overlap
const r = parseInt(background.substr(1, 2), 16);
const g = parseInt(background.substr(3, 2), 16);
const b = parseInt(background.substr(5, 2), 16);
this._clearColor(charAtlasImageData, r, g, b);

const promise = window.createImageBitmap(charAtlasImageData);
// Clear the rect while the promise is in progress
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
return promise;
}

private _clearColor(imageData: ImageData, r: number, g: number, b: number): void {
for (let offset = 0; offset < imageData.data.length; offset += 4) {
if (imageData.data[offset] === r &&
imageData.data[offset + 1] === g &&
imageData.data[offset + 2] === b) {
imageData.data[offset + 3] = 0;
}
}
}
}
15 changes: 14 additions & 1 deletion src/renderer/ColorManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class ColorManager implements IColorManager {
*/
public setTheme(theme: ITheme): void {
this.colors.foreground = theme.foreground || DEFAULT_FOREGROUND;
this.colors.background = theme.background || DEFAULT_BACKGROUND;
this.colors.background = this._validateColor(theme.background, DEFAULT_BACKGROUND);
this.colors.cursor = theme.cursor || DEFAULT_CURSOR;
this.colors.cursorAccent = theme.cursorAccent || DEFAULT_CURSOR_ACCENT;
this.colors.selection = theme.selection || DEFAULT_SELECTION;
Expand All @@ -107,4 +107,17 @@ export class ColorManager implements IColorManager {
this.colors.ansi[14] = theme.brightCyan || DEFAULT_ANSI_COLORS[14];
this.colors.ansi[15] = theme.brightWhite || DEFAULT_ANSI_COLORS[15];
}

private _validateColor(color: string, fallback: string): string {
if (color.length === 7 && color.charAt(0) === '#') {
return color;
}
if (color.length === 4 && color.charAt(0) === '#') {
const r = color.charAt(1);
const g = color.charAt(2);
const b = color.charAt(3);
return `#${r}${r}${g}${g}${b}${b}`;
}
return fallback;
}
}
37 changes: 21 additions & 16 deletions src/renderer/TextRenderLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ export class TextRenderLayer extends BaseRenderLayer {
const row = y + terminal.buffer.ydisp;
const line = terminal.buffer.lines.get(row);

this.clearCells(0, y, terminal.cols, 1);
// for (let x = 0; x < terminal.cols; x++) {
// this._state.cache[x][y] = null;
// }

for (let x = 0; x < terminal.cols; x++) {
const charData = line[x];
const code: number = <number>charData[CHAR_DATA_CODE_INDEX];
Expand All @@ -69,7 +74,7 @@ export class TextRenderLayer extends BaseRenderLayer {
// The character to the left is a wide character, drawing is owned by
// the char at x-1
if (width === 0) {
this._state.cache[x][y] = null;
// this._state.cache[x][y] = null;
continue;
}

Expand All @@ -86,19 +91,19 @@ export class TextRenderLayer extends BaseRenderLayer {
}

// Skip rendering if the character is identical
const state = this._state.cache[x][y];
if (state && state[CHAR_DATA_CHAR_INDEX] === char && state[CHAR_DATA_ATTR_INDEX] === attr) {
// Skip render, contents are identical
this._state.cache[x][y] = charData;
continue;
}
// const state = this._state.cache[x][y];
// if (state && state[CHAR_DATA_CHAR_INDEX] === char && state[CHAR_DATA_ATTR_INDEX] === attr) {
// // Skip render, contents are identical
// this._state.cache[x][y] = charData;
// continue;
// }

// Clear the old character was not a space with the default background
const wasInverted = !!(state && state[CHAR_DATA_ATTR_INDEX] && state[CHAR_DATA_ATTR_INDEX] >> 18 & FLAGS.INVERSE);
if (state && !(state[CHAR_DATA_CODE_INDEX] === 32 /*' '*/ && (state[CHAR_DATA_ATTR_INDEX] & 0x1ff) >= 256 && !wasInverted)) {
this._clearChar(x, y);
}
this._state.cache[x][y] = charData;
// const wasInverted = !!(state && state[CHAR_DATA_ATTR_INDEX] && state[CHAR_DATA_ATTR_INDEX] >> 18 & FLAGS.INVERSE);
// if (state && !(state[CHAR_DATA_CODE_INDEX] === 32 /*' '*/ && (state[CHAR_DATA_ATTR_INDEX] & 0x1ff) >= 256 && !wasInverted)) {
// this._clearChar(x, y);
// }
// this._state.cache[x][y] = charData;

const flags = attr >> 18;
let bg = attr & 0x1ff;
Expand All @@ -120,14 +125,14 @@ export class TextRenderLayer extends BaseRenderLayer {
// space is added. Without this, the first half of `b` would never
// get removed, and `a` would not re-render because it thinks it's
// already in the correct state.
this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;
// this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;
if (x < line.length - 1 && line[x + 1][CHAR_DATA_CODE_INDEX] === 32 /*' '*/) {
width = 2;
this._clearChar(x + 1, y);
// this._clearChar(x + 1, y);
// The overlapping char's char data will force a clear and render when the
// overlapping char is no longer to the left of the character and also when
// the space changes to another character.
this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA;
// this._state.cache[x + 1][y] = OVERLAP_OWNED_CHAR_DATA;
}
}

Expand All @@ -148,7 +153,7 @@ export class TextRenderLayer extends BaseRenderLayer {

// Clear the cell next to this character if it's wide
if (width === 2) {
this.clearCells(x + 1, y, 1, 1);
// this.clearCells(x + 1, y, 1, 1);
}

// Draw background
Expand Down

0 comments on commit 06b9f1d

Please sign in to comment.