@xterm/addon-webgl 0.20.0-beta.30 → 0.20.0-beta.300
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 +60 -29
- package/src/RectangleRenderer.ts +15 -11
- package/src/RenderModel.ts +7 -5
- package/src/TextureAtlas.ts +147 -79
- package/src/Types.ts +9 -6
- 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 +71 -23
- package/src/renderLayer/BaseRenderLayer.ts +1 -1
- package/typings/addon-webgl.d.ts +5 -2
|
@@ -4,9 +4,33 @@
|
|
|
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
|
|
|
11
|
+
type PatternCanvas = HTMLCanvasElement | OffscreenCanvas;
|
|
12
|
+
type PatternCanvasFactory = (width: number, height: number) => PatternCanvas;
|
|
13
|
+
|
|
14
|
+
const createOffscreenPatternCanvas: PatternCanvasFactory | undefined = typeof OffscreenCanvas === 'undefined'
|
|
15
|
+
? undefined
|
|
16
|
+
: (width, height) => new OffscreenCanvas(width, height);
|
|
17
|
+
|
|
18
|
+
const createDomPatternCanvas: PatternCanvasFactory = (width, height) => {
|
|
19
|
+
const canvas = document.createElement('canvas');
|
|
20
|
+
canvas.width = width;
|
|
21
|
+
canvas.height = height;
|
|
22
|
+
return canvas;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function createPatternCanvas(
|
|
26
|
+
width: number,
|
|
27
|
+
height: number,
|
|
28
|
+
offscreenCanvasFactory: PatternCanvasFactory | undefined = createOffscreenPatternCanvas,
|
|
29
|
+
domCanvasFactory: PatternCanvasFactory = createDomPatternCanvas
|
|
30
|
+
): PatternCanvas {
|
|
31
|
+
return offscreenCanvasFactory?.(width, height) ?? domCanvasFactory(width, height);
|
|
32
|
+
}
|
|
33
|
+
|
|
10
34
|
/**
|
|
11
35
|
* Try drawing a custom block element or box drawing character, returning whether it was
|
|
12
36
|
* successfully drawn.
|
|
@@ -22,14 +46,16 @@ export function tryDrawCustomGlyph(
|
|
|
22
46
|
deviceCharHeight: number,
|
|
23
47
|
fontSize: number,
|
|
24
48
|
devicePixelRatio: number,
|
|
25
|
-
|
|
49
|
+
logService: ILogService,
|
|
50
|
+
backgroundColor?: string,
|
|
51
|
+
variantOffset: number = 0
|
|
26
52
|
): boolean {
|
|
27
53
|
const unifiedCharDefinition = customGlyphDefinitions[c];
|
|
28
54
|
if (unifiedCharDefinition) {
|
|
29
55
|
// Normalize to array for uniform handling
|
|
30
56
|
const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition];
|
|
31
57
|
for (const part of parts) {
|
|
32
|
-
drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor);
|
|
58
|
+
drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, logService, backgroundColor, variantOffset);
|
|
33
59
|
}
|
|
34
60
|
return true;
|
|
35
61
|
}
|
|
@@ -48,7 +74,9 @@ function drawDefinitionPart(
|
|
|
48
74
|
deviceCharHeight: number,
|
|
49
75
|
fontSize: number,
|
|
50
76
|
devicePixelRatio: number,
|
|
51
|
-
|
|
77
|
+
logService: ILogService,
|
|
78
|
+
backgroundColor?: string,
|
|
79
|
+
variantOffset: number = 0
|
|
52
80
|
): void {
|
|
53
81
|
// Handle scaleType - adjust dimensions and offset when scaling to character area
|
|
54
82
|
let drawWidth = deviceCellWidth;
|
|
@@ -74,10 +102,10 @@ function drawDefinitionPart(
|
|
|
74
102
|
drawBlockVectorChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
75
103
|
break;
|
|
76
104
|
case CustomGlyphDefinitionType.BLOCK_PATTERN:
|
|
77
|
-
drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
105
|
+
drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, variantOffset);
|
|
78
106
|
break;
|
|
79
107
|
case CustomGlyphDefinitionType.PATH_FUNCTION:
|
|
80
|
-
drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
|
|
108
|
+
drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, logService, part.strokeWidth);
|
|
81
109
|
break;
|
|
82
110
|
case CustomGlyphDefinitionType.PATH:
|
|
83
111
|
drawPathDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
|
|
@@ -86,7 +114,7 @@ function drawDefinitionPart(
|
|
|
86
114
|
drawPathNegativeDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, backgroundColor);
|
|
87
115
|
break;
|
|
88
116
|
case CustomGlyphDefinitionType.VECTOR_SHAPE:
|
|
89
|
-
drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio);
|
|
117
|
+
drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio, logService);
|
|
90
118
|
break;
|
|
91
119
|
case CustomGlyphDefinitionType.BRAILLE:
|
|
92
120
|
drawBrailleCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
@@ -216,8 +244,8 @@ function drawPathDefinitionCharacter(
|
|
|
216
244
|
const rx = parseFloat(args[0]) * deviceCellWidth;
|
|
217
245
|
const ry = parseFloat(args[1]) * deviceCellHeight;
|
|
218
246
|
const xAxisRotation = parseFloat(args[2]) * Math.PI / 180;
|
|
219
|
-
const largeArcFlag = parseInt(args[3]);
|
|
220
|
-
const sweepFlag = parseInt(args[4]);
|
|
247
|
+
const largeArcFlag = parseInt(args[3], 10);
|
|
248
|
+
const sweepFlag = parseInt(args[4], 10);
|
|
221
249
|
const x = xOffset + parseFloat(args[5]) * deviceCellWidth;
|
|
222
250
|
const y = yOffset + parseFloat(args[6]) * deviceCellHeight;
|
|
223
251
|
drawSvgArc(ctx, currentX, currentY, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y);
|
|
@@ -437,7 +465,8 @@ function drawPatternChar(
|
|
|
437
465
|
xOffset: number,
|
|
438
466
|
yOffset: number,
|
|
439
467
|
deviceCellWidth: number,
|
|
440
|
-
deviceCellHeight: number
|
|
468
|
+
deviceCellHeight: number,
|
|
469
|
+
variantOffset: number = 0
|
|
441
470
|
): void {
|
|
442
471
|
let patternSet = cachedPatterns.get(charDefinition);
|
|
443
472
|
if (!patternSet) {
|
|
@@ -452,9 +481,9 @@ function drawPatternChar(
|
|
|
452
481
|
if (!pattern) {
|
|
453
482
|
const width = charDefinition[0].length;
|
|
454
483
|
const height = charDefinition.length;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
tmpCanvas
|
|
484
|
+
// The atlas canvas can be adopted into another document, so temporary resources must not use
|
|
485
|
+
// its mutable ownerDocument.
|
|
486
|
+
const tmpCanvas = createPatternCanvas(width, height);
|
|
458
487
|
const tmpCtx = throwIfFalsy(tmpCanvas.getContext('2d'));
|
|
459
488
|
const imageData = new ImageData(width, height);
|
|
460
489
|
|
|
@@ -486,6 +515,15 @@ function drawPatternChar(
|
|
|
486
515
|
pattern = throwIfFalsy(ctx.createPattern(tmpCanvas, null));
|
|
487
516
|
patternSet.set(fillStyle, pattern);
|
|
488
517
|
}
|
|
518
|
+
// Apply pattern offset to ensure seamless tiling across cells when cell dimensions are odd.
|
|
519
|
+
// variantOffset encodes: bit 1 = x pixel shift, bit 0 = y pixel shift.
|
|
520
|
+
const dx = (variantOffset >> 1) & 1;
|
|
521
|
+
const dy = variantOffset & 1;
|
|
522
|
+
if (dx !== 0 || dy !== 0) {
|
|
523
|
+
pattern.setTransform(new DOMMatrix().translateSelf(-dx, -dy));
|
|
524
|
+
} else {
|
|
525
|
+
pattern.setTransform(new DOMMatrix());
|
|
526
|
+
}
|
|
489
527
|
ctx.fillStyle = pattern;
|
|
490
528
|
ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
491
529
|
}
|
|
@@ -498,8 +536,14 @@ function drawPathFunctionCharacter(
|
|
|
498
536
|
deviceCellWidth: number,
|
|
499
537
|
deviceCellHeight: number,
|
|
500
538
|
devicePixelRatio: number,
|
|
539
|
+
logService: ILogService,
|
|
501
540
|
strokeWidth?: number
|
|
502
541
|
): void {
|
|
542
|
+
ctx.save();
|
|
543
|
+
ctx.beginPath();
|
|
544
|
+
ctx.rect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
545
|
+
ctx.clip();
|
|
546
|
+
|
|
503
547
|
ctx.beginPath();
|
|
504
548
|
let actualInstructions: string;
|
|
505
549
|
if (typeof charDefinition === 'function') {
|
|
@@ -519,14 +563,14 @@ function drawPathFunctionCharacter(
|
|
|
519
563
|
}
|
|
520
564
|
const f = svgToCanvasInstructionMap[type];
|
|
521
565
|
if (!f) {
|
|
522
|
-
|
|
566
|
+
logService.error(`Could not find drawing instructions for "${type}"`);
|
|
523
567
|
continue;
|
|
524
568
|
}
|
|
525
569
|
const args: string[] = instruction.substring(1).split(',');
|
|
526
570
|
if (!args[0] || !args[1]) {
|
|
527
571
|
continue;
|
|
528
572
|
}
|
|
529
|
-
f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio), state);
|
|
573
|
+
f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio, 0, 0, false), state);
|
|
530
574
|
state.lastCommand = type;
|
|
531
575
|
}
|
|
532
576
|
if (strokeWidth !== undefined) {
|
|
@@ -537,6 +581,7 @@ function drawPathFunctionCharacter(
|
|
|
537
581
|
ctx.fill();
|
|
538
582
|
}
|
|
539
583
|
ctx.closePath();
|
|
584
|
+
ctx.restore();
|
|
540
585
|
}
|
|
541
586
|
|
|
542
587
|
/**
|
|
@@ -580,7 +625,8 @@ function drawVectorShape(
|
|
|
580
625
|
deviceCellWidth: number,
|
|
581
626
|
deviceCellHeight: number,
|
|
582
627
|
fontSize: number,
|
|
583
|
-
devicePixelRatio: number
|
|
628
|
+
devicePixelRatio: number,
|
|
629
|
+
logService: ILogService
|
|
584
630
|
): void {
|
|
585
631
|
// Clip the cell to make sure drawing doesn't occur beyond bounds
|
|
586
632
|
const clipRegion = new Path2D();
|
|
@@ -601,7 +647,7 @@ function drawVectorShape(
|
|
|
601
647
|
}
|
|
602
648
|
const f = svgToCanvasInstructionMap[type];
|
|
603
649
|
if (!f) {
|
|
604
|
-
|
|
650
|
+
logService.error(`Could not find drawing instructions for "${type}"`);
|
|
605
651
|
continue;
|
|
606
652
|
}
|
|
607
653
|
const args: string[] = instruction.substring(1).split(',');
|
|
@@ -685,7 +731,7 @@ const svgToCanvasInstructionMap: { [index: string]: (ctx: CanvasRenderingContext
|
|
|
685
731
|
}
|
|
686
732
|
};
|
|
687
733
|
|
|
688
|
-
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
|
|
734
|
+
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
735
|
const result = args.map(e => parseFloat(e) || parseInt(e));
|
|
690
736
|
|
|
691
737
|
if (result.length < 2) {
|
|
@@ -695,10 +741,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
|
|
695
741
|
for (let x = 0; x < result.length; x += 2) {
|
|
696
742
|
// Translate from 0-1 to 0-cellWidth
|
|
697
743
|
result[x] *= cellWidth - (leftPadding * devicePixelRatio) - (rightPadding * devicePixelRatio);
|
|
698
|
-
//
|
|
699
|
-
//
|
|
744
|
+
// Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
|
|
745
|
+
// clamp to the cell bounds.
|
|
700
746
|
if (doClamp && result[x] !== 0) {
|
|
701
|
-
|
|
747
|
+
const rounded = Math.round(result[x] + 0.5) - 0.5;
|
|
748
|
+
result[x] = clampToCell ? clamp(rounded, cellWidth, 0) : rounded;
|
|
702
749
|
}
|
|
703
750
|
// Apply the cell's offset (ie. x*cellWidth)
|
|
704
751
|
result[x] += xOffset + (leftPadding * devicePixelRatio);
|
|
@@ -707,10 +754,11 @@ function translateArgs(args: string[], cellWidth: number, cellHeight: number, xO
|
|
|
707
754
|
for (let y = 1; y < result.length; y += 2) {
|
|
708
755
|
// Translate from 0-1 to 0-cellHeight
|
|
709
756
|
result[y] *= cellHeight;
|
|
710
|
-
//
|
|
711
|
-
//
|
|
757
|
+
// Round to the nearest 0.5 to ensure a crisp line at 100% devicePixelRatio, and optionally
|
|
758
|
+
// clamp to the cell bounds.
|
|
712
759
|
if (doClamp && result[y] !== 0) {
|
|
713
|
-
|
|
760
|
+
const rounded = Math.round(result[y] + 0.5) - 0.5;
|
|
761
|
+
result[y] = clampToCell ? clamp(rounded, cellHeight, 0) : rounded;
|
|
714
762
|
}
|
|
715
763
|
// Apply the cell's offset (ie. x*cellHeight)
|
|
716
764
|
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))
|