@xterm/addon-webgl 0.20.0-beta.9 → 0.20.0-beta.91
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/lib/addon-webgl.js +1 -1
- package/lib/addon-webgl.js.map +1 -1
- package/lib/addon-webgl.mjs +16 -16
- package/lib/addon-webgl.mjs.map +4 -4
- package/package.json +3 -3
- package/src/CharAtlasCache.ts +3 -2
- package/src/CharAtlasUtils.ts +2 -2
- package/src/CursorBlinkStateManager.ts +67 -8
- package/src/TextureAtlas.ts +58 -50
- package/src/WebglAddon.ts +8 -4
- package/src/WebglRenderer.ts +12 -2
- package/src/customGlyphs/CustomGlyphDefinitions.ts +411 -235
- package/src/customGlyphs/CustomGlyphRasterizer.ts +218 -157
- package/src/customGlyphs/Types.ts +39 -15
- package/typings/addon-webgl.d.ts +31 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
|
|
7
7
|
import { customGlyphDefinitions } from './CustomGlyphDefinitions';
|
|
8
|
-
import { CustomGlyphDefinitionType, CustomGlyphVectorType, type
|
|
8
|
+
import { CustomGlyphDefinitionType, CustomGlyphScaleType, CustomGlyphVectorType, type CustomGlyphDefinitionPart, type CustomGlyphPathDrawFunctionDefinition, type CustomGlyphPatternDefinition, type ICustomGlyphSolidOctantBlockVector, type ICustomGlyphVectorShape } from './Types';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Try drawing a custom block element or box drawing character, returning whether it was
|
|
@@ -18,48 +18,86 @@ export function tryDrawCustomGlyph(
|
|
|
18
18
|
yOffset: number,
|
|
19
19
|
deviceCellWidth: number,
|
|
20
20
|
deviceCellHeight: number,
|
|
21
|
+
deviceCharWidth: number,
|
|
22
|
+
deviceCharHeight: number,
|
|
21
23
|
fontSize: number,
|
|
22
24
|
devicePixelRatio: number,
|
|
23
25
|
backgroundColor?: string
|
|
24
26
|
): boolean {
|
|
25
27
|
const unifiedCharDefinition = customGlyphDefinitions[c];
|
|
26
28
|
if (unifiedCharDefinition) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
case CustomGlyphDefinitionType.BLOCK_PATTERN:
|
|
32
|
-
drawPatternChar(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
33
|
-
return true;
|
|
34
|
-
case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION:
|
|
35
|
-
drawBlockPatternWithRegion(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
36
|
-
return true;
|
|
37
|
-
case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR:
|
|
38
|
-
drawBlockPatternWithRegion(ctx, unifiedCharDefinition.data.pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
39
|
-
drawBlockVectorChar(ctx, unifiedCharDefinition.data.vectors, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
40
|
-
return true;
|
|
41
|
-
case CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH:
|
|
42
|
-
drawBlockPatternWithClipPath(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
43
|
-
return true;
|
|
44
|
-
case CustomGlyphDefinitionType.PATH_FUNCTION:
|
|
45
|
-
case CustomGlyphDefinitionType.PATH:
|
|
46
|
-
drawPathDefinitionCharacter(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
47
|
-
return true;
|
|
48
|
-
case CustomGlyphDefinitionType.PATH_NEGATIVE:
|
|
49
|
-
drawPathNegativeDefinitionCharacter(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio, backgroundColor);
|
|
50
|
-
return true;
|
|
51
|
-
case CustomGlyphDefinitionType.VECTOR_SHAPE:
|
|
52
|
-
drawVectorShape(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, fontSize, devicePixelRatio);
|
|
53
|
-
return true;
|
|
54
|
-
case CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT:
|
|
55
|
-
drawPathDefinitionCharacterWithWeight(ctx, unifiedCharDefinition.data, xOffset, yOffset, deviceCellWidth, deviceCellHeight, devicePixelRatio);
|
|
56
|
-
return true;
|
|
29
|
+
// Normalize to array for uniform handling
|
|
30
|
+
const parts = Array.isArray(unifiedCharDefinition) ? unifiedCharDefinition : [unifiedCharDefinition];
|
|
31
|
+
for (const part of parts) {
|
|
32
|
+
drawDefinitionPart(ctx, part, xOffset, yOffset, deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, fontSize, devicePixelRatio, backgroundColor);
|
|
57
33
|
}
|
|
34
|
+
return true;
|
|
58
35
|
}
|
|
59
36
|
|
|
60
37
|
return false;
|
|
61
38
|
}
|
|
62
39
|
|
|
40
|
+
function drawDefinitionPart(
|
|
41
|
+
ctx: CanvasRenderingContext2D,
|
|
42
|
+
part: CustomGlyphDefinitionPart,
|
|
43
|
+
xOffset: number,
|
|
44
|
+
yOffset: number,
|
|
45
|
+
deviceCellWidth: number,
|
|
46
|
+
deviceCellHeight: number,
|
|
47
|
+
deviceCharWidth: number,
|
|
48
|
+
deviceCharHeight: number,
|
|
49
|
+
fontSize: number,
|
|
50
|
+
devicePixelRatio: number,
|
|
51
|
+
backgroundColor?: string
|
|
52
|
+
): void {
|
|
53
|
+
// Handle scaleType - adjust dimensions and offset when scaling to character area
|
|
54
|
+
let drawWidth = deviceCellWidth;
|
|
55
|
+
let drawHeight = deviceCellHeight;
|
|
56
|
+
let drawXOffset = xOffset;
|
|
57
|
+
let drawYOffset = yOffset;
|
|
58
|
+
if (part.scaleType === CustomGlyphScaleType.CHAR) {
|
|
59
|
+
drawWidth = deviceCharWidth;
|
|
60
|
+
drawHeight = deviceCharHeight;
|
|
61
|
+
// Center the character within the cell
|
|
62
|
+
drawXOffset = xOffset + (deviceCellWidth - deviceCharWidth) / 2;
|
|
63
|
+
drawYOffset = yOffset + (deviceCellHeight - deviceCharHeight) / 2;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle clipPath generically for any definition type
|
|
67
|
+
if (part.clipPath) {
|
|
68
|
+
ctx.save();
|
|
69
|
+
applyClipPath(ctx, part.clipPath, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
switch (part.type) {
|
|
73
|
+
case CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR:
|
|
74
|
+
drawBlockVectorChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
75
|
+
break;
|
|
76
|
+
case CustomGlyphDefinitionType.BLOCK_PATTERN:
|
|
77
|
+
drawPatternChar(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
78
|
+
break;
|
|
79
|
+
case CustomGlyphDefinitionType.PATH_FUNCTION:
|
|
80
|
+
drawPathFunctionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
|
|
81
|
+
break;
|
|
82
|
+
case CustomGlyphDefinitionType.PATH:
|
|
83
|
+
drawPathDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, part.strokeWidth);
|
|
84
|
+
break;
|
|
85
|
+
case CustomGlyphDefinitionType.PATH_NEGATIVE:
|
|
86
|
+
drawPathNegativeDefinitionCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, devicePixelRatio, backgroundColor);
|
|
87
|
+
break;
|
|
88
|
+
case CustomGlyphDefinitionType.VECTOR_SHAPE:
|
|
89
|
+
drawVectorShape(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight, fontSize, devicePixelRatio);
|
|
90
|
+
break;
|
|
91
|
+
case CustomGlyphDefinitionType.BRAILLE:
|
|
92
|
+
drawBrailleCharacter(ctx, part.data, drawXOffset, drawYOffset, drawWidth, drawHeight);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (part.clipPath) {
|
|
97
|
+
ctx.restore();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
63
101
|
function drawBlockVectorChar(
|
|
64
102
|
ctx: CanvasRenderingContext2D,
|
|
65
103
|
charDefinition: ICustomGlyphSolidOctantBlockVector[],
|
|
@@ -81,13 +119,61 @@ function drawBlockVectorChar(
|
|
|
81
119
|
}
|
|
82
120
|
}
|
|
83
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Braille dot positions in octant coordinates (x, y for center of each dot area)
|
|
124
|
+
* Columns: left=1-2, right=5-6 (leaving 0 and 7 as margins, 3-4 as gap)
|
|
125
|
+
* Rows: 0-1, 2-3, 4-5, 6-7 for the 4 rows
|
|
126
|
+
*/
|
|
127
|
+
const brailleDotPositions = new Uint8Array([
|
|
128
|
+
1, 0, // dot 1 - bit 0
|
|
129
|
+
1, 2, // dot 2 - bit 1
|
|
130
|
+
1, 4, // dot 3 - bit 2
|
|
131
|
+
5, 0, // dot 4 - bit 3
|
|
132
|
+
5, 2, // dot 5 - bit 4
|
|
133
|
+
5, 4, // dot 6 - bit 5
|
|
134
|
+
1, 6, // dot 7 - bit 6
|
|
135
|
+
5, 6, // dot 8 - bit 7
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Draws a braille pattern
|
|
140
|
+
*/
|
|
141
|
+
function drawBrailleCharacter(
|
|
142
|
+
ctx: CanvasRenderingContext2D,
|
|
143
|
+
pattern: number,
|
|
144
|
+
xOffset: number,
|
|
145
|
+
yOffset: number,
|
|
146
|
+
deviceCellWidth: number,
|
|
147
|
+
deviceCellHeight: number
|
|
148
|
+
): void {
|
|
149
|
+
const xEighth = deviceCellWidth / 8;
|
|
150
|
+
const paddingY = deviceCellHeight * 0.1;
|
|
151
|
+
const usableHeight = deviceCellHeight * 0.8;
|
|
152
|
+
const yEighth = usableHeight / 8;
|
|
153
|
+
const radius = Math.min(xEighth, yEighth);
|
|
154
|
+
|
|
155
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
156
|
+
if (pattern & (1 << bit)) {
|
|
157
|
+
const x = brailleDotPositions[bit * 2];
|
|
158
|
+
const y = brailleDotPositions[bit * 2 + 1];
|
|
159
|
+
const cx = xOffset + (x + 1) * xEighth;
|
|
160
|
+
const cy = yOffset + paddingY + (y + 1) * yEighth;
|
|
161
|
+
ctx.beginPath();
|
|
162
|
+
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
|
|
163
|
+
ctx.fill();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
84
168
|
function drawPathDefinitionCharacter(
|
|
85
169
|
ctx: CanvasRenderingContext2D,
|
|
86
170
|
charDefinition: CustomGlyphPathDrawFunctionDefinition | string,
|
|
87
171
|
xOffset: number,
|
|
88
172
|
yOffset: number,
|
|
89
173
|
deviceCellWidth: number,
|
|
90
|
-
deviceCellHeight: number
|
|
174
|
+
deviceCellHeight: number,
|
|
175
|
+
devicePixelRatio: number,
|
|
176
|
+
strokeWidth?: number
|
|
91
177
|
): void {
|
|
92
178
|
const instructions = typeof charDefinition === 'string' ? charDefinition : charDefinition(0, 0);
|
|
93
179
|
ctx.beginPath();
|
|
@@ -188,7 +274,13 @@ function drawPathDefinitionCharacter(
|
|
|
188
274
|
}
|
|
189
275
|
lastCommand = type;
|
|
190
276
|
}
|
|
191
|
-
|
|
277
|
+
if (strokeWidth !== undefined) {
|
|
278
|
+
ctx.strokeStyle = ctx.fillStyle;
|
|
279
|
+
ctx.lineWidth = devicePixelRatio * strokeWidth;
|
|
280
|
+
ctx.stroke();
|
|
281
|
+
} else {
|
|
282
|
+
ctx.fill();
|
|
283
|
+
}
|
|
192
284
|
}
|
|
193
285
|
|
|
194
286
|
/**
|
|
@@ -398,139 +490,66 @@ function drawPatternChar(
|
|
|
398
490
|
ctx.fillRect(xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
399
491
|
}
|
|
400
492
|
|
|
401
|
-
|
|
402
|
-
* Draws rectangular shade characters - medium shade pattern clipped to a region.
|
|
403
|
-
* Uses a checkerboard pattern that shifts 1px each row (same as medium shade U+2592).
|
|
404
|
-
*/
|
|
405
|
-
function drawBlockPatternWithRegion(
|
|
493
|
+
function drawPathFunctionCharacter(
|
|
406
494
|
ctx: CanvasRenderingContext2D,
|
|
407
|
-
|
|
408
|
-
xOffset: number,
|
|
409
|
-
yOffset: number,
|
|
410
|
-
deviceCellWidth: number,
|
|
411
|
-
deviceCellHeight: number
|
|
412
|
-
): void {
|
|
413
|
-
const [pattern, region] = definition;
|
|
414
|
-
const [rx, ry, rw, rh] = region;
|
|
415
|
-
const regionX = Math.round(xOffset + rx * deviceCellWidth);
|
|
416
|
-
const regionY = Math.round(yOffset + ry * deviceCellHeight);
|
|
417
|
-
const regionW = Math.round(rw * deviceCellWidth);
|
|
418
|
-
const regionH = Math.round(rh * deviceCellHeight);
|
|
419
|
-
|
|
420
|
-
// Save context state
|
|
421
|
-
ctx.save();
|
|
422
|
-
|
|
423
|
-
// Clip to the region
|
|
424
|
-
ctx.beginPath();
|
|
425
|
-
ctx.rect(regionX, regionY, regionW, regionH);
|
|
426
|
-
ctx.clip();
|
|
427
|
-
|
|
428
|
-
// Draw the pattern
|
|
429
|
-
drawPatternChar(ctx, pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
430
|
-
|
|
431
|
-
// Restore context state
|
|
432
|
-
ctx.restore();
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Draws the following box drawing characters by mapping a subset of SVG d attribute instructions to
|
|
437
|
-
* canvas draw calls.
|
|
438
|
-
*
|
|
439
|
-
* Box styles: ┎┰┒┍┯┑╓╥╖╒╤╕ ┏┳┓┌┲┓┌┬┐┏┱┐
|
|
440
|
-
* ┌─┬─┐ ┏━┳━┓ ╔═╦═╗ ┠╂┨┝┿┥╟╫╢╞╪╡ ┡╇┩├╊┫┢╈┪┣╉┤
|
|
441
|
-
* │ │ │ ┃ ┃ ┃ ║ ║ ║ ┖┸┚┕┷┙╙╨╜╘╧╛ └┴┘└┺┛┗┻┛┗┹┘
|
|
442
|
-
* ├─┼─┤ ┣━╋━┫ ╠═╬═╣ ┏┱┐┌┲┓┌┬┐┌┬┐ ┏┳┓┌┮┓┌┬┐┏┭┐
|
|
443
|
-
* │ │ │ ┃ ┃ ┃ ║ ║ ║ ┡╃┤├╄┩├╆┪┢╅┤ ┞╀┦├┾┫┟╁┧┣┽┤
|
|
444
|
-
* └─┴─┘ ┗━┻━┛ ╚═╩═╝ └┴┘└┴┘└┺┛┗┹┘ └┴┘└┶┛┗┻┛┗┵┘
|
|
445
|
-
*
|
|
446
|
-
* Other:
|
|
447
|
-
* ╭─╮ ╲ ╱ ╷╻╎╏┆┇┊┋ ╺╾╴ ╌╌╌ ┄┄┄ ┈┈┈
|
|
448
|
-
* │ │ ╳ ╽╿╎╏┆┇┊┋ ╶╼╸ ╍╍╍ ┅┅┅ ┉┉┉
|
|
449
|
-
* ╰─╯ ╱ ╲ ╹╵╎╏┆┇┊┋
|
|
450
|
-
*
|
|
451
|
-
* All box drawing characters:
|
|
452
|
-
* ─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏
|
|
453
|
-
* ┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟
|
|
454
|
-
* ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯
|
|
455
|
-
* ┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿
|
|
456
|
-
* ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏
|
|
457
|
-
* ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟
|
|
458
|
-
* ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯
|
|
459
|
-
* ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿
|
|
460
|
-
*
|
|
461
|
-
* ---
|
|
462
|
-
*
|
|
463
|
-
* Box drawing alignment tests: █
|
|
464
|
-
* ▉
|
|
465
|
-
* ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
|
|
466
|
-
* ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
|
|
467
|
-
* ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
|
|
468
|
-
* ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
|
|
469
|
-
* ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
|
|
470
|
-
* ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
|
|
471
|
-
* ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
|
|
472
|
-
*
|
|
473
|
-
* Source: https://www.w3.org/2001/06/utf-8-test/UTF-8-demo.html
|
|
474
|
-
*/
|
|
475
|
-
function drawPathDefinitionCharacterWithWeight(
|
|
476
|
-
ctx: CanvasRenderingContext2D,
|
|
477
|
-
charDefinition: { [fontWeight: number]: string | ((xp: number, yp: number) => string) },
|
|
495
|
+
charDefinition: string | ((xp: number, yp: number) => string),
|
|
478
496
|
xOffset: number,
|
|
479
497
|
yOffset: number,
|
|
480
498
|
deviceCellWidth: number,
|
|
481
499
|
deviceCellHeight: number,
|
|
482
|
-
devicePixelRatio: number
|
|
500
|
+
devicePixelRatio: number,
|
|
501
|
+
strokeWidth?: number
|
|
483
502
|
): void {
|
|
484
|
-
ctx.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
503
|
+
ctx.beginPath();
|
|
504
|
+
let actualInstructions: string;
|
|
505
|
+
if (typeof charDefinition === 'function') {
|
|
506
|
+
const xp = .15;
|
|
507
|
+
const yp = .15 / deviceCellHeight * deviceCellWidth;
|
|
508
|
+
actualInstructions = charDefinition(xp, yp);
|
|
509
|
+
} else {
|
|
510
|
+
actualInstructions = charDefinition;
|
|
511
|
+
}
|
|
512
|
+
const state: ISvgPathState = { currentX: 0, currentY: 0, lastControlX: 0, lastControlY: 0, lastCommand: '' };
|
|
513
|
+
for (const instruction of actualInstructions.split(' ')) {
|
|
514
|
+
const type = instruction[0];
|
|
515
|
+
if (type === 'Z') {
|
|
516
|
+
ctx.closePath();
|
|
517
|
+
state.lastCommand = type;
|
|
518
|
+
continue;
|
|
495
519
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
continue;
|
|
501
|
-
}
|
|
502
|
-
const f = svgToCanvasInstructionMap[type];
|
|
503
|
-
if (!f) {
|
|
504
|
-
console.error(`Could not find drawing instructions for "${type}"`);
|
|
505
|
-
continue;
|
|
506
|
-
}
|
|
507
|
-
const args: string[] = instruction.substring(1).split(',');
|
|
508
|
-
if (!args[0] || !args[1]) {
|
|
509
|
-
continue;
|
|
510
|
-
}
|
|
511
|
-
f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio));
|
|
520
|
+
const f = svgToCanvasInstructionMap[type];
|
|
521
|
+
if (!f) {
|
|
522
|
+
console.error(`Could not find drawing instructions for "${type}"`);
|
|
523
|
+
continue;
|
|
512
524
|
}
|
|
525
|
+
const args: string[] = instruction.substring(1).split(',');
|
|
526
|
+
if (!args[0] || !args[1]) {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
f(ctx, translateArgs(args, deviceCellWidth, deviceCellHeight, xOffset, yOffset, true, devicePixelRatio), state);
|
|
530
|
+
state.lastCommand = type;
|
|
531
|
+
}
|
|
532
|
+
if (strokeWidth !== undefined) {
|
|
533
|
+
ctx.strokeStyle = ctx.fillStyle;
|
|
534
|
+
ctx.lineWidth = devicePixelRatio * strokeWidth;
|
|
513
535
|
ctx.stroke();
|
|
514
|
-
|
|
536
|
+
} else {
|
|
537
|
+
ctx.fill();
|
|
515
538
|
}
|
|
539
|
+
ctx.closePath();
|
|
516
540
|
}
|
|
517
541
|
|
|
518
542
|
/**
|
|
519
|
-
*
|
|
543
|
+
* Applies a clip path to the canvas context from SVG-like path instructions.
|
|
520
544
|
*/
|
|
521
|
-
function
|
|
545
|
+
function applyClipPath(
|
|
522
546
|
ctx: CanvasRenderingContext2D,
|
|
523
|
-
|
|
547
|
+
clipPath: string,
|
|
524
548
|
xOffset: number,
|
|
525
549
|
yOffset: number,
|
|
526
550
|
deviceCellWidth: number,
|
|
527
551
|
deviceCellHeight: number
|
|
528
552
|
): void {
|
|
529
|
-
const [pattern, clipPath] = definition;
|
|
530
|
-
|
|
531
|
-
ctx.save();
|
|
532
|
-
|
|
533
|
-
// Build clip path from SVG-like instructions
|
|
534
553
|
ctx.beginPath();
|
|
535
554
|
for (const instruction of clipPath.split(' ')) {
|
|
536
555
|
const type = instruction[0];
|
|
@@ -551,11 +570,6 @@ function drawBlockPatternWithClipPath(
|
|
|
551
570
|
}
|
|
552
571
|
}
|
|
553
572
|
ctx.clip();
|
|
554
|
-
|
|
555
|
-
// Draw the pattern
|
|
556
|
-
drawPatternChar(ctx, pattern, xOffset, yOffset, deviceCellWidth, deviceCellHeight);
|
|
557
|
-
|
|
558
|
-
ctx.restore();
|
|
559
573
|
}
|
|
560
574
|
|
|
561
575
|
function drawVectorShape(
|
|
@@ -577,10 +591,12 @@ function drawVectorShape(
|
|
|
577
591
|
// Scale the stroke with DPR and font size
|
|
578
592
|
const cssLineWidth = fontSize / 12;
|
|
579
593
|
ctx.lineWidth = devicePixelRatio * cssLineWidth;
|
|
594
|
+
const state: ISvgPathState = { currentX: 0, currentY: 0, lastControlX: 0, lastControlY: 0, lastCommand: '' };
|
|
580
595
|
for (const instruction of charDefinition.d.split(' ')) {
|
|
581
596
|
const type = instruction[0];
|
|
582
597
|
if (type === 'Z') {
|
|
583
598
|
ctx.closePath();
|
|
599
|
+
state.lastCommand = type;
|
|
584
600
|
continue;
|
|
585
601
|
}
|
|
586
602
|
const f = svgToCanvasInstructionMap[type];
|
|
@@ -602,7 +618,8 @@ function drawVectorShape(
|
|
|
602
618
|
devicePixelRatio,
|
|
603
619
|
(charDefinition.leftPadding ?? 0) * (cssLineWidth / 2),
|
|
604
620
|
(charDefinition.rightPadding ?? 0) * (cssLineWidth / 2)
|
|
605
|
-
));
|
|
621
|
+
), state);
|
|
622
|
+
state.lastCommand = type;
|
|
606
623
|
}
|
|
607
624
|
if (charDefinition.type === CustomGlyphVectorType.STROKE) {
|
|
608
625
|
ctx.strokeStyle = ctx.fillStyle;
|
|
@@ -617,11 +634,55 @@ function clamp(value: number, max: number, min: number = 0): number {
|
|
|
617
634
|
return Math.max(Math.min(value, max), min);
|
|
618
635
|
}
|
|
619
636
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
637
|
+
interface ISvgPathState {
|
|
638
|
+
currentX: number;
|
|
639
|
+
currentY: number;
|
|
640
|
+
lastControlX: number;
|
|
641
|
+
lastControlY: number;
|
|
642
|
+
lastCommand: string;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const svgToCanvasInstructionMap: { [index: string]: (ctx: CanvasRenderingContext2D, args: number[], state: ISvgPathState) => void } = {
|
|
646
|
+
'C': (ctx, args, state) => {
|
|
647
|
+
ctx.bezierCurveTo(args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
648
|
+
state.lastControlX = args[2];
|
|
649
|
+
state.lastControlY = args[3];
|
|
650
|
+
state.currentX = args[4];
|
|
651
|
+
state.currentY = args[5];
|
|
652
|
+
},
|
|
653
|
+
'L': (ctx, args, state) => {
|
|
654
|
+
ctx.lineTo(args[0], args[1]);
|
|
655
|
+
state.lastControlX = state.currentX = args[0];
|
|
656
|
+
state.lastControlY = state.currentY = args[1];
|
|
657
|
+
},
|
|
658
|
+
'M': (ctx, args, state) => {
|
|
659
|
+
ctx.moveTo(args[0], args[1]);
|
|
660
|
+
state.lastControlX = state.currentX = args[0];
|
|
661
|
+
state.lastControlY = state.currentY = args[1];
|
|
662
|
+
},
|
|
663
|
+
'Q': (ctx, args, state) => {
|
|
664
|
+
ctx.quadraticCurveTo(args[0], args[1], args[2], args[3]);
|
|
665
|
+
state.lastControlX = args[0];
|
|
666
|
+
state.lastControlY = args[1];
|
|
667
|
+
state.currentX = args[2];
|
|
668
|
+
state.currentY = args[3];
|
|
669
|
+
},
|
|
670
|
+
'T': (ctx, args, state) => {
|
|
671
|
+
let cpX: number;
|
|
672
|
+
let cpY: number;
|
|
673
|
+
if (state.lastCommand === 'Q' || state.lastCommand === 'T') {
|
|
674
|
+
cpX = 2 * state.currentX - state.lastControlX;
|
|
675
|
+
cpY = 2 * state.currentY - state.lastControlY;
|
|
676
|
+
} else {
|
|
677
|
+
cpX = state.currentX;
|
|
678
|
+
cpY = state.currentY;
|
|
679
|
+
}
|
|
680
|
+
ctx.quadraticCurveTo(cpX, cpY, args[0], args[1]);
|
|
681
|
+
state.lastControlX = cpX;
|
|
682
|
+
state.lastControlY = cpY;
|
|
683
|
+
state.currentX = args[0];
|
|
684
|
+
state.currentY = args[1];
|
|
685
|
+
}
|
|
625
686
|
};
|
|
626
687
|
|
|
627
688
|
function translateArgs(args: string[], cellWidth: number, cellHeight: number, xOffset: number, yOffset: number, doClamp: boolean, devicePixelRatio: number, leftPadding: number = 0, rightPadding: number = 0): number[] {
|
|
@@ -33,29 +33,53 @@ export type CustomGlyphPatternDefinition = number[][];
|
|
|
33
33
|
export const enum CustomGlyphDefinitionType {
|
|
34
34
|
SOLID_OCTANT_BLOCK_VECTOR,
|
|
35
35
|
BLOCK_PATTERN,
|
|
36
|
-
BLOCK_PATTERN_WITH_REGION,
|
|
37
|
-
BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR,
|
|
38
|
-
BLOCK_PATTERN_WITH_CLIP_PATH,
|
|
39
36
|
PATH_FUNCTION,
|
|
40
|
-
PATH_FUNCTION_WITH_WEIGHT,
|
|
41
37
|
PATH,
|
|
42
38
|
PATH_NEGATIVE,
|
|
43
39
|
VECTOR_SHAPE,
|
|
40
|
+
BRAILLE,
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
export type
|
|
47
|
-
|
|
48
|
-
export type CustomGlyphCharacterDefinition = (
|
|
43
|
+
export type CustomGlyphDefinitionPartRaw = (
|
|
49
44
|
{ type: CustomGlyphDefinitionType.SOLID_OCTANT_BLOCK_VECTOR, data: ICustomGlyphSolidOctantBlockVector[] } |
|
|
50
45
|
{ type: CustomGlyphDefinitionType.BLOCK_PATTERN, data: CustomGlyphPatternDefinition } |
|
|
51
|
-
{ type: CustomGlyphDefinitionType.
|
|
52
|
-
// TODO: Consolidate these, draws should be possible via regions/clipping instead of special
|
|
53
|
-
// casing
|
|
54
|
-
{ type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_REGION_AND_SOLID_OCTANT_BLOCK_VECTOR, data: { pattern: [pattern: CustomGlyphPatternDefinition, region: CustomGlyphRegionDefinition], vectors: ICustomGlyphSolidOctantBlockVector[] } } |
|
|
55
|
-
{ type: CustomGlyphDefinitionType.BLOCK_PATTERN_WITH_CLIP_PATH, data: [pattern: CustomGlyphPatternDefinition, clipPath: string] } |
|
|
56
|
-
{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: CustomGlyphPathDrawFunctionDefinition } |
|
|
57
|
-
{ type: CustomGlyphDefinitionType.PATH_FUNCTION_WITH_WEIGHT, data: { [fontWeight: number]: string | CustomGlyphPathDrawFunctionDefinition } } |
|
|
46
|
+
{ type: CustomGlyphDefinitionType.PATH_FUNCTION, data: CustomGlyphPathDrawFunctionDefinition | string } |
|
|
58
47
|
{ type: CustomGlyphDefinitionType.PATH, data: string } |
|
|
59
48
|
{ type: CustomGlyphDefinitionType.PATH_NEGATIVE, data: ICustomGlyphVectorShape } |
|
|
60
|
-
{ type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: ICustomGlyphVectorShape
|
|
49
|
+
{ type: CustomGlyphDefinitionType.VECTOR_SHAPE, data: ICustomGlyphVectorShape} |
|
|
50
|
+
{ type: CustomGlyphDefinitionType.BRAILLE, data: number }
|
|
61
51
|
);
|
|
52
|
+
|
|
53
|
+
export const enum CustomGlyphScaleType {
|
|
54
|
+
/**
|
|
55
|
+
* Scale to the entire cell, including letter spacing and line height.
|
|
56
|
+
*/
|
|
57
|
+
CELL,
|
|
58
|
+
/**
|
|
59
|
+
* Scale to only the character area, excluding letter spacing and line height.
|
|
60
|
+
*/
|
|
61
|
+
CHAR,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ICustomGlyphDefinitionCommon {
|
|
65
|
+
/**
|
|
66
|
+
* A custom clip path for the draw definition, restricting the area it can draw to.
|
|
67
|
+
*/
|
|
68
|
+
clipPath?: string;
|
|
69
|
+
/**
|
|
70
|
+
* The stroke width to use when drawing the path. Defaults to 1.
|
|
71
|
+
*/
|
|
72
|
+
strokeWidth?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Defines how to scale the draw. Defaults to scaling to the full cell including letter spacing
|
|
75
|
+
* and line height.
|
|
76
|
+
*/
|
|
77
|
+
scaleType?: CustomGlyphScaleType;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type CustomGlyphDefinitionPart = CustomGlyphDefinitionPartRaw & ICustomGlyphDefinitionCommon;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A character definition that can be a single part or an array of parts drawn in sequence.
|
|
84
|
+
*/
|
|
85
|
+
export type CustomGlyphCharacterDefinition = CustomGlyphDefinitionPart | CustomGlyphDefinitionPart[];
|
package/typings/addon-webgl.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ declare module '@xterm/addon-webgl' {
|
|
|
32
32
|
*/
|
|
33
33
|
public readonly onRemoveTextureAtlasCanvas: IEvent<HTMLCanvasElement>;
|
|
34
34
|
|
|
35
|
-
constructor(
|
|
35
|
+
constructor(options?: IWebglAddonOptions);
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Activates the addon.
|
|
@@ -50,4 +50,34 @@ declare module '@xterm/addon-webgl' {
|
|
|
50
50
|
*/
|
|
51
51
|
public clearTextureAtlas(): void;
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
export interface IWebglAddonOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Whether to draw custom glyphs instead of using the font for the following
|
|
57
|
+
* unicode ranges:
|
|
58
|
+
*
|
|
59
|
+
* - Box Drawing (U+2500-U+257F)
|
|
60
|
+
* - Block Elements (U+2580-U+259F)
|
|
61
|
+
* - Braille Patterns (U+2800-U+28FF)
|
|
62
|
+
* - Powerline Symbols (U+E0A0-U+E0D4, Private Use Area with widespread
|
|
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)).
|
|
67
|
+
* - Git Branch Symbols (U+F5D0-U+F60D, Private Use Area initially adopted
|
|
68
|
+
* in [Kitty in 2024](https://github.com/kovidgoyal/kitty/pull/7681) by
|
|
69
|
+
* author of [vim-flog](https://github.com/rbong/vim-flog))
|
|
70
|
+
* - Symbols for Legacy Computing (U+1FB00-U+1FBFF)
|
|
71
|
+
*
|
|
72
|
+
* This will typically result in better rendering with continuous lines,
|
|
73
|
+
* even when line height and letter spacing is used. The default is true.
|
|
74
|
+
*/
|
|
75
|
+
customGlyphs?: boolean;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Whether to enable the preserveDrawingBuffer flag when creating the WebGL
|
|
79
|
+
* context. This may be useful in tests. This default is false.
|
|
80
|
+
*/
|
|
81
|
+
preserveDrawingBuffer?: boolean
|
|
82
|
+
}
|
|
53
83
|
}
|