Terminal graphics

This story began when I learned about existence bpytop ... I was amazed at the detail of the charts and I began to figure out how it was done. It turned out that Braille characters were used to display the graphs, which are a combination of 8 points: 2 points in width and 4 points in height. After looking for ready-made solutions using this approach, I found on reddit announcement such the project ... In the very first comment of the announcement, I read:
It's cool, but why don't people just rediscover ReGIS (vector graphics in the terminal) and sixel (pixel graphics in the terminal).
Until this moment, I did not know anything about sixel. Digging deeper, I found out that in theory sixel should be supported by xterm. I ran xterm on my ubuntu 20.04 in vt340 emulation mode

xterm -xrm "XTerm*decTerminalID: vt340" -xrm "XTerm*numColorRegisters: 256" executed the following command (convert is a command from the imagemagick package)

clear && convert <(curl -s https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png) sixel:- and saw this:

Wow, it works!

First of all, I checked that sixel can be found on Habré. It turned out that not a lot. There is a very short article 2010 without technical details and mention in comments to article about porting Far to Linux. Let's see how sixel works and how you can draw with it in a compatible terminal.

The sixel mode is enabled by the escape sequence DCSp1; p2; p3; q, where DCS is the Device Control Sequence (it can be either an eight-bit character with decimal value 144, or a sequence of two seven-bit characters "Escape", "P". Parameters p1, p2 and p3 optional.
p1 Pixel Aspect Ratio (Vertical:Horizontal)
Not specified 2:1 (default)
0, 1 2:1
2 5:1
3, 4 3:1
5, 6 2:1
7, 8, 9 1:1
This parameter is deprecated. In modern code it should be set to 0 and use the bitmap control attribute in the sixel line. Details can be found indocumentation ...

p2 can take 3 values and determines how the terminal draws the background color.
p2 What is he doing
0 or 2 (default) Pixels with a value of 0 are drawn with the current background color.
1 The color of pixels with a value of 0 does not change.
The p3 parameter controls the horizontal grid spacing (the distance between adjacent pixels). This parameter is ignored if the output device has a fixed grid spacing.

In my experiments in xterm, I always entered sixel mode using the sequence 0x1bPq (0x1b - escape) without using optional parameters.

The sixel mode is turned off by the ST (String Terminator) sequence. ST can be either an 8-bit character with a decimal value of 156 or a sequence of two 7-bit characters "Escape, backslash" (ESC \).

After switching the output device to sixel mode, we need to supply graphic data to it. The name sixel is derived from six pixels. In sixel mode, we draw a column of 6 pixels at a time (the least significant bit is the top). Rendering is carried out in the selected color (more on this later). The value of one pixel can be from 0 (all bits 0) to 63 (all bits 1). This value is added to 63 (ascii '?'). Thus, an empty pixel is represented by '?', And a pixel with all bits set to 1 by '~'.

The sixel format provides primitive compression. The construction '! 42 ~' means that we want to output the pixel '~' 42 times. The official documentation does not mention any restrictions on the counter values. Wherein known that vt240 does not use values greater than 255 when dumping graphic data.

When outputting a sixel line, there are 2 ways to control the cursor. The '$' character moves the cursor to the beginning of the same line. This allows pixels to be displayed in different colors. You can select a color, display a set of pixels, return to the beginning of the same line, select a different color, display a different set of pixels. After the current line is drawn properly, you can use the '-' symbol and move the cursor to the next line.

Color management is carried out in 2 stages. First, we need to define the color registers. This can be done with the command #NN; p1; p2; p3; p4. Here

NN register number (from 0 to 255)
p1 - type of color space (1 - HLS or 2 - RGB)
p2, p3, p4 - hue, lightness, saturation values for HLS or red, green, blue for RGB. Allowed parameter values are from 0 to 100 for all except hue, which can vary from 0 to 360.

Now, to switch to a specific color, we need to use a command like #NN.

Armed with this information, we can write the code which will display the following image for us:


For comparison, this is how the same image will look like. code using braille characters:


And the same image displayed code using plain ascii:

Tags: sixel
KlauS 25 march 2021, 19:00
Vote for this post
Bring it to the Main Page


Leave a Reply

Avaible tags
  • <b>...</b>highlighting important text on the page in bold
  • <i>..</i>highlighting important text on the page in italic
  • <u>...</u>allocated with tag <u> text shownas underlined
  • <s>...</s>allocated with tag <s> text shown as strikethrough
  • <sup>...</sup>, <sub>...</sub>text in the tag <sup> appears as a superscript, <sub> - subscript
  • <blockquote>...</blockquote>For  highlight citation, use the tag <blockquote>
  • <code lang="lang">...</code>highlighting the program code (supported by bash, cpp, cs, css, xml, html, java, javascript, lisp, lua, php, perl, python, ruby, sql, scala, text)
  • <a href="http://...">...</a>link, specify the desired Internet address in the href attribute
  • <img src="http://..." alt="text" />specify the full path of image in the src attribute