transfer changes from 0.4.3

This commit is contained in:
Jörg Breitbart
2023-08-01 16:43:14 +02:00
parent e8a925b6af
commit fe838726b0
7 changed files with 80 additions and 44 deletions
+1
View File
@@ -36,6 +36,7 @@
"sourceType": "module"
},
"ignorePatterns": [
"**/inwasm-sdks/*",
"**/typings/*.d.ts",
"**/node_modules",
"**/*.js"
+4 -5
View File
@@ -108,14 +108,13 @@ export class IIPHandler implements IOscHandler, IResetHandler {
const blob = new Blob([this._dec.data8], { type: this._metrics.mime });
this._dec.release();
const win = this._coreTerminal._core._coreBrowserService.window;
if (!win.createImageBitmap) {
if (!window.createImageBitmap) {
const url = URL.createObjectURL(blob);
const img = new Image();
return new Promise<boolean>(r => {
img.addEventListener('load', () => {
URL.revokeObjectURL(url);
const canvas = ImageRenderer.createCanvas(win, w, h);
const canvas = ImageRenderer.createCanvas(window.document, w, h);
canvas.getContext('2d')?.drawImage(img, 0, 0, w, h);
this._storage.addImage(canvas);
r(true);
@@ -126,9 +125,9 @@ export class IIPHandler implements IOscHandler, IResetHandler {
setTimeout(() => r(true), 1000);
});
}
return win.createImageBitmap(blob, { resizeWidth: w, resizeHeight: h })
return createImageBitmap(blob, { resizeWidth: w, resizeHeight: h })
.then(bm => {
this._storage.addImage(bm as unknown as HTMLCanvasElement);
this._storage.addImage(bm);
return true;
});
}
+1 -1
View File
@@ -80,7 +80,7 @@ export class ImageAddon implements ITerminalAddon {
this._terminal = terminal;
// internal data structures
this._renderer = new ImageRenderer(terminal, this._opts.showPlaceholder);
this._renderer = new ImageRenderer(terminal);
this._storage = new ImageStorage(terminal, this._renderer, this._opts);
// enable size reports
+50 -24
View File
@@ -28,8 +28,17 @@ export class ImageRenderer implements IDisposable {
private _oldSetRenderer: ((renderer: any) => void) | undefined;
// drawing primitive - canvas
public static createCanvas(base: Window, width: number, height: number): HTMLCanvasElement {
const canvas = base.document.createElement('canvas');
public static createCanvas(localDocument: Document | undefined, width: number, height: number): HTMLCanvasElement {
/**
* NOTE: We normally dont care, from which document the canvas
* gets created, so we can fall back to global document,
* if the terminal has no document associated yet.
* This way early image loads before calling .open keep working
* (still discouraged though, as the metrics will be screwed up).
* Only the DOM output canvas should be on the terminal's document,
* which gets explicitly checked in `insertLayerToDom`.
*/
const canvas = (localDocument || document).createElement('canvas');
canvas.width = width | 0;
canvas.height = height | 0;
return canvas;
@@ -58,7 +67,7 @@ export class ImageRenderer implements IDisposable {
}
constructor(private _terminal: ITerminalExt, private _showPlaceholder: boolean) {
constructor(private _terminal: ITerminalExt) {
this._oldOpen = this._terminal._core.open;
this._terminal._core.open = (parent: HTMLElement): void => {
this._oldOpen?.call(this._terminal._core, parent);
@@ -79,7 +88,7 @@ export class ImageRenderer implements IDisposable {
public dispose(): void {
this._optionsRefresh?.dispose();
this._removeLayerFromDom();
this.removeLayerFromDom();
if (this._terminal._core && this._oldOpen) {
this._terminal._core.open = this._oldOpen;
this._oldOpen = undefined;
@@ -204,7 +213,7 @@ export class ImageRenderer implements IDisposable {
const finalWidth = width + sx > img.width ? img.width - sx : width;
const finalHeight = sy + height > img.height ? img.height - sy : height;
const canvas = ImageRenderer.createCanvas(this._terminal._core._coreBrowserService.window, finalWidth, finalHeight);
const canvas = ImageRenderer.createCanvas(this.document, finalWidth, finalHeight);
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.drawImage(
@@ -220,7 +229,7 @@ export class ImageRenderer implements IDisposable {
* Draw a line with placeholder on the image layer canvas.
*/
public drawPlaceholder(col: number, row: number, count: number = 1): void {
if ((this._placeholderBitmap || this._placeholder) && this._ctx) {
if (this._ctx) {
const { width, height } = this.cellSize;
// Don't try to draw anything, if we cannot get valid renderer metrics.
@@ -228,9 +237,12 @@ export class ImageRenderer implements IDisposable {
return;
}
if (height >= this._placeholder!.height) {
if (!this._placeholder) {
this._createPlaceHolder(Math.max(height + 1, PLACEHOLDER_HEIGHT));
} else if (height >= this._placeholder!.height) {
this._createPlaceHolder(height + 1);
}
if (!this._placeholder) return;
this._ctx.drawImage(
this._placeholderBitmap || this._placeholder!,
col * width,
@@ -274,7 +286,7 @@ export class ImageRenderer implements IDisposable {
return;
}
const canvas = ImageRenderer.createCanvas(
this._terminal._core._coreBrowserService.window,
this.document,
Math.ceil(spec.orig!.width * currentWidth / originalWidth),
Math.ceil(spec.orig!.height * currentHeight / originalHeight)
);
@@ -294,25 +306,35 @@ export class ImageRenderer implements IDisposable {
this._renderService = this._terminal._core._renderService;
this._oldSetRenderer = this._renderService.setRenderer.bind(this._renderService);
this._renderService.setRenderer = (renderer: any) => {
this._removeLayerFromDom();
this.removeLayerFromDom();
this._oldSetRenderer?.call(this._renderService, renderer);
this._insertLayerToDom();
};
this._insertLayerToDom();
if (this._showPlaceholder) {
this._createPlaceHolder();
}
public insertLayerToDom(): void {
// make sure that the terminal is attached to a document and to DOM
if (this.document && this._terminal._core.screenElement) {
if (!this.canvas) {
this.canvas = ImageRenderer.createCanvas(
this.document, this.dimensions?.css.canvas.width || 0,
this.dimensions?.css.canvas.height || 0
);
this.canvas.classList.add('xterm-image-layer');
this._terminal._core.screenElement.appendChild(this.canvas);
this._ctx = this.canvas.getContext('2d', { alpha: true, desynchronized: true });
this.clearAll();
}
} else {
console.warn('image addon: cannot insert output canvas to DOM, missing document or screenElement');
}
}
private _insertLayerToDom(): void {
this.canvas = ImageRenderer.createCanvas(this._terminal._core._coreBrowserService.window, this.dimensions?.css.canvas.width || 0, this.dimensions?.css.canvas.height || 0);
this.canvas.classList.add('xterm-image-layer');
this._terminal._core.screenElement?.appendChild(this.canvas);
this._ctx = this.canvas.getContext('2d', { alpha: true, desynchronized: true });
}
private _removeLayerFromDom(): void {
this.canvas?.parentNode?.removeChild(this.canvas);
public removeLayerFromDom(): void {
if (this.canvas) {
this._ctx = undefined;
this.canvas.remove();
this.canvas = undefined;
}
}
private _createPlaceHolder(height: number = PLACEHOLDER_HEIGHT): void {
@@ -321,7 +343,7 @@ export class ImageRenderer implements IDisposable {
// create blueprint to fill placeholder with
const bWidth = 32; // must be 2^n
const blueprint = ImageRenderer.createCanvas(this._terminal._core._coreBrowserService.window, bWidth, height);
const blueprint = ImageRenderer.createCanvas(this.document, bWidth, height);
const ctx = blueprint.getContext('2d', { alpha: false });
if (!ctx) return;
const imgData = ImageRenderer.createImageData(ctx, bWidth, height);
@@ -340,7 +362,7 @@ export class ImageRenderer implements IDisposable {
// create placeholder line, width aligned to blueprint width
const width = (screen.width + bWidth - 1) & ~(bWidth - 1) || PLACEHOLDER_LENGTH;
this._placeholder = ImageRenderer.createCanvas(this._terminal._core._coreBrowserService.window, width, height);
this._placeholder = ImageRenderer.createCanvas(this.document, width, height);
const ctx2 = this._placeholder.getContext('2d', { alpha: false });
if (!ctx2) {
this._placeholder = undefined;
@@ -351,4 +373,8 @@ export class ImageRenderer implements IDisposable {
}
ImageRenderer.createImageBitmap(this._placeholder).then(bitmap => this._placeholderBitmap = bitmap);
}
public get document(): Document | undefined {
return this._terminal._core._coreBrowserService?.window.document;
}
}
+14 -9
View File
@@ -177,8 +177,7 @@ export class ImageStorage implements IDisposable {
const spec = this._images.get(id);
this._images.delete(id);
// FIXME: really ugly workaround to get bitmaps deallocated :(
const win = this._terminal._core._coreBrowserService.window;
if (spec && win.ImageBitmap && spec.orig instanceof ImageBitmap) {
if (spec && window.ImageBitmap && spec.orig instanceof ImageBitmap) {
spec.orig.close();
}
}
@@ -224,7 +223,7 @@ export class ImageStorage implements IDisposable {
/**
* Method to add an image to the storage.
*/
public addImage(img: HTMLCanvasElement): void {
public addImage(img: HTMLCanvasElement | ImageBitmap): void {
// never allow storage to exceed memory limit
this._evictOldest(img.width * img.height);
@@ -327,9 +326,13 @@ export class ImageStorage implements IDisposable {
*/
// TODO: Should we move this to the ImageRenderer?
public render(range: { start: number, end: number }): void {
// exit early if we dont have a canvas
if (!this._renderer.canvas) {
return;
// setup image canvas in case we have none yet, but have images in store
if (!this._renderer.canvas && this._images.size) {
this._renderer.insertLayerToDom();
// safety measure - in case we cannot spawn a canvas at all, just exit
if (!this._renderer.canvas) {
return;
}
}
// rescale if needed
this._renderer.rescaleCanvas();
@@ -340,6 +343,9 @@ export class ImageStorage implements IDisposable {
this._fullyCleared = true;
this._needsFullClear = false;
}
if (this._renderer.canvas) {
this._renderer.removeLayerFromDom();
}
return;
}
@@ -472,9 +478,8 @@ export class ImageStorage implements IDisposable {
const e: IExtendedAttrsImage = line._extendedAttrs[x] || EMPTY_ATTRS;
if (e.imageId && e.imageId !== -1) {
const orig = this._images.get(e.imageId)?.orig;
const win = this._terminal._core._coreBrowserService.window;
if (win.ImageBitmap && orig instanceof ImageBitmap) {
const canvas = ImageRenderer.createCanvas(win, orig.width, orig.height);
if (window.ImageBitmap && orig instanceof ImageBitmap) {
const canvas = ImageRenderer.createCanvas(window.document, orig.width, orig.height);
canvas.getContext('2d')?.drawImage(orig, 0, 0, orig.width, orig.height);
return canvas;
}
+8 -3
View File
@@ -57,7 +57,7 @@ export class SixelHandler implements IDcsHandler, IResetHandler {
if (this._dec) {
const fillColor = params.params[1] === 1 ? 0 : extractActiveBg(
this._coreTerminal._core._inputHandler._curAttrData,
this._coreTerminal._core._themeService.colors);
this._coreTerminal._core._themeService?.colors);
this._dec.init(fillColor, null, this._opts.sixelPaletteLimit);
}
}
@@ -98,7 +98,7 @@ export class SixelHandler implements IDcsHandler, IResetHandler {
return true;
}
const canvas = ImageRenderer.createCanvas(this._coreTerminal._core._coreBrowserService.window, width, height);
const canvas = ImageRenderer.createCanvas(undefined, width, height);
canvas.getContext('2d')?.putImageData(new ImageData(this._dec.data8, width, height), 0, 0);
if (this._dec.memoryUsage > MEM_PERMA_LIMIT) {
this._dec.release();
@@ -115,8 +115,13 @@ export class SixelHandler implements IDcsHandler, IResetHandler {
// get currently active background color from terminal
// also respect INVERSE setting
function extractActiveBg(attr: AttributeData, colors: ReadonlyColorSet): RGBA8888 {
function extractActiveBg(attr: AttributeData, colors: ReadonlyColorSet | undefined): RGBA8888 {
let bg = 0;
if (!colors) {
// FIXME: theme service is prolly not available yet,
// happens if .open() was not called yet (bug in core?)
return bg;
}
if (attr.isInverse()) {
if (attr.isFgDefault()) {
bg = convertLe(colors.foreground.rgba);
+2 -2
View File
@@ -77,10 +77,10 @@ interface IInputHandlerExt extends IInputHandler {
}
export interface ICoreTerminalExt extends ITerminal {
_themeService: IThemeService;
_themeService: IThemeService | undefined;
_inputHandler: IInputHandlerExt;
_renderService: IRenderService;
_coreBrowserService: ICoreBrowserService;
_coreBrowserService: ICoreBrowserService | undefined;
}
export interface ITerminalExt extends Terminal {