@xterm/addon-webgl 0.20.0-beta.29 → 0.20.0-beta.291
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 +45 -20
- package/src/renderLayer/BaseRenderLayer.ts +1 -1
- package/typings/addon-webgl.d.ts +5 -2
package/src/TextureAtlas.ts
CHANGED
|
@@ -14,8 +14,8 @@ import { IdleTaskQueue } from 'common/TaskQueue';
|
|
|
14
14
|
import { IColor } from 'common/Types';
|
|
15
15
|
import { AttributeData } from 'common/buffer/AttributeData';
|
|
16
16
|
import { Attributes, DEFAULT_COLOR, DEFAULT_EXT, UnderlineStyle } from 'common/buffer/Constants';
|
|
17
|
-
import { IUnicodeService } from 'common/services/Services';
|
|
18
|
-
import { Emitter } from '
|
|
17
|
+
import { ILogService, IUnicodeService } from 'common/services/Services';
|
|
18
|
+
import { Emitter } from 'common/Event';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* A shared object which is used to draw nothing for a particular cell.
|
|
@@ -69,7 +69,7 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
69
69
|
private _overflowSizePage: AtlasPage | undefined;
|
|
70
70
|
|
|
71
71
|
private _tmpCanvas: HTMLCanvasElement;
|
|
72
|
-
// A temporary context that glyphs are drawn to before being
|
|
72
|
+
// A temporary context that glyphs are drawn to before being transferred to the atlas.
|
|
73
73
|
private _tmpCtx: CanvasRenderingContext2D;
|
|
74
74
|
|
|
75
75
|
private _workBoundingBox: IBoundingBox = { top: 0, left: 0, bottom: 0, right: 0 };
|
|
@@ -88,7 +88,8 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
88
88
|
constructor(
|
|
89
89
|
private readonly _document: Document,
|
|
90
90
|
private readonly _config: ICharAtlasConfig,
|
|
91
|
-
private readonly _unicodeService: IUnicodeService
|
|
91
|
+
private readonly _unicodeService: IUnicodeService,
|
|
92
|
+
private readonly _logService: ILogService
|
|
92
93
|
) {
|
|
93
94
|
this._createNewPage();
|
|
94
95
|
this._tmpCanvas = createCanvas(
|
|
@@ -108,6 +109,7 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
108
109
|
page.canvas.remove();
|
|
109
110
|
}
|
|
110
111
|
this._onAddTextureAtlasCanvas.dispose();
|
|
112
|
+
this._onRemoveTextureAtlasCanvas.dispose();
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
public warmUp(): void {
|
|
@@ -119,7 +121,7 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
119
121
|
|
|
120
122
|
private _doWarmUp(): void {
|
|
121
123
|
// Pre-fill with ASCII 33-126, this is not urgent and done in idle callbacks
|
|
122
|
-
const queue = new IdleTaskQueue();
|
|
124
|
+
const queue = new IdleTaskQueue(this._logService);
|
|
123
125
|
for (let i = 33; i < 126; i++) {
|
|
124
126
|
queue.enqueue(() => {
|
|
125
127
|
if (!this._cacheMap.get(i, DEFAULT_COLOR, DEFAULT_COLOR, DEFAULT_EXT)) {
|
|
@@ -130,10 +132,8 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
130
132
|
}
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
private
|
|
134
|
-
public
|
|
135
|
-
return this._requestClearModel;
|
|
136
|
-
}
|
|
135
|
+
private _pageLayoutVersion = 0;
|
|
136
|
+
public get pageLayoutVersion(): number { return this._pageLayoutVersion; }
|
|
137
137
|
|
|
138
138
|
public clearTexture(): void {
|
|
139
139
|
if (this._pages[0].currentRow.x === 0 && this._pages[0].currentRow.y === 0) {
|
|
@@ -145,10 +145,15 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
145
145
|
this._cacheMap.clear();
|
|
146
146
|
this._cacheMapCombined.clear();
|
|
147
147
|
this._didWarmUp = false;
|
|
148
|
+
|
|
149
|
+
// Invalidate renderer models so all texture pages are refreshed. The atlas may be shared, in
|
|
150
|
+
// which case the clearing renderer has cleared only its own model and every other owner still
|
|
151
|
+
// holds texture coords into the rows just wiped.
|
|
152
|
+
this._pageLayoutVersion++;
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
private _createNewPage(): AtlasPage {
|
|
151
|
-
// Try merge the set of the 4 most used pages of the largest size. This is
|
|
156
|
+
// Try merge the set of the 4 most used pages of the largest size. This is deferred to a
|
|
152
157
|
// microtask to ensure it does not interrupt textures that will be rendered in the current
|
|
153
158
|
// animation frame which would result in blank rendered areas. This is actually not that
|
|
154
159
|
// expensive relative to drawing the glyphs, so there is no need to wait for an idle callback.
|
|
@@ -176,12 +181,25 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
176
181
|
|
|
177
182
|
// Gather details of the merge
|
|
178
183
|
const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4);
|
|
179
|
-
|
|
184
|
+
|
|
185
|
+
// Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot
|
|
186
|
+
// effectively reduce page count and merging would cause issues.
|
|
187
|
+
if (mergingPages.length < 4 || mergingPages.some(p => p.canvas.width !== mergingPages[0].canvas.width)) {
|
|
188
|
+
// Evict instead of adding a page beyond the renderer's texture capacity.
|
|
189
|
+
this._evictAllPages();
|
|
190
|
+
const newPage = new AtlasPage(this._document, this._textureSize);
|
|
191
|
+
this._pages.push(newPage);
|
|
192
|
+
this._activePages.push(newPage);
|
|
193
|
+
this._onAddTextureAtlasCanvas.fire(newPage.canvas);
|
|
194
|
+
return newPage;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a - b);
|
|
180
198
|
const mergedPageIndex = this.pages.length - mergingPages.length;
|
|
181
199
|
|
|
182
200
|
// Merge into the new page
|
|
183
201
|
const mergedPage = this._mergePages(mergingPages, mergedPageIndex);
|
|
184
|
-
mergedPage.version
|
|
202
|
+
mergedPage.version = ++AtlasPage.nextVersion;
|
|
185
203
|
|
|
186
204
|
// Delete the pages, shifting glyph texture pages as needed
|
|
187
205
|
for (let i = sortedMergingPagesIndexes.length - 1; i >= 0; i--) {
|
|
@@ -191,8 +209,8 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
191
209
|
// Add the new merged page to the end
|
|
192
210
|
this.pages.push(mergedPage);
|
|
193
211
|
|
|
194
|
-
//
|
|
195
|
-
this.
|
|
212
|
+
// Invalidate renderer models so all texture pages are refreshed.
|
|
213
|
+
this._pageLayoutVersion++;
|
|
196
214
|
this._onAddTextureAtlasCanvas.fire(mergedPage.canvas);
|
|
197
215
|
}
|
|
198
216
|
|
|
@@ -239,8 +257,25 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
239
257
|
for (const g of adjustingPage.glyphs) {
|
|
240
258
|
g.texturePage--;
|
|
241
259
|
}
|
|
242
|
-
adjustingPage.version
|
|
260
|
+
adjustingPage.version = ++AtlasPage.nextVersion;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private _evictAllPages(): void {
|
|
265
|
+
const startTime = performance.now();
|
|
266
|
+
const pageCount = this._pages.length;
|
|
267
|
+
for (const page of this._pages) {
|
|
268
|
+
this._onRemoveTextureAtlasCanvas.fire(page.canvas);
|
|
269
|
+
page.canvas.remove();
|
|
243
270
|
}
|
|
271
|
+
this._pages.length = 0;
|
|
272
|
+
this._activePages.length = 0;
|
|
273
|
+
this._overflowSizePage = undefined;
|
|
274
|
+
this._cacheMap.clear();
|
|
275
|
+
this._cacheMapCombined.clear();
|
|
276
|
+
this._didWarmUp = false;
|
|
277
|
+
this._pageLayoutVersion++;
|
|
278
|
+
this._logService.debug(`Evicted ${pageCount} WebGL atlas pages in ${(performance.now() - startTime).toFixed(2)}ms`);
|
|
244
279
|
}
|
|
245
280
|
|
|
246
281
|
public getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph {
|
|
@@ -399,7 +434,7 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
399
434
|
const cache = this._getContrastCache(dim);
|
|
400
435
|
const adjustedColor = cache.getColor(bg, fg);
|
|
401
436
|
if (adjustedColor !== undefined) {
|
|
402
|
-
return adjustedColor
|
|
437
|
+
return adjustedColor ?? undefined;
|
|
403
438
|
}
|
|
404
439
|
|
|
405
440
|
const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse);
|
|
@@ -514,7 +549,8 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
514
549
|
// Draw custom characters if applicable
|
|
515
550
|
let customGlyph = false;
|
|
516
551
|
if (this._config.customGlyphs !== false) {
|
|
517
|
-
|
|
552
|
+
const variantOffset = this._workAttributeData.getUnderlineVariantOffset();
|
|
553
|
+
customGlyph = tryDrawCustomGlyph(this._tmpCtx, chars, padding, padding, this._config.deviceCellWidth, this._config.deviceCellHeight, this._config.deviceCharWidth, this._config.deviceCharHeight, this._config.fontSize, this._config.devicePixelRatio, this._logService, backgroundColor.css, variantOffset);
|
|
518
554
|
}
|
|
519
555
|
|
|
520
556
|
// Whether to clear pixels based on a threshold difference between the glyph color and the
|
|
@@ -551,61 +587,67 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
551
587
|
}
|
|
552
588
|
this._tmpCtx.strokeStyle = this._getColorFromAnsiIndex(fg).css;
|
|
553
589
|
}
|
|
590
|
+
this._tmpCtx.fillStyle = this._tmpCtx.strokeStyle;
|
|
554
591
|
|
|
555
592
|
// Underline style/stroke
|
|
556
593
|
this._tmpCtx.beginPath();
|
|
557
594
|
const xLeft = padding;
|
|
558
|
-
const
|
|
559
|
-
const
|
|
560
|
-
const yBot = yTop + lineWidth * 2;
|
|
595
|
+
const yTopDefault = Math.ceil(padding + this._config.deviceCharHeight) - yOffset - (restrictToCellHeight ? lineWidth * 2 : 0);
|
|
596
|
+
const yBotDefault = yTopDefault + lineWidth * 2;
|
|
561
597
|
let nextOffset = this._workAttributeData.getUnderlineVariantOffset();
|
|
598
|
+
let yTop = 0;
|
|
599
|
+
let yBot = 0;
|
|
562
600
|
|
|
563
601
|
for (let i = 0; i < chWidth; i++) {
|
|
602
|
+
let wasFilled = false;
|
|
564
603
|
this._tmpCtx.save();
|
|
604
|
+
yTop = yTopDefault;
|
|
605
|
+
yBot = yBotDefault;
|
|
565
606
|
const xChLeft = xLeft + i * this._config.deviceCellWidth;
|
|
566
607
|
const xChRight = xLeft + (i + 1) * this._config.deviceCellWidth;
|
|
567
|
-
const xChMid = xChLeft + this._config.deviceCellWidth / 2;
|
|
568
608
|
switch (this._workAttributeData.extended.underlineStyle) {
|
|
569
609
|
case UnderlineStyle.DOUBLE:
|
|
570
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
571
|
-
this._tmpCtx.lineTo(xChRight,
|
|
572
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
573
|
-
this._tmpCtx.lineTo(xChRight,
|
|
610
|
+
this._tmpCtx.moveTo(xChLeft, yTopDefault);
|
|
611
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
612
|
+
this._tmpCtx.moveTo(xChLeft, yBotDefault);
|
|
613
|
+
this._tmpCtx.lineTo(xChRight, yBotDefault);
|
|
574
614
|
break;
|
|
575
615
|
case UnderlineStyle.CURLY:
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
const yCurlyTop = lineWidth <= 1 ? yTop : Math.ceil(padding + this._config.deviceCharHeight + lineWidth / 2) - yOffset;
|
|
580
|
-
// Clip the left and right edges of the underline such that it can be drawn just outside
|
|
581
|
-
// the edge of the cell to ensure a continuous stroke when there are multiple underlined
|
|
582
|
-
// glyphs adjacent to one another.
|
|
616
|
+
yTop = this._config.deviceCharHeight + 1;
|
|
617
|
+
yBot = yTop + 3 * this._config.devicePixelRatio;
|
|
618
|
+
|
|
583
619
|
const clipRegion = new Path2D();
|
|
584
620
|
clipRegion.rect(xChLeft, yTop, this._config.deviceCellWidth, yBot - yTop);
|
|
585
621
|
this._tmpCtx.clip(clipRegion);
|
|
586
|
-
|
|
587
|
-
//
|
|
588
|
-
|
|
589
|
-
this.
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
622
|
+
|
|
623
|
+
// Draw a zigzag pattern, this is derived from the SVG used in monaco for the same
|
|
624
|
+
// style. The viewbox is 6x3 so scale it using that.
|
|
625
|
+
const cellW = this._config.deviceCellWidth;
|
|
626
|
+
const curlyH = (yBot - yTop);
|
|
627
|
+
const scaleX = cellW / 6;
|
|
628
|
+
const scaleY = curlyH / 3;
|
|
629
|
+
|
|
630
|
+
const polygons: number[][] = [
|
|
631
|
+
[0, 2, 1, 3, 2.4, 3, 0, 0.6],
|
|
632
|
+
[5.5, 0, 2.5, 3, 1.1, 3, 4.1, 0],
|
|
633
|
+
[4, 0, 6, 2, 6, 0.6, 5.4, 0],
|
|
634
|
+
];
|
|
635
|
+
|
|
636
|
+
for (const polygon of polygons) {
|
|
637
|
+
this._tmpCtx.beginPath();
|
|
638
|
+
for (let i = 0; i < polygon.length; i += 2) {
|
|
639
|
+
const x = xChLeft + polygon[i] * scaleX;
|
|
640
|
+
const y = yBot - polygon[i + 1] * scaleY;
|
|
641
|
+
if (i === 0) {
|
|
642
|
+
this._tmpCtx.moveTo(x, y);
|
|
643
|
+
} else {
|
|
644
|
+
this._tmpCtx.lineTo(x, y);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
this._tmpCtx.closePath();
|
|
648
|
+
this._tmpCtx.fill();
|
|
649
|
+
}
|
|
650
|
+
wasFilled = true;
|
|
609
651
|
break;
|
|
610
652
|
case UnderlineStyle.DOTTED:
|
|
611
653
|
const offsetWidth = nextOffset === 0 ? 0 :
|
|
@@ -614,14 +656,14 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
614
656
|
const isLineStart = nextOffset >= lineWidth ? false : true;
|
|
615
657
|
if (isLineStart === false || offsetWidth === 0) {
|
|
616
658
|
this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
|
|
617
|
-
this._tmpCtx.moveTo(xChLeft + offsetWidth,
|
|
618
|
-
this._tmpCtx.lineTo(xChRight,
|
|
659
|
+
this._tmpCtx.moveTo(xChLeft + offsetWidth, yTopDefault);
|
|
660
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
619
661
|
} else {
|
|
620
662
|
this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
|
|
621
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
622
|
-
this._tmpCtx.lineTo(xChLeft + offsetWidth,
|
|
623
|
-
this._tmpCtx.moveTo(xChLeft + offsetWidth + lineWidth,
|
|
624
|
-
this._tmpCtx.lineTo(xChRight,
|
|
663
|
+
this._tmpCtx.moveTo(xChLeft, yTopDefault);
|
|
664
|
+
this._tmpCtx.lineTo(xChLeft + offsetWidth, yTopDefault);
|
|
665
|
+
this._tmpCtx.moveTo(xChLeft + offsetWidth + lineWidth, yTopDefault);
|
|
666
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
625
667
|
}
|
|
626
668
|
nextOffset = computeNextVariantOffset(xChRight - xChLeft, lineWidth, nextOffset);
|
|
627
669
|
break;
|
|
@@ -634,16 +676,18 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
634
676
|
const gap = Math.floor(gapRatio * xChWidth);
|
|
635
677
|
const end = xChWidth - line - gap;
|
|
636
678
|
this._tmpCtx.setLineDash([line, gap, end]);
|
|
637
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
638
|
-
this._tmpCtx.lineTo(xChRight,
|
|
679
|
+
this._tmpCtx.moveTo(xChLeft, yTopDefault);
|
|
680
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
639
681
|
break;
|
|
640
682
|
case UnderlineStyle.SINGLE:
|
|
641
683
|
default:
|
|
642
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
643
|
-
this._tmpCtx.lineTo(xChRight,
|
|
684
|
+
this._tmpCtx.moveTo(xChLeft, yTopDefault);
|
|
685
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
644
686
|
break;
|
|
645
687
|
}
|
|
646
|
-
|
|
688
|
+
if (!wasFilled) {
|
|
689
|
+
this._tmpCtx.stroke();
|
|
690
|
+
}
|
|
647
691
|
this._tmpCtx.restore();
|
|
648
692
|
}
|
|
649
693
|
this._tmpCtx.restore();
|
|
@@ -791,11 +835,15 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
791
835
|
// Create a new page for oversized glyphs as they come up
|
|
792
836
|
if (rasterizedGlyph.size.x > this._textureSize) {
|
|
793
837
|
if (!this._overflowSizePage) {
|
|
838
|
+
// Make room for the oversized page without exceeding texture capacity.
|
|
839
|
+
if (TextureAtlas.maxAtlasPages && this._pages.length >= TextureAtlas.maxAtlasPages) {
|
|
840
|
+
this._evictAllPages();
|
|
841
|
+
}
|
|
794
842
|
this._overflowSizePage = new AtlasPage(this._document, this._config.deviceMaxTextureSize);
|
|
795
843
|
this.pages.push(this._overflowSizePage);
|
|
796
844
|
|
|
797
|
-
//
|
|
798
|
-
this.
|
|
845
|
+
// Invalidate renderer models so all texture pages are refreshed.
|
|
846
|
+
this._pageLayoutVersion++;
|
|
799
847
|
this._onAddTextureAtlasCanvas.fire(this._overflowSizePage.canvas);
|
|
800
848
|
}
|
|
801
849
|
activePage = this._overflowSizePage;
|
|
@@ -916,7 +964,7 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
916
964
|
rasterizedGlyph.size.y
|
|
917
965
|
);
|
|
918
966
|
activePage.addGlyph(rasterizedGlyph);
|
|
919
|
-
activePage.version
|
|
967
|
+
activePage.version = ++AtlasPage.nextVersion;
|
|
920
968
|
|
|
921
969
|
return rasterizedGlyph;
|
|
922
970
|
}
|
|
@@ -1026,9 +1074,13 @@ class AtlasPage {
|
|
|
1026
1074
|
}
|
|
1027
1075
|
|
|
1028
1076
|
/**
|
|
1029
|
-
*
|
|
1077
|
+
* Monotonically increasing across all atlas pages globally. Used to detect when the texture
|
|
1078
|
+
* unit at a given index needs to be re-uploaded — both for content changes within the same
|
|
1079
|
+
* page and for a page object swap at the same index (which happens after a page merge,
|
|
1080
|
+
* where a per-page counter could coincide with the previously-bound page's value).
|
|
1030
1081
|
*/
|
|
1031
|
-
public
|
|
1082
|
+
public static nextVersion: number = 0;
|
|
1083
|
+
public version = ++AtlasPage.nextVersion;
|
|
1032
1084
|
|
|
1033
1085
|
// Texture atlas current positioning data. The texture packing strategy used is to fill from
|
|
1034
1086
|
// left-to-right and top-to-bottom. When the glyph being written is less than half of the current
|
|
@@ -1052,17 +1104,33 @@ class AtlasPage {
|
|
|
1052
1104
|
size: number,
|
|
1053
1105
|
sourcePages?: AtlasPage[]
|
|
1054
1106
|
) {
|
|
1055
|
-
if (sourcePages) {
|
|
1056
|
-
for (const p of sourcePages) {
|
|
1057
|
-
this._glyphs.push(...p.glyphs);
|
|
1058
|
-
this._usedPixels += p._usedPixels;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
1107
|
this.canvas = createCanvas(document, size, size);
|
|
1062
1108
|
// The canvas needs alpha because we use clearColor to convert the background color to alpha.
|
|
1063
1109
|
// It might also contain some characters with transparent backgrounds if allowTransparency is
|
|
1064
1110
|
// set.
|
|
1065
1111
|
this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true }));
|
|
1112
|
+
if (sourcePages) {
|
|
1113
|
+
if (sourcePages.length === 4) {
|
|
1114
|
+
// optimized for quadmerge
|
|
1115
|
+
this._glyphs = this._glyphs.concat(
|
|
1116
|
+
sourcePages[0].glyphs,
|
|
1117
|
+
sourcePages[1].glyphs,
|
|
1118
|
+
sourcePages[2].glyphs,
|
|
1119
|
+
sourcePages[3].glyphs
|
|
1120
|
+
);
|
|
1121
|
+
this._usedPixels = sourcePages[0]._usedPixels +
|
|
1122
|
+
sourcePages[1]._usedPixels +
|
|
1123
|
+
sourcePages[2]._usedPixels +
|
|
1124
|
+
sourcePages[3]._usedPixels;
|
|
1125
|
+
} else {
|
|
1126
|
+
// fallback for non quadmerges (should never be used)
|
|
1127
|
+
for (let i = 0; i < sourcePages.length; ++i) {
|
|
1128
|
+
this._glyphs = this._glyphs.concat(sourcePages[i].glyphs);
|
|
1129
|
+
this._usedPixels += sourcePages[i]._usedPixels;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1066
1134
|
}
|
|
1067
1135
|
|
|
1068
1136
|
public clear(): void {
|
|
@@ -1071,7 +1139,7 @@ class AtlasPage {
|
|
|
1071
1139
|
this.currentRow.y = 0;
|
|
1072
1140
|
this.currentRow.height = 0;
|
|
1073
1141
|
this.fixedRows.length = 0;
|
|
1074
|
-
this.version
|
|
1142
|
+
this.version = ++AtlasPage.nextVersion;
|
|
1075
1143
|
}
|
|
1076
1144
|
}
|
|
1077
1145
|
|
|
@@ -1097,7 +1165,7 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol
|
|
|
1097
1165
|
// were covered (fg=#8ae234, bg=#c4a000).
|
|
1098
1166
|
const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12);
|
|
1099
1167
|
|
|
1100
|
-
// Set alpha channel of
|
|
1168
|
+
// Set alpha channel of relevant pixels to 0
|
|
1101
1169
|
let isEmpty = true;
|
|
1102
1170
|
for (let offset = 0; offset < imageData.data.length; offset += 4) {
|
|
1103
1171
|
// Check exact match
|
package/src/Types.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { FontWeight } from '@xterm/xterm';
|
|
|
7
7
|
import { IColorSet } from 'browser/Types';
|
|
8
8
|
import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
|
|
9
9
|
import { CursorInactiveStyle, CursorStyle, type IDisposable } from 'common/Types';
|
|
10
|
-
import type {
|
|
10
|
+
import type { IEvent } from 'common/Event';
|
|
11
11
|
|
|
12
12
|
export interface IRenderModel {
|
|
13
13
|
cells: Uint32Array;
|
|
@@ -28,6 +28,7 @@ export interface ICursorRenderModel {
|
|
|
28
28
|
export interface IWebGL2RenderingContext extends WebGLRenderingContext {
|
|
29
29
|
vertexAttribDivisor(index: number, divisor: number): void;
|
|
30
30
|
createVertexArray(): IWebGLVertexArrayObject;
|
|
31
|
+
deleteVertexArray(vao: IWebGLVertexArrayObject): void;
|
|
31
32
|
bindVertexArray(vao: IWebGLVertexArrayObject): void;
|
|
32
33
|
drawElementsInstanced(mode: number, count: number, type: number, offset: number, instanceCount: number): void;
|
|
33
34
|
}
|
|
@@ -58,8 +59,8 @@ export interface ICharAtlasConfig {
|
|
|
58
59
|
export interface ITextureAtlas extends IDisposable {
|
|
59
60
|
readonly pages: { canvas: HTMLCanvasElement, version: number }[];
|
|
60
61
|
|
|
61
|
-
onAddTextureAtlasCanvas:
|
|
62
|
-
onRemoveTextureAtlasCanvas:
|
|
62
|
+
onAddTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
|
|
63
|
+
onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
* Warm up the texture atlas, adding common glyphs to avoid slowing early frame.
|
|
@@ -67,10 +68,12 @@ export interface ITextureAtlas extends IDisposable {
|
|
|
67
68
|
warmUp(): void;
|
|
68
69
|
|
|
69
70
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
71
|
+
* Incremented whenever cached glyph texture page mappings may be stale, such as after atlas page
|
|
72
|
+
* merges or overflow page creation. Renderers compare this against their own last-seen value and
|
|
73
|
+
* rebuild their model when it changes; a shared atlas can have many renderers, so this must not
|
|
74
|
+
* be a consume-once flag.
|
|
72
75
|
*/
|
|
73
|
-
|
|
76
|
+
readonly pageLayoutVersion: number;
|
|
74
77
|
|
|
75
78
|
/**
|
|
76
79
|
* Clear all glyphs from the texture atlas.
|
package/src/WebglAddon.ts
CHANGED
|
@@ -7,15 +7,14 @@ import type { ITerminalAddon, Terminal } from '@xterm/xterm';
|
|
|
7
7
|
import type { IWebglAddonOptions, WebglAddon as IWebglApi } from '@xterm/addon-webgl';
|
|
8
8
|
import { ICharacterJoinerService, ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
|
|
9
9
|
import { ITerminal } from 'browser/Types';
|
|
10
|
-
import { Disposable, toDisposable } from '
|
|
10
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
11
11
|
import { getSafariVersion, isSafari } from 'common/Platform';
|
|
12
12
|
import { ICoreService, IDecorationService, ILogService, IOptionsService } from 'common/services/Services';
|
|
13
13
|
import { IWebGL2RenderingContext } from './Types';
|
|
14
14
|
import { WebglRenderer } from './WebglRenderer';
|
|
15
|
-
import {
|
|
16
|
-
import { Emitter, Event } from 'vs/base/common/event';
|
|
15
|
+
import { Emitter, EventUtils } from 'common/Event';
|
|
17
16
|
|
|
18
|
-
export class WebglAddon extends Disposable implements ITerminalAddon
|
|
17
|
+
export class WebglAddon extends Disposable implements ITerminalAddon, IWebglApi {
|
|
19
18
|
private _terminal?: Terminal;
|
|
20
19
|
private _renderer?: WebglRenderer;
|
|
21
20
|
|
|
@@ -41,7 +40,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi
|
|
|
41
40
|
};
|
|
42
41
|
const gl = document.createElement('canvas').getContext('webgl2', contextAttributes) as IWebGL2RenderingContext;
|
|
43
42
|
if (!gl) {
|
|
44
|
-
throw new Error('
|
|
43
|
+
throw new Error('WebGL2 is only supported on Safari 16 and above');
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
super();
|
|
@@ -69,10 +68,6 @@ export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi
|
|
|
69
68
|
const logService: ILogService = unsafeCore._logService;
|
|
70
69
|
const themeService: IThemeService = unsafeCore._themeService;
|
|
71
70
|
|
|
72
|
-
// Set trace logger just in case it hasn't been yet which could happen when the addon is
|
|
73
|
-
// bundled separately to the core module
|
|
74
|
-
setTraceLogger(logService);
|
|
75
|
-
|
|
76
71
|
this._renderer = this._register(new WebglRenderer(
|
|
77
72
|
terminal,
|
|
78
73
|
characterJoinerService,
|
|
@@ -80,15 +75,16 @@ export class WebglAddon extends Disposable implements ITerminalAddon , IWebglApi
|
|
|
80
75
|
coreBrowserService,
|
|
81
76
|
coreService,
|
|
82
77
|
decorationService,
|
|
78
|
+
logService,
|
|
83
79
|
optionsService,
|
|
84
80
|
themeService,
|
|
85
81
|
this._customGlyphs,
|
|
86
82
|
this._preserveDrawingBuffer
|
|
87
83
|
));
|
|
88
|
-
this._register(
|
|
89
|
-
this._register(
|
|
90
|
-
this._register(
|
|
91
|
-
this._register(
|
|
84
|
+
this._register(EventUtils.forward(this._renderer.onContextLoss, this._onContextLoss));
|
|
85
|
+
this._register(EventUtils.forward(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas));
|
|
86
|
+
this._register(EventUtils.forward(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas));
|
|
87
|
+
this._register(EventUtils.forward(this._renderer.onRemoveTextureAtlasCanvas, this._onRemoveTextureAtlasCanvas));
|
|
92
88
|
renderService.setRenderer(this._renderer);
|
|
93
89
|
|
|
94
90
|
this._register(toDisposable(() => {
|