@xterm/addon-image 0.7.0-beta.1

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.
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Copyright (c) 2020, 2023 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { ImageStorage } from './ImageStorage';
7
+ import { IDcsHandler, IParams, IImageAddonOptions, ITerminalExt, AttributeData, IResetHandler, ReadonlyColorSet } from './Types';
8
+ import { toRGBA8888, BIG_ENDIAN, PALETTE_ANSI_256, PALETTE_VT340_COLOR } from 'sixel/lib/Colors';
9
+ import { RGBA8888 } from 'sixel/lib/Types';
10
+ import { ImageRenderer } from './ImageRenderer';
11
+
12
+ import { DecoderAsync, Decoder } from 'sixel/lib/Decoder';
13
+
14
+ // always free decoder ressources after decoding if it exceeds this limit
15
+ const MEM_PERMA_LIMIT = 4194304; // 1024 pixels * 1024 pixels * 4 channels = 4MB
16
+
17
+ // custom default palette: VT340 (lower 16 colors) + ANSI256 (up to 256) + zeroed (up to 4096)
18
+ const DEFAULT_PALETTE = PALETTE_ANSI_256;
19
+ DEFAULT_PALETTE.set(PALETTE_VT340_COLOR);
20
+
21
+
22
+ export class SixelHandler implements IDcsHandler, IResetHandler {
23
+ private _size = 0;
24
+ private _aborted = false;
25
+ private _dec: Decoder | undefined;
26
+
27
+ constructor(
28
+ private readonly _opts: IImageAddonOptions,
29
+ private readonly _storage: ImageStorage,
30
+ private readonly _coreTerminal: ITerminalExt
31
+ ) {
32
+ DecoderAsync({
33
+ memoryLimit: this._opts.pixelLimit * 4,
34
+ palette: DEFAULT_PALETTE,
35
+ paletteLimit: this._opts.sixelPaletteLimit
36
+ }).then(d => this._dec = d);
37
+ }
38
+
39
+ public reset(): void {
40
+ /**
41
+ * reset sixel decoder to defaults:
42
+ * - release all memory
43
+ * - nullify palette (4096)
44
+ * - apply default palette (256)
45
+ */
46
+ if (this._dec) {
47
+ this._dec.release();
48
+ // FIXME: missing interface on decoder to nullify full palette
49
+ (this._dec as any)._palette.fill(0);
50
+ this._dec.init(0, DEFAULT_PALETTE, this._opts.sixelPaletteLimit);
51
+ }
52
+ }
53
+
54
+ public hook(params: IParams): void {
55
+ this._size = 0;
56
+ this._aborted = false;
57
+ if (this._dec) {
58
+ const fillColor = params.params[1] === 1 ? 0 : extractActiveBg(
59
+ this._coreTerminal._core._inputHandler._curAttrData,
60
+ this._coreTerminal._core._themeService?.colors);
61
+ this._dec.init(fillColor, null, this._opts.sixelPaletteLimit);
62
+ }
63
+ }
64
+
65
+ public put(data: Uint32Array, start: number, end: number): void {
66
+ if (this._aborted || !this._dec) {
67
+ return;
68
+ }
69
+ this._size += end - start;
70
+ if (this._size > this._opts.sixelSizeLimit) {
71
+ console.warn(`SIXEL: too much data, aborting`);
72
+ this._aborted = true;
73
+ this._dec.release();
74
+ return;
75
+ }
76
+ try {
77
+ this._dec.decode(data, start, end);
78
+ } catch (e) {
79
+ console.warn(`SIXEL: error while decoding image - ${e}`);
80
+ this._aborted = true;
81
+ this._dec.release();
82
+ }
83
+ }
84
+
85
+ public unhook(success: boolean): boolean | Promise<boolean> {
86
+ if (this._aborted || !success || !this._dec) {
87
+ return true;
88
+ }
89
+
90
+ const width = this._dec.width;
91
+ const height = this._dec.height;
92
+
93
+ // partial fix for https://github.com/jerch/xterm-addon-image/issues/37
94
+ if (!width || ! height) {
95
+ if (height) {
96
+ this._storage.advanceCursor(height);
97
+ }
98
+ return true;
99
+ }
100
+
101
+ const canvas = ImageRenderer.createCanvas(undefined, width, height);
102
+ canvas.getContext('2d')?.putImageData(new ImageData(this._dec.data8, width, height), 0, 0);
103
+ if (this._dec.memoryUsage > MEM_PERMA_LIMIT) {
104
+ this._dec.release();
105
+ }
106
+ this._storage.addImage(canvas);
107
+ return true;
108
+ }
109
+ }
110
+
111
+
112
+ /**
113
+ * Some helpers to extract current terminal colors.
114
+ */
115
+
116
+ // get currently active background color from terminal
117
+ // also respect INVERSE setting
118
+ function extractActiveBg(attr: AttributeData, colors: ReadonlyColorSet | undefined): RGBA8888 {
119
+ let bg = 0;
120
+ if (!colors) {
121
+ // FIXME: theme service is prolly not available yet,
122
+ // happens if .open() was not called yet (bug in core?)
123
+ return bg;
124
+ }
125
+ if (attr.isInverse()) {
126
+ if (attr.isFgDefault()) {
127
+ bg = convertLe(colors.foreground.rgba);
128
+ } else if (attr.isFgRGB()) {
129
+ const t = (attr.constructor as typeof AttributeData).toColorRGB(attr.getFgColor());
130
+ bg = toRGBA8888(...t);
131
+ } else {
132
+ bg = convertLe(colors.ansi[attr.getFgColor()].rgba);
133
+ }
134
+ } else {
135
+ if (attr.isBgDefault()) {
136
+ bg = convertLe(colors.background.rgba);
137
+ } else if (attr.isBgRGB()) {
138
+ const t = (attr.constructor as typeof AttributeData).toColorRGB(attr.getBgColor());
139
+ bg = toRGBA8888(...t);
140
+ } else {
141
+ bg = convertLe(colors.ansi[attr.getBgColor()].rgba);
142
+ }
143
+ }
144
+ return bg;
145
+ }
146
+
147
+ // rgba values on the color managers are always in BE, thus convert to LE
148
+ function convertLe(color: number): RGBA8888 {
149
+ if (BIG_ENDIAN) return color;
150
+ return (color & 0xFF) << 24 | (color >>> 8 & 0xFF) << 16 | (color >>> 16 & 0xFF) << 8 | color >>> 24 & 0xFF;
151
+ }
package/src/Types.d.ts ADDED
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Copyright (c) 2020 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { IDisposable, IMarker, Terminal } from '@xterm/xterm';
7
+
8
+ // private imports from base repo we build against
9
+ import { Attributes, BgFlags, Content, ExtFlags, UnderlineStyle } from 'common/buffer/Constants';
10
+ import type { AttributeData } from 'common/buffer/AttributeData';
11
+ import type { IParams, IDcsHandler, IOscHandler, IEscapeSequenceParser } from 'common/parser/Types';
12
+ import type { IBufferLine, IExtendedAttrs, IInputHandler } from 'common/Types';
13
+ import type { ITerminal, ReadonlyColorSet } from 'browser/Types';
14
+ import type { IRenderDimensions } from 'browser/renderer/shared/Types';
15
+ import type { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
16
+
17
+ export const enum Cell {
18
+ CONTENT = 0, // codepoint and wcwidth information (enum Content)
19
+ FG = 1, // foreground color in lower 3 bytes (rgb), attrs in 4th byte (enum FgFlags)
20
+ BG = 2, // background color in lower 3 bytes (rgb), attrs in 4th byte (enum BgFlags)
21
+ SIZE = 3 // size of single cell on buffer array
22
+ }
23
+
24
+ // export some privates for local usage
25
+ export { AttributeData, IParams, IDcsHandler, IOscHandler, BgFlags, IRenderDimensions, IRenderService, Content, ExtFlags, Attributes, UnderlineStyle, ReadonlyColorSet };
26
+
27
+ /**
28
+ * Plugin ctor options.
29
+ */
30
+ export interface IImageAddonOptions {
31
+ enableSizeReports: boolean;
32
+ pixelLimit: number;
33
+ storageLimit: number;
34
+ showPlaceholder: boolean;
35
+ sixelSupport: boolean;
36
+ sixelScrolling: boolean;
37
+ sixelPaletteLimit: number;
38
+ sixelSizeLimit: number;
39
+ iipSupport: boolean;
40
+ iipSizeLimit: number;
41
+ }
42
+
43
+ export interface IResetHandler {
44
+ // attached to RIS and DECSTR
45
+ reset(): void;
46
+ }
47
+
48
+ /**
49
+ * Stub into private interfaces.
50
+ * This should be kept in line with common libs.
51
+ * Any change made here should be replayed in the accessors test case to
52
+ * have a somewhat reliable testing against code changes in the core repo.
53
+ */
54
+
55
+ // overloaded IExtendedAttrs to hold image refs
56
+ export interface IExtendedAttrsImage extends IExtendedAttrs {
57
+ imageId: number;
58
+ tileId: number;
59
+ clone(): IExtendedAttrsImage;
60
+ }
61
+
62
+ /* eslint-disable */
63
+ export interface IBufferLineExt extends IBufferLine {
64
+ _extendedAttrs: {[index: number]: IExtendedAttrsImage | undefined};
65
+ _data: Uint32Array;
66
+ }
67
+
68
+ interface IInputHandlerExt extends IInputHandler {
69
+ _parser: IEscapeSequenceParser;
70
+ _curAttrData: AttributeData;
71
+ _dirtyRowTracker: {
72
+ markRangeDirty(y1: number, y2: number): void;
73
+ markAllDirty(): void;
74
+ markDirty(y: number): void;
75
+ };
76
+ onRequestReset(handler: () => void): IDisposable;
77
+ }
78
+
79
+ export interface ICoreTerminalExt extends ITerminal {
80
+ _themeService: IThemeService | undefined;
81
+ _inputHandler: IInputHandlerExt;
82
+ _renderService: IRenderService;
83
+ _coreBrowserService: ICoreBrowserService | undefined;
84
+ }
85
+
86
+ export interface ITerminalExt extends Terminal {
87
+ _core: ICoreTerminalExt;
88
+ }
89
+ /* eslint-enable */
90
+
91
+
92
+ /**
93
+ * Some storage definitions.
94
+ */
95
+ export interface ICellSize {
96
+ width: number;
97
+ height: number;
98
+ }
99
+
100
+ export interface IImageSpec {
101
+ orig: HTMLCanvasElement | ImageBitmap | undefined;
102
+ origCellSize: ICellSize;
103
+ actual: HTMLCanvasElement | ImageBitmap | undefined;
104
+ actualCellSize: ICellSize;
105
+ marker: IMarker | undefined;
106
+ tileCount: number;
107
+ bufferType: 'alternate' | 'normal';
108
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Copyright (c) 2022 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { Terminal, ITerminalAddon } from '@xterm/xterm';
7
+
8
+ declare module '@xterm/addon-image' {
9
+ export interface IImageAddonOptions {
10
+ /**
11
+ * Enable size reports in windowOptions:
12
+ * - getWinSizePixels (CSI 14 t)
13
+ * - getCellSizePixels (CSI 16 t)
14
+ * - getWinSizeChars (CSI 18 t)
15
+ *
16
+ * If `true` (default), the reports will be activated during addon loading.
17
+ * If `false`, no settings will be touched. Use `false`, if you have high
18
+ * security constraints and/or deal with windowOptions by other means.
19
+ * On addon disposal, the settings will not change.
20
+ */
21
+ enableSizeReports?: boolean;
22
+
23
+ /**
24
+ * Maximum pixels a single image may hold. Images exceeding this number will
25
+ * be discarded during processing with no changes to the terminal buffer
26
+ * (no cursor advance, no placeholder).
27
+ * This setting is mainly used to restrict images sizes during initial decoding
28
+ * including the final canvas creation.
29
+ *
30
+ * Note: The image worker decoder may hold additional memory up to
31
+ * `pixelLimit` * 4 bytes permanently, plus the same amount on top temporarily
32
+ * for pixel transfers, which should be taken into account under memory pressure conditions.
33
+ *
34
+ * Note: Browsers restrict allowed canvas dimensions further. We dont reflect those
35
+ * limits here, thus the construction of an oddly shaped image having most pixels
36
+ * in one dimension still can fail.
37
+ *
38
+ * Note: `storageLimit` bytes are calculated from images by multiplying the pixels with 4
39
+ * (4 channels with one byte, images are stored as RGBA8888).
40
+ *
41
+ * Default is 2^16 (4096 x 4096 pixels).
42
+ */
43
+ pixelLimit?: number;
44
+
45
+ /**
46
+ * Storage limit in MB.
47
+ * The storage implements a FIFO cache removing old images, when the limit gets hit.
48
+ * Also exposed as addon property for runtime adjustments.
49
+ * Default is 128 MB.
50
+ */
51
+ storageLimit?: number;
52
+
53
+ /**
54
+ * Whether to show a placeholder for images removed from cache, default is true.
55
+ */
56
+ showPlaceholder?: boolean;
57
+
58
+ /**
59
+ * SIXEL settings
60
+ */
61
+
62
+ /** Whether SIXEL is enabled (default is true). */
63
+ sixelSupport?: boolean;
64
+ /** Whether SIXEL scrolling is enabled (default is true). Same as DECSET 80. */
65
+ sixelScrolling?: boolean;
66
+ /** Palette color limit (default 256). */
67
+ sixelPaletteLimit?: number;
68
+ /** SIXEL image size limit in bytes (default 25000000 bytes). */
69
+ sixelSizeLimit?: number;
70
+
71
+ /**
72
+ * IIP settings (iTerm image protocol)
73
+ */
74
+
75
+ /** Whether iTerm image protocol style is enabled (default is true). */
76
+ iipSupport?: boolean;
77
+ /** IIP sequence size limit (default 20000000 bytes). */
78
+ iipSizeLimit?: number;
79
+ }
80
+
81
+ export class ImageAddon implements ITerminalAddon {
82
+ constructor(options?: IImageAddonOptions);
83
+ public activate(terminal: Terminal): void;
84
+ public dispose(): void;
85
+
86
+ /**
87
+ * Reset the image addon.
88
+ *
89
+ * This resets all runtime options to default values (as given to the ctor)
90
+ * and resets the image storage.
91
+ */
92
+ public reset(): void;
93
+
94
+ /**
95
+ * Getter/Setter for the storageLimit in MB.
96
+ * Synchronously deletes images if the stored data exceeds the new value.
97
+ */
98
+ public storageLimit: number;
99
+
100
+ /**
101
+ * Current memory usage of the stored images in MB.
102
+ */
103
+ public readonly storageUsage: number;
104
+
105
+ /**
106
+ * Getter/Setter whether the placeholder should be shown.
107
+ */
108
+ public showPlaceholder: boolean;
109
+
110
+ /**
111
+ * Get original image canvas at buffer position.
112
+ */
113
+ public getImageAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined;
114
+
115
+ /**
116
+ * Extract single tile canvas at buffer position.
117
+ */
118
+ public extractTileAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined;
119
+ }
120
+ }