@xterm/addon-webgl 0.20.0-beta.28 → 0.20.0-beta.280
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/addon-webgl.js +1 -1
- package/lib/addon-webgl.js.map +1 -1
- package/lib/addon-webgl.mjs +5 -31
- package/lib/addon-webgl.mjs.map +4 -4
- package/package.json +4 -4
- package/src/CellColorResolver.ts +6 -3
- package/src/CharAtlasCache.ts +3 -1
- package/src/CharAtlasUtils.ts +3 -0
- package/src/CursorBlinkStateManager.ts +67 -8
- package/src/DevicePixelObserver.ts +1 -1
- package/src/GlyphRenderer.ts +38 -27
- package/src/RectangleRenderer.ts +15 -11
- package/src/RenderModel.ts +7 -5
- package/src/TextureAtlas.ts +116 -72
- package/src/Types.ts +4 -3
- package/src/WebglAddon.ts +9 -13
- package/src/WebglRenderer.ts +123 -36
- package/src/WebglUtils.ts +7 -6
- package/src/customGlyphs/CustomGlyphDefinitions.ts +76 -31
- package/src/customGlyphs/CustomGlyphRasterizer.ts +45 -20
- package/src/renderLayer/BaseRenderLayer.ts +1 -1
- package/typings/addon-webgl.d.ts +5 -2
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
|
7
|
+
import type { ILogService } from 'common/services/Services';
|
|
7
8
|
import { customGlyphDefinitions } from './CustomGlyphDefinitions';
|
|
8
9
|
import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types';
|
|
9
10
|
|
|
@@ -22,14 +23,16 @@ export function tryDrawCustomGlyph(
|
|
|
22
23
|
deviceCharHeight: number,
|
|
23
24
|
fontSize: number,
|
|
24
25
|
devicePixelRatio: number,
|
|
25
|
-
|
|
26
|
+
logService: ILogService,
|
|
27
|
+
backgroundColor?: string,
|
|
28
|
+
variantOffset: number = 0
|
|
26
29
|
): boolean {
|
|
27
30
|
const unifiedCharDefinition = customGlyphDefinitions[c];
|
|
28
31
|
if (unifiedCharDefinition) {
|
|
29
32
|
// Normalize to array for uniform handling
|
|
30
33
|
const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition];
|
|
31
34
|
for (const part of parts) {
|
|
32
|
-
drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor);
|
|
35
|
+
drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, logService, backgroundColor, variantOffset);
|
|
33
36
|
}
|
|
34
37
|
return true;
|
|
35
38
|
}
|
|
@@ -48,7 +51,9 @@ function drawDefinitionPart(
|
|
|
48
51
|
deviceCharHeight: number,
|
|
49
52
|
fontSize: number,
|
|
50
53
|
devicePixelRatio: number,
|
|
51
|
-
|
|
54
|
+
logService: ILogService,
|
|
55
|
+
backgroundColor?: string,
|
|
56
|
+
variantOffset: number = 0
|
|
52
57
|
): void {
|
|
53
58
|
// Handle scaleType - adjust dimensions and offset when scaling to character area
|
|
54
59
|
let drawWidth = deviceCellWidth;
|
|
@@ -74,10 +79,10 @@ function drawDefinitionPart(
|
|
|
74
79
|
drawBlockVectorChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
75
80
|
break;
|
|
76
81
|
case CustomGlyphDefinitionType.BLOCK_PATTERN:
|
|
77
|
-
drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
82
|
+
drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, variantOffset);
|
|
78
83
|
break;
|
|
79
84
|
case CustomGlyphDefinitionType.PATH_FUNCTION:
|
|
80
|
-
drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
|
|
85
|
+
drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, logService, part.strokeWidth);
|
|
81
86
|
break;
|
|
82
87
|
case CustomGlyphDefinitionType.PATH:
|
|
83
88
|
drawPathDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
|
|
@@ -86,7 +91,7 @@ function drawDefinitionPart(
|
|
|
86
91
|
drawPathNegativeDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, backgroundColor);
|
|
87
92
|
break;
|
|
88
93
|
case CustomGlyphDefinitionType.VECTOR_SHAPE:
|
|
89
|
-
drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio);
|
|
94
|
+
drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio, logService);
|
|
90
95
|
break;
|
|
91
96
|
case CustomGlyphDefinitionType.BRAILLE:
|
|
92
97
|
drawBrailleCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
@@ -216,8 +221,8 @@ function drawPathDefinitionCharacter(
|
|
|
216
221
|
const rx = parseFloat(args[0]) * deviceCellWidth;
|
|
217
222
|
const ry = parseFloat(args[1]) * deviceCellHeight;
|
|
218
223
|
const xAxisRotation = parseFloat(args[2]) * Math.PI / 180;
|
|
219
|
-
const largeArcFlag = parseInt(args[3]);
|
|
220
|
-
const sweepFlag = parseInt(args[4]);
|
|
224
|
+
const largeArcFlag = parseInt(args[3], 10);
|
|
225
|
+
const sweepFlag = parseInt(args[4], 10);
|
|
221
226
|
const x = xOffset + parseFloat(args[5]) * deviceCellWidth;
|
|
222
227
|
const y = yOffset + parseFloat(args[6]) * deviceCellHeight;
|
|
223
228
|
drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y);
|
|
@@ -437,7 +442,8 @@ function drawPatternChar(
|
|
|
437
442
|
xOffset: number,
|
|
438
443
|
yOffset: number,
|
|
439
444
|
deviceCellWidth: number,
|
|
440
|
-
deviceCellHeight: number
|
|
445
|
+
deviceCellHeight: number,
|
|
446
|
+
variantOffset: number = 0
|
|
441
447
|
): void {
|
|
442
448
|
let patternSet = cachedPatterns.get(charDefinition);
|
|
443
449
|
if (!patternSet) {
|
|
@@ -486,6 +492,15 @@ function drawPatternChar(
|
|
|
486
492
|
pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null));
|
|
487
493
|
patternSet.set(fillStyle, pattern);
|
|
488
494
|
}
|
|
495
|
+
// Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd.
|
|
496
|
+
// variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift.
|
|
497
|
+
const dx = (variantOffset >> 1) & 1;
|
|
498
|
+
const dy = variantOffset & 1;
|
|
499
|
+
if (dx !== 0 || dy !== 0) {
|
|
500
|
+
pattern.setTransform(new DOMMatrix().translateSelf(-dx, -dy));
|
|
501
|
+
} else {
|
|
502
|
+
pattern.setTransform(new DOMMatrix());
|
|
503
|
+
}
|
|
489
504
|
ctx.fillStyle = pattern;
|
|
490
505
|
ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
491
506
|
}
|
|
@@ -498,8 +513,14 @@ function drawPathFunctionCharacter(
|
|
|
498
513
|
deviceCellWidth: number,
|
|
499
514
|
deviceCellHeight: number,
|
|
500
515
|
devicePixelRatio: number,
|
|
516
|
+
logService: ILogService,
|
|
501
517
|
strokeWidth?: number
|
|
502
518
|
): void {
|
|
519
|
+
ctx.save();
|
|
520
|
+
ctx.beginPath();
|
|
521
|
+
ctx.rect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
522
|
+
ctx.clip();
|
|
523
|
+
|
|
503
524
|
ctx.beginPath();
|
|
504
525
|
let actualInstructions: string;
|
|
505
526
|
if (typeof charDefinition === 'function') {
|
|
@@ -519,14 +540,14 @@ function drawPathFunctionCharacter(
|
|
|
519
540
|
}
|
|
520
541
|
const f = svgToCanvasInstructionMap[type];
|
|
521
542
|
if (!f) {
|
|
522
|
-
|
|
543
|
+
logService.error(`Could not find drawing instructions for "${type}"`);
|
|
523
544
|
continue;
|
|
524
545
|
}
|
|
525
546
|
const args: string[] = instruction.substring(1).split(',');
|
|
526
547
|
if (!args[0] || !args[1]) {
|
|
527
548
|
continue;
|
|
528
549
|
}
|
|
529
|
-
f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio), state);
|
|
550
|
+
f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio, 0, 0, false), state);
|
|
530
551
|
state.lastCommand = type;
|
|
531
552
|
}
|
|
532
553
|
if (strokeWidth !== undefined) {
|
|
@@ -537,6 +558,7 @@ function drawPathFunctionCharacter(
|
|
|
537
558
|
ctx.fill();
|
|
538
559
|
}
|
|
539
560
|
ctx.closePath();
|
|
561
|
+
ctx.restore();
|
|
540
562
|
}
|
|
541
563
|
|
|
542
564
|
/**
|
|
@@ -580,7 +602,8 @@ function drawVectorShape(
|
|
|
580
602
|
deviceCellWidth: number,
|
|
581
603
|
deviceCellHeight: number,
|
|
582
604
|
fontSize: number,
|
|
583
|
-
devicePixelRatio: number
|
|
605
|
+
devicePixelRatio: number,
|
|
606
|
+
logService: ILogService
|
|
584
607
|
): void {
|
|
585
608
|
// Clip the cell to make sure drawing doesn't occur beyond bounds
|
|
586
609
|
const clipRegion = new Path2D();
|
|
@@ -601,7 +624,7 @@ function drawVectorShape(
|
|
|
601
624
|
}
|
|
602
625
|
const f = svgToCanvasInstructionMap[type];
|
|
603
626
|
if (!f) {
|
|
604
|
-
|
|
627
|
+
logService.error(`Could not find drawing instructions for "${type}"`);
|
|
605
628
|
continue;
|
|
606
629
|
}
|
|
607
630
|
const args: string[] = instruction.substring(1).split(',');
|
|
@@ -685,7 +708,7 @@ const svgToCanvasInstructionMap: { [index: string]: (ctx: CanvasRenderingContext
|
|
|
685
708
|
}
|
|
686
709
|
};
|
|
687
710
|
|
|
688
|
-
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
|
|
711
|
+
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0, clampToCell: boolean = true): number[] {
|
|
689
712
|
const result = args.map(e => parseFloat(e) || parseInt(e));
|
|
690
713
|
|
|
691
714
|
if (result.length < 2) {
|
|
@@ -695,10 +718,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
|
|
695
718
|
for (let x = 0; x < result.length; x += 2) {
|
|
696
719
|
// Translate from 0-1 to 0-cellWidth
|
|
697
720
|
result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio);
|
|
698
|
-
//
|
|
699
|
-
//
|
|
721
|
+
// Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
|
|
722
|
+
// clamp to the cell bounds.
|
|
700
723
|
if (doClamp && result[x] !== 0) {
|
|
701
|
-
|
|
724
|
+
const rounded = Math.round(result[x] + 0.5) - 0.5;
|
|
725
|
+
result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded;
|
|
702
726
|
}
|
|
703
727
|
// Apply the cell's offset (ie. x*cellWidth)
|
|
704
728
|
result[x] += xOffset + (leftPadding * devicePixelRatio);
|
|
@@ -707,10 +731,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
|
|
707
731
|
for (let y = 1; y < result.length; y += 2) {
|
|
708
732
|
// Translate from 0-1 to 0-cellHeight
|
|
709
733
|
result[y] *= cellHeight;
|
|
710
|
-
//
|
|
711
|
-
//
|
|
734
|
+
// Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
|
|
735
|
+
// clamp to the cell bounds.
|
|
712
736
|
if (doClamp && result[y] !== 0) {
|
|
713
|
-
|
|
737
|
+
const rounded = Math.round(result[y] + 0.5) - 0.5;
|
|
738
|
+
result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded;
|
|
714
739
|
}
|
|
715
740
|
// Apply the cell's offset (ie. x*cellHeight)
|
|
716
741
|
result[y] += yOffset;
|
|
@@ -7,7 +7,7 @@ import { ReadonlyColorSet } from 'browser/Types';
|
|
|
7
7
|
import { acquireTextureAtlas } from '../CharAtlasCache';
|
|
8
8
|
import { IRenderDimensions } from 'browser/renderer/shared/Types';
|
|
9
9
|
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
|
|
10
|
-
import { Disposable, toDisposable } from '
|
|
10
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
11
11
|
import { CellData } from 'common/buffer/CellData';
|
|
12
12
|
import { IOptionsService } from 'common/services/Services';
|
|
13
13
|
import { Terminal } from '@xterm/xterm';
|
package/typings/addon-webgl.d.ts
CHANGED
|
@@ -23,12 +23,12 @@ declare module '@xterm/addon-webgl' {
|
|
|
23
23
|
public readonly onChangeTextureAtlas: IEvent<HTMLCanvasElement>;
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* An event that is fired when
|
|
26
|
+
* An event that is fired when a new page is added to the texture atlas.
|
|
27
27
|
*/
|
|
28
28
|
public readonly onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* An event that is fired when
|
|
31
|
+
* An event that is fired when a page is removed from the texture atlas.
|
|
32
32
|
*/
|
|
33
33
|
public readonly onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
|
|
34
34
|
|
|
@@ -61,6 +61,9 @@ declare module '@xterm/addon-webgl' {
|
|
|
61
61
|
* - Braille Patterns (U+2800-U+28FF)
|
|
62
62
|
* - Powerline Symbols (U+E0A0-U+E0D4, Private Use Area with widespread
|
|
63
63
|
* adoption)
|
|
64
|
+
* - Progress Indicators (U+EE00-U+EE0B, Private Use Area initially added in
|
|
65
|
+
* [Fira Code](https://github.com/tonsky/FiraCode) and later
|
|
66
|
+
* [Nerd Fonts](https://github.com/ryanoasis/nerd-fonts/pull/1733)).
|
|
64
67
|
* - Git Branch Symbols (U+F5D0-U+F60D, Private Use Area initially adopted
|
|
65
68
|
* in [Kitty in 2024](https://github.com/kovidgoyal/kitty/pull/7681) by
|
|
66
69
|
* author of [vim-flog](https://github.com/rbong/vim-flog))
|