Skip to content

Commit

Permalink
Merge pull request #3985 from Tyriar/3969
Browse files Browse the repository at this point in the history
Fix powerlines, custom glyph offset and improve demo
  • Loading branch information
Tyriar authored Jul 31, 2022
2 parents 889e3ad + 8d92f91 commit face97d
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 42 deletions.
7 changes: 4 additions & 3 deletions addons/xterm-addon-webgl/src/GlyphRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,11 @@ export class GlyphRenderer extends Disposable {
return;
}

if (bg !== lastBg && rasterizedGlyph.offset.x > 0) {
const clippedPixels = rasterizedGlyph.offset.x;
const leftCellPadding = Math.floor((this._dimensions.scaledCellWidth - this._dimensions.scaledCharWidth) / 2);
if (bg !== lastBg && rasterizedGlyph.offset.x > leftCellPadding) {
const clippedPixels = rasterizedGlyph.offset.x - leftCellPadding;
// a_origin
array[i ] = this._dimensions.scaledCharLeft;
array[i ] = -(rasterizedGlyph.offset.x - clippedPixels) + this._dimensions.scaledCharLeft;
array[i + 1] = -rasterizedGlyph.offset.y + this._dimensions.scaledCharTop;
// a_size
array[i + 2] = (rasterizedGlyph.size.x - clippedPixels) / this._dimensions.scaledCanvasWidth;
Expand Down
8 changes: 6 additions & 2 deletions addons/xterm-addon-webgl/src/WebglAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import { Terminal, ITerminalAddon, IEvent } from 'xterm';
import { WebglRenderer } from './WebglRenderer';
import { ICharacterJoinerService, ICoreBrowserService, IRenderService } from 'browser/services/Services';
import { IColorSet } from 'browser/Types';
import { EventEmitter } from 'common/EventEmitter';
import { EventEmitter, forwardEvent } from 'common/EventEmitter';
import { isSafari } from 'common/Platform';
import { ICoreService, IDecorationService } from 'common/services/Services';

export class WebglAddon implements ITerminalAddon {
private _terminal?: Terminal;
private _renderer?: WebglRenderer;

private _onChangeTextureAtlas = new EventEmitter<HTMLElement>();
public get onChangeTextureAtlas(): IEvent<HTMLElement> { return this._onChangeTextureAtlas.event; }
private _onContextLoss = new EventEmitter<void>();
public get onContextLoss(): IEvent<void> { return this._onContextLoss.event; }

Expand All @@ -36,7 +39,8 @@ export class WebglAddon implements ITerminalAddon {
const decorationService: IDecorationService = (terminal as any)._core._decorationService;
const colors: IColorSet = (terminal as any)._core._colorManager.colors;
this._renderer = new WebglRenderer(terminal, colors, characterJoinerService, coreBrowserService, coreService, decorationService, this._preserveDrawingBuffer);
this._renderer.onContextLoss(() => this._onContextLoss.fire());
forwardEvent(this._renderer.onContextLoss, this._onContextLoss);
forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas);
renderService.setRenderer(this._renderer);
}

Expand Down
8 changes: 6 additions & 2 deletions addons/xterm-addon-webgl/src/WebglRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/S
import { CharData, ICellData } from 'common/Types';
import { AttributeData } from 'common/buffer/AttributeData';
import { ICoreService, IDecorationService } from 'common/services/Services';
import { color, rgba as rgbaNs } from 'common/Color';

export class WebglRenderer extends Disposable implements IRenderer {
private _renderLayers: IRenderLayer[];
Expand All @@ -46,6 +45,8 @@ export class WebglRenderer extends Disposable implements IRenderer {
private _core: ITerminal;
private _isAttached: boolean;

private _onChangeTextureAtlas = new EventEmitter<HTMLCanvasElement>();
public get onChangeTextureAtlas(): IEvent<HTMLCanvasElement> { return this._onChangeTextureAtlas.event; }
private _onRequestRedraw = new EventEmitter<IRequestRedrawEvent>();
public get onRequestRedraw(): IEvent<IRequestRedrawEvent> { return this._onRequestRedraw.event; }

Expand Down Expand Up @@ -240,7 +241,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
if (!('getRasterizedGlyph' in atlas)) {
throw new Error('The webgl renderer only works with the webgl char atlas');
}
this._charAtlas = atlas as WebglCharAtlas;
if (this._charAtlas !== atlas) {
this._onChangeTextureAtlas.fire(atlas.cacheCanvas);
}
this._charAtlas = atlas;
this._charAtlas.warmUp();
this._glyphRenderer.setAtlas(this._charAtlas);
}
Expand Down
29 changes: 12 additions & 17 deletions addons/xterm-addon-webgl/src/atlas/WebglCharAtlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,23 +417,23 @@ export class WebglCharAtlas implements IDisposable {
`${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`;
this._tmpCtx.textBaseline = TEXT_BASELINE;

const powerLineGlyph = chars.length === 1 && isPowerlineGlyph(chars.charCodeAt(0));
const powerlineGlyph = chars.length === 1 && isPowerlineGlyph(chars.charCodeAt(0));
const foregroundColor = this._getForegroundColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, inverse, dim, bold, excludeFromContrastRatioDemands(chars.charCodeAt(0)));
this._tmpCtx.fillStyle = foregroundColor.css;

// For powerline glyphs left/top padding is excluded (https://github.com/microsoft/vscode/issues/120129)
const padding = powerLineGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING * 2;
const padding = powerlineGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING * 2;

// Draw custom characters if applicable
let drawSuccess = false;
let customGlyph = false;
if (this._config.customGlyphs !== false) {
drawSuccess = tryDrawCustomChar(this._tmpCtx, chars, padding, padding, this._config.scaledCellWidth, this._config.scaledCellHeight);
customGlyph = 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;
let enableClearThresholdCheck = !powerlineGlyph;

// Draw underline
if (underline) {
Expand Down Expand Up @@ -529,7 +529,7 @@ export class WebglCharAtlas implements IDisposable {

// Draw stroke in the background color for non custom characters in order to give an outline
// between the text and the underline
if (!drawSuccess) {
if (!customGlyph) {
// This only works when transparency is disabled because it's not clear how to clear stroked
// text
if (!this._config.allowTransparency && chars !== ' ') {
Expand All @@ -550,7 +550,7 @@ export class WebglCharAtlas implements IDisposable {
}

// Draw the character
if (!drawSuccess) {
if (!customGlyph) {
this._tmpCtx.fillText(chars, padding, padding + this._config.scaledCharHeight);
}

Expand Down Expand Up @@ -603,7 +603,7 @@ export class WebglCharAtlas implements IDisposable {
return NULL_RASTERIZED_GLYPH;
}

const rasterizedGlyph = this._findGlyphBoundingBox(imageData, this._workBoundingBox, allowedWidth, powerLineGlyph, drawSuccess);
const rasterizedGlyph = this._findGlyphBoundingBox(imageData, this._workBoundingBox, allowedWidth, powerlineGlyph, customGlyph, padding);
const clippedImageData = this._clipImageData(imageData, this._workBoundingBox);

// Find the best atlas row to use
Expand Down Expand Up @@ -680,10 +680,10 @@ export class WebglCharAtlas implements IDisposable {
* @param imageData The image data to read.
* @param boundingBox An IBoundingBox to put the clipped bounding box values.
*/
private _findGlyphBoundingBox(imageData: ImageData, boundingBox: IBoundingBox, allowedWidth: number, restrictedGlyph: boolean, customGlyph: boolean): IRasterizedGlyph {
private _findGlyphBoundingBox(imageData: ImageData, boundingBox: IBoundingBox, allowedWidth: number, restrictedGlyph: boolean, customGlyph: boolean, padding: number): IRasterizedGlyph {
boundingBox.top = 0;
const height = restrictedGlyph ? this._config.scaledCellHeight : this._tmpCanvas.height;
const width = restrictedGlyph ? this._config.scaledCharWidth : allowedWidth;
const width = restrictedGlyph ? this._config.scaledCellWidth : allowedWidth;
let found = false;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
Expand Down Expand Up @@ -755,8 +755,8 @@ export class WebglCharAtlas implements IDisposable {
y: (boundingBox.bottom - boundingBox.top + 1) / TEXTURE_HEIGHT
},
offset: {
x: -boundingBox.left + (restrictedGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING) + (customGlyph ? Math.floor(this._config.letterSpacing / 2) : 0),
y: -boundingBox.top + (restrictedGlyph ? 0 : TMP_CANVAS_GLYPH_PADDING) + (customGlyph ? this._config.lineHeight === 1 ? 0 : Math.round((this._config.scaledCellHeight - this._config.scaledCharHeight) / 2) : 0)
x: -boundingBox.left + padding + ((restrictedGlyph || customGlyph) ? Math.floor((this._config.scaledCellWidth - this._config.scaledCharWidth) / 2) : 0),
y: -boundingBox.top + padding + ((restrictedGlyph || customGlyph) ? this._config.lineHeight === 1 ? 0 : Math.round((this._config.scaledCellHeight - this._config.scaledCharHeight) / 2) : 0)
}
};
}
Expand Down Expand Up @@ -833,8 +833,3 @@ function checkCompletelyTransparent(imageData: ImageData): boolean {
}
return true;
}

function toPaddedHex(c: number): string {
const s = c.toString(16);
return s.length < 2 ? '0' + s : s;
}
15 changes: 10 additions & 5 deletions addons/xterm-addon-webgl/typings/xterm-addon-webgl.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ declare module 'xterm-addon-webgl' {
export class WebglAddon implements ITerminalAddon {
public textureAtlas?: HTMLCanvasElement;

/**
* An event that is fired when the renderer loses its canvas context.
*/
public get onContextLoss(): IEvent<void>;

/**
* An event that is fired when the texture atlas of the renderer changes.
*/
public get onChangeTextureAtlas(): IEvent<HTMLCanvasElement>;

constructor(preserveDrawingBuffer?: boolean);

/**
Expand All @@ -29,10 +39,5 @@ declare module 'xterm-addon-webgl' {
* Clears the terminal's texture atlas and triggers a redraw.
*/
public clearTextureAtlas(): void;

/**
* Fired when the WebglRenderer loses context
*/
public get onContextLoss(): IEvent<void>;
}
}
26 changes: 13 additions & 13 deletions demo/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ function createTerminal(): void {
addons.fit.instance!.fit();
typedTerm.loadAddon(addons.webgl.instance);
setTimeout(() => {
document.body.appendChild(addons.webgl.instance.textureAtlas);
addTextureAtlas(addons.webgl.instance.textureAtlas);
addons.webgl.instance.onChangeTextureAtlas(e => addTextureAtlas(e));
}, 0);
term.focus();

Expand Down Expand Up @@ -399,20 +400,18 @@ function initOptions(term: TerminalType): void {
const input = <HTMLInputElement>document.getElementById(`opt-${o}`);
addDomListener(input, 'change', () => {
console.log('change', o, input.value);
if (o === 'cols' || o === 'rows') {
updateTerminalSize();
} else if (o === 'lineHeight') {
if (o === 'lineHeight') {
term.options.lineHeight = parseFloat(input.value);
updateTerminalSize();
} else if (o === 'scrollSensitivity') {
term.options.scrollSensitivity = parseFloat(input.value);
updateTerminalSize();
} else if (o === 'scrollback') {
term.options.scrollback = parseInt(input.value);
setTimeout(() => updateTerminalSize(), 5);
} else {
term.options[o] = parseInt(input.value);
}
// Always update terminal size in case the option changes the dimensions
updateTerminalSize();
});
});
Object.keys(stringOptions).forEach(o => {
Expand Down Expand Up @@ -504,9 +503,7 @@ function initAddons(term: TerminalType): void {
addon.instance = new addon.ctor();
term.loadAddon(addon.instance);
if (name === 'webgl') {
setTimeout(() => {
document.body.appendChild((addon.instance as WebglAddon).textureAtlas);
}, 0);
(addon.instance as WebglAddon).onChangeTextureAtlas(e => addTextureAtlas(e));
} else if (name === 'unicode11') {
term.unicode.activeVersion = '11';
} else if (name === 'search') {
Expand Down Expand Up @@ -590,6 +587,9 @@ function htmlSerializeButtonHandler(): void {
document.getElementById("htmlserialize-output-result").innerText = "Copied to clipboard";
}

function addTextureAtlas(e: HTMLCanvasElement) {
document.querySelector('#texture-atlas').appendChild(e);
}

function writeCustomGlyphHandler() {
term.write('\n\r');
Expand Down Expand Up @@ -688,7 +688,7 @@ function powerlineSymbolTest() {
` 3 \ue0b1 \x1b[33;44m\ue0b0\x1b[39m` +
` 4 \ue0b1 \x1b[34;45m\ue0b0\x1b[39m` +
` 5 \ue0b1 \x1b[35;46m\ue0b0\x1b[39m` +
` 6 \ue0b1 \x1b[36;47m\ue0b0\x1b[39m` +
` 6 \ue0b1 \x1b[36;47m\ue0b0\x1b[30m` +
` 7 \ue0b1 \x1b[37;49m\ue0b0\x1b[0m`
);
term.writeln('');
Expand All @@ -701,7 +701,7 @@ function powerlineSymbolTest() {
` 3 \ue0b3 \x1b[7;33;44m\ue0b2\x1b[27;39m` +
` 4 \ue0b3 \x1b[7;34;45m\ue0b2\x1b[27;39m` +
` 5 \ue0b3 \x1b[7;35;46m\ue0b2\x1b[27;39m` +
` 6 \ue0b3 \x1b[7;36;47m\ue0b2\x1b[27;39m` +
` 6 \ue0b3 \x1b[7;36;47m\ue0b2\x1b[27;30m` +
` 7 \ue0b3 \x1b[7;37;49m\ue0b2\x1b[0m`
);
term.writeln('');
Expand All @@ -714,7 +714,7 @@ function powerlineSymbolTest() {
` 3 \ue0b5 \x1b[33;44m\ue0b4\x1b[39m` +
` 4 \ue0b5 \x1b[34;45m\ue0b4\x1b[39m` +
` 5 \ue0b5 \x1b[35;46m\ue0b4\x1b[39m` +
` 6 \ue0b5 \x1b[36;47m\ue0b4\x1b[39m` +
` 6 \ue0b5 \x1b[36;47m\ue0b4\x1b[30m` +
` 7 \ue0b5 \x1b[37;49m\ue0b4\x1b[0m`
);
term.writeln('');
Expand All @@ -727,7 +727,7 @@ function powerlineSymbolTest() {
` 3 \ue0b7 \x1b[7;33;44m\ue0b6\x1b[27;39m` +
` 4 \ue0b7 \x1b[7;34;45m\ue0b6\x1b[27;39m` +
` 5 \ue0b7 \x1b[7;35;46m\ue0b6\x1b[27;39m` +
` 6 \ue0b7 \x1b[7;36;47m\ue0b6\x1b[27;39m` +
` 6 \ue0b7 \x1b[7;36;47m\ue0b6\x1b[27;30m` +
` 7 \ue0b7 \x1b[7;37;49m\ue0b6\x1b[0m`
);
term.writeln('');
Expand Down
1 change: 1 addition & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ <h3>Test</h3>
</div>
</div>
</div>
<div id="texture-atlas"></div>
<script src="dist/client-bundle.js" defer ></script>
<script>
var tab = localStorage.getItem("tab");
Expand Down
14 changes: 14 additions & 0 deletions demo/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,17 @@ pre {
max-height: 100vh;
overflow-y: auto;
}

#texture-atlas {
width: 100%;
height: 600px;
overflow: scroll;
}
#texture-atlas canvas {
image-rendering: pixelated;
}
#texture-atlas canvas:hover {
/* zoom to 4x on hover */
width: 4096px;
height: 4096px;
}

0 comments on commit face97d

Please sign in to comment.