Check out my first novel, midnight's simulacra!

Notcurses: Difference between revisions

From dankwiki
Line 61: Line 61:


For performance evaluation, I run `notcurses-demo` in each terminal three times. Each terminal is configured to use Inconsolata Medium 12 as its primary font, to 70% opacity (if supported), and run at a 70x80 geometry. I report the average of the three wall clock times, and the three FPS (i.e. frames rendered divided by time spent within <tt>notcurses_render()</tt>) measurements. If DirectColor output could not be rendered, the terminal is reported as a <b>failure</b> (this is perhaps/probably due to my ignorance). Some demos are a fixed number of frames, with a fixed target time. Some are a fixed number of frames, to be rendered as quickly as possible. Some are fixed time, with an intention of rendering as many frames as possible.
For performance evaluation, I run `notcurses-demo` in each terminal three times. Each terminal is configured to use Inconsolata Medium 12 as its primary font, to 70% opacity (if supported), and run at a 70x80 geometry. I report the average of the three wall clock times, and the three FPS (i.e. frames rendered divided by time spent within <tt>notcurses_render()</tt>) measurements. If DirectColor output could not be rendered, the terminal is reported as a <b>failure</b> (this is perhaps/probably due to my ignorance). Some demos are a fixed number of frames, with a fixed target time. Some are a fixed number of frames, to be rendered as quickly as possible. Some are fixed time, with an intention of rendering as many frames as possible.
For the FPS measurement, higher is better. For the time measurement, lower is better.


{| class="wikitable sortable"
{| class="wikitable sortable"
|+ Last evaluated 2019-12-21 (notcurses 0.9.1)
|+ Last evaluated 2019-12-21 (notcurses 0.9.1)
! Emulator
! Emulator
! colspan="2"|Perf <br/> FPS/Time
! colspan="2"|Perf <br/> FPS / Seconds
! <tt>TERM</tt>  
! <tt>TERM</tt>  
! Notes
! Notes
Line 71: Line 73:
| xterm
| xterm
| 11.87
| 11.87
|
| 196.8
| xterm-direct
| xterm-direct
| <tt>eagle</tt> and <tt>view</tt>were very choppy<br/><tt>widecolor</tt> doesn't colorize flame emoji<br/><tt>uniblock</tt> ran without box breakage (only terminal to do so)
| <tt>eagle</tt> and <tt>view</tt> were very choppy<br/><tt>widecolor</tt> doesn't colorize flame emoji<br/><tt>uniblock</tt> ran without box breakage (only terminal to do so)
|-
|-
| rxvt
| rxvt
Line 83: Line 85:
| libvte<br/>(using xfce4-terminal)
| libvte<br/>(using xfce4-terminal)
| 205.9
| 205.9
|
| 75.8
| vte-direct
| vte-direct
|
|
Line 89: Line 91:
| kitty
| kitty
| 682.3
| 682.3
|
| 72.9
| kitty-direct
| kitty-direct
| <tt>uniblock</tt> is deformed in a varying fashion<br/><tt>widecolor</tt> is missing some ranges<br/><tt>widecolor</tt> replaces some ranges with blocks<br/>seems very smooth under load
| <tt>uniblock</tt> is deformed in a varying fashion<br/><tt>widecolor</tt> is missing some ranges<br/><tt>widecolor</tt> replaces some ranges with blocks<br/>seems very smooth under load
Line 95: Line 97:
| alacritty
| alacritty
| 553.7
| 553.7
|
| 74.0
| alacritty-direct
| alacritty-direct
| <tt>uniblock</tt> doesn't fade in properly
| <tt>uniblock</tt> doesn't fade in properly
Line 101: Line 103:
| terminator<br/>(also VTE, but python)
| terminator<br/>(also VTE, but python)
| 206.0
| 206.0
|
| 75.7
| vte-direct
| vte-direct
|
|
Line 113: Line 115:
| konsole
| konsole
| 168.5
| 168.5
|
| 83.8
| konsole-direct
| konsole-direct
| <tt>uniblock</tt> doesn't fade in properly<br/><tt>uniblock</tt> persistent damage on one line<br/>boldface seems particularly strong
| <tt>uniblock</tt> doesn't fade in properly<br/><tt>uniblock</tt> persistent damage on one line<br/>boldface seems particularly strong

Revision as of 03:48, 22 December 2019

My library for building complex, vibrant textual user interfaces (TUIs) on modern terminal emulators. It does not use Ncurses (though it does make use of libtinfo from that package), nor is it an X/Open Curses source-compatible replacement. It is written in C, with C++-safe headers.

Source and issue-tracking live at Github.

Contact sheet from Notcurses 0.4.0 demo
Contact sheet from Notcurses 0.4.0 demo

Features

  • Optional use of "alternate screen" where available (smcup/rmcup terminfo capabilities)
  • All APIs use 24-bit 8bpc RGB color natively
    • Color is quantized down for indexed palette terminals
  • Transparency/semi-transparency plus dynamic high-contrast text
    • Lower planes can affect color of higher translucent ones
    • Sprites!
  • Full support for Unicode, including wide glyphs and bidirectional text
    • Composed keys (number pad, etc.) are mapped into Private Supplementary Area B
  • Image/video support via ffmpeg
  • Subregion fade in/out
  • Linear interpolation for coloring geometric objects

Rendering

The vast majority of functions draw to ncplane objects. A partial order (currently a total order) always exists over the planes. There is always at least one plane, the "standard plane". This plane cannot be resized, deleted, moved relative to the visible area, or reparented. Whenever notcurses updates its idea of the visible area's dimensions, it will resize the standard plane (references to the standard plane are not invalidated). Planes may otherwise be any size (invisible regions will not be rendered, and count only against memory), and can be moved to any position relative to the visible area. All planes, including the standard plane, can be freely moved along the z axis.

Every plane, and the notcurses object as a whole, maintains a per-row damage map, one boolean entry per line of the plane. These damage maps are likely to be replaced with O(1) damage detection.

When notcurses_render() is called, the visual area is constructed from the top left to the bottom right, row by row and column by column.

Each row is considered undamaged until proven otherwise. At each column, the character to be emitted (and its styling) is computed. This will require consulting some number of planes (see Transparency for more information). A damage value is computed as the boolean union over all relevant damage map entries. Relevant entries are those from the toplevel damage map, and the damage maps of all planes which contributed to the output glyph or styling.

Transparency/Contrasting

It is not obvious what "transparency" or "alpha blending" means in a character context. I have assigned my own meanings. Each of the foreground and background channel of an ncplane's cell have two bits dedicated to alpha. Channels are always initialized to 0, and thus the default alpha setting is CELL_ALPHA_OPAQUE. It is important to note that glyph selection is independent of alpha. The first glyph found while descending the planes intersecting a cell will be the glyph used. Only presentation of the glyph is modified by alpha.

Value Macro Foreground Background
00 CELL_ALPHA_OPAQUE Use the fg color unchanged. Use the bg color unchanged.
01 CELL_ALPHA_BLEND Blend the fg color down. Blend the bg color down.
10 CELL_ALPHA_TRANSPARENT Select the next fg color. Select the next bg color.
11 CELL_ALPHA_HIGHCONTRAST Complement bg color computed through this plane. Forbidden.

The value 11 is currently forbidden for a bg alpha setting, but might be used in the future. To "blend the color down" means to average the colors encountered until hitting an opaque channel, or running out of planes. To "select the next color" means to ignore this color, and instead take the color as computed by lower planes.

High-contrast text is not strictly defined. notcurses will attempt to make the glyph as readable as possible, given the background color computed at the plane.

Note that a cell with the zero glyph will not have its channels considered. The containing plane's default channels will instead be factored into any color/transparency calculations (if a default glyph has been defined for the plane). A cell containing a zero glyph on a plane with a default zero glyph cannot impact the rendered scene; any associated channels will be ignored.

If loaded multimedia supports transparency (e.g. PNGs), transparent pixels will be considered as if CELL_ALPHA_TRANSPARENT was used.

Sprites

Transparency plus bitmaps yield sprites. The "luigi" demo uses three bitmaps from Luigi's walking cycle in Super Mario Bros. Only one is shown at a time; the other two are hidden under the standard plane (onto which a background has been rendered).

Linear interpolation

notcurses can color lines via linear interpolation between the two endpoints. This is done with ncplane_hline_interp() and ncplane_vline_interp(). If provided two endpoints of the same color, the line will be that single color.

Terminal emulators

The two primary environmental factors affecting notcurses are the terminal emulator and the configured fonts.

For performance evaluation, I run `notcurses-demo` in each terminal three times. Each terminal is configured to use Inconsolata Medium 12 as its primary font, to 70% opacity (if supported), and run at a 70x80 geometry. I report the average of the three wall clock times, and the three FPS (i.e. frames rendered divided by time spent within notcurses_render()) measurements. If DirectColor output could not be rendered, the terminal is reported as a failure (this is perhaps/probably due to my ignorance). Some demos are a fixed number of frames, with a fixed target time. Some are a fixed number of frames, to be rendered as quickly as possible. Some are fixed time, with an intention of rendering as many frames as possible.

For the FPS measurement, higher is better. For the time measurement, lower is better.

Last evaluated 2019-12-21 (notcurses 0.9.1)
Emulator Perf
FPS / Seconds
TERM Notes
xterm 11.87 196.8 xterm-direct eagle and view were very choppy
widecolor doesn't colorize flame emoji
uniblock ran without box breakage (only terminal to do so)
rxvt x x failure failure
libvte
(using xfce4-terminal)
205.9 75.8 vte-direct
kitty 682.3 72.9 kitty-direct uniblock is deformed in a varying fashion
widecolor is missing some ranges
widecolor replaces some ranges with blocks
seems very smooth under load
alacritty 553.7 74.0 alacritty-direct uniblock doesn't fade in properly
terminator
(also VTE, but python)
206.0 75.7 vte-direct
terminology x x failure failure
konsole 168.5 83.8 konsole-direct uniblock doesn't fade in properly
uniblock persistent damage on one line
boldface seems particularly strong

Note: some of these terminals (e.g. xterm) export an RGB-capable setaf/setab, which notcurses does not use due to its special casing of low values. notcurses instead generates its own escapes outside of the terminfo framework. I am uncertain as to whether this might have performance effects.

Raw data:

alacritty: 10140, 10121, 10143, 555.6, 553.8, 551.8, 1m13.9, 1m14.0, 1m14.1s
konsole: 5655, 6286, 6433, 152.9, 174.3, 178.3, 1m24.3, 1m23.6, 1m23.5
terminator: 4628, 4788, 4759, 211.1, 208.6, 198.4, 1m15.1, 1m15.6, 1m16.5
kitty: 9475, 9539, 9518, 679.4, 687.5, 679.9, 1m12.9, 1m12.8, 1m12.9
xfce4-terminal: 4873, 4431, 4787, 214.7, 191.5, 211.4, 1m16.0, 1m16.1, 1m15.2
xterm: 1926, 1914, 1918, 11.8, 12.0, 11.8, 3m18.0, 3m14.7, 3m17.8

Releases

Release Date Key features
0.9.1 2019-12-21 Visual-adaptive planes, true ncvisual transparency, char32_t input
0.9.0 2019-12-18 Recognize COLORTERM, damage map, quantized colors, _yx API extensions, alignment
0.4.0 2019-12-05 Cell API, input, resize handling
0.3.0 2019-12-02 Video support, transparent planes, fades
0.2.0 2019-12-02 Panelreels, image support
0.1.0 2019-11-30 Ncplanes, basic output

See Also