From 1207acc49517e6ecde9128a1b33bef68b1116e56 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 07:05:52 -0700
Subject: [PATCH 01/23] Add underline test button to demo
---
demo/client.ts | 18 ++++++++++++++++++
demo/index.html | 1 +
2 files changed, 19 insertions(+)
diff --git a/demo/client.ts b/demo/client.ts
index 03a9044c..078d97f9 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -161,6 +161,7 @@ if (document.location.pathname === '/test') {
document.getElementById('custom-glyph').addEventListener('click', writeCustomGlyphHandler);
document.getElementById('load-test').addEventListener('click', loadTest);
document.getElementById('powerline-symbol-test').addEventListener('click', powerlineSymbolTest);
+ document.getElementById('underline-test').addEventListener('click', underlineTest);
document.getElementById('add-decoration').addEventListener('click', addDecoration);
document.getElementById('add-overview-ruler').addEventListener('click', addOverviewRuler);
}
@@ -650,6 +651,23 @@ function powerlineSymbolTest() {
term.writeln('nf-mdi-github_face (\\uFbd9) \ufbd9');
}
+function underlineTest() {
+ function u(style: number): string {
+ return `\x1b[4:${style}m`;
+ }
+ function c(): string {
+ return '\x1b[0m';
+ }
+ term.write('\n\n\r');
+ term.writeln('Underline styles:');
+ term.writeln(`${u(0)}4:0m - No underline`);
+ term.writeln(`${u(1)}4:1m - Straight`);
+ term.writeln(`${u(2)}4:2m - Double`);
+ term.writeln(`${u(3)}4:3m - Curly`);
+ term.writeln(`${u(4)}4:4m - Dotted`);
+ term.writeln(`${u(5)}4:5m - Dashed\x1b[0m`);
+}
+
function addDecoration() {
term.options['overviewRulerWidth'] = 15;
const marker = term.addMarker(1);
diff --git a/demo/index.html b/demo/index.html
index 23f78aa6..bff5b4b2 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -77,6 +77,7 @@
Styles
+
Decorations
From cc8748313e6f294bc936a6aae64ba4810239e880 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 07:13:29 -0700
Subject: [PATCH 02/23] Expose underline attrs as getter/setter
In preparation for storing them in a packed format for glyph caching
---
src/common/buffer/AttributeData.ts | 27 +++++++++++++++++++++------
src/common/buffer/BufferLine.ts | 2 +-
2 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/src/common/buffer/AttributeData.ts b/src/common/buffer/AttributeData.ts
index 43d378ea..1ee608a7 100644
--- a/src/common/buffer/AttributeData.ts
+++ b/src/common/buffer/AttributeData.ts
@@ -30,7 +30,7 @@ export class AttributeData implements IAttributeData {
// data
public fg = 0;
public bg = 0;
- public extended = new ExtendedAttrs();
+ public extended: IExtendedAttrs = new ExtendedAttrs();
// flags
public isInverse(): number { return this.fg & FgFlags.INVERSE; }
@@ -127,12 +127,27 @@ export class AttributeData implements IAttributeData {
* Holds information about different underline styles and color.
*/
export class ExtendedAttrs implements IExtendedAttrs {
+ // underline style, NONE is empty
+ private _underlineStyle: UnderlineStyle = UnderlineStyle.NONE;
+ public get underlineStyle(): UnderlineStyle { return this._underlineStyle; }
+ public set underlineStyle(value: UnderlineStyle) {
+ this._underlineStyle = value;
+ }
+
+ // underline color, -1 is empty (same as FG)
+ private _underlineColor: number = -1;
+ public get underlineColor(): number { return this._underlineColor; }
+ public set underlineColor(value: number) {
+ this._underlineColor = value;
+ }
+
constructor(
- // underline style, NONE is empty
- public underlineStyle: UnderlineStyle = UnderlineStyle.NONE,
- // underline color, -1 is empty (same as FG)
- public underlineColor: number = -1
- ) {}
+ underlineStyle: UnderlineStyle = UnderlineStyle.NONE,
+ underlineColor: number = -1
+ ) {
+ this._underlineStyle = underlineStyle;
+ this._underlineColor = underlineColor;
+ }
public clone(): IExtendedAttrs {
return new ExtendedAttrs(this.underlineStyle, this.underlineColor);
diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts
index f0bf4fcb..6d2a442f 100644
--- a/src/common/buffer/BufferLine.ts
+++ b/src/common/buffer/BufferLine.ts
@@ -55,7 +55,7 @@ export const DEFAULT_ATTR_DATA = Object.freeze(new AttributeData());
export class BufferLine implements IBufferLine {
protected _data: Uint32Array;
protected _combined: {[index: number]: string} = {};
- protected _extendedAttrs: {[index: number]: ExtendedAttrs} = {};
+ protected _extendedAttrs: {[index: number]: IExtendedAttrs} = {};
public length: number;
constructor(cols: number, fillCellData?: ICellData, public isWrapped: boolean = false) {
From 34cd5966b9345398f08ab8ac0500639a1426d41d Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 07:31:51 -0700
Subject: [PATCH 03/23] Webgl 3 key cache map
---
addons/xterm-addon-webgl/src/GlyphRenderer.ts | 6 +-
addons/xterm-addon-webgl/src/Types.d.ts | 2 +-
.../src/atlas/WebglCharAtlas.ts | 72 ++++++++++---------
src/common/buffer/Constants.ts | 1 +
4 files changed, 43 insertions(+), 38 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts
index 9e60a7fc..28b37c29 100644
--- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts
+++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts
@@ -192,9 +192,11 @@ export class GlyphRenderer extends Disposable {
// Get the glyph
if (chars && chars.length > 1) {
- rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg);
+ // TODO: Use actual ext
+ rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg, 0);
} else {
- rasterizedGlyph = this._atlas.getRasterizedGlyph(code, bg, fg);
+ // TODO: Use actual ext
+ rasterizedGlyph = this._atlas.getRasterizedGlyph(code, bg, fg, 0);
}
// Fill empty if no glyph was found
diff --git a/addons/xterm-addon-webgl/src/Types.d.ts b/addons/xterm-addon-webgl/src/Types.d.ts
index d8a27aa7..c803d3e4 100644
--- a/addons/xterm-addon-webgl/src/Types.d.ts
+++ b/addons/xterm-addon-webgl/src/Types.d.ts
@@ -4,7 +4,7 @@
*/
export interface IRasterizedGlyphSet {
- [bg: number]: { [fg: number]: IRasterizedGlyph } | undefined;
+ [bg: number]: { [fg: number]: { [ext: number]: IRasterizedGlyph } } | undefined;
}
/**
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 05751c42..642c0e83 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -6,12 +6,12 @@
import { ICharAtlasConfig } from './Types';
import { DIM_OPACITY, TEXT_BASELINE } from 'browser/renderer/atlas/Constants';
import { IRasterizedGlyph, IBoundingBox, IRasterizedGlyphSet } from '../Types';
-import { DEFAULT_COLOR, Attributes } from 'common/buffer/Constants';
+import { DEFAULT_COLOR, Attributes, DEFAULT_EXT } from 'common/buffer/Constants';
import { throwIfFalsy } from '../WebglUtils';
import { IColor } from 'common/Types';
import { IDisposable } from 'xterm';
import { AttributeData } from 'common/buffer/AttributeData';
-import { channels, color, rgba } from 'common/Color';
+import { color, rgba } from 'common/Color';
import { tryDrawCustomChar } from 'browser/renderer/CustomGlyphs';
import { excludeFromContrastRatioDemands, isPowerlineGlyph } from 'browser/renderer/RendererUtils';
@@ -109,7 +109,9 @@ export class WebglCharAtlas implements IDisposable {
const rasterizedGlyph = this._drawToCache(i, DEFAULT_COLOR, DEFAULT_COLOR);
this._cacheMap[i] = {
[DEFAULT_COLOR]: {
- [DEFAULT_COLOR]: rasterizedGlyph
+ [DEFAULT_COLOR]: {
+ [DEFAULT_EXT]: rasterizedGlyph
+ }
}
};
}
@@ -137,48 +139,50 @@ export class WebglCharAtlas implements IDisposable {
this._didWarmUp = false;
}
- public getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number): IRasterizedGlyph {
- let rasterizedGlyphSet = this._cacheMapCombined[chars];
- if (!rasterizedGlyphSet) {
- rasterizedGlyphSet = {};
- this._cacheMapCombined[chars] = rasterizedGlyphSet;
- }
- let rasterizedGlyph: IRasterizedGlyph | undefined;
- const rasterizedGlyphSetBg = rasterizedGlyphSet[bg];
- if (rasterizedGlyphSetBg) {
- rasterizedGlyph = rasterizedGlyphSetBg[fg];
- }
- if (!rasterizedGlyph) {
- rasterizedGlyph = this._drawToCache(chars, bg, fg);
- if (!rasterizedGlyphSet[bg]) {
- rasterizedGlyphSet[bg] = {};
- }
- rasterizedGlyphSet[bg]![fg] = rasterizedGlyph;
- }
- return rasterizedGlyph;
+ public getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number, ext: number): IRasterizedGlyph {
+ return this._getFromCacheMap(this._cacheMapCombined, chars, bg, fg, ext);
+ }
+
+ public getRasterizedGlyph(code: number, bg: number, fg: number, ext: number): IRasterizedGlyph {
+ return this._getFromCacheMap(this._cacheMap, code, bg, fg, ext);
}
/**
* Gets the glyphs texture coords, drawing the texture if it's not already
*/
- public getRasterizedGlyph(code: number, bg: number, fg: number): IRasterizedGlyph {
- let rasterizedGlyphSet = this._cacheMap[code];
+ private _getFromCacheMap(
+ cacheMap: { [key: string | number]: IRasterizedGlyphSet },
+ key: string | number,
+ bg: number,
+ fg: number,
+ ext: number
+ ): IRasterizedGlyph {
+ let rasterizedGlyphSet = cacheMap[key];
if (!rasterizedGlyphSet) {
rasterizedGlyphSet = {};
- this._cacheMap[code] = rasterizedGlyphSet;
+ this._cacheMapCombined[key] = rasterizedGlyphSet;
}
+
+ let rasterizedGlyphSetBg = rasterizedGlyphSet[bg];
+ if (!rasterizedGlyphSetBg) {
+ rasterizedGlyphSetBg = {};
+ rasterizedGlyphSet[bg] = rasterizedGlyphSetBg;
+ }
+
let rasterizedGlyph: IRasterizedGlyph | undefined;
- const rasterizedGlyphSetBg = rasterizedGlyphSet[bg];
- if (rasterizedGlyphSetBg) {
- rasterizedGlyph = rasterizedGlyphSetBg[fg];
+ let rasterizedGlyphSetFg = rasterizedGlyphSetBg[fg];
+ if (!rasterizedGlyphSetFg) {
+ rasterizedGlyphSetFg = {};
+ rasterizedGlyphSetBg[fg] = rasterizedGlyphSetFg;
+ } else {
+ rasterizedGlyph = rasterizedGlyphSetFg[ext];
}
+
if (!rasterizedGlyph) {
- rasterizedGlyph = this._drawToCache(code, bg, fg);
- if (!rasterizedGlyphSet[bg]) {
- rasterizedGlyphSet[bg] = {};
- }
- rasterizedGlyphSet[bg]![fg] = rasterizedGlyph;
+ rasterizedGlyph = this._drawToCache(key, bg, fg);
+ rasterizedGlyphSetFg[ext] = rasterizedGlyph;
}
+
return rasterizedGlyph;
}
@@ -308,8 +312,6 @@ export class WebglCharAtlas implements IDisposable {
return color;
}
- private _drawToCache(code: number, bg: number, fg: number): IRasterizedGlyph;
- private _drawToCache(chars: string, bg: number, fg: number): IRasterizedGlyph;
private _drawToCache(codeOrChars: number | string, bg: number, fg: number): IRasterizedGlyph {
const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars;
diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts
index a2c1b884..13dec2c1 100644
--- a/src/common/buffer/Constants.ts
+++ b/src/common/buffer/Constants.ts
@@ -5,6 +5,7 @@
export const DEFAULT_COLOR = 256;
export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0);
+export const DEFAULT_EXT = 0;
export const CHAR_DATA_ATTR_INDEX = 0;
export const CHAR_DATA_CHAR_INDEX = 1;
From 8b4e8199d7e67e1b048c102dc29781cd602ea6b2 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 07:38:34 -0700
Subject: [PATCH 04/23] Get underline style affecting webgl rendering
---
addons/xterm-addon-webgl/src/GlyphRenderer.ts | 10 +++----
addons/xterm-addon-webgl/src/WebglRenderer.ts | 8 ++++--
.../src/atlas/WebglCharAtlas.ts | 28 +++++++++++++++----
3 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts
index 28b37c29..e679232c 100644
--- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts
+++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts
@@ -169,11 +169,11 @@ export class GlyphRenderer extends Disposable {
return this._atlas ? this._atlas.beginFrame() : true;
}
- public updateCell(x: number, y: number, code: number, bg: number, fg: number, chars: string, lastBg: number): void {
- this._updateCell(this._vertices.attributes, x, y, code, bg, fg, chars, lastBg);
+ public updateCell(x: number, y: number, code: number, bg: number, fg: number, ext: number, chars: string, lastBg: number): void {
+ this._updateCell(this._vertices.attributes, x, y, code, bg, fg, ext, chars, lastBg);
}
- private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, chars: string, lastBg: number): void {
+ private _updateCell(array: Float32Array, x: number, y: number, code: number | undefined, bg: number, fg: number, ext: number, chars: string, lastBg: number): void {
const terminal = this._terminal;
const i = (y * terminal.cols + x) * INDICES_PER_CELL;
@@ -193,10 +193,10 @@ export class GlyphRenderer extends Disposable {
// Get the glyph
if (chars && chars.length > 1) {
// TODO: Use actual ext
- rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg, 0);
+ rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg, ext);
} else {
// TODO: Use actual ext
- rasterizedGlyph = this._atlas.getRasterizedGlyph(code, bg, fg, 0);
+ rasterizedGlyph = this._atlas.getRasterizedGlyph(code, bg, fg, ext);
}
// Fill empty if no glyph was found
diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts
index 1e385b33..eb8b8d8d 100644
--- a/addons/xterm-addon-webgl/src/WebglRenderer.ts
+++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts
@@ -32,7 +32,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
private _model: RenderModel = new RenderModel();
private _workCell: CellData = new CellData();
- private _workColors: { fg: number, bg: number } = { fg: 0, bg: 0 };
+ private _workColors: { fg: number, bg: number, ext: number } = { fg: 0, bg: 0, ext: 0 };
private _canvas: HTMLCanvasElement;
private _gl: IWebGL2RenderingContext;
@@ -357,7 +357,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._model.cells[i + RENDER_MODEL_BG_OFFSET] = this._workColors.bg;
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._workColors.fg;
- this._glyphRenderer.updateCell(x, y, code, this._workColors.bg, this._workColors.fg, chars, lastBg);
+ this._glyphRenderer.updateCell(x, y, code, this._workColors.bg, this._workColors.fg, this._workColors.ext, chars, lastBg);
if (isJoined) {
// Restore work cell
@@ -366,7 +366,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
// Null out non-first cells
for (x++; x < lastCharX; x++) {
const j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
- this._glyphRenderer.updateCell(x, y, NULL_CELL_CODE, 0, 0, NULL_CELL_CHAR, 0);
+ this._glyphRenderer.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0);
this._model.cells[j] = NULL_CELL_CODE;
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._workColors.bg;
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._workColors.fg;
@@ -384,6 +384,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
private _loadColorsForCell(x: number, y: number): void {
this._workColors.bg = this._workCell.bg;
this._workColors.fg = this._workCell.fg;
+ // TODO: Use extended packed format as key
+ this._workColors.ext = this._workCell.extended.underlineStyle;
// Get any foreground/background overrides, this happens on the model to avoid spreading
// override logic throughout the different sub-renderers
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 642c0e83..7971fd87 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -6,7 +6,7 @@
import { ICharAtlasConfig } from './Types';
import { DIM_OPACITY, TEXT_BASELINE } from 'browser/renderer/atlas/Constants';
import { IRasterizedGlyph, IBoundingBox, IRasterizedGlyphSet } from '../Types';
-import { DEFAULT_COLOR, Attributes, DEFAULT_EXT } from 'common/buffer/Constants';
+import { DEFAULT_COLOR, Attributes, DEFAULT_EXT, UnderlineStyle } from 'common/buffer/Constants';
import { throwIfFalsy } from '../WebglUtils';
import { IColor } from 'common/Types';
import { IDisposable } from 'xterm';
@@ -106,7 +106,7 @@ export class WebglCharAtlas implements IDisposable {
private _doWarmUp(): void {
// Pre-fill with ASCII 33-126
for (let i = 33; i < 126; i++) {
- const rasterizedGlyph = this._drawToCache(i, DEFAULT_COLOR, DEFAULT_COLOR);
+ const rasterizedGlyph = this._drawToCache(i, DEFAULT_COLOR, DEFAULT_COLOR, DEFAULT_EXT);
this._cacheMap[i] = {
[DEFAULT_COLOR]: {
[DEFAULT_COLOR]: {
@@ -179,7 +179,7 @@ export class WebglCharAtlas implements IDisposable {
}
if (!rasterizedGlyph) {
- rasterizedGlyph = this._drawToCache(key, bg, fg);
+ rasterizedGlyph = this._drawToCache(key, bg, fg, ext);
rasterizedGlyphSetFg[ext] = rasterizedGlyph;
}
@@ -312,7 +312,7 @@ export class WebglCharAtlas implements IDisposable {
return color;
}
- private _drawToCache(codeOrChars: number | string, bg: number, fg: number): IRasterizedGlyph {
+ private _drawToCache(codeOrChars: number | string, bg: number, fg: number, ext: number): IRasterizedGlyph {
const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars;
this.hasCanvasChanged = true;
@@ -333,6 +333,8 @@ export class WebglCharAtlas implements IDisposable {
this._workAttributeData.fg = fg;
this._workAttributeData.bg = bg;
+ // TODO: Use packed ext format
+ this._workAttributeData.extended.underlineStyle = ext;
const invisible = !!this._workAttributeData.isInvisible();
if (invisible) {
@@ -421,8 +423,22 @@ export class WebglCharAtlas implements IDisposable {
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
this._tmpCtx.beginPath();
if (underline) {
- this._tmpCtx.moveTo(padding, padding + this._config.scaledCharHeight - yOffset);
- this._tmpCtx.lineTo(padding + this._config.scaledCharWidth, padding + this._config.scaledCharHeight - yOffset);
+ console.log('underline', this._workAttributeData.extended.underlineStyle);
+ switch (this._workAttributeData.extended.underlineStyle) {
+ case UnderlineStyle.DOUBLE:
+ break;
+ case UnderlineStyle.CURLY:
+ break;
+ case UnderlineStyle.DOTTED:
+ break;
+ case UnderlineStyle.DASHED:
+ break;
+ case UnderlineStyle.SINGLE:
+ default:
+ this._tmpCtx.moveTo(padding, padding + this._config.scaledCharHeight - yOffset);
+ this._tmpCtx.lineTo(padding + this._config.scaledCharWidth, padding + this._config.scaledCharHeight - yOffset);
+ break;
+ }
}
if (strikethrough) {
this._tmpCtx.moveTo(padding, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
From c116fa645c67516bfb009070ff8f6c875595bc4e Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 08:23:02 -0700
Subject: [PATCH 05/23] Render underline style on webgl
---
.../src/atlas/WebglCharAtlas.ts | 41 +++++++++++++++----
1 file changed, 34 insertions(+), 7 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 7971fd87..501aec86 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -325,7 +325,7 @@ export class WebglCharAtlas implements IDisposable {
this._tmpCanvas.width = allowedWidth;
}
// Include line height when drawing glyphs
- const allowedHeight = this._config.scaledCellHeight + TMP_CANVAS_GLYPH_PADDING * 2;
+ const allowedHeight = this._config.scaledCellHeight + TMP_CANVAS_GLYPH_PADDING * 4;
if (this._tmpCanvas.height < allowedHeight) {
this._tmpCanvas.height = allowedHeight;
}
@@ -386,7 +386,7 @@ export class WebglCharAtlas implements IDisposable {
}
// For powerline glyphs left/top padding is excluded (https://github.com/microsoft/vscode/issues/120129)
- const padding = powerLineGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING;
+ const padding = powerLineGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING * 2;
// Draw custom characters if applicable
let drawSuccess = false;
@@ -417,26 +417,54 @@ export class WebglCharAtlas implements IDisposable {
// Draw underline and strikethrough
if (underline || strikethrough) {
- const lineWidth = Math.max(1, Math.floor(this._config.fontSize / 10));
+ const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 10));
const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position
this._tmpCtx.lineWidth = lineWidth;
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
this._tmpCtx.beginPath();
if (underline) {
- console.log('underline', this._workAttributeData.extended.underlineStyle);
+ const xLeft = padding;
+ const xRight = padding + this._config.scaledCharWidth;
+ const yMid = padding + this._config.scaledCharHeight - yOffset;
switch (this._workAttributeData.extended.underlineStyle) {
case UnderlineStyle.DOUBLE:
+ const yBot = Math.ceil(padding + this._config.scaledCharHeight + lineWidth) - yOffset;
+ const yTop = Math.ceil(padding + this._config.scaledCharHeight - lineWidth) - yOffset;
+ this._tmpCtx.moveTo(xLeft, yTop);
+ this._tmpCtx.lineTo(xRight, yTop);
+ this._tmpCtx.moveTo(xLeft, yBot);
+ this._tmpCtx.lineTo(xRight, yBot);
break;
case UnderlineStyle.CURLY:
+ const xMid = padding + this._config.scaledCharWidth / 2;
+ const yMidBot = Math.ceil(padding + this._config.scaledCharHeight - lineWidth / 2) - yOffset;
+ const yMidTop = Math.ceil(padding + this._config.scaledCharHeight + lineWidth / 2) - yOffset;
+ this._tmpCtx.moveTo(xLeft, yMid);
+ this._tmpCtx.bezierCurveTo(
+ xLeft, yMidBot,
+ xMid, yMidBot,
+ xMid, yMid
+ );
+ this._tmpCtx.bezierCurveTo(
+ xMid, yMidTop,
+ xRight, yMidTop,
+ xRight, yMid
+ );
break;
case UnderlineStyle.DOTTED:
+ this._tmpCtx.setLineDash([window.devicePixelRatio * 2, window.devicePixelRatio]);
+ this._tmpCtx.moveTo(xLeft, yMid);
+ this._tmpCtx.lineTo(xRight, yMid);
break;
case UnderlineStyle.DASHED:
+ this._tmpCtx.setLineDash([window.devicePixelRatio * 4, window.devicePixelRatio * 3]);
+ this._tmpCtx.moveTo(xLeft, yMid);
+ this._tmpCtx.lineTo(xRight, yMid);
break;
case UnderlineStyle.SINGLE:
default:
- this._tmpCtx.moveTo(padding, padding + this._config.scaledCharHeight - yOffset);
- this._tmpCtx.lineTo(padding + this._config.scaledCharWidth, padding + this._config.scaledCharHeight - yOffset);
+ this._tmpCtx.moveTo(xLeft, yMid);
+ this._tmpCtx.lineTo(xRight, yMid);
break;
}
}
@@ -445,7 +473,6 @@ export class WebglCharAtlas implements IDisposable {
this._tmpCtx.lineTo(padding + this._config.scaledCharWidth, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
}
this._tmpCtx.stroke();
- this._tmpCtx.closePath();
}
this._tmpCtx.restore();
From baa8a1e6eca336131dba21df584b60cc7b2cb22a Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 08:27:15 -0700
Subject: [PATCH 06/23] Stub out canvas renderer underline style
---
src/browser/renderer/TextRenderLayer.ts | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/browser/renderer/TextRenderLayer.ts b/src/browser/renderer/TextRenderLayer.ts
index ef5a9b62..a6fba850 100644
--- a/src/browser/renderer/TextRenderLayer.ts
+++ b/src/browser/renderer/TextRenderLayer.ts
@@ -8,7 +8,7 @@ import { CharData, ICellData } from 'common/Types';
import { GridCache } from 'browser/renderer/GridCache';
import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
import { AttributeData } from 'common/buffer/AttributeData';
-import { NULL_CELL_CODE, Content } from 'common/buffer/Constants';
+import { NULL_CELL_CODE, Content, UnderlineStyle } from 'common/buffer/Constants';
import { IColorSet } from 'browser/Types';
import { CellData } from 'common/buffer/CellData';
import { IOptionsService, IBufferService, IDecorationService } from 'common/services/Services';
@@ -262,7 +262,20 @@ export class TextRenderLayer extends BaseRenderLayer {
this._fillMiddleLineAtCells(x, y, cell.getWidth());
}
if (cell.isUnderline()) {
- this._fillBottomLineAtCells(x, y, cell.getWidth());
+ switch (cell.extended.underlineStyle) {
+ case UnderlineStyle.DOUBLE:
+ break;
+ case UnderlineStyle.CURLY:
+ break;
+ case UnderlineStyle.DOTTED:
+ break;
+ case UnderlineStyle.DASHED:
+ break;
+ case UnderlineStyle.SINGLE:
+ default:
+ this._fillBottomLineAtCells(x, y, cell.getWidth());
+ break;
+ }
}
this._ctx.restore();
}
From 1d9b4d4778f679d086e3f6bd96ae20a078d9f5a4 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 08:58:10 -0700
Subject: [PATCH 07/23] Canvas renderer underline style
---
src/browser/renderer/BaseRenderLayer.ts | 67 ++++++++++++++++++++++++-
src/browser/renderer/TextRenderLayer.ts | 5 ++
2 files changed, 70 insertions(+), 2 deletions(-)
diff --git a/src/browser/renderer/BaseRenderLayer.ts b/src/browser/renderer/BaseRenderLayer.ts
index 0a9b8057..759c3129 100644
--- a/src/browser/renderer/BaseRenderLayer.ts
+++ b/src/browser/renderer/BaseRenderLayer.ts
@@ -188,14 +188,77 @@ export abstract class BaseRenderLayer implements IRenderLayer {
* @param x The column to fill.
* @param y The row to fill.
*/
- protected _fillBottomLineAtCells(x: number, y: number, width: number = 1): void {
+ protected _fillBottomLineAtCells(x: number, y: number, width: number = 1, pixelOffset: number = 0): void {
this._ctx.fillRect(
x * this._scaledCellWidth,
- (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,
+ (y + 1) * this._scaledCellHeight + pixelOffset - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,
width * this._scaledCellWidth,
window.devicePixelRatio);
}
+ protected _curlyUnderlineAtCell(x: number, y: number, width: number = 1): void {
+ this._ctx.save();
+ this._ctx.beginPath();
+ this._ctx.strokeStyle = this._ctx.fillStyle;
+ this._ctx.lineWidth = window.devicePixelRatio;
+ for (let xOffset = 0; xOffset < width; xOffset++) {
+ const xLeft = (x + xOffset) * this._scaledCellWidth;
+ const xMid = (x + xOffset + 0.5) * this._scaledCellWidth;
+ const xRight = (x + xOffset + 1) * this._scaledCellWidth;
+ const yMid = (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1;
+ const yMidBot = yMid - window.devicePixelRatio;
+ const yMidTop = yMid + window.devicePixelRatio;
+ this._ctx.moveTo(xLeft, yMid);
+ this._ctx.bezierCurveTo(
+ xLeft, yMidBot,
+ xMid, yMidBot,
+ xMid, yMid
+ );
+ this._ctx.bezierCurveTo(
+ xMid, yMidTop,
+ xRight, yMidTop,
+ xRight, yMid
+ );
+ }
+ this._ctx.stroke();
+ this._ctx.restore();
+ }
+
+ protected _dottedUnderlineAtCell(x: number, y: number, width: number = 1): void {
+ this._ctx.save();
+ this._ctx.beginPath();
+ this._ctx.strokeStyle = this._ctx.fillStyle;
+ this._ctx.lineWidth = window.devicePixelRatio;
+ this._ctx.setLineDash([window.devicePixelRatio * 2, window.devicePixelRatio]);
+ const xLeft = x * this._scaledCellWidth;
+ const yMid = (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1;
+ this._ctx.moveTo(xLeft, yMid);
+ for (let xOffset = 0; xOffset < width; xOffset++) {
+ // const xLeft = x * this._scaledCellWidth;
+ const xRight = (x + width + xOffset) * this._scaledCellWidth;
+ this._ctx.lineTo(xRight, yMid);
+ }
+ this._ctx.stroke();
+ this._ctx.closePath();
+ this._ctx.restore();
+ }
+
+ protected _dashedUnderlineAtCell(x: number, y: number, width: number = 1): void {
+ this._ctx.save();
+ this._ctx.beginPath();
+ this._ctx.strokeStyle = this._ctx.fillStyle;
+ this._ctx.lineWidth = window.devicePixelRatio;
+ this._ctx.setLineDash([window.devicePixelRatio * 4, window.devicePixelRatio * 3]);
+ const xLeft = x * this._scaledCellWidth;
+ const xRight = (x + width) * this._scaledCellWidth;
+ const yMid = (y + 1) * this._scaledCellHeight - window.devicePixelRatio - 1;
+ this._ctx.moveTo(xLeft, yMid);
+ this._ctx.lineTo(xRight, yMid);
+ this._ctx.stroke();
+ this._ctx.closePath();
+ this._ctx.restore();
+ }
+
/**
* Fills a 1px line (2px on HDPI) at the left of the cell. This uses the
* existing fillStyle on the context.
diff --git a/src/browser/renderer/TextRenderLayer.ts b/src/browser/renderer/TextRenderLayer.ts
index a6fba850..d4173e1f 100644
--- a/src/browser/renderer/TextRenderLayer.ts
+++ b/src/browser/renderer/TextRenderLayer.ts
@@ -264,12 +264,17 @@ export class TextRenderLayer extends BaseRenderLayer {
if (cell.isUnderline()) {
switch (cell.extended.underlineStyle) {
case UnderlineStyle.DOUBLE:
+ this._fillBottomLineAtCells(x, y, cell.getWidth(), -window.devicePixelRatio);
+ this._fillBottomLineAtCells(x, y, cell.getWidth(), window.devicePixelRatio);
break;
case UnderlineStyle.CURLY:
+ this._curlyUnderlineAtCell(x, y, cell.getWidth());
break;
case UnderlineStyle.DOTTED:
+ this._dottedUnderlineAtCell(x, y, cell.getWidth());
break;
case UnderlineStyle.DASHED:
+ this._dashedUnderlineAtCell(x, y, cell.getWidth());
break;
case UnderlineStyle.SINGLE:
default:
From 72d18f7d5ea506fd486884eddb97ea87cfabdfcd Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 09:20:53 -0700
Subject: [PATCH 08/23] DOM renderer underline style
---
css/xterm.css | 8 +++++---
src/browser/renderer/dom/DomRendererRowFactory.ts | 13 ++++++++-----
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/css/xterm.css b/css/xterm.css
index 95fc61ed..e9fd8153 100644
--- a/css/xterm.css
+++ b/css/xterm.css
@@ -163,9 +163,11 @@
opacity: 0.5;
}
-.xterm-underline {
- text-decoration: underline;
-}
+.xterm-underline-1 { text-decoration: underline; }
+.xterm-underline-2 { text-decoration: double underline; }
+.xterm-underline-3 { text-decoration: wavy underline; }
+.xterm-underline-4 { text-decoration: dotted underline; }
+.xterm-underline-5 { text-decoration: dashed underline; }
.xterm-strikethrough {
text-decoration: line-through;
diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts
index fadf5032..47f4b31c 100644
--- a/src/browser/renderer/dom/DomRendererRowFactory.ts
+++ b/src/browser/renderer/dom/DomRendererRowFactory.ts
@@ -5,7 +5,7 @@
import { IBufferLine, ICellData, IColor } from 'common/Types';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
-import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants';
+import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes, UnderlineStyle } from 'common/buffer/Constants';
import { CellData } from 'common/buffer/CellData';
import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services';
import { color, rgba } from 'common/Color';
@@ -155,16 +155,19 @@ export class DomRendererRowFactory {
charElement.classList.add(DIM_CLASS);
}
- if (cell.isUnderline()) {
- charElement.classList.add(UNDERLINE_CLASS);
- }
-
if (cell.isInvisible()) {
charElement.textContent = WHITESPACE_CELL_CHAR;
} else {
charElement.textContent = cell.getChars() || WHITESPACE_CELL_CHAR;
}
+ if (cell.isUnderline()) {
+ charElement.classList.add(`${UNDERLINE_CLASS}-${cell.extended.underlineStyle}`);
+ if (charElement.textContent === ' ') {
+ charElement.innerHTML = ' ';
+ }
+ }
+
if (cell.isStrikethrough()) {
charElement.classList.add(STRIKETHROUGH_CLASS);
}
From 8184e0824b86127286fd83fe05b6726e22bdc6c9 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 10:13:26 -0700
Subject: [PATCH 09/23] Add tests for dom underline styles
---
.../dom/DomRendererRowFactory.test.ts | 66 ++++++++++++++++---
1 file changed, 57 insertions(+), 9 deletions(-)
diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts
index db5d258e..026d8881 100644
--- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts
+++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts
@@ -6,7 +6,7 @@
import jsdom = require('jsdom');
import { assert } from 'chai';
import { DomRendererRowFactory } from 'browser/renderer/dom/DomRendererRowFactory';
-import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes } from 'common/buffer/Constants';
+import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, BgFlags, Attributes, UnderlineStyle } from 'common/buffer/Constants';
import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { IBufferLine } from 'common/Types';
import { CellData } from 'common/buffer/CellData';
@@ -129,14 +129,62 @@ describe('DomRendererRowFactory', () => {
);
});
- it('should add class for underline', () => {
- const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
- cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.UNDERLINE;
- lineData.setCell(0, cell);
- const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
- assert.equal(getFragmentHtml(fragment),
- 'a'
- );
+ describe('underline', () => {
+ it('should add class for straight underline style', () => {
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.UNDERLINE;
+ cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
+ cell.extended.underlineStyle = UnderlineStyle.SINGLE;
+ lineData.setCell(0, cell);
+ const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
+ assert.equal(getFragmentHtml(fragment),
+ 'a'
+ );
+ });
+ it('should add class for double underline style', () => {
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.UNDERLINE;
+ cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
+ cell.extended.underlineStyle = UnderlineStyle.DOUBLE;
+ lineData.setCell(0, cell);
+ const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
+ assert.equal(getFragmentHtml(fragment),
+ 'a'
+ );
+ });
+ it('should add class for curly underline style', () => {
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.UNDERLINE;
+ cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
+ cell.extended.underlineStyle = UnderlineStyle.CURLY;
+ lineData.setCell(0, cell);
+ const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
+ assert.equal(getFragmentHtml(fragment),
+ 'a'
+ );
+ });
+ it('should add class for double dotted style', () => {
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.UNDERLINE;
+ cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
+ cell.extended.underlineStyle = UnderlineStyle.DOTTED;
+ lineData.setCell(0, cell);
+ const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
+ assert.equal(getFragmentHtml(fragment),
+ 'a'
+ );
+ });
+ it('should add class for dashed underline style', () => {
+ const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]);
+ cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.UNDERLINE;
+ cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED;
+ cell.extended.underlineStyle = UnderlineStyle.DASHED;
+ lineData.setCell(0, cell);
+ const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
+ assert.equal(getFragmentHtml(fragment),
+ 'a'
+ );
+ });
});
it('should add class for strikethrough', () => {
From 8287e783a2582f3b6debe728395462581d03f283 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 10:24:51 -0700
Subject: [PATCH 10/23] Underline color canvas
---
src/browser/renderer/TextRenderLayer.ts | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/browser/renderer/TextRenderLayer.ts b/src/browser/renderer/TextRenderLayer.ts
index d4173e1f..7adb8f85 100644
--- a/src/browser/renderer/TextRenderLayer.ts
+++ b/src/browser/renderer/TextRenderLayer.ts
@@ -262,6 +262,18 @@ export class TextRenderLayer extends BaseRenderLayer {
this._fillMiddleLineAtCells(x, y, cell.getWidth());
}
if (cell.isUnderline()) {
+ const color = cell.extended.underlineColor;
+ if (!cell.isUnderlineColorDefault()) {
+ if (cell.isUnderlineColorRGB()) {
+ this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getUnderlineColor()).join(',')})`;
+ } else {
+ let fg = cell.getUnderlineColor();
+ if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
+ fg += 8;
+ }
+ this._ctx.fillStyle = this._colors.ansi[fg].css;
+ }
+ }
switch (cell.extended.underlineStyle) {
case UnderlineStyle.DOUBLE:
this._fillBottomLineAtCells(x, y, cell.getWidth(), -window.devicePixelRatio);
From 1f89223c2a254b5b06008b62120c05ba477c79f9 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 10:26:20 -0700
Subject: [PATCH 11/23] DOM renderer underline color
---
src/browser/renderer/TextRenderLayer.ts | 1 -
src/browser/renderer/dom/DomRendererRowFactory.ts | 12 ++++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/browser/renderer/TextRenderLayer.ts b/src/browser/renderer/TextRenderLayer.ts
index 7adb8f85..4f0dc6df 100644
--- a/src/browser/renderer/TextRenderLayer.ts
+++ b/src/browser/renderer/TextRenderLayer.ts
@@ -262,7 +262,6 @@ export class TextRenderLayer extends BaseRenderLayer {
this._fillMiddleLineAtCells(x, y, cell.getWidth());
}
if (cell.isUnderline()) {
- const color = cell.extended.underlineColor;
if (!cell.isUnderlineColorDefault()) {
if (cell.isUnderlineColorRGB()) {
this._ctx.fillStyle = `rgb(${AttributeData.toColorRGB(cell.getUnderlineColor()).join(',')})`;
diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts
index 47f4b31c..d9003332 100644
--- a/src/browser/renderer/dom/DomRendererRowFactory.ts
+++ b/src/browser/renderer/dom/DomRendererRowFactory.ts
@@ -13,6 +13,7 @@ import { IColorSet } from 'browser/Types';
import { ICharacterJoinerService, ISelectionService } from 'browser/services/Services';
import { JoinedCellData } from 'browser/services/CharacterJoinerService';
import { excludeFromContrastRatioDemands } from 'browser/renderer/RendererUtils';
+import { AttributeData } from 'common/buffer/AttributeData';
export const BOLD_CLASS = 'xterm-bold';
export const DIM_CLASS = 'xterm-dim';
@@ -166,6 +167,17 @@ export class DomRendererRowFactory {
if (charElement.textContent === ' ') {
charElement.innerHTML = ' ';
}
+ if (!cell.isUnderlineColorDefault()) {
+ if (cell.isUnderlineColorRGB()) {
+ charElement.style.textDecorationColor = `rgb(${AttributeData.toColorRGB(cell.getUnderlineColor()).join(',')})`;
+ } else {
+ let fg = cell.getUnderlineColor();
+ if (this._optionsService.rawOptions.drawBoldTextInBrightColors && cell.isBold() && fg < 8) {
+ fg += 8;
+ }
+ charElement.style.textDecorationColor = this._colors.ansi[fg].css;
+ }
+ }
}
if (cell.isStrikethrough()) {
From 8cd01dc421489398c90a493f5a0d2a6135f5019f Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 10:38:58 -0700
Subject: [PATCH 12/23] Add underline color test to demo
---
demo/client.ts | 31 +++++++++++++++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 078d97f9..1cfc43e5 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -655,8 +655,8 @@ function underlineTest() {
function u(style: number): string {
return `\x1b[4:${style}m`;
}
- function c(): string {
- return '\x1b[0m';
+ function c(color: string): string {
+ return `\x1b[58:${color}m`;
}
term.write('\n\n\r');
term.writeln('Underline styles:');
@@ -666,6 +666,33 @@ function underlineTest() {
term.writeln(`${u(3)}4:3m - Curly`);
term.writeln(`${u(4)}4:4m - Dotted`);
term.writeln(`${u(5)}4:5m - Dashed\x1b[0m`);
+ term.writeln('');
+ term.writeln(`Underline colors (256 color mode):`);
+ for (let i = 0; i < 256; i++) {
+ term.write((i !== 0 ? '\x1b[0m, ' : '') + u(1 + i % 5) + c('5:' + i) + i);
+ }
+ term.writeln(`\n\n\rUnderline colors (true color mode):`);
+ term.write('\n\r');
+ for (let i = 0; i < 80; i++) {
+ const v = Math.round(i / 79 * 255);
+ term.write(u(1) + c(`2:0:${v}:${v}:${v}`) + (i < 4 ? 'grey'[i] : ' '));
+ }
+ term.write('\n\r');
+ for (let i = 0; i < 80; i++) {
+ const v = Math.round(i / 79 * 255);
+ term.write(u(1) + c(`2:0:${v}:${0}:${0}`) + (i < 3 ? 'red'[i] : ' '));
+ }
+ term.write('\n\r');
+ for (let i = 0; i < 80; i++) {
+ const v = Math.round(i / 79 * 255);
+ term.write(u(1) + c(`2:0:${0}:${v}:${0}`) + (i < 5 ? 'green'[i] : ' '));
+ }
+ term.write('\n\r');
+ for (let i = 0; i < 80; i++) {
+ const v = Math.round(i / 79 * 255);
+ term.write(u(1) + c(`2:0:${0}:${0}:${v}`) + (i < 4 ? 'blue'[i] : ' '));
+ }
+ term.write('\x1b[0m\n\r');
}
function addDecoration() {
From 03f15a7fb77d45741475d516a49e6691009174a4 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 11:12:17 -0700
Subject: [PATCH 13/23] Store extended attributes in single number
---
addons/xterm-addon-webgl/src/WebglRenderer.ts | 3 +-
.../src/atlas/WebglCharAtlas.ts | 4 +--
src/common/Types.d.ts | 1 +
src/common/buffer/AttributeData.ts | 28 +++++++++++--------
src/common/buffer/Constants.ts | 7 +++++
5 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts
index eb8b8d8d..7790b8c2 100644
--- a/addons/xterm-addon-webgl/src/WebglRenderer.ts
+++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts
@@ -384,8 +384,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
private _loadColorsForCell(x: number, y: number): void {
this._workColors.bg = this._workCell.bg;
this._workColors.fg = this._workCell.fg;
- // TODO: Use extended packed format as key
- this._workColors.ext = this._workCell.extended.underlineStyle;
+ this._workColors.ext = this._workCell.extended.ext;
// Get any foreground/background overrides, this happens on the model to avoid spreading
// override logic throughout the different sub-renderers
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 501aec86..19e7d992 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -333,8 +333,7 @@ export class WebglCharAtlas implements IDisposable {
this._workAttributeData.fg = fg;
this._workAttributeData.bg = bg;
- // TODO: Use packed ext format
- this._workAttributeData.extended.underlineStyle = ext;
+ this._workAttributeData.extended.ext = ext;
const invisible = !!this._workAttributeData.isInvisible();
if (invisible) {
@@ -423,6 +422,7 @@ export class WebglCharAtlas implements IDisposable {
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
this._tmpCtx.beginPath();
if (underline) {
+ console.log('ext', ext, this._workAttributeData.extended.underlineColor, this._workAttributeData.extended.underlineStyle);
const xLeft = padding;
const xRight = padding + this._config.scaledCharWidth;
const yMid = padding + this._config.scaledCharHeight - yOffset;
diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts
index ed9a7124..56815da0 100644
--- a/src/common/Types.d.ts
+++ b/src/common/Types.d.ts
@@ -113,6 +113,7 @@ export interface IColor {
export type IColorRGB = [number, number, number];
export interface IExtendedAttrs {
+ ext: number;
underlineStyle: number;
underlineColor: number;
clone(): IExtendedAttrs;
diff --git a/src/common/buffer/AttributeData.ts b/src/common/buffer/AttributeData.ts
index 1ee608a7..6878069a 100644
--- a/src/common/buffer/AttributeData.ts
+++ b/src/common/buffer/AttributeData.ts
@@ -4,7 +4,7 @@
*/
import { IAttributeData, IColorRGB, IExtendedAttrs } from 'common/Types';
-import { Attributes, FgFlags, BgFlags, UnderlineStyle } from 'common/buffer/Constants';
+import { Attributes, FgFlags, BgFlags, UnderlineStyle, ExtFlags } from 'common/buffer/Constants';
export class AttributeData implements IAttributeData {
public static toColorRGB(value: number): IColorRGB {
@@ -127,26 +127,32 @@ export class AttributeData implements IAttributeData {
* Holds information about different underline styles and color.
*/
export class ExtendedAttrs implements IExtendedAttrs {
- // underline style, NONE is empty
- private _underlineStyle: UnderlineStyle = UnderlineStyle.NONE;
- public get underlineStyle(): UnderlineStyle { return this._underlineStyle; }
+ private _ext: number = 0;
+ public get ext(): number { return this._ext; }
+ public set ext(value: number) { this._ext = value; }
+
+ public get underlineStyle(): UnderlineStyle {
+ return (this._ext & ExtFlags.UNDERLINE_STYLE) >> 26;
+ }
public set underlineStyle(value: UnderlineStyle) {
- this._underlineStyle = value;
+ this._ext &= ~ExtFlags.UNDERLINE_STYLE;
+ this._ext |= (value << 26) & ExtFlags.UNDERLINE_STYLE;
}
- // underline color, -1 is empty (same as FG)
- private _underlineColor: number = -1;
- public get underlineColor(): number { return this._underlineColor; }
+ public get underlineColor(): number {
+ return this._ext & (Attributes.CM_MASK | Attributes.RGB_MASK);
+ }
public set underlineColor(value: number) {
- this._underlineColor = value;
+ this._ext &= ~(Attributes.CM_MASK | Attributes.RGB_MASK);
+ this._ext |= value & (Attributes.CM_MASK | Attributes.RGB_MASK);
}
constructor(
underlineStyle: UnderlineStyle = UnderlineStyle.NONE,
underlineColor: number = -1
) {
- this._underlineStyle = underlineStyle;
- this._underlineColor = underlineColor;
+ this.underlineStyle = underlineStyle;
+ this.underlineColor = underlineColor;
}
public clone(): IExtendedAttrs {
diff --git a/src/common/buffer/Constants.ts b/src/common/buffer/Constants.ts
index 13dec2c1..0dfa86fd 100644
--- a/src/common/buffer/Constants.ts
+++ b/src/common/buffer/Constants.ts
@@ -130,6 +130,13 @@ export const enum BgFlags {
HAS_EXTENDED = 0x10000000
}
+export const enum ExtFlags {
+ /**
+ * bit 27..32 (upper 3 unused)
+ */
+ UNDERLINE_STYLE = 0x1C000000
+}
+
export const enum UnderlineStyle {
NONE = 0,
SINGLE = 1,
From d9703aa7fe74078622d69506802cfdca0f494a6f Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 11:19:35 -0700
Subject: [PATCH 14/23] Webgl underline color rendering
---
.../src/atlas/WebglCharAtlas.ts | 117 ++++++++++--------
1 file changed, 67 insertions(+), 50 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 19e7d992..f8be6a5e 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -414,63 +414,80 @@ export class WebglCharAtlas implements IDisposable {
}
}
- // Draw underline and strikethrough
- if (underline || strikethrough) {
+ // Draw underline
+ if (strikethrough) {
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 10));
const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position
this._tmpCtx.lineWidth = lineWidth;
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
this._tmpCtx.beginPath();
- if (underline) {
- console.log('ext', ext, this._workAttributeData.extended.underlineColor, this._workAttributeData.extended.underlineStyle);
- const xLeft = padding;
- const xRight = padding + this._config.scaledCharWidth;
- const yMid = padding + this._config.scaledCharHeight - yOffset;
- switch (this._workAttributeData.extended.underlineStyle) {
- case UnderlineStyle.DOUBLE:
- const yBot = Math.ceil(padding + this._config.scaledCharHeight + lineWidth) - yOffset;
- const yTop = Math.ceil(padding + this._config.scaledCharHeight - lineWidth) - yOffset;
- this._tmpCtx.moveTo(xLeft, yTop);
- this._tmpCtx.lineTo(xRight, yTop);
- this._tmpCtx.moveTo(xLeft, yBot);
- this._tmpCtx.lineTo(xRight, yBot);
- break;
- case UnderlineStyle.CURLY:
- const xMid = padding + this._config.scaledCharWidth / 2;
- const yMidBot = Math.ceil(padding + this._config.scaledCharHeight - lineWidth / 2) - yOffset;
- const yMidTop = Math.ceil(padding + this._config.scaledCharHeight + lineWidth / 2) - yOffset;
- this._tmpCtx.moveTo(xLeft, yMid);
- this._tmpCtx.bezierCurveTo(
- xLeft, yMidBot,
- xMid, yMidBot,
- xMid, yMid
- );
- this._tmpCtx.bezierCurveTo(
- xMid, yMidTop,
- xRight, yMidTop,
- xRight, yMid
- );
- break;
- case UnderlineStyle.DOTTED:
- this._tmpCtx.setLineDash([window.devicePixelRatio * 2, window.devicePixelRatio]);
- this._tmpCtx.moveTo(xLeft, yMid);
- this._tmpCtx.lineTo(xRight, yMid);
- break;
- case UnderlineStyle.DASHED:
- this._tmpCtx.setLineDash([window.devicePixelRatio * 4, window.devicePixelRatio * 3]);
- this._tmpCtx.moveTo(xLeft, yMid);
- this._tmpCtx.lineTo(xRight, yMid);
- break;
- case UnderlineStyle.SINGLE:
- default:
- this._tmpCtx.moveTo(xLeft, yMid);
- this._tmpCtx.lineTo(xRight, yMid);
- break;
+ this._tmpCtx.moveTo(padding, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
+ this._tmpCtx.lineTo(padding + this._config.scaledCharWidth, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
+ this._tmpCtx.stroke();
+ }
+
+ // Draw underline
+ if (underline) {
+ const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 10));
+ const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position
+ this._tmpCtx.lineWidth = lineWidth;
+ // Underline color
+ if (this._workAttributeData.isUnderlineColorDefault()) {
+ this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
+ } else if (this._workAttributeData.isUnderlineColorRGB()) {
+ this._tmpCtx.strokeStyle = `rgb(${AttributeData.toColorRGB(this._workAttributeData.getUnderlineColor()).join(',')})`;
+ } else {
+ let fg = this._workAttributeData.getUnderlineColor();
+ if (this._config.drawBoldTextInBrightColors && this._workAttributeData.isBold() && fg < 8) {
+ fg += 8;
}
+ this._tmpCtx.strokeStyle = this._getColorFromAnsiIndex(fg).css;
}
- if (strikethrough) {
- this._tmpCtx.moveTo(padding, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
- this._tmpCtx.lineTo(padding + this._config.scaledCharWidth, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
+ // Underline style/stroke
+ this._tmpCtx.beginPath();
+ const xLeft = padding;
+ const xRight = padding + this._config.scaledCharWidth;
+ const yMid = padding + this._config.scaledCharHeight - yOffset;
+ switch (this._workAttributeData.extended.underlineStyle) {
+ case UnderlineStyle.DOUBLE:
+ const yBot = Math.ceil(padding + this._config.scaledCharHeight + lineWidth) - yOffset;
+ const yTop = Math.ceil(padding + this._config.scaledCharHeight - lineWidth) - yOffset;
+ this._tmpCtx.moveTo(xLeft, yTop);
+ this._tmpCtx.lineTo(xRight, yTop);
+ this._tmpCtx.moveTo(xLeft, yBot);
+ this._tmpCtx.lineTo(xRight, yBot);
+ break;
+ case UnderlineStyle.CURLY:
+ const xMid = padding + this._config.scaledCharWidth / 2;
+ const yMidBot = Math.ceil(padding + this._config.scaledCharHeight - lineWidth / 2) - yOffset;
+ const yMidTop = Math.ceil(padding + this._config.scaledCharHeight + lineWidth / 2) - yOffset;
+ this._tmpCtx.moveTo(xLeft, yMid);
+ this._tmpCtx.bezierCurveTo(
+ xLeft, yMidBot,
+ xMid, yMidBot,
+ xMid, yMid
+ );
+ this._tmpCtx.bezierCurveTo(
+ xMid, yMidTop,
+ xRight, yMidTop,
+ xRight, yMid
+ );
+ break;
+ case UnderlineStyle.DOTTED:
+ this._tmpCtx.setLineDash([window.devicePixelRatio * 2, window.devicePixelRatio]);
+ this._tmpCtx.moveTo(xLeft, yMid);
+ this._tmpCtx.lineTo(xRight, yMid);
+ break;
+ case UnderlineStyle.DASHED:
+ this._tmpCtx.setLineDash([window.devicePixelRatio * 4, window.devicePixelRatio * 3]);
+ this._tmpCtx.moveTo(xLeft, yMid);
+ this._tmpCtx.lineTo(xRight, yMid);
+ break;
+ case UnderlineStyle.SINGLE:
+ default:
+ this._tmpCtx.moveTo(xLeft, yMid);
+ this._tmpCtx.lineTo(xRight, yMid);
+ break;
}
this._tmpCtx.stroke();
}
From 70b5e0249c8f2cca87a44235bf1d9fecbe43a33c Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 11:22:40 -0700
Subject: [PATCH 15/23] Underline demo formatting
---
demo/client.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/demo/client.ts b/demo/client.ts
index 1cfc43e5..1e17a922 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -660,6 +660,7 @@ function underlineTest() {
}
term.write('\n\n\r');
term.writeln('Underline styles:');
+ term.writeln('');
term.writeln(`${u(0)}4:0m - No underline`);
term.writeln(`${u(1)}4:1m - Straight`);
term.writeln(`${u(2)}4:2m - Double`);
@@ -668,11 +669,12 @@ function underlineTest() {
term.writeln(`${u(5)}4:5m - Dashed\x1b[0m`);
term.writeln('');
term.writeln(`Underline colors (256 color mode):`);
+ term.writeln('');
for (let i = 0; i < 256; i++) {
term.write((i !== 0 ? '\x1b[0m, ' : '') + u(1 + i % 5) + c('5:' + i) + i);
}
- term.writeln(`\n\n\rUnderline colors (true color mode):`);
- term.write('\n\r');
+ term.writeln(`\x1b[0m\n\n\rUnderline colors (true color mode):`);
+ term.writeln('');
for (let i = 0; i < 80; i++) {
const v = Math.round(i / 79 * 255);
term.write(u(1) + c(`2:0:${v}:${v}:${v}`) + (i < 4 ? 'grey'[i] : ' '));
From 19fb806214a8a9349054f1e9be26bf906ea35a81 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 13:44:12 -0700
Subject: [PATCH 16/23] Fix tests
---
src/browser/renderer/dom/DomRendererRowFactory.test.ts | 10 +++++-----
src/common/buffer/BufferLine.test.ts | 4 ++--
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts
index 026d8881..fdbcea33 100644
--- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts
+++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts
@@ -138,7 +138,7 @@ describe('DomRendererRowFactory', () => {
lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
assert.equal(getFragmentHtml(fragment),
- 'a'
+ 'a'
);
});
it('should add class for double underline style', () => {
@@ -149,7 +149,7 @@ describe('DomRendererRowFactory', () => {
lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
assert.equal(getFragmentHtml(fragment),
- 'a'
+ 'a'
);
});
it('should add class for curly underline style', () => {
@@ -160,7 +160,7 @@ describe('DomRendererRowFactory', () => {
lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
assert.equal(getFragmentHtml(fragment),
- 'a'
+ 'a'
);
});
it('should add class for double dotted style', () => {
@@ -171,7 +171,7 @@ describe('DomRendererRowFactory', () => {
lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
assert.equal(getFragmentHtml(fragment),
- 'a'
+ 'a'
);
});
it('should add class for dashed underline style', () => {
@@ -182,7 +182,7 @@ describe('DomRendererRowFactory', () => {
lineData.setCell(0, cell);
const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
assert.equal(getFragmentHtml(fragment),
- 'a'
+ 'a'
);
});
});
diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts
index fa15a854..111aae03 100644
--- a/src/common/buffer/BufferLine.test.ts
+++ b/src/common/buffer/BufferLine.test.ts
@@ -45,7 +45,7 @@ describe('AttributeData', () => {
assert.equal(attrs.getUnderlineColor(), 45);
// should use FG color if underlineColor holds no value
- attrs.extended.underlineColor = -1;
+ attrs.extended.underlineColor = 0;
attrs.fg |= Attributes.CM_P256 | 123;
assert.equal(attrs.getUnderlineColor(), 123);
});
@@ -62,7 +62,7 @@ describe('AttributeData', () => {
assert.equal(attrs.getUnderlineColor(), (1 << 16) | (2 << 8) | 3);
// should use FG color if underlineColor holds no value
- attrs.extended.underlineColor = -1;
+ attrs.extended.underlineColor = 0;
attrs.fg |= Attributes.CM_P256 | 123;
assert.equal(attrs.getUnderlineColor(), 123);
});
From fd8fc183020e78f6377a182fc790a9729bf1c046 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 14:28:15 -0700
Subject: [PATCH 17/23] Clear stroke around text in underline
---
.../src/atlas/WebglCharAtlas.ts | 78 +++++++++++--------
demo/client.ts | 1 -
2 files changed, 46 insertions(+), 33 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index f8be6a5e..98852b5e 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -393,38 +393,6 @@ export class WebglCharAtlas implements IDisposable {
drawSuccess = tryDrawCustomChar(this._tmpCtx, chars, padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight);
}
- // Draw the character
- if (!drawSuccess) {
- this._tmpCtx.fillText(chars, padding, padding + this._config.scaledCharHeight);
- }
-
- // If this charcater is underscore and beyond the cell bounds, shift it up until it is visible,
- // try for a maximum of 5 pixels.
- if (chars === '_' && !this._config.allowTransparency) {
- let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight), backgroundColor, foregroundColor, this._config.allowTransparency);
- if (isBeyondCellBounds) {
- for (let offset = 1; offset <= 5; offset++) {
- this._tmpCtx.clearRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height);
- this._tmpCtx.fillText(chars, padding, padding + this._config.scaledCharHeight - offset);
- isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight), backgroundColor, foregroundColor, this._config.allowTransparency);
- if (!isBeyondCellBounds) {
- break;
- }
- }
- }
- }
-
- // Draw underline
- if (strikethrough) {
- const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 10));
- const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position
- this._tmpCtx.lineWidth = lineWidth;
- this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
- this._tmpCtx.beginPath();
- this._tmpCtx.moveTo(padding, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
- this._tmpCtx.lineTo(padding + this._config.scaledCharWidth, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
- this._tmpCtx.stroke();
- }
// Draw underline
if (underline) {
@@ -490,6 +458,52 @@ export class WebglCharAtlas implements IDisposable {
break;
}
this._tmpCtx.stroke();
+
+ // Draw stroke in the background color for non custom characters in order to give an outline
+ // between the text and the underline
+ if (!drawSuccess) {
+ // This only works when transparency is disabled because it's not clear how to clear stroked
+ // text
+ if (!this._config.allowTransparency) {
+ // This translates to 1/2 the line width in either direction
+ this._tmpCtx.lineWidth = window.devicePixelRatio * 3;
+ this._tmpCtx.strokeStyle = backgroundColor.css;
+ this._tmpCtx.strokeText(chars, padding, padding + this._config.scaledCharHeight);
+ }
+ }
+ }
+
+ // Draw the character
+ if (!drawSuccess) {
+ this._tmpCtx.fillText(chars, padding, padding + this._config.scaledCharHeight);
+ }
+
+ // If this charcater is underscore and beyond the cell bounds, shift it up until it is visible,
+ // try for a maximum of 5 pixels.
+ if (chars === '_' && !this._config.allowTransparency) {
+ let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight), backgroundColor, foregroundColor, this._config.allowTransparency);
+ if (isBeyondCellBounds) {
+ for (let offset = 1; offset <= 5; offset++) {
+ this._tmpCtx.clearRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height);
+ this._tmpCtx.fillText(chars, padding, padding + this._config.scaledCharHeight - offset);
+ isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight), backgroundColor, foregroundColor, this._config.allowTransparency);
+ if (!isBeyondCellBounds) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Draw strokethrough
+ if (strikethrough) {
+ const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 10));
+ const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position
+ this._tmpCtx.lineWidth = lineWidth;
+ this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
+ this._tmpCtx.beginPath();
+ this._tmpCtx.moveTo(padding, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
+ this._tmpCtx.lineTo(padding + this._config.scaledCharWidth, padding + Math.floor(this._config.scaledCharHeight / 2) - yOffset);
+ this._tmpCtx.stroke();
}
this._tmpCtx.restore();
diff --git a/demo/client.ts b/demo/client.ts
index 1e17a922..ae7f1968 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -174,7 +174,6 @@ function createTerminal(): void {
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0;
term = new Terminal({
- allowTransparency: true,
windowsMode: isWindows,
fontFamily: 'Fira Code, courier-new, courier, monospace'
} as ITerminalOptions);
From 43bbea8eb546a6f64026414351eaacd641de32c4 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 14:43:14 -0700
Subject: [PATCH 18/23] Set webgl as default demo renderer
---
demo/client.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/demo/client.ts b/demo/client.ts
index ae7f1968..4098c775 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -184,6 +184,7 @@ function createTerminal(): void {
addons.serialize.instance = new SerializeAddon();
addons.fit.instance = new FitAddon();
addons.unicode11.instance = new Unicode11Addon();
+ addons.webgl.instance = new WebglAddon();
// TODO: Remove arguments when link provider API is the default
addons['web-links'].instance = new WebLinksAddon(undefined, undefined, true);
typedTerm.loadAddon(addons.fit.instance);
@@ -208,6 +209,7 @@ function createTerminal(): void {
term.open(terminalContainer);
addons.fit.instance!.fit();
+ typedTerm.loadAddon(addons.webgl.instance);
term.focus();
addDomListener(paddingElement, 'change', setPadding);
From 759a5928a205336618a27bbf399231fd24d87392 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 14:48:26 -0700
Subject: [PATCH 19/23] Continuous curly underline
---
.../src/atlas/WebglCharAtlas.ts | 27 ++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 98852b5e..1e7d7eda 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -396,6 +396,7 @@ export class WebglCharAtlas implements IDisposable {
// Draw underline
if (underline) {
+ this._tmpCtx.save();
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 10));
const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position
this._tmpCtx.lineWidth = lineWidth;
@@ -413,13 +414,15 @@ export class WebglCharAtlas implements IDisposable {
}
// Underline style/stroke
this._tmpCtx.beginPath();
+
+ // TODO: Support letter spacing
const xLeft = padding;
const xRight = padding + this._config.scaledCharWidth;
+ const yTop = Math.ceil(padding + this._config.scaledCharHeight - lineWidth) - yOffset;
const yMid = padding + this._config.scaledCharHeight - yOffset;
+ const yBot = Math.ceil(padding + this._config.scaledCharHeight + lineWidth) - yOffset;
switch (this._workAttributeData.extended.underlineStyle) {
case UnderlineStyle.DOUBLE:
- const yBot = Math.ceil(padding + this._config.scaledCharHeight + lineWidth) - yOffset;
- const yTop = Math.ceil(padding + this._config.scaledCharHeight - lineWidth) - yOffset;
this._tmpCtx.moveTo(xLeft, yTop);
this._tmpCtx.lineTo(xRight, yTop);
this._tmpCtx.moveTo(xLeft, yBot);
@@ -429,7 +432,19 @@ export class WebglCharAtlas implements IDisposable {
const xMid = padding + this._config.scaledCharWidth / 2;
const yMidBot = Math.ceil(padding + this._config.scaledCharHeight - lineWidth / 2) - yOffset;
const yMidTop = Math.ceil(padding + this._config.scaledCharHeight + lineWidth / 2) - yOffset;
- this._tmpCtx.moveTo(xLeft, yMid);
+ // Clip the left and right edges of the underline such that it can be drawn just outside
+ // the edge of the cell to ensure a continuous stroke when there are multiple underlined
+ // glyphs adjacent to one another.
+ const clipRegion = new Path2D();
+ clipRegion.rect(xLeft, yTop, this._config.scaledCellWidth, yBot - yTop);
+ this._tmpCtx.clip(clipRegion);
+ // Start 1/2 cell before and end 1/2 cells after to ensure a smooth curve with other cells
+ this._tmpCtx.moveTo(xLeft - this._config.scaledCharWidth / 2, yMid);
+ this._tmpCtx.bezierCurveTo(
+ xLeft - this._config.scaledCharWidth / 2, yMidTop,
+ xLeft, yMidTop,
+ xLeft, yMid
+ );
this._tmpCtx.bezierCurveTo(
xLeft, yMidBot,
xMid, yMidBot,
@@ -440,6 +455,11 @@ export class WebglCharAtlas implements IDisposable {
xRight, yMidTop,
xRight, yMid
);
+ this._tmpCtx.bezierCurveTo(
+ xRight, yMidBot,
+ xRight + this._config.scaledCellWidth / 2, yMidBot,
+ xRight + this._config.scaledCellWidth / 2, yMid
+ );
break;
case UnderlineStyle.DOTTED:
this._tmpCtx.setLineDash([window.devicePixelRatio * 2, window.devicePixelRatio]);
@@ -458,6 +478,7 @@ export class WebglCharAtlas implements IDisposable {
break;
}
this._tmpCtx.stroke();
+ this._tmpCtx.restore();
// Draw stroke in the background color for non custom characters in order to give an outline
// between the text and the underline
From 4087284822affa02ab26e20a5723eed040098c0f Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 23 Jul 2022 15:19:17 -0700
Subject: [PATCH 20/23] Fix ext caching issues
---
addons/xterm-addon-webgl/src/GlyphRenderer.ts | 4 +---
addons/xterm-addon-webgl/src/RenderModel.ts | 3 ++-
addons/xterm-addon-webgl/src/WebglRenderer.ts | 12 +++++++-----
addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts | 2 +-
demo/client.ts | 3 +++
5 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts
index e679232c..6eff4ced 100644
--- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts
+++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts
@@ -185,17 +185,15 @@ export class GlyphRenderer extends Disposable {
return;
}
- let rasterizedGlyph: IRasterizedGlyph;
if (!this._atlas) {
return;
}
// Get the glyph
+ let rasterizedGlyph: IRasterizedGlyph;
if (chars && chars.length > 1) {
- // TODO: Use actual ext
rasterizedGlyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg, ext);
} else {
- // TODO: Use actual ext
rasterizedGlyph = this._atlas.getRasterizedGlyph(code, bg, fg, ext);
}
diff --git a/addons/xterm-addon-webgl/src/RenderModel.ts b/addons/xterm-addon-webgl/src/RenderModel.ts
index b93e1e85..2969a6d1 100644
--- a/addons/xterm-addon-webgl/src/RenderModel.ts
+++ b/addons/xterm-addon-webgl/src/RenderModel.ts
@@ -6,9 +6,10 @@
import { IRenderModel, ISelectionRenderModel } from './Types';
import { fill } from 'common/TypedArrayUtils';
-export const RENDER_MODEL_INDICIES_PER_CELL = 3;
+export const RENDER_MODEL_INDICIES_PER_CELL = 4;
export const RENDER_MODEL_BG_OFFSET = 1;
export const RENDER_MODEL_FG_OFFSET = 2;
+export const RENDER_MODEL_EXT_OFFSET = 3;
export const COMBINED_CHAR_BIT_MASK = 0x80000000;
diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts
index 7790b8c2..904e8d5a 100644
--- a/addons/xterm-addon-webgl/src/WebglRenderer.ts
+++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts
@@ -10,9 +10,9 @@ import { acquireCharAtlas } from './atlas/CharAtlasCache';
import { WebglCharAtlas } from './atlas/WebglCharAtlas';
import { RectangleRenderer } from './RectangleRenderer';
import { IWebGL2RenderingContext } from './Types';
-import { RenderModel, COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
+import { RenderModel, COMBINED_CHAR_BIT_MASK, RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_EXT_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel';
import { Disposable } from 'common/Lifecycle';
-import { Attributes, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
+import { Attributes, BgFlags, Content, FgFlags, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants';
import { Terminal, IEvent } from 'xterm';
import { IRenderLayer } from './renderLayer/Types';
import { IRenderDimensions, IRenderer, IRequestRedrawEvent } from 'browser/renderer/Types';
@@ -343,7 +343,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
// Nothing has changed, no updates needed
if (this._model.cells[i] === code &&
this._model.cells[i + RENDER_MODEL_BG_OFFSET] === this._workColors.bg &&
- this._model.cells[i + RENDER_MODEL_FG_OFFSET] === this._workColors.fg) {
+ this._model.cells[i + RENDER_MODEL_FG_OFFSET] === this._workColors.fg &&
+ this._model.cells[i + RENDER_MODEL_EXT_OFFSET] === this._workColors.ext) {
continue;
}
@@ -356,6 +357,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._model.cells[i] = code;
this._model.cells[i + RENDER_MODEL_BG_OFFSET] = this._workColors.bg;
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._workColors.fg;
+ this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._workColors.ext;
this._glyphRenderer.updateCell(x, y, code, this._workColors.bg, this._workColors.fg, this._workColors.ext, chars, lastBg);
@@ -370,6 +372,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._model.cells[j] = NULL_CELL_CODE;
this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._workColors.bg;
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._workColors.fg;
+ this._model.cells[j + RENDER_MODEL_EXT_OFFSET] = this._workColors.ext;
}
}
}
@@ -384,8 +387,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
private _loadColorsForCell(x: number, y: number): void {
this._workColors.bg = this._workCell.bg;
this._workColors.fg = this._workCell.fg;
- this._workColors.ext = this._workCell.extended.ext;
-
+ this._workColors.ext = this._workCell.bg & BgFlags.HAS_EXTENDED ? this._workCell.extended.ext : 0;
// Get any foreground/background overrides, this happens on the model to avoid spreading
// override logic throughout the different sub-renderers
let bgOverride: number | undefined;
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 1e7d7eda..8638eec1 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -160,7 +160,7 @@ export class WebglCharAtlas implements IDisposable {
let rasterizedGlyphSet = cacheMap[key];
if (!rasterizedGlyphSet) {
rasterizedGlyphSet = {};
- this._cacheMapCombined[key] = rasterizedGlyphSet;
+ cacheMap[key] = rasterizedGlyphSet;
}
let rasterizedGlyphSetBg = rasterizedGlyphSet[bg];
diff --git a/demo/client.ts b/demo/client.ts
index 4098c775..f8fa7eb5 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -210,6 +210,9 @@ function createTerminal(): void {
term.open(terminalContainer);
addons.fit.instance!.fit();
typedTerm.loadAddon(addons.webgl.instance);
+ setTimeout(() => {
+ document.body.appendChild(addons.webgl.instance.textureAtlas);
+ }, 0);
term.focus();
addDomListener(paddingElement, 'change', setPadding);
From 941cc5100d7eb8afcfe80583412ed5d9cc12fc9a Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 30 Jul 2022 06:53:34 -0700
Subject: [PATCH 21/23] Increase curly height when dpr <= 1
---
.../src/atlas/WebglCharAtlas.ts | 22 ++++++++++---------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 93a98c98..48418c48 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -451,8 +451,10 @@ export class WebglCharAtlas implements IDisposable {
break;
case UnderlineStyle.CURLY:
const xMid = padding + this._config.scaledCharWidth / 2;
- const yMidBot = Math.ceil(padding + this._config.scaledCharHeight - lineWidth / 2) - yOffset;
- const yMidTop = Math.ceil(padding + this._config.scaledCharHeight + lineWidth / 2) - yOffset;
+ // Choose the bezier top and bottom based on the device pixel ratio, the curly line is
+ // made taller when the line width is as otherwise it's not very clear otherwise.
+ const yCurlyBot = lineWidth <= 1 ? yBot : Math.ceil(padding + this._config.scaledCharHeight - lineWidth / 2) - yOffset;
+ const yCurlyTop = lineWidth <= 1 ? yTop : Math.ceil(padding + this._config.scaledCharHeight + lineWidth / 2) - yOffset;
// Clip the left and right edges of the underline such that it can be drawn just outside
// the edge of the cell to ensure a continuous stroke when there are multiple underlined
// glyphs adjacent to one another.
@@ -462,23 +464,23 @@ export class WebglCharAtlas implements IDisposable {
// Start 1/2 cell before and end 1/2 cells after to ensure a smooth curve with other cells
this._tmpCtx.moveTo(xLeft - this._config.scaledCharWidth / 2, yMid);
this._tmpCtx.bezierCurveTo(
- xLeft - this._config.scaledCharWidth / 2, yMidTop,
- xLeft, yMidTop,
+ xLeft - this._config.scaledCharWidth / 2, yCurlyTop,
+ xLeft, yCurlyTop,
xLeft, yMid
);
this._tmpCtx.bezierCurveTo(
- xLeft, yMidBot,
- xMid, yMidBot,
+ xLeft, yCurlyBot,
+ xMid, yCurlyBot,
xMid, yMid
);
this._tmpCtx.bezierCurveTo(
- xMid, yMidTop,
- xRight, yMidTop,
+ xMid, yCurlyTop,
+ xRight, yCurlyTop,
xRight, yMid
);
this._tmpCtx.bezierCurveTo(
- xRight, yMidBot,
- xRight + this._config.scaledCellWidth / 2, yMidBot,
+ xRight, yCurlyBot,
+ xRight + this._config.scaledCellWidth / 2, yCurlyBot,
xRight + this._config.scaledCellWidth / 2, yMid
);
break;
From d0ec9f2591158df9c059d77ef7432c5efc7c3bbc Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 30 Jul 2022 07:05:05 -0700
Subject: [PATCH 22/23] Support letter spacing in webgl
---
addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 48418c48..68857853 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -421,6 +421,7 @@ export class WebglCharAtlas implements IDisposable {
const lineWidth = Math.max(1, Math.floor(this._config.fontSize * window.devicePixelRatio / 10));
const yOffset = this._tmpCtx.lineWidth % 2 === 1 ? 0.5 : 0; // When the width is odd, draw at 0.5 position
this._tmpCtx.lineWidth = lineWidth;
+
// Underline color
if (this._workAttributeData.isUnderlineColorDefault()) {
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
@@ -433,12 +434,11 @@ export class WebglCharAtlas implements IDisposable {
}
this._tmpCtx.strokeStyle = this._getColorFromAnsiIndex(fg).css;
}
+
// Underline style/stroke
this._tmpCtx.beginPath();
-
- // TODO: Support letter spacing
const xLeft = padding;
- const xRight = padding + this._config.scaledCharWidth;
+ const xRight = padding + this._config.scaledCellWidth;
const yTop = Math.ceil(padding + this._config.scaledCharHeight - lineWidth) - yOffset;
const yMid = padding + this._config.scaledCharHeight - yOffset;
const yBot = Math.ceil(padding + this._config.scaledCharHeight + lineWidth) - yOffset;
@@ -450,7 +450,7 @@ export class WebglCharAtlas implements IDisposable {
this._tmpCtx.lineTo(xRight, yBot);
break;
case UnderlineStyle.CURLY:
- const xMid = padding + this._config.scaledCharWidth / 2;
+ const xMid = padding + this._config.scaledCellWidth / 2;
// Choose the bezier top and bottom based on the device pixel ratio, the curly line is
// made taller when the line width is as otherwise it's not very clear otherwise.
const yCurlyBot = lineWidth <= 1 ? yBot : Math.ceil(padding + this._config.scaledCharHeight - lineWidth / 2) - yOffset;
@@ -462,9 +462,9 @@ export class WebglCharAtlas implements IDisposable {
clipRegion.rect(xLeft, yTop, this._config.scaledCellWidth, yBot - yTop);
this._tmpCtx.clip(clipRegion);
// Start 1/2 cell before and end 1/2 cells after to ensure a smooth curve with other cells
- this._tmpCtx.moveTo(xLeft - this._config.scaledCharWidth / 2, yMid);
+ this._tmpCtx.moveTo(xLeft - this._config.scaledCellWidth / 2, yMid);
this._tmpCtx.bezierCurveTo(
- xLeft - this._config.scaledCharWidth / 2, yCurlyTop,
+ xLeft - this._config.scaledCellWidth / 2, yCurlyTop,
xLeft, yCurlyTop,
xLeft, yMid
);
From f0ddaf49ea3aa6b8995ca7268de507b06e1deeb2 Mon Sep 17 00:00:00 2001
From: Daniel Imms <2193314+Tyriar@users.noreply.github.com>
Date: Sat, 30 Jul 2022 07:34:07 -0700
Subject: [PATCH 23/23] Disable threshold check for glyphs with colored
underlines
This causes some underlines to disappear which looks very bad for
colored underlines
---
.../src/atlas/WebglCharAtlas.ts | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
index 68857853..e5f0fe6c 100644
--- a/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
+++ b/addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
@@ -414,6 +414,10 @@ export class WebglCharAtlas implements IDisposable {
drawSuccess = tryDrawCustomChar(this._tmpCtx, chars, padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight);
}
+ // Whether to clear pixels based on a threshold difference between the glyph color and the
+ // background color. This should be disabled when the glyph contains multiple colors such as
+ // underline colors to prevent important colors could get cleared.
+ let enableClearThresholdCheck = true;
// Draw underline
if (underline) {
@@ -426,8 +430,10 @@ export class WebglCharAtlas implements IDisposable {
if (this._workAttributeData.isUnderlineColorDefault()) {
this._tmpCtx.strokeStyle = this._tmpCtx.fillStyle;
} else if (this._workAttributeData.isUnderlineColorRGB()) {
+ enableClearThresholdCheck = false;
this._tmpCtx.strokeStyle = `rgb(${AttributeData.toColorRGB(this._workAttributeData.getUnderlineColor()).join(',')})`;
} else {
+ enableClearThresholdCheck = false;
let fg = this._workAttributeData.getUnderlineColor();
if (this._config.drawBoldTextInBrightColors && this._workAttributeData.isBold() && fg < 8) {
fg += 8;
@@ -508,7 +514,7 @@ export class WebglCharAtlas implements IDisposable {
if (!drawSuccess) {
// This only works when transparency is disabled because it's not clear how to clear stroked
// text
- if (!this._config.allowTransparency) {
+ if (!this._config.allowTransparency && chars !== ' ') {
// This translates to 1/2 the line width in either direction
this._tmpCtx.lineWidth = window.devicePixelRatio * 3;
this._tmpCtx.strokeStyle = backgroundColor.css;
@@ -525,12 +531,12 @@ export class WebglCharAtlas implements IDisposable {
// If this charcater is underscore and beyond the cell bounds, shift it up until it is visible
// even on the bottom row, try for a maximum of 5 pixels.
if (chars === '_' && !this._config.allowTransparency) {
- let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight), backgroundColor, foregroundColor);
+ let isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight), backgroundColor, foregroundColor, enableClearThresholdCheck);
if (isBeyondCellBounds) {
for (let offset = 1; offset <= 5; offset++) {
this._tmpCtx.clearRect(0, 0, this._tmpCanvas.width, this._tmpCanvas.height);
this._tmpCtx.fillText(chars, padding, padding + this._config.scaledCharHeight - offset);
- isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight), backgroundColor, foregroundColor);
+ isBeyondCellBounds = clearColor(this._tmpCtx.getImageData(padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight), backgroundColor, foregroundColor, enableClearThresholdCheck);
if (!isBeyondCellBounds) {
break;
}
@@ -561,7 +567,7 @@ export class WebglCharAtlas implements IDisposable {
// Clear out the background color and determine if the glyph is empty.
let isEmpty: boolean;
if (!this._config.allowTransparency) {
- isEmpty = clearColor(imageData, backgroundColor, foregroundColor);
+ isEmpty = clearColor(imageData, backgroundColor, foregroundColor, enableClearThresholdCheck);
} else {
isEmpty = checkCompletelyTransparent(imageData);
}
@@ -708,7 +714,7 @@ export class WebglCharAtlas implements IDisposable {
* transparent.
* @returns True if the result is "empty", meaning all pixels are fully transparent.
*/
-function clearColor(imageData: ImageData, bg: IColor, fg: IColor): boolean {
+function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThresholdCheck: boolean): boolean {
// Get color channels
const r = bg.rgba >>> 24;
const g = bg.rgba >>> 16 & 0xFF;
@@ -735,7 +741,8 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor): boolean {
imageData.data[offset + 3] = 0;
} else {
// Check the threshold based difference
- if ((Math.abs(imageData.data[offset] - r) +
+ if (enableThresholdCheck &&
+ (Math.abs(imageData.data[offset] - r) +
Math.abs(imageData.data[offset + 1] - g) +
Math.abs(imageData.data[offset + 2] - b)) < threshold) {
imageData.data[offset + 3] = 0;