Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

split extremely wide ligature glyphs to fit atlas #4386

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 32 additions & 26 deletions addons/xterm-addon-canvas/src/BaseRenderLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,37 +368,43 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
protected _drawChars(cell: ICellData, x: number, y: number): void {
const chars = cell.getChars();
this._cellColorResolver.resolve(cell, x, this._bufferService.buffer.ydisp + y);
let glyph: IRasterizedGlyph;
let glyphs: IRasterizedGlyph[];
if (chars && chars.length > 1) {
glyph = this._charAtlas.getRasterizedGlyphCombinedChar(chars, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
glyphs = this._charAtlas.getRasterizedGlyphCombinedChar(chars, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
} else {
glyph = this._charAtlas.getRasterizedGlyph(cell.getCode() || WHITESPACE_CELL_CODE, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
glyphs = this._charAtlas.getRasterizedGlyph(cell.getCode() || WHITESPACE_CELL_CODE, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext);
}
if (!glyph.size.x || !glyph.size.y) {
return;
}
this._ctx.save();
this._clipRow(y);
// Draw the image, use the bitmap if it's available
if (this._charAtlas.pages[glyph.texturePage].version !== this._bitmapGenerator[glyph.texturePage]?.version) {
if (!this._bitmapGenerator[glyph.texturePage]) {
this._bitmapGenerator[glyph.texturePage] = new BitmapGenerator(this._charAtlas.pages[glyph.texturePage].canvas);

let glyphsOffsetX = 0;
for (let i = 0; i < glyphs.length; i++) {
const glyph = glyphs[i];
if (!glyph.size.x || !glyph.size.y) {
continue;
}
this._bitmapGenerator[glyph.texturePage]!.refresh();
this._bitmapGenerator[glyph.texturePage]!.version = this._charAtlas.pages[glyph.texturePage].version;
this._ctx.save();
this._clipRow(y);
// Draw the image, use the bitmap if it's available
if (this._charAtlas.pages[glyph.texturePage].version !== this._bitmapGenerator[glyph.texturePage]?.version) {
if (!this._bitmapGenerator[glyph.texturePage]) {
this._bitmapGenerator[glyph.texturePage] = new BitmapGenerator(this._charAtlas.pages[glyph.texturePage].canvas);
}
this._bitmapGenerator[glyph.texturePage]!.refresh();
this._bitmapGenerator[glyph.texturePage]!.version = this._charAtlas.pages[glyph.texturePage].version;
}
this._ctx.drawImage(
this._bitmapGenerator[glyph.texturePage]?.bitmap || this._charAtlas!.pages[glyph.texturePage].canvas,
glyph.texturePosition.x,
glyph.texturePosition.y,
glyph.size.x,
glyph.size.y,
x * this._deviceCellWidth + this._deviceCharLeft - glyph.offset.x + glyphsOffsetX,
y * this._deviceCellHeight + this._deviceCharTop - glyph.offset.y,
glyph.size.x,
glyph.size.y
);
glyphsOffsetX += glyph.size.x - glyph.offset.x;
this._ctx.restore();
}
this._ctx.drawImage(
this._bitmapGenerator[glyph.texturePage]?.bitmap || this._charAtlas!.pages[glyph.texturePage].canvas,
glyph.texturePosition.x,
glyph.texturePosition.y,
glyph.size.x,
glyph.size.y,
x * this._deviceCellWidth + this._deviceCharLeft - glyph.offset.x,
y * this._deviceCellHeight + this._deviceCharTop - glyph.offset.y,
glyph.size.x,
glyph.size.y
);
this._ctx.restore();
}

/**
Expand Down
79 changes: 45 additions & 34 deletions addons/xterm-addon-webgl/src/GlyphRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ const CELL_POSITION_INDICES = 2;

// Work variables to avoid garbage collection
let $i = 0;
let $j = 0;
let $glyph: IRasterizedGlyph | undefined = undefined;
let $glyphs: IRasterizedGlyph[] | undefined = undefined;
let $leftCellPadding = 0;
let $clippedPixels = 0;
let $offset = 0;

export class GlyphRenderer extends Disposable {
private readonly _program: WebGLProgram;
Expand Down Expand Up @@ -236,43 +239,51 @@ export class GlyphRenderer extends Disposable {

// Get the glyph
if (chars && chars.length > 1) {
$glyph = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg, ext);
$glyphs = this._atlas.getRasterizedGlyphCombinedChar(chars, bg, fg, ext);
} else {
$glyph = this._atlas.getRasterizedGlyph(code, bg, fg, ext);
$glyphs = this._atlas.getRasterizedGlyph(code, bg, fg, ext);
}

$leftCellPadding = Math.floor((this._dimensions.device.cell.width - this._dimensions.device.char.width) / 2);
if (bg !== lastBg && $glyph.offset.x > $leftCellPadding) {
$clippedPixels = $glyph.offset.x - $leftCellPadding;
// a_origin
array[$i ] = -($glyph.offset.x - $clippedPixels) + this._dimensions.device.char.left;
array[$i + 1] = -$glyph.offset.y + this._dimensions.device.char.top;
// a_size
array[$i + 2] = ($glyph.size.x - $clippedPixels) / this._dimensions.device.canvas.width;
array[$i + 3] = $glyph.size.y / this._dimensions.device.canvas.height;
// a_texpage
array[$i + 4] = $glyph.texturePage;
// a_texcoord
array[$i + 5] = $glyph.texturePositionClipSpace.x + $clippedPixels / this._atlas.pages[$glyph.texturePage].canvas.width;
array[$i + 6] = $glyph.texturePositionClipSpace.y;
// a_texsize
array[$i + 7] = $glyph.sizeClipSpace.x - $clippedPixels / this._atlas.pages[$glyph.texturePage].canvas.width;
array[$i + 8] = $glyph.sizeClipSpace.y;
} else {
// a_origin
array[$i ] = -$glyph.offset.x + this._dimensions.device.char.left;
array[$i + 1] = -$glyph.offset.y + this._dimensions.device.char.top;
// a_size
array[$i + 2] = $glyph.size.x / this._dimensions.device.canvas.width;
array[$i + 3] = $glyph.size.y / this._dimensions.device.canvas.height;
// a_texpage
array[$i + 4] = $glyph.texturePage;
// a_texcoord
array[$i + 5] = $glyph.texturePositionClipSpace.x;
array[$i + 6] = $glyph.texturePositionClipSpace.y;
// a_texsize
array[$i + 7] = $glyph.sizeClipSpace.x;
array[$i + 8] = $glyph.sizeClipSpace.y;
$offset = $glyphs[0].offset.x;

for ($j = 0; $j < $glyphs.length; $j++) {
$i = (y * this._terminal.cols + x + $j) * INDICES_PER_CELL;
$glyph = $glyphs[$j];

$leftCellPadding = Math.floor((this._dimensions.device.cell.width - this._dimensions.device.char.width) / 2);
if (bg !== lastBg && $offset > $leftCellPadding) {
$clippedPixels = $offset - $leftCellPadding;
// a_origin
array[$i ] = -($offset - $clippedPixels) + this._dimensions.device.char.left;
array[$i + 1] = -$glyph.offset.y + this._dimensions.device.char.top;
// a_size
array[$i + 2] = ($glyph.size.x - $clippedPixels) / this._dimensions.device.canvas.width;
array[$i + 3] = $glyph.size.y / this._dimensions.device.canvas.height;
// a_texpage
array[$i + 4] = $glyph.texturePage;
// a_texcoord
array[$i + 5] = $glyph.texturePositionClipSpace.x + $clippedPixels / this._atlas.pages[$glyph.texturePage].canvas.width;
array[$i + 6] = $glyph.texturePositionClipSpace.y;
// a_texsize
array[$i + 7] = $glyph.sizeClipSpace.x - $clippedPixels / this._atlas.pages[$glyph.texturePage].canvas.width;
array[$i + 8] = $glyph.sizeClipSpace.y;
} else {
// a_origin
array[$i ] = -$offset + this._dimensions.device.char.left;
array[$i + 1] = -$glyph.offset.y + this._dimensions.device.char.top;
// a_size
array[$i + 2] = $glyph.size.x / this._dimensions.device.canvas.width;
array[$i + 3] = $glyph.size.y / this._dimensions.device.canvas.height;
// a_texpage
array[$i + 4] = $glyph.texturePage;
// a_texcoord
array[$i + 5] = $glyph.texturePositionClipSpace.x;
array[$i + 6] = $glyph.texturePositionClipSpace.y;
// a_texsize
array[$i + 7] = $glyph.sizeClipSpace.x;
array[$i + 8] = $glyph.sizeClipSpace.y;
}
$offset -= $glyph.size.x - this._dimensions.device.cell.width;
}
// a_cellpos only changes on resize
}
Expand Down
11 changes: 6 additions & 5 deletions addons/xterm-addon-webgl/src/WebglRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
let i: number;
let x: number;
let j: number;
let k: number;
start = clamp(start, terminal.rows - 1, 0);
end = clamp(end, terminal.rows - 1, 0);
for (y = start; y <= end; y++) {
Expand Down Expand Up @@ -434,22 +435,22 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._model.cells[i + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
this._model.cells[i + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;

this._glyphRenderer!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, lastBg);

if (isJoined) {
// Restore work cell
cell = this._workCell;

// Null out non-first cells
for (x++; x < lastCharX; x++) {
j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL;
this._glyphRenderer!.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0);
for (k = x + 1; k < lastCharX; k++) {
j = ((y * terminal.cols) + k) * RENDER_MODEL_INDICIES_PER_CELL;
this._glyphRenderer!.updateCell(k, 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._cellColorResolver.result.bg;
this._model.cells[j + RENDER_MODEL_FG_OFFSET] = this._cellColorResolver.result.fg;
this._model.cells[j + RENDER_MODEL_EXT_OFFSET] = this._cellColorResolver.result.ext;
}
}
this._glyphRenderer!.updateCell(x, y, code, this._cellColorResolver.result.bg, this._cellColorResolver.result.fg, this._cellColorResolver.result.ext, chars, lastBg);
x = lastCharX;
}
}
this._rectangleRenderer!.updateBackgrounds(this._model);
Expand Down
Loading