@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.
- package/LICENSE +19 -0
- package/README.md +231 -0
- package/lib/addon-image.js +3 -0
- package/lib/addon-image.js.LICENSE.txt +24 -0
- package/lib/addon-image.js.map +1 -0
- package/out/IIPHandler.js +148 -0
- package/out/IIPHandler.js.map +1 -0
- package/out/IIPHeaderParser.js +156 -0
- package/out/IIPHeaderParser.js.map +1 -0
- package/out/IIPHeaderParser.test.js +137 -0
- package/out/IIPHeaderParser.test.js.map +1 -0
- package/out/IIPMetrics.js +71 -0
- package/out/IIPMetrics.js.map +1 -0
- package/out/IIPMetrics.test.js +38 -0
- package/out/IIPMetrics.test.js.map +1 -0
- package/out/ImageAddon.js +261 -0
- package/out/ImageAddon.js.map +1 -0
- package/out/ImageRenderer.js +330 -0
- package/out/ImageRenderer.js.map +1 -0
- package/out/ImageStorage.js +552 -0
- package/out/ImageStorage.js.map +1 -0
- package/out/SixelHandler.js +140 -0
- package/out/SixelHandler.js.map +1 -0
- package/package.json +31 -0
- package/src/IIPHandler.ts +161 -0
- package/src/IIPHeaderParser.test.ts +138 -0
- package/src/IIPHeaderParser.ts +186 -0
- package/src/IIPMetrics.test.ts +43 -0
- package/src/IIPMetrics.ts +81 -0
- package/src/ImageAddon.ts +317 -0
- package/src/ImageRenderer.ts +379 -0
- package/src/ImageStorage.ts +592 -0
- package/src/SixelHandler.ts +151 -0
- package/src/Types.d.ts +108 -0
- package/typings/addon-image.d.ts +120 -0
|
@@ -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
|
+
}
|