pass metrics for correct rescaling, minor fixes
This commit is contained in:
@@ -7,6 +7,7 @@ import { IDisposable } from '@xterm/xterm';
|
||||
import type { ImageRenderer } from './ImageRenderer';
|
||||
import { EMPTY_ATTRS, type ImageStorage } from './ImageStorage';
|
||||
import { BgFlags, IBufferLineExt, IImageSpec, ITerminalExt, IExtendedAttrsImage, IResetHandler } from './Types';
|
||||
import { IMetrics } from 'IIPMetrics';
|
||||
|
||||
|
||||
/**
|
||||
@@ -30,8 +31,8 @@ interface IAnimation {
|
||||
id: number;
|
||||
/** image bytes */
|
||||
data: Blob;
|
||||
/** mime type */
|
||||
mime: string;
|
||||
/** metrics of the image */
|
||||
metrics: IMetrics;
|
||||
/** width */
|
||||
w: number;
|
||||
/** height */
|
||||
@@ -258,8 +259,8 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
* @param w Output width in pixel.
|
||||
* @param h Output height in pixel.
|
||||
*/
|
||||
public async registerAnimation(id: number, data: Blob, mime: string, w: number, h: number): Promise<void> {
|
||||
let decoder = await BlobDecoder(data, mime);
|
||||
public async registerAnimation(id: number, data: Blob, metrics: IMetrics, w: number, h: number): Promise<void> {
|
||||
let decoder = await BlobDecoder(data, metrics.mime);
|
||||
const track = decoder.tracks.selectedTrack;
|
||||
if (!track || track.frameCount < 2) {
|
||||
decoder.close();
|
||||
@@ -282,7 +283,7 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
frame.close();
|
||||
// skip wrong dimensions and zero duration
|
||||
// FIXME: needs orig image dimensions in IAnimation
|
||||
if (width !== w || height !== h || !duration) {
|
||||
if (width !== metrics.width || height !== metrics.height || !duration) {
|
||||
if (durations.length) {
|
||||
durations[durations.length - 1] += duration;
|
||||
elapsed += duration;
|
||||
@@ -317,11 +318,12 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
|
||||
if (isFirefox) {
|
||||
decoder.close();
|
||||
decoder = await BlobDecoder(data, mime);
|
||||
decoder = await BlobDecoder(data, metrics.mime);
|
||||
}
|
||||
|
||||
const anim: IAnimation = {
|
||||
id, data, mime, w, h, decoder,
|
||||
id, data, w, h, decoder,
|
||||
metrics,
|
||||
numFrames: frames.length,
|
||||
decoderPromise: undefined,
|
||||
cache: new BitmapBuffer(),
|
||||
@@ -340,7 +342,7 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
this.viewportUpdated();
|
||||
// early schedule to give decoder time to fill the cache before RAF
|
||||
this._schedule(anim);
|
||||
this._animationFrame ??= this._requestAF(ts => this._loop(ts));
|
||||
this._animationFrame ??= this._requestAF(this._loop);
|
||||
}
|
||||
|
||||
|
||||
@@ -363,7 +365,7 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
* Scratches the viewport buffer to collect draws for animation RAF
|
||||
* and to pause/resume invisible/visible animations.
|
||||
*/
|
||||
public viewportUpdated(range?: { start: number, end: number }): any {
|
||||
public viewportUpdated(_range?: { start: number, end: number }): void {
|
||||
const rows = this._terminal.rows;
|
||||
const cols = this._terminal.cols;
|
||||
const buffer = this._terminal._core.buffer;
|
||||
@@ -452,7 +454,7 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
anim.frameIdx = frameIdx;
|
||||
anim.current = anim.frames[frameIdx];
|
||||
if (!anim.decoder && !anim.decoderPromise) {
|
||||
anim.decoderPromise = BlobDecoder(anim.data, anim.mime);
|
||||
anim.decoderPromise = BlobDecoder(anim.data, anim.metrics.mime);
|
||||
}
|
||||
this._schedule(anim);
|
||||
} else if (!isVisible && anim.inViewport) {
|
||||
@@ -462,7 +464,7 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
}
|
||||
}
|
||||
if (visibles.size && this._animationFrame === undefined) {
|
||||
this._animationFrame = this._requestAF(ts => this._loop(ts));
|
||||
this._animationFrame = this._requestAF(this._loop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,7 +530,7 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
this._renderer.draw(d.imgSpec, d.tileId, d.col, d.row, d.count);
|
||||
}
|
||||
}
|
||||
this._animationFrame = anyVisible ? this._requestAF(ts => this._loop(ts)) : undefined;
|
||||
this._animationFrame = anyVisible ? this._requestAF(this._loop) : undefined;
|
||||
};
|
||||
|
||||
|
||||
@@ -571,7 +573,7 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
if (!a.decoder) {
|
||||
const decP = a.decoderPromise;
|
||||
a.decoderPromise = undefined;
|
||||
a.decoder = await (decP ?? BlobDecoder(a.data, a.mime));
|
||||
a.decoder = await (decP ?? BlobDecoder(a.data, a.metrics.mime));
|
||||
}
|
||||
const n1 = a.frames[(a.frameIdx + 1) % a.numFrames];
|
||||
const n2 = a.frames[(a.frameIdx + 2) % a.numFrames];
|
||||
@@ -582,7 +584,7 @@ export class AnimationManager implements IDisposable, IResetHandler {
|
||||
if (n <= a.highest) {
|
||||
a.highest = -1;
|
||||
a.decoder?.close();
|
||||
a.decoder = await BlobDecoder(a.data, a.mime);
|
||||
a.decoder = await BlobDecoder(a.data, a.metrics.mime);
|
||||
}
|
||||
a.highest = Math.max(n, a.highest);
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ export class IIPHandler implements IOscHandler, IResetHandler {
|
||||
.then(bm => {
|
||||
const id = this._storage.addImage(bm);
|
||||
if (this._aniManager && metrics.animated) {
|
||||
this._aniManager.registerAnimation(id, blob as Blob, metrics.mime, w, h);
|
||||
this._aniManager.registerAnimation(id, blob as Blob, metrics, w, h);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
|
||||
@@ -70,9 +70,10 @@ const DEFAULT_OPTIONS: IImageAddonOptions = {
|
||||
showPlaceholder: true,
|
||||
iipSupport: true,
|
||||
iipSizeLimit: 33554432,
|
||||
iipAnimation: true, // alpha
|
||||
kittySupport: true,
|
||||
kittySizeLimit: 33554432
|
||||
kittySizeLimit: 33554432,
|
||||
animationSupport: true, // alpha
|
||||
animationPixelLimit: 4194304 // limit to 2048 * 2048 pixels
|
||||
};
|
||||
|
||||
// max palette size supported by the sixel lib (compile time setting)
|
||||
@@ -147,7 +148,7 @@ export class ImageAddon implements ITerminalAddon, IImageApi {
|
||||
terminal.options.windowOptions = windowOps;
|
||||
}
|
||||
|
||||
if (this._opts.iipAnimation && typeof ImageDecoder === 'function') {
|
||||
if (this._opts.animationSupport && typeof ImageDecoder === 'function') {
|
||||
this._aniManager = new AnimationManager(this._storage, this._renderer, terminal);
|
||||
this._disposeLater(terminal.onRender(range => this._aniManager!.viewportUpdated(range)));
|
||||
}
|
||||
|
||||
@@ -39,9 +39,10 @@ export interface IImageAddonOptions {
|
||||
sixelSizeLimit: number;
|
||||
iipSupport: boolean;
|
||||
iipSizeLimit: number;
|
||||
iipAnimation: boolean;
|
||||
kittySupport: boolean;
|
||||
kittySizeLimit: number;
|
||||
animationSupport: boolean;
|
||||
animationPixelLimit: number;
|
||||
}
|
||||
|
||||
export interface IResetHandler {
|
||||
|
||||
@@ -24,9 +24,10 @@ export interface IImageAddonOptions {
|
||||
sixelSizeLimit: number;
|
||||
iipSupport: boolean;
|
||||
iipSizeLimit: number;
|
||||
iipAnimation: boolean;
|
||||
kittySupport: boolean;
|
||||
kittySizeLimit: number;
|
||||
animationSupport: boolean;
|
||||
animationPixelLimit: number;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
@@ -138,9 +139,10 @@ test.describe('ImageAddon', () => {
|
||||
showPlaceholder: true,
|
||||
iipSupport: true,
|
||||
iipSizeLimit: 33554432,
|
||||
iipAnimation: true, // alpha
|
||||
kittySupport: true,
|
||||
kittySizeLimit: 33554432
|
||||
kittySizeLimit: 33554432,
|
||||
animationSupport: true,
|
||||
animationPixelLimit: 4194304
|
||||
};
|
||||
deepStrictEqual(await ctx.page.evaluate(`window.imageAddon._opts`), DEFAULT_OPTIONS);
|
||||
});
|
||||
@@ -156,9 +158,10 @@ test.describe('ImageAddon', () => {
|
||||
showPlaceholder: false,
|
||||
iipSupport: false,
|
||||
iipSizeLimit: 1000,
|
||||
iipAnimation: true, // alpha
|
||||
kittySupport: false,
|
||||
kittySizeLimit: 1000
|
||||
kittySizeLimit: 1000,
|
||||
animationSupport: false,
|
||||
animationPixelLimit: 1000
|
||||
};
|
||||
await ctx.page.evaluate(opts => {
|
||||
(window as any).imageAddonCustom = new ImageAddon(opts.opts);
|
||||
|
||||
Reference in New Issue
Block a user