Merge pull request #4070 from Tyriar/4066
Improve glyph padding, especially in large font sizes
This commit is contained in:
@@ -344,7 +344,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
// Draw custom characters if applicable
|
// Draw custom characters if applicable
|
||||||
let drawSuccess = false;
|
let drawSuccess = false;
|
||||||
if (this._optionsService.rawOptions.customGlyphs !== false) {
|
if (this._optionsService.rawOptions.customGlyphs !== false) {
|
||||||
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight);
|
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight, this._optionsService.rawOptions.fontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the character
|
// Draw the character
|
||||||
@@ -473,7 +473,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
|||||||
// Draw custom characters if applicable
|
// Draw custom characters if applicable
|
||||||
let drawSuccess = false;
|
let drawSuccess = false;
|
||||||
if (this._optionsService.rawOptions.customGlyphs !== false) {
|
if (this._optionsService.rawOptions.customGlyphs !== false) {
|
||||||
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight);
|
drawSuccess = tryDrawCustomChar(this._ctx, cell.getChars(), x * this._scaledCellWidth, y * this._scaledCellHeight, this._scaledCellWidth, this._scaledCellHeight, this._optionsService.rawOptions.fontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the character
|
// Draw the character
|
||||||
|
|||||||
@@ -427,7 +427,7 @@ export class WebglCharAtlas implements IDisposable {
|
|||||||
// Draw custom characters if applicable
|
// Draw custom characters if applicable
|
||||||
let customGlyph = false;
|
let customGlyph = false;
|
||||||
if (this._config.customGlyphs !== false) {
|
if (this._config.customGlyphs !== false) {
|
||||||
customGlyph = 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, this._config.fontSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether to clear pixels based on a threshold difference between the glyph color and the
|
// Whether to clear pixels based on a threshold difference between the glyph color and the
|
||||||
|
|||||||
+1
-1
@@ -203,7 +203,7 @@ function createTerminal(): void {
|
|||||||
term = new Terminal({
|
term = new Terminal({
|
||||||
allowProposedApi: true,
|
allowProposedApi: true,
|
||||||
windowsMode: isWindows,
|
windowsMode: isWindows,
|
||||||
fontFamily: 'Fira Code, courier-new, courier, monospace',
|
fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"',
|
||||||
theme: xtermjsTheme
|
theme: xtermjsTheme
|
||||||
} as ITerminalOptions);
|
} as ITerminalOptions);
|
||||||
|
|
||||||
|
|||||||
@@ -349,25 +349,27 @@ const enum VectorType {
|
|||||||
* not been patched with powerline characters and also to get pixel perfect rendering as rendering
|
* not been patched with powerline characters and also to get pixel perfect rendering as rendering
|
||||||
* issues can occur around AA/SPAA.
|
* issues can occur around AA/SPAA.
|
||||||
*
|
*
|
||||||
|
* The line variants draw beyond the cell and get clipped to ensure the end of the line is not visible.
|
||||||
|
*
|
||||||
* Original symbols defined in https://github.com/powerline/fontpatcher
|
* Original symbols defined in https://github.com/powerline/fontpatcher
|
||||||
*/
|
*/
|
||||||
export const powerlineDefinitions: { [index: string]: IVectorShape } = {
|
export const powerlineDefinitions: { [index: string]: IVectorShape } = {
|
||||||
// Right triangle solid
|
// Right triangle solid
|
||||||
'\u{E0B0}': { d: 'M0,0 L1,.5 L0,1', type: VectorType.FILL },
|
'\u{E0B0}': { d: 'M0,0 L1,.5 L0,1', type: VectorType.FILL, rightPadding: 2 },
|
||||||
// Right triangle line
|
// Right triangle line
|
||||||
'\u{E0B1}': { d: 'M0,0 L1,.5 L0,1', type: VectorType.STROKE, leftPadding: window.devicePixelRatio / 2, rightPadding: window.devicePixelRatio / 2 },
|
'\u{E0B1}': { d: 'M-1,-.5 L1,.5 L-1,1.5', type: VectorType.STROKE, leftPadding: 1, rightPadding: 1 },
|
||||||
// Left triangle solid
|
// Left triangle solid
|
||||||
'\u{E0B2}': { d: 'M1,0 L0,.5 L1,1', type: VectorType.FILL },
|
'\u{E0B2}': { d: 'M1,0 L0,.5 L1,1', type: VectorType.FILL, leftPadding: 2 },
|
||||||
// Left triangle line
|
// Left triangle line
|
||||||
'\u{E0B3}': { d: 'M1,0 L0,.5 L1,1', type: VectorType.STROKE, leftPadding: window.devicePixelRatio / 2, rightPadding: window.devicePixelRatio / 2 },
|
'\u{E0B3}': { d: 'M2,-.5 L0,.5 L2,1.5', type: VectorType.STROKE, leftPadding: 1, rightPadding: 1 },
|
||||||
// Right semi-circle solid,
|
// Right semi-circle solid,
|
||||||
'\u{E0B4}': { d: 'M0,0 L0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0', type: VectorType.FILL },
|
'\u{E0B4}': { d: 'M0,0 L0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0', type: VectorType.FILL, rightPadding: 1 },
|
||||||
// Right semi-circle line,
|
// Right semi-circle line,
|
||||||
'\u{E0B5}': { d: 'M0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0', type: VectorType.STROKE, rightPadding: window.devicePixelRatio / 2 },
|
'\u{E0B5}': { d: 'M0,1 C0.552,1,1,0.776,1,.5 C1,0.224,0.552,0,0,0', type: VectorType.STROKE, rightPadding: 1 },
|
||||||
// Left semi-circle solid,
|
// Left semi-circle solid,
|
||||||
'\u{E0B6}': { d: 'M1,0 L1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0', type: VectorType.FILL },
|
'\u{E0B6}': { d: 'M1,0 L1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0', type: VectorType.FILL, leftPadding: 1 },
|
||||||
// Left semi-circle line,
|
// Left semi-circle line,
|
||||||
'\u{E0B7}': { d: 'M1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0', type: VectorType.STROKE, leftPadding: window.devicePixelRatio / 2 }
|
'\u{E0B7}': { d: 'M1,1 C0.448,1,0,0.776,0,.5 C0,0.224,0.448,0,1,0', type: VectorType.STROKE, leftPadding: 1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -380,7 +382,8 @@ export function tryDrawCustomChar(
|
|||||||
xOffset: number,
|
xOffset: number,
|
||||||
yOffset: number,
|
yOffset: number,
|
||||||
scaledCellWidth: number,
|
scaledCellWidth: number,
|
||||||
scaledCellHeight: number
|
scaledCellHeight: number,
|
||||||
|
fontSize: number
|
||||||
): boolean {
|
): boolean {
|
||||||
const blockElementDefinition = blockElementDefinitions[c];
|
const blockElementDefinition = blockElementDefinitions[c];
|
||||||
if (blockElementDefinition) {
|
if (blockElementDefinition) {
|
||||||
@@ -402,7 +405,7 @@ export function tryDrawCustomChar(
|
|||||||
|
|
||||||
const powerlineDefinition = powerlineDefinitions[c];
|
const powerlineDefinition = powerlineDefinitions[c];
|
||||||
if (powerlineDefinition) {
|
if (powerlineDefinition) {
|
||||||
drawPowerlineChar(ctx, powerlineDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight);
|
drawPowerlineChar(ctx, powerlineDefinition, xOffset, yOffset, scaledCellWidth, scaledCellHeight, fontSize);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,7 +565,7 @@ function drawBoxDrawingChar(
|
|||||||
if (!args[0] || !args[1]) {
|
if (!args[0] || !args[1]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
f(ctx, translateArgs(args, scaledCellWidth, scaledCellHeight, xOffset, yOffset));
|
f(ctx, translateArgs(args, scaledCellWidth, scaledCellHeight, xOffset, yOffset, true));
|
||||||
}
|
}
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
@@ -575,10 +578,13 @@ function drawPowerlineChar(
|
|||||||
xOffset: number,
|
xOffset: number,
|
||||||
yOffset: number,
|
yOffset: number,
|
||||||
scaledCellWidth: number,
|
scaledCellWidth: number,
|
||||||
scaledCellHeight: number
|
scaledCellHeight: number,
|
||||||
|
fontSize: number
|
||||||
): void {
|
): void {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.lineWidth = window.devicePixelRatio;
|
// Scale the stroke with DPR and font size
|
||||||
|
const cssLineWidth = fontSize / 12;
|
||||||
|
ctx.lineWidth = window.devicePixelRatio * cssLineWidth;
|
||||||
for (const instruction of charDefinition.d.split(' ')) {
|
for (const instruction of charDefinition.d.split(' ')) {
|
||||||
const type = instruction[0];
|
const type = instruction[0];
|
||||||
const f = svgToCanvasInstructionMap[type];
|
const f = svgToCanvasInstructionMap[type];
|
||||||
@@ -590,7 +596,16 @@ function drawPowerlineChar(
|
|||||||
if (!args[0] || !args[1]) {
|
if (!args[0] || !args[1]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
f(ctx, translateArgs(args, scaledCellWidth, scaledCellHeight, xOffset, yOffset, charDefinition.leftPadding, charDefinition.rightPadding));
|
f(ctx, translateArgs(
|
||||||
|
args,
|
||||||
|
scaledCellWidth,
|
||||||
|
scaledCellHeight,
|
||||||
|
xOffset,
|
||||||
|
yOffset,
|
||||||
|
false,
|
||||||
|
(charDefinition.leftPadding ?? 0) * (cssLineWidth / 2),
|
||||||
|
(charDefinition.rightPadding ?? 0) * (cssLineWidth / 2)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if (charDefinition.type === VectorType.STROKE) {
|
if (charDefinition.type === VectorType.STROKE) {
|
||||||
ctx.strokeStyle = ctx.fillStyle;
|
ctx.strokeStyle = ctx.fillStyle;
|
||||||
@@ -611,7 +626,7 @@ const svgToCanvasInstructionMap: { [index: string]: any } = {
|
|||||||
'M': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.moveTo(args[0], args[1])
|
'M': (ctx: CanvasRenderingContext2D, args: number[]) => ctx.moveTo(args[0], args[1])
|
||||||
};
|
};
|
||||||
|
|
||||||
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
|
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, leftPadding: number = 0, rightPadding: number = 0): number[] {
|
||||||
const result = args.map(e => parseFloat(e) || parseInt(e));
|
const result = args.map(e => parseFloat(e) || parseInt(e));
|
||||||
|
|
||||||
if (result.length < 2) {
|
if (result.length < 2) {
|
||||||
@@ -623,7 +638,7 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
|||||||
result[x] *= cellWidth - (leftPadding * window.devicePixelRatio) - (rightPadding * window.devicePixelRatio);
|
result[x] *= cellWidth - (leftPadding * window.devicePixelRatio) - (rightPadding * window.devicePixelRatio);
|
||||||
// Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
|
// Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
|
||||||
// line at 100% devicePixelRatio
|
// line at 100% devicePixelRatio
|
||||||
if (result[x] !== 0) {
|
if (doClamp && result[x] !== 0) {
|
||||||
result[x] = clamp(Math.round(result[x] + 0.5) - 0.5, cellWidth, 0);
|
result[x] = clamp(Math.round(result[x] + 0.5) - 0.5, cellWidth, 0);
|
||||||
}
|
}
|
||||||
// Apply the cell's offset (ie. x*cellWidth)
|
// Apply the cell's offset (ie. x*cellWidth)
|
||||||
@@ -635,7 +650,7 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
|||||||
result[y] *= cellHeight;
|
result[y] *= cellHeight;
|
||||||
// Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
|
// Ensure coordinate doesn't escape cell bounds and round to the nearest 0.5 to ensure a crisp
|
||||||
// line at 100% devicePixelRatio
|
// line at 100% devicePixelRatio
|
||||||
if (result[y] !== 0) {
|
if (doClamp && result[y] !== 0) {
|
||||||
result[y] = clamp(Math.round(result[y] + 0.5) - 0.5, cellHeight, 0);
|
result[y] = clamp(Math.round(result[y] + 0.5) - 0.5, cellHeight, 0);
|
||||||
}
|
}
|
||||||
// Apply the cell's offset (ie. x*cellHeight)
|
// Apply the cell's offset (ie. x*cellHeight)
|
||||||
|
|||||||
Reference in New Issue
Block a user