@xterm/addon-webgl 0.20.0-beta.29 → 0.20.0-beta.290
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 +142 -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) {
|
|
@@ -148,7 +148,7 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
private _createNewPage(): AtlasPage {
|
|
151
|
-
// Try merge the set of the 4 most used pages of the largest size. This is
|
|
151
|
+
// Try merge the set of the 4 most used pages of the largest size. This is deferred to a
|
|
152
152
|
// microtask to ensure it does not interrupt textures that will be rendered in the current
|
|
153
153
|
// animation frame which would result in blank rendered areas. This is actually not that
|
|
154
154
|
// expensive relative to drawing the glyphs, so there is no need to wait for an idle callback.
|
|
@@ -176,12 +176,25 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
176
176
|
|
|
177
177
|
// Gather details of the merge
|
|
178
178
|
const mergingPages = pagesBySize.slice(sameSizeI, sameSizeI + 4);
|
|
179
|
-
|
|
179
|
+
|
|
180
|
+
// Only proceed with merge if we have exactly 4 same-sized pages. If not, we cannot
|
|
181
|
+
// effectively reduce page count and merging would cause issues.
|
|
182
|
+
if (mergingPages.length < 4 || mergingPages.some(p => p.canvas.width !== mergingPages[0].canvas.width)) {
|
|
183
|
+
// Evict instead of adding a page beyond the renderer's texture capacity.
|
|
184
|
+
this._evictAllPages();
|
|
185
|
+
const newPage = new AtlasPage(this._document, this._textureSize);
|
|
186
|
+
this._pages.push(newPage);
|
|
187
|
+
this._activePages.push(newPage);
|
|
188
|
+
this._onAddTextureAtlasCanvas.fire(newPage.canvas);
|
|
189
|
+
return newPage;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const sortedMergingPagesIndexes = mergingPages.map(e => e.glyphs[0].texturePage).sort((a, b) => a - b);
|
|
180
193
|
const mergedPageIndex = this.pages.length - mergingPages.length;
|
|
181
194
|
|
|
182
195
|
// Merge into the new page
|
|
183
196
|
const mergedPage = this._mergePages(mergingPages, mergedPageIndex);
|
|
184
|
-
mergedPage.version
|
|
197
|
+
mergedPage.version = ++AtlasPage.nextVersion;
|
|
185
198
|
|
|
186
199
|
// Delete the pages, shifting glyph texture pages as needed
|
|
187
200
|
for (let i = sortedMergingPagesIndexes.length - 1; i >= 0; i--) {
|
|
@@ -191,8 +204,8 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
191
204
|
// Add the new merged page to the end
|
|
192
205
|
this.pages.push(mergedPage);
|
|
193
206
|
|
|
194
|
-
//
|
|
195
|
-
this.
|
|
207
|
+
// Invalidate renderer models so all texture pages are refreshed.
|
|
208
|
+
this._pageLayoutVersion++;
|
|
196
209
|
this._onAddTextureAtlasCanvas.fire(mergedPage.canvas);
|
|
197
210
|
}
|
|
198
211
|
|
|
@@ -239,10 +252,27 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
239
252
|
for (const g of adjustingPage.glyphs) {
|
|
240
253
|
g.texturePage--;
|
|
241
254
|
}
|
|
242
|
-
adjustingPage.version
|
|
255
|
+
adjustingPage.version = ++AtlasPage.nextVersion;
|
|
243
256
|
}
|
|
244
257
|
}
|
|
245
258
|
|
|
259
|
+
private _evictAllPages(): void {
|
|
260
|
+
const startTime = performance.now();
|
|
261
|
+
const pageCount = this._pages.length;
|
|
262
|
+
for (const page of this._pages) {
|
|
263
|
+
this._onRemoveTextureAtlasCanvas.fire(page.canvas);
|
|
264
|
+
page.canvas.remove();
|
|
265
|
+
}
|
|
266
|
+
this._pages.length = 0;
|
|
267
|
+
this._activePages.length = 0;
|
|
268
|
+
this._overflowSizePage = undefined;
|
|
269
|
+
this._cacheMap.clear();
|
|
270
|
+
this._cacheMapCombined.clear();
|
|
271
|
+
this._didWarmUp = false;
|
|
272
|
+
this._pageLayoutVersion++;
|
|
273
|
+
this._logService.debug(`Evicted ${pageCount} WebGL atlas pages in ${(performance.now() - startTime).toFixed(2)}ms`);
|
|
274
|
+
}
|
|
275
|
+
|
|
246
276
|
public getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph {
|
|
247
277
|
return this._getFromCacheMap(this._cacheMapCombined, chars, bg, fg, ext, restrictToCellHeight, domContainer);
|
|
248
278
|
}
|
|
@@ -399,7 +429,7 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
399
429
|
const cache = this._getContrastCache(dim);
|
|
400
430
|
const adjustedColor = cache.getColor(bg, fg);
|
|
401
431
|
if (adjustedColor !== undefined) {
|
|
402
|
-
return adjustedColor
|
|
432
|
+
return adjustedColor ?? undefined;
|
|
403
433
|
}
|
|
404
434
|
|
|
405
435
|
const bgRgba = this._resolveBackgroundRgba(bgColorMode, bgColor, inverse);
|
|
@@ -514,7 +544,8 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
514
544
|
// Draw custom characters if applicable
|
|
515
545
|
let customGlyph = false;
|
|
516
546
|
if (this._config.customGlyphs !== false) {
|
|
517
|
-
|
|
547
|
+
const variantOffset = this._workAttributeData.getUnderlineVariantOffset();
|
|
548
|
+
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
549
|
}
|
|
519
550
|
|
|
520
551
|
// Whether to clear pixels based on a threshold difference between the glyph color and the
|
|
@@ -551,61 +582,67 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
551
582
|
}
|
|
552
583
|
this._tmpCtx.strokeStyle = this._getColorFromAnsiIndex(fg).css;
|
|
553
584
|
}
|
|
585
|
+
this._tmpCtx.fillStyle = this._tmpCtx.strokeStyle;
|
|
554
586
|
|
|
555
587
|
// Underline style/stroke
|
|
556
588
|
this._tmpCtx.beginPath();
|
|
557
589
|
const xLeft = padding;
|
|
558
|
-
const
|
|
559
|
-
const
|
|
560
|
-
const yBot = yTop + lineWidth * 2;
|
|
590
|
+
const yTopDefault = Math.ceil(padding + this._config.deviceCharHeight) - yOffset - (restrictToCellHeight ? lineWidth * 2 : 0);
|
|
591
|
+
const yBotDefault = yTopDefault + lineWidth * 2;
|
|
561
592
|
let nextOffset = this._workAttributeData.getUnderlineVariantOffset();
|
|
593
|
+
let yTop = 0;
|
|
594
|
+
let yBot = 0;
|
|
562
595
|
|
|
563
596
|
for (let i = 0; i < chWidth; i++) {
|
|
597
|
+
let wasFilled = false;
|
|
564
598
|
this._tmpCtx.save();
|
|
599
|
+
yTop = yTopDefault;
|
|
600
|
+
yBot = yBotDefault;
|
|
565
601
|
const xChLeft = xLeft + i * this._config.deviceCellWidth;
|
|
566
602
|
const xChRight = xLeft + (i + 1) * this._config.deviceCellWidth;
|
|
567
|
-
const xChMid = xChLeft + this._config.deviceCellWidth / 2;
|
|
568
603
|
switch (this._workAttributeData.extended.underlineStyle) {
|
|
569
604
|
case UnderlineStyle.DOUBLE:
|
|
570
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
571
|
-
this._tmpCtx.lineTo(xChRight,
|
|
572
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
573
|
-
this._tmpCtx.lineTo(xChRight,
|
|
605
|
+
this._tmpCtx.moveTo(xChLeft, yTopDefault);
|
|
606
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
607
|
+
this._tmpCtx.moveTo(xChLeft, yBotDefault);
|
|
608
|
+
this._tmpCtx.lineTo(xChRight, yBotDefault);
|
|
574
609
|
break;
|
|
575
610
|
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.
|
|
611
|
+
yTop = this._config.deviceCharHeight + 1;
|
|
612
|
+
yBot = yTop + 3 * this._config.devicePixelRatio;
|
|
613
|
+
|
|
583
614
|
const clipRegion = new Path2D();
|
|
584
615
|
clipRegion.rect(xChLeft, yTop, this._config.deviceCellWidth, yBot - yTop);
|
|
585
616
|
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
|
-
|
|
617
|
+
|
|
618
|
+
// Draw a zigzag pattern, this is derived from the SVG used in monaco for the same
|
|
619
|
+
// style. The viewbox is 6x3 so scale it using that.
|
|
620
|
+
const cellW = this._config.deviceCellWidth;
|
|
621
|
+
const curlyH = (yBot - yTop);
|
|
622
|
+
const scaleX = cellW / 6;
|
|
623
|
+
const scaleY = curlyH / 3;
|
|
624
|
+
|
|
625
|
+
const polygons: number[][] = [
|
|
626
|
+
[0, 2, 1, 3, 2.4, 3, 0, 0.6],
|
|
627
|
+
[5.5, 0, 2.5, 3, 1.1, 3, 4.1, 0],
|
|
628
|
+
[4, 0, 6, 2, 6, 0.6, 5.4, 0],
|
|
629
|
+
];
|
|
630
|
+
|
|
631
|
+
for (const polygon of polygons) {
|
|
632
|
+
this._tmpCtx.beginPath();
|
|
633
|
+
for (let i = 0; i < polygon.length; i += 2) {
|
|
634
|
+
const x = xChLeft + polygon[i] * scaleX;
|
|
635
|
+
const y = yBot - polygon[i + 1] * scaleY;
|
|
636
|
+
if (i === 0) {
|
|
637
|
+
this._tmpCtx.moveTo(x, y);
|
|
638
|
+
} else {
|
|
639
|
+
this._tmpCtx.lineTo(x, y);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
this._tmpCtx.closePath();
|
|
643
|
+
this._tmpCtx.fill();
|
|
644
|
+
}
|
|
645
|
+
wasFilled = true;
|
|
609
646
|
break;
|
|
610
647
|
case UnderlineStyle.DOTTED:
|
|
611
648
|
const offsetWidth = nextOffset === 0 ? 0 :
|
|
@@ -614,14 +651,14 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
614
651
|
const isLineStart = nextOffset >= lineWidth ? false : true;
|
|
615
652
|
if (isLineStart === false || offsetWidth === 0) {
|
|
616
653
|
this._tmpCtx.setLineDash([Math.round(lineWidth), Math.round(lineWidth)]);
|
|
617
|
-
this._tmpCtx.moveTo(xChLeft + offsetWidth,
|
|
618
|
-
this._tmpCtx.lineTo(xChRight,
|
|
654
|
+
this._tmpCtx.moveTo(xChLeft + offsetWidth, yTopDefault);
|
|
655
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
619
656
|
} else {
|
|
620
657
|
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,
|
|
658
|
+
this._tmpCtx.moveTo(xChLeft, yTopDefault);
|
|
659
|
+
this._tmpCtx.lineTo(xChLeft + offsetWidth, yTopDefault);
|
|
660
|
+
this._tmpCtx.moveTo(xChLeft + offsetWidth + lineWidth, yTopDefault);
|
|
661
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
625
662
|
}
|
|
626
663
|
nextOffset = computeNextVariantOffset(xChRight - xChLeft, lineWidth, nextOffset);
|
|
627
664
|
break;
|
|
@@ -634,16 +671,18 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
634
671
|
const gap = Math.floor(gapRatio * xChWidth);
|
|
635
672
|
const end = xChWidth - line - gap;
|
|
636
673
|
this._tmpCtx.setLineDash([line, gap, end]);
|
|
637
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
638
|
-
this._tmpCtx.lineTo(xChRight,
|
|
674
|
+
this._tmpCtx.moveTo(xChLeft, yTopDefault);
|
|
675
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
639
676
|
break;
|
|
640
677
|
case UnderlineStyle.SINGLE:
|
|
641
678
|
default:
|
|
642
|
-
this._tmpCtx.moveTo(xChLeft,
|
|
643
|
-
this._tmpCtx.lineTo(xChRight,
|
|
679
|
+
this._tmpCtx.moveTo(xChLeft, yTopDefault);
|
|
680
|
+
this._tmpCtx.lineTo(xChRight, yTopDefault);
|
|
644
681
|
break;
|
|
645
682
|
}
|
|
646
|
-
|
|
683
|
+
if (!wasFilled) {
|
|
684
|
+
this._tmpCtx.stroke();
|
|
685
|
+
}
|
|
647
686
|
this._tmpCtx.restore();
|
|
648
687
|
}
|
|
649
688
|
this._tmpCtx.restore();
|
|
@@ -791,11 +830,15 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
791
830
|
// Create a new page for oversized glyphs as they come up
|
|
792
831
|
if (rasterizedGlyph.size.x > this._textureSize) {
|
|
793
832
|
if (!this._overflowSizePage) {
|
|
833
|
+
// Make room for the oversized page without exceeding texture capacity.
|
|
834
|
+
if (TextureAtlas.maxAtlasPages && this._pages.length >= TextureAtlas.maxAtlasPages) {
|
|
835
|
+
this._evictAllPages();
|
|
836
|
+
}
|
|
794
837
|
this._overflowSizePage = new AtlasPage(this._document, this._config.deviceMaxTextureSize);
|
|
795
838
|
this.pages.push(this._overflowSizePage);
|
|
796
839
|
|
|
797
|
-
//
|
|
798
|
-
this.
|
|
840
|
+
// Invalidate renderer models so all texture pages are refreshed.
|
|
841
|
+
this._pageLayoutVersion++;
|
|
799
842
|
this._onAddTextureAtlasCanvas.fire(this._overflowSizePage.canvas);
|
|
800
843
|
}
|
|
801
844
|
activePage = this._overflowSizePage;
|
|
@@ -916,7 +959,7 @@ export class TextureAtlas implements ITextureAtlas {
|
|
|
916
959
|
rasterizedGlyph.size.y
|
|
917
960
|
);
|
|
918
961
|
activePage.addGlyph(rasterizedGlyph);
|
|
919
|
-
activePage.version
|
|
962
|
+
activePage.version = ++AtlasPage.nextVersion;
|
|
920
963
|
|
|
921
964
|
return rasterizedGlyph;
|
|
922
965
|
}
|
|
@@ -1026,9 +1069,13 @@ class AtlasPage {
|
|
|
1026
1069
|
}
|
|
1027
1070
|
|
|
1028
1071
|
/**
|
|
1029
|
-
*
|
|
1072
|
+
* Monotonically increasing across all atlas pages globally. Used to detect when the texture
|
|
1073
|
+
* unit at a given index needs to be re-uploaded — both for content changes within the same
|
|
1074
|
+
* page and for a page object swap at the same index (which happens after a page merge,
|
|
1075
|
+
* where a per-page counter could coincide with the previously-bound page's value).
|
|
1030
1076
|
*/
|
|
1031
|
-
public
|
|
1077
|
+
public static nextVersion: number = 0;
|
|
1078
|
+
public version = ++AtlasPage.nextVersion;
|
|
1032
1079
|
|
|
1033
1080
|
// Texture atlas current positioning data. The texture packing strategy used is to fill from
|
|
1034
1081
|
// left-to-right and top-to-bottom. When the glyph being written is less than half of the current
|
|
@@ -1052,17 +1099,33 @@ class AtlasPage {
|
|
|
1052
1099
|
size: number,
|
|
1053
1100
|
sourcePages?: AtlasPage[]
|
|
1054
1101
|
) {
|
|
1055
|
-
if (sourcePages) {
|
|
1056
|
-
for (const p of sourcePages) {
|
|
1057
|
-
this._glyphs.push(...p.glyphs);
|
|
1058
|
-
this._usedPixels += p._usedPixels;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
1102
|
this.canvas = createCanvas(document, size, size);
|
|
1062
1103
|
// The canvas needs alpha because we use clearColor to convert the background color to alpha.
|
|
1063
1104
|
// It might also contain some characters with transparent backgrounds if allowTransparency is
|
|
1064
1105
|
// set.
|
|
1065
1106
|
this.ctx = throwIfFalsy(this.canvas.getContext('2d', { alpha: true }));
|
|
1107
|
+
if (sourcePages) {
|
|
1108
|
+
if (sourcePages.length === 4) {
|
|
1109
|
+
// optimized for quadmerge
|
|
1110
|
+
this._glyphs = this._glyphs.concat(
|
|
1111
|
+
sourcePages[0].glyphs,
|
|
1112
|
+
sourcePages[1].glyphs,
|
|
1113
|
+
sourcePages[2].glyphs,
|
|
1114
|
+
sourcePages[3].glyphs
|
|
1115
|
+
);
|
|
1116
|
+
this._usedPixels = sourcePages[0]._usedPixels +
|
|
1117
|
+
sourcePages[1]._usedPixels +
|
|
1118
|
+
sourcePages[2]._usedPixels +
|
|
1119
|
+
sourcePages[3]._usedPixels;
|
|
1120
|
+
} else {
|
|
1121
|
+
// fallback for non quadmerges (should never be used)
|
|
1122
|
+
for (let i = 0; i < sourcePages.length; ++i) {
|
|
1123
|
+
this._glyphs = this._glyphs.concat(sourcePages[i].glyphs);
|
|
1124
|
+
this._usedPixels += sourcePages[i]._usedPixels;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1066
1129
|
}
|
|
1067
1130
|
|
|
1068
1131
|
public clear(): void {
|
|
@@ -1071,7 +1134,7 @@ class AtlasPage {
|
|
|
1071
1134
|
this.currentRow.y = 0;
|
|
1072
1135
|
this.currentRow.height = 0;
|
|
1073
1136
|
this.fixedRows.length = 0;
|
|
1074
|
-
this.version
|
|
1137
|
+
this.version = ++AtlasPage.nextVersion;
|
|
1075
1138
|
}
|
|
1076
1139
|
}
|
|
1077
1140
|
|
|
@@ -1097,7 +1160,7 @@ function clearColor(imageData: ImageData, bg: IColor, fg: IColor, enableThreshol
|
|
|
1097
1160
|
// were covered (fg=#8ae234, bg=#c4a000).
|
|
1098
1161
|
const threshold = Math.floor((Math.abs(r - fgR) + Math.abs(g - fgG) + Math.abs(b - fgB)) / 12);
|
|
1099
1162
|
|
|
1100
|
-
// Set alpha channel of
|
|
1163
|
+
// Set alpha channel of relevant pixels to 0
|
|
1101
1164
|
let isEmpty = true;
|
|
1102
1165
|
for (let offset = 0; offset < imageData.data.length; offset += 4) {
|
|
1103
1166
|
// 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(() => {
|