Image addon for xterm.js

Overview

xterm-addon-image

Image output in xterm.js.

⚠️ This is an experimental addon, that is still under construction. ⚠️

Install from npm (not yet released)

npm install --save xterm-addon-image

Clone & Build

The addon integrates tightly with the xterm.js base repo, esp. for tests and the demo. To properly set up all needed resources see bootstrap.sh or run it directly with

curl -s https://raw.githubusercontent.com/jerch/xterm-addon-image/master/bootstrap.sh | bash

The addon sources and npm package definition resides under addons/xterm-addon-image.

Usage

import { Terminal } from 'xterm';
import { ImageAddon, IImageAddonOptions } from 'xterm-addon-image';

const WORKER_PATH = '/path/to/xterm-addon-image-worker.js';

// customize as needed
const customSettings: IImageAddonOptions = {
  sixelSupport: true,
  ...
}

// initialization
const terminal = new Terminal();
const imageAddon = new ImageAddon(WORKER_PATH, customSettings);
terminal.loadAddon(imageAddon);

General Notes

  • The image decoding is done with a worker, therefore the addon will only work, if you expose the worker file as well. The worker is distributed under lib/xterm-addon-image-worker.js. Place the exported worker path as the first argument of the addon constructor, e.g. new ImageAddon('/your/path/to/image/worker'). Additionally make sure, that your main integration has proper read and execution permissions on the worker file, otherwise the addon will log a worker error and disable itself on the first image decoding attempt (lazy evaluated).

  • By default the addon will activate these windowOptions reports on the terminal:

    • getWinSizePixels (CSI 14 t)
    • getCellSizePixels (CSI 16 t)
    • getWinSizeChars (CSI 18 t)

    to help applications getting useful terminal metrics for their image preparations. Set enableSizeReports in the constructor options to false, if you dont want the addon to alter these terminal settings. This is especially useful, if you have very strict security needs not allowing any terminal reports, or deal with windowOptions by other means.

Operation Modes

  • SIXEL Support
    Set by default, change it with {sixelSupport: true}.

  • Scrolling On | Off
    By default scrolling is on, thus an image will advance the cursor at the bottom if needed. The cursor will move with the image and be placed either right to the image or in the next line (see cursor positioning).

    If scrolling is off, the image gets painted from the top left of the current viewport and might be truncated if the image exceeds the viewport size. The cursor position does not change.

    You can customize this behavior with the constructor option {sixelScrolling: false} or with DECSET 80 (off, binary: \x1b [ ? 80 h) and DECRST 80 (on, binary: \x1b [ ? 80 l) during runtime.

  • Cursor Positioning
    If scrolling is set, the cursor will be placed at the beginning of the next row by default. You can change this behavior with the following terminal sequences:

    • DECSET 8452 (binary: \x1b [ ? 8452 h)
      For images not overflowing to the right, the cursor will move to the next right cell of the last image cell. Images overflowing to the right, move the cursor to the next line. Same as the constructor option {cursorRight: true}.

    • DECRST 8452 (binary: \x1b [ ? 8452 l)
      Always moves the cursor to the next line (default). Same as the constructor option {cursorRight: false}.

    • DECRST 7730 (binary: \x1b [ ? 7730 l)
      Move the cursor on the next line to the image start offset instead of the beginning. This setting only applies if the cursor will wrap to the next line (thus never for scrolling off, for DECSET 8452 only after overflowing to the right). Same as the constructor option {cursorBelow: true}.

    • DECSET 7730 (binary: \x1b [ ? 7730 h)
      Keep the cursor on the next line at the beginning (default). Same as the constructor option {cursorBelow: false}.

  • SIXEL Palette Handling
    By default the addon limits the palette size to 256 registers (as demanded by the DEC specification). The limit can be increased to a maximum of 4096 registers (via sixelPaletteLimit).

    If sixelPrivatePalette is set (default), images are initialized with their own private palette derived from the default palette ('VT340-COLOR'). If sixelPrivatePalette is not set, the palette of the previous image will be used as initial palette.

    Note that the underlying SIXEL library currently applies colors immediately to pixels (printer mode), thus it is technically possible to use more colors in one image than the palette has color slots. This feature is called high-color in libsixel.

    In contrast older terminals were always bound to the palette due hardware limitations. This limitation is mimicked by xterm's shared palette mode, which will re-color previous images from palette changes treating all sixel images as indexed images. This true shared-palette terminal mode is currently not supported by xterm.js, as it always operates in printer mode.

  • SIXEL Raster Attributes Handling
    If raster attributes were found in the SIXEL data (level 2), the image will always be limited to the given height/width extend. We deviate here from the specification on purpose, as it allows several processing optimizations. For level 1 SIXEL data without any raster attributes the image can freely grow in width and height up to the last data byte, which has a much higher processing penalty. In general encoding libraries should not create level 1 data anymore and should not produce pixel information beyond the announced height/width extend. Both is discouraged by the >30 years old specification.

    Currently the SIXEL implementation of the addon does not take custom pixel sizes into account, a SIXEL pixel will map 1:1 to a screen pixel.

Storage and Drawing Settings

The internal storage holds images up to storageLimit (in MB, calculated as 4-channel RBGA unpacked, default 100 MB). Once hit images get evicted by FIFO rules. Furthermore images on the alternate buffer will always be erased on buffer changes.

The addon exposes two properties to interact with the storage limits at runtime:

  • storageLimit
    Change the value to your needs at runtime. This is especially useful, if you have multiple terminal instances running, that all add to one upper memory limit.
  • storageUsage
    Inspect the current memory usage of the image storage.

By default the addon will show a placeholder pattern for evicted images that are still part of the terminal (e.g. in the scrollback). The pattern can be deactivated by toggling showPlaceholder.

Image Data Retrieval

The addon provides the following API endpoints to retrieve raw image data as canvas:

  • getImageAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined
    Returns the canvas containing the original image data (not resized) at the given buffer position. The buffer position is the 0-based absolute index (including scrollback at top).

  • extractTileAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined
    Returns a canvas containing the actual single tile image data (maybe resized) at the given buffer position. The buffer position is the 0-based absolute index (including scrollback at top). Note that the canvas gets created and data copied over for every call, thus it is not suitable for performance critical actions.

Memory Usage

The addon does most image processing in Javascript and therefore can occupy a rather big amount of memory. To get an idea where the memory gets eaten, lets look at the data flow and processing steps:

  • Incomming image data chunk at term.write (terminal)
    term.write might stock up incoming chunks. To circumvent this, you will need proper flow control (see xterm.js docs). Note that with image output it is more likely to run into this issue, as it can create lots of MBs in very short time.
  • Sequence Parser (terminal)
    The parser operates on a buffer containing up to 2^17 codepoints (~0.5 MB).
  • Sequence Handler - Chunk Processing (addon / mainthread)
    Image data chunks are copied over and sent to the decoder worker as transferables with postMessage. To avoid a data congestion at the message port, allowed SIXEL data is hard limited by sixelSizeLimit (default 25 MB). The transport chunks are pooled, the pool cache may hold up to ~6 MB during active data transmission.
  • Image Decoder (addon / worker)
    The decoder works chunkwise allocating memory as needed. The allowed image size gets restricted by pixelLimit (default 16M pixels), the decoder holds 2 pixel buffers at maximum during decoding (RGBA, ~128 MB for 16M pixels). After decoding the final pixel buffer is transferred back to the sequence handler.
  • Sequence Handler - Image Finalization (addon / mainthread)
    The pixel data gets written to a canvas of the same size (~64 MB for 16M pixels) and added to the storage. The pixel buffer is sent back to the worker to be used for the next image.
  • Image Storage (addon / mainthread)
    The image storage implements a FIFO cache, that will remove old images, if a new one arrives and storageLimit is hit (default 128 MB). The storage holds a canvas with the original image, and may additionally hold resized versions of images after a font rescaling. Both are accounted in storageUsage as a rough estimation of pixels x 4 channels.

Following the steps above, a rough estimation of maximum memory usage by the addon can be calculated with these formulas (in bytes):

// storage alone
const storageBytes = storageUsage * storageLimit * 1024 * 1024;
// decoding alone
const decodingBytes = sixelSizeLimit + 2 * (pixelLimit * 4);

// totals
// inactive decoding
const totalInactive = storageBytes;
// active decoding
const totalActive = storageBytes + decodingBytes;

Note that browsers have offloading tricks for rarely touched memory segments, esp. storageBytes might not directly translate into real memory usage. Usage peaks will happen during active decoding of multiple big images due to the need of 2 full pixel buffers at the same time, which cannot be offloaded. Thus you may want to keep an eye on pixelLimit under limited memory conditions.
Further note that the formulas above do not respect the Javascript object's overhead. Compared to the raw buffer needs the book keeping by these objects is rather small (<<5%).

Why should I care about memory usage at all?
Well you don't have to, and it probably will just work fine with the addon defaults. But for bigger integrations, where much more data is held in the Javascript context (like multiple terminals on one page), it is likely to hit the engine's memory limit sooner or later under decoding and/or storage pressure.

How can I adjust the memory usage?

  • pixelLimit
    A constructor settings, thus you would have to anticipate, whether (multiple) terminals in your page gonna do lots of concurrent decoding. Since this is normally not the case and the memory usage is only temporarily peaking, a rather high value should work even with multiple terminals in one page.
  • storageLimit
    A constructor and a runtime setting. In conjunction with storageUsage you can do runtime checks and adjust the limit to your needs. If you have to lower the limit below the current usage, images will be removed and may turn into a placeholder in the terminal's scrollback (if showPlaceholder is set). When adjusting keep in mind to leave enough room for memory peaking for decoding.
  • sixelSizeLimit
    A constructor setting. This has only a small direct impact on peaking memory during decoding. It still will avoid processing of overly big or broken sequences at an earlier phase, thus may stop the decoder from entering its memory intensive task for potentially invalid data.
Comments
  • pending changes

    pending changes

    • remove private / shared palette separation, instead always carry entries forward
    • think about a less invasive palette reset than RIS/DECSTR (maybe hook into some XTSMGRAPHICS action)
    • properly define and document default palette as VT340 (lower 16 colors) + ANSI256 (up to 256) + zeroed (up to 4096)
    • remove other default palettes to shrink package size
    • transit to VT340 cursor positioning, remove all others for sixels
    • update tests & docs regarding changes
    • check if worker loop is properly guarded against malicious sixel data
    opened by jerch 23
  • Sticky image tiles

    Sticky image tiles

    Coming from #16 - there are circumstances, where image tiles are not properly cleared from screen output.

    Until we figured out the root of the issue (prolly some xterm.js action not properly updating the changed lines record), we should do a full clear + redraw cycle for image tiles to get rid of outdated image parts.

    bug 
    opened by jerch 21
  • ETA for npm package

    ETA for npm package

    This seems really useful, but I'm trying to get it working with an upstream project (gotty) that uses base xterm.js, so having the npm version released would be super helpful. Is there an ETA for when that can happen? Thanks so much!

    opened by CoconutMacaroon 3
  • reload default palette from XTSMGRAPHICS

    reload default palette from XTSMGRAPHICS

    Currently default palette reloading is only coupled to RIS and DECSTR, which is rather intrusive to get initial colors back. Until we have something better, clamp "reload default palette" to XTSMGRAPHICS.registers.SET_DEFAULT.

    opened by jerch 2
  • yarn: There are no scenarios

    yarn: There are no scenarios

    Running bootstrap.sh seems to setup the repository correctly, but then just ends with an error message from yarn.

    00h00m00s 0/0: : ERROR: There are no scenarios; must have at least one.
    

    I have never used xterm.js. How does one launch it?

    opened by hackerb9 2
  • possible striping in image output

    possible striping in image output

    On different browser zoom levels (pixelDeviceRatio != 1) image output may show nasty striping. This prolly results from always flooring values here: https://github.com/jerch/xterm-addon-image/blob/2229ef1c5879f1b411f605a83de11e8c7d6785ef/src/ImageRenderer.ts#L184-L185

    A quickfix is to always ceil the right and the bottom values. This further needs a workaround for safari, which will go bonkers on out of bounds access. A more involved solution is to determine the edge cases, where flooring creates those gaps and ceil only in that case (conditional overprinting).

    bug 
    opened by jerch 1
  • adjust to 5.1 changes

    adjust to 5.1 changes

    Prepare for upcoming 5.1 changes in xterm.js.

    TODO, once 5.1.0 is released:

    • [ ] change XTERMJS var to 5.1.0
    • [ ] new 0.3.1 release (+ readme changes)
    opened by jerch 0
  • xterm.js v5 compatibility

    xterm.js v5 compatibility

    TODO:

    • [x] fix code base
      • [x] adjust ExtendedAttr
      • [x] adjust demo code
      • [x] adjust to new popout mode (see https://github.com/xtermjs/xterm.js/pull/4069)
    • [x] fix linter rules
    • [x] fix overwrite files
    • [x] adjust CI tests
    • [x] revert CI script and bootstrap.sh to default branches upon master merge
    opened by jerch 0
  • new default palette

    new default palette

    Shape it as VT340 (lower 16 colors) + ANSI256 (up to 256) + zeroed (up to 4096). Remove all other palettes.

    This furthermore needs a hook to reset the decoder on several occasions, like RIS and buffer switch.

    opened by jerch 0
  • Increase speed a thousand fold by caching

    Increase speed a thousand fold by caching

    On my little laptop, the current script output rate is about 1.8 KBytes/s, which is nearly equal to the speed of the VT340's serial port (19.2 kbps). That is plenty fast for my VT340+ as the graphics processor is (probably) the bottleneck.

    However, the script could be made much faster by caching the calculations in an array. Here is a version that increases the speed from 1.8 KBps to 1.8 MBps, as measured by ./endless.sh | pv -brat > /dev/null.

    Code is included, but commented out, that would increase the speed to 88 MBps by caching into a string instead of an array.

    If desired, startup time could be shrunk. It takes about three seconds to create the cache during which the output rate is the same as before (1.8 KBps), but that be done more swiftly by moving the for loop within bc instead of forking bc 600 times.

    opened by hackerb9 10
  • proper vertical cursor advance

    proper vertical cursor advance

    echo -e '\x1bPq$-$-$-$-\x1b\\' should move the text cursor downwards, even if no pixels were modified (given sixel scrolling is on).

    @hackerb9 If you find some time, can you confirm, that an "empty" sixel sequence (no pixel notion) with just GCR/GLFs would still advance the text cursor downwards on a vt340? Thats at least how I read the sixel docs, but kinda none of the terminals except xterm does it atm, so I am not sure if I misinterpret things here.

    bug 
    opened by jerch 39
  • allow transparency composition

    allow transparency composition

    In older terminals the graphics output would get printed above any previous content. With backgroundSelect=1 SIXEL has a mode to take advantage of that, where old pixels get not touched, if not explicitly colored by SIXEL data.

    We cannot do it quite the same way, as we mix in graphics late during rendering. We could still fake the partial "overprinting" by allowing transparent pixels to blend with old pixels from a previous graphics print.

    opened by jerch 9
Owner
null
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
optimize image & upload file to cloud as image bed with tiny image automic.

Rush! 图片压缩 & 直传图床工具 这是一个兴趣使然的项目, 希望 Rush! 能让这个世界的网络资源浪费减少一点点 下载 Downloads 获取最新发行版 功能 Features 拖拽批量压缩图片, 支持格式 jpg/png/gif Drop to optimize, jpg/png/gif

{ Chao } 3 Nov 12, 2022
Simple & Quick Access Addon For Home Assistant

Home Assistant - Firefox Addon Quick Access Home Assistant - Firefox Addon Usage Create a Custom Dashboard With Quick Access Entity In Home Assistant

Varun Sridharan 7 Dec 25, 2022
Simple & Quick Access Addon For Homer Dashboard

Homer Dashboard - Firefox Addon Quick Access Homer Dashboard - Firefox Addon Usage Install Addon [ Firefox ] Configure The Addon Add The Quick Access

Varun Sridharan 7 Jan 22, 2022
L'addon qui sauveras les prochaines années

GachaCleaner Une Extension Firefox pour purifier la lecture Download Changer "iel" ?? en : Permet de changer l'inexistant terme en quelque chose qui e

null 7 Jul 4, 2022
Jump to github urls (browser addon)

Currently a Firefox addon. Find GitHub locations quickly using your browser's history. Usage Use your mouse or keyboard. Use the filter to search for

madprops 4 Nov 28, 2022
This is a dependency-free easy-to-use vanilla JavaScript addon allowing you to create HTML currency inputs with various different currencies and formattings.

intl-currency-input This is a dependency-free easy-to-use vanilla JavaScript addon allowing you to create HTML currency inputs with various different

null 6 Jan 4, 2023
p5.grain is a p5.js addon for conveniently adding grain and texture overlays to artworks.

?? p5.grain p5.grain is a p5.js addon for conveniently adding grain and texture overlays to artworks. p5.grain was also created with fxhash projects i

meezwhite 39 Dec 24, 2022
Storybook Addon Root Attributes to switch html, body or some element attributes (multiple) at runtime for you story

Storybook Addon Root Attributes What is this This project was inspired by le0pard/storybook-addon-root-attribute The existing library received only on

정현수 5 Sep 6, 2022
An addon/plugin template for Zotero.

Zotero Addon Template This is an addon/plugin template for Zotero. Documentation(Chinese, provides English translation) ?? You are currently in bootst

null 36 Jan 2, 2023
🖼️ Image proxy for Next.js. Makes it possible to use dynamic domains in next/image component.

Next.js Image Proxy Image proxy for Next.js. Makes it possible to use dynamic domains in next/image component. ❔ Motivation This library makes it poss

Blazity 30 Dec 1, 2022
A Javascript library that discourages and prevents image theft/download by preventing client ability to retrieve the image.

ProtectImage.js ProtectImage.js is a Javascript library that helps prevent image theft by disabling traditional user interactions to download/copy ima

null 4 Aug 18, 2022
Simple JavaScript Library to add parallax image to background-image

Backpax Simple JavaScript Library to add parallax image to background-image Install $ npm install backpax --save Demo Demo page is here Usage If you

appleple 13 Oct 12, 2022
Piccloud is a full-stack (Angular & Spring Boot) online image clipboard that lets you share images over the internet by generating a unique URL. Others can access the image via this URL.

Piccloud Piccloud is a full-stack application built with Angular & Spring Boot. It is an online image clipboard that lets you share images over the in

Olayinka Atobiloye 3 Dec 15, 2022
Hashmat Noorani 4 Mar 21, 2023
Change the color of an image to a specific color you have in mind.

image-recolor Run it: https://image-recolor.vercel.app/ image.recolor.mov Acknowledgments Daniel Büchele for the algorithm: https://twitter.com/daniel

Christopher Chedeau 21 Oct 25, 2022
A refined tool for exploring open-source projects on GitHub with a file tree, rich Markdown and image previews, multi-pane multi-tab layouts and first-class support for Ink syntax highlighting.

Ink codebase browser, "Kin" ?? The Ink codebase browser is a tool to explore open-source code on GitHub, especially my side projects written in the In

Linus Lee 20 Oct 30, 2022