1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
.\" Automatically generated by Pandoc 3.5
.\"
.TH "cha\-image" "5" "" "" "Image support in Chawan"
.SH Inline images
On terminals that support images, Chawan can display various bit\-mapped
image formats.
.SS Enabling images
There are actually two switches for images in the config:
.IP \[bu] 2
buffer.images: this enables downloading images, \f[I]even if they cannot
be displayed\f[R].
.IP \[bu] 2
display.image\-mode: sets the inline image display method.
Defaults to \[lq]auto\[rq], but may also be set to \[lq]sixel\[rq] or
\[lq]kitty\[rq] manually.
.PP
In most cases, all you need to do is to set \[lq]buffer.images\[rq] to
true:
.IP
.EX
\f[I]# in \[ti]/.chawan/config.toml (or \[ti]/.config/chawan/config.toml)\f[R]
\f[B][buffer]\f[R]
images = true
.EE
.PP
With the default image\-mode, Chawan will find the best image display
method supported by your terminal.
However, if your terminal fails to tell Chawan that it can display
sixels, you may also have to set \[lq]display.image\-mode\[rq]
appropriately.
See below for further discussion of sixel configuration.
.SS Output formats
Supported output formats are:
.IP \[bu] 2
The DEC Sixel format
.IP \[bu] 2
The Kitty terminal graphics protocol
.PP
The former is supported because it\[cq]s ubiquitiously adopted; the
latter because it is technically superior to all existing alternatives.
.PP
Support for other protocols (iTerm, MLTerm, etc.)
is not planned.
(To my knowledge, all image\-capable terminals support at least one of
the above two anyways.)
.PP
Support for hacks such as w3mimgdisplay, ueberzug, etc.
is not planned.
.SS Sixel
Sixel is the most widely supported image format.
See \c
.UR https://arewesixelyet.com
.UE \c
\ to find a terminal that supports it.
.PP
Known quirks and implementation details:
.IP \[bu] 2
XTerm needs extensive configuration for ideal sixel support.
In particular, you will want to set the decTerminalID,
numColorRegisters, and maxGraphicSize attributes.
See \f[CR]man xterm\f[R] for details.
.IP \[bu] 2
We assume private color registers are supported.
On terminals where they aren\[cq]t (e.g.\ SyncTERM or hardware
terminals), colors will get messed up with multiple images on screen.
.IP \[bu] 2
We send XTSMGRAPHICS for retrieving the number of color registers; on
failure, we fall back to 256.
You can override color register count using the
\f[CR]display.sixel\-colors\f[R] configuration value.
.IP \[bu] 2
For the most efficient sixel display, you will want a cell height that
is a multiple of 6.
Otherwise, the images will have to be re\-coded several times on scroll.
.IP \[bu] 2
Normally, Sixel encoding runs in two passes.
On slow computers, you can try setting
\f[CR]display.sixel\-colors = 2\f[R], which will skip the first pass
(but will also display everything in monochrome).
.IP \[bu] 2
Transparency \f[I]is\f[R] supported, but looks weird because we
approximate an 8\-bit alpha channel with Sixel\[cq]s 1\-bit alpha
channel.
Also, some terminals don\[cq]t emulate it correctly \- when in doubt,
try XTerm (which does).
.SS Kitty
On terminals that support it, Kitty\[cq]s protocol is preferred over
Sixel.
Its main benefit is that images do not have to be sent again every time
they move on the screen (i.e.\ on scroll), but the initial transfer
should also be faster (because PNG\[cq]s compression tends to outperform
Sixel\[cq]s RLE).
.PP
Unlike Sixel, the Kitty protocol fully supports transparency.
.SS Input formats
Currently, the supported input formats are:
.IP \[bu] 2
BMP, PNG, JPEG, GIF (through stb_image)
.IP \[bu] 2
WebP (through JebP)
.PP
More formats may be added in the future, provided there exists a
reasonably small implementation, preferably in the public domain.
(I do not want to depend on external image decoding libraries, but
something like stbi is OK to vendor.)
.SS Codec module system
All image codec implementations are specified by the URL scheme
\[lq]img\-codec+name:\[rq], where \[lq]name\[rq] is the MIME subtype.
e.g.\ for image/png, it is \[lq]img\-codec+png:\[rq].
(This indeed means that only \[lq]image\[rq] MIME types can be used.)
.PP
Like all schemes, these are defined (and overridable) in the
urimethodmap file, and are implemented as local CGI programs.
These programs take an encoded image on stdin, and dump the decoded RGBA
data to stdout \- when encoding, vice versa.
.PP
This means that it is possible for users to define image decoders for
their preferred formats, or even override the built\-in ones.
(If you actually end up doing this for some reason, please shoot me a
mail so I can add it to the bonus directory.)
.PP
A codec can have one of, or both, \[lq]decode\[rq] and \[lq]encode\[rq]
instructions; these are set in the path name.
So \[lq]img\-codec+png:decode\[rq] is called for decoding PNGs, and
\[lq]img\-codec+png:encode\[rq] for encoding them.
.PP
Headers are used for transferring metadata (like image dimensions), both
from the browser (input) and to the browser (output).
Detailed description of the decoder & encoder interfaces follows.
.SS decoding
When the path equals \[lq]decode\[rq], a codec CGI script must take a
binary stream of an encoded image on its standard input and print the
equivalent binary stream of big\-endian 8\-bit (per component) RGBA
values to stdout.
.PP
Input headers:
.IP \[bu] 2
Cha\-Image\-Info\-Only: 1
.PP
This tells the image decoder to only send image metadata (i.e.\ size).
Technically, the decoder is free to actually decode the image too, but
the browser will ignore any output after headers.
.PP
Output headers:
.IP \[bu] 2
Cha\-Image\-Dimensions: {width}x{height}
.PP
The size of the decoded image.
.PP
The dimension format is such that e.g.\ for 123x456, 123 is width and
456 is height.
.SS encoding
When the path equals \[lq]encode\[rq], a codec CGI script must take a
binary stream of big\-endian 8\-bit (per component) RGBA values on its
standard input and print the equivalent encoded image to its standard
output.
.PP
Input headers:
.IP \[bu] 2
Cha\-Image\-Dimensions: {width}x{height}
.PP
Specifies the dimensions of the input RGBA image.
This means that {width} * {height} * 4 == {size of data received on
stdin}.
.PP
The format is the same as above; in fact, the design is such that you
could directly pipe the output of decode to encode (and vice versa).
.IP \[bu] 2
Cha\-Image\-Quality: {number}
.PP
The requested encoding quality, ranging from 1 to 100 inclusive
(i.e.\ 1..100).
It is up to the encoder to interpret this number.
.PP
(The stb_image JPEG encoder uses this.)
.PP
Output headers:
.PP
Currently, no output headers are defined for encoders.
.SS Skipping copies with mmap
The naive implementation of the above system would have to copy the
output at least twice when an image is resized.
To skip these copies, stdin and/or stdout is (currently) a file in the
tmp directory for:
.IP \[bu] 2
decode stdin, when the image is already downloaded
.IP \[bu] 2
decode stdout, always
.IP \[bu] 2
encode stdin, always
.PP
This makes it possible to mmap(3) stdin/stdout instead of streaming
through them with read(3) and write(3).
When doing this, mind the following:
.IP \[bu] 2
When reading, you must check your initial position in the file with
lseek(2).
.IP \[bu] 2
When writing, your headers are part of the output.
At the very least, you must place a newline at the file\[cq]s beginning.
.IP \[bu] 2
This \f[I]is\f[R] an implementation detail, and might change at any time
in the future (e.g.\ if we add a \[lq]no cache files\[rq] mode).
Always check for S_ISREG to ensure that you are actually dealing with a
file.
(Use io/dynstream.nim\[cq]s recvDataLoopOrMmap and maybeMmapForSend to
deal with this automatically.)
.SS See also
\f[B]cha\f[R](1)
|