This patch is a slightly modified version of the original patch made by kmiya found at
http://nanno.dip.jp/softlib/man/rlogin/#REGWIND
It implements a new terminal sixelgd
which produces sixel graphics like the sixel
terminal, but uses gdlib to draw instead of the built-in bitmap code. It hence offers support for "truecolor", truetype fonts, transparency, and anti-aliased lines. Plots with more than 256 colors are displayed using dithering.
The original sixel
terminal can by found here: [patches:#647].
Additional changes by me:
sixelgd
to avoid name conflict with the existing sixel
terminaltransparent
in truecolor
mode by filling with "zeroth" colorThe libsixel library https://github.com/saitoha/libsixel/ is derived from the same code, and is maintained actively. So it might be worthwhile considering to use that instead of including parts of its code. I am not sure though if that wouldn't require having a second copy of the bitmap in memory and if that solution would still support "transparent" graphs. On the other hand this patch adds only around ~500 lines and does not add another dependency and configuration check.
The same could be implemented using the cairo terminal code rather easily.
License: The code in sixel.c is should be covered by a permissive license which is only given in Japanese. Below is the translation given on the libsixel github site:
Anyone is free to use this program for any purpose,
commercial or non-commercial, without any restriction.Anyone is free to distribute, copy, publish, or
advertise this software, without any contact.Anyone is free to distribute with modification of the
source code, but I "hope" that its based version or
date will be written clearly.
2014/10/05 kmiya
I really like this. It removes the last reason I had for configuring --with-bitmap-terminals
Comments from initial testing. I am using xterm in vt340 mode to display sixel graphics.
- The file sixel.c should include attribution and copyright information
I see some peculiar artifacts.
- There is a solid dark bar across the top of each plot, not always the same color but usually black or grey. I think perhaps the color is taken from the last pen used in the previous plot,
- Use of transparency causes splotches of pure black, particularly in the key samples. The attached image shows both of these artifacts.
- Infrequently, the terminal background flips to a different color (again usually black or gray).
These various glitches are visible in the output from the "test" command as well as in individual plots.
Except maybe the odd transparency in key samples, these problems are likely due to incomplete initialization at the start of a new plot.
Last edit: Ethan Merritt 2016-11-30
Interesting. No such observations here using mintty 2.6.2 terminal on Windows. Works for both
truecolor
andnotruecolor
modes. So maybe the sixel encoding is no quite compatible with xterm?Here's an updated version which is based on a more recent version of
sixel.c
and now also includes license information. Does that still give errors for you?One nice thing about sixel graphics is that you can debug it by dumping the output to file and then using a normal text editor to fiddle with it.
What I see is that the terminal output is using a palette of 256 RGB colors rather than a smaller set of indexed colors even if you select "notruecolor" rather than "truecolor". A real DEC vt340 terminal only offered 16 colors. The xterm vt340 emulator supposedly supports up to 256 color indices, but that doesn't seem to be true in practice. If I edit sixel.c to use only 16 colors rather than gdMaxColors then the default (notruecolor) mode works better, but the truecolor mode for some reason stops working. That's weird because in truecolor mode it shouldn't matter what the palette size is. Maybe there is a conflict in the expectations of gd.trm and sixel.c.
Note that the limitation is not on the total number of colors used, only on the number that are defined at any one time. You could for instance always use index #3 to draw colored lines so long as you redefine its RGB setting on each change of color. So even when limiting the output to 16 indices pm3d plots look reasonably nice.
I will continue to play with this as I find time, but as of now I'm thinking that if "notruecolor" is set then the terminal should only use 16 color indices (a.k.a. linetypes). In truecolor mode it can do whatever it wants, which currently seems to be using a palette of size gdMaxColors. That's not really "truecolor" so the terminal option is a bit misleading.
I'm still seeing an extraneous solid bar at the top of each plot, so that's apparently a separate problem.
Hi Ethan,
Sorry for the response four years later, but I just found this thread.
XTerm does support any number (up to 32k) of colors, but you have to tell it how many to emulate. This is because the actual terminal hardware would "wrap around" when given color indicies above the maximum, and XTerm emulates this behavior.
For 256 colors, you can start XTerm like this:
xterm -ti vt340 -xrm 'XTerm*numColorRegisters: 256'
The trick of reusing colors won't work either because XTerm emulates the indexed color hardware -- changing color #2 from red to green will retroactively change prior uses of color #2 in the same image (or all images if the private colors setting is disabled). This matches what a real VT241 or VT340 would do.
I'm not sure what the issue is with the solid bars.
If I can make a suggestion, adding support for any number of colors would be nice. The VT241 only had four colors, for example. It would also be nice to be able to use thousands of colors (when including continuous tones in the image).
That may be so, but it's a foolish motivation. It is hard to imagine that anyone prefers accuracy in emulation of long gone hardware over implementing the obvious intent of the user in choosing 24bit RGB color. Particularly since the underlying hardware is almost certain to support 24bit RGB as its native mode. That ship sailed decades ago.
Gnuplot has had that since forever:
set palette maxcolors N
. The usual reason for choosing to use that is to limit the output to a very small number of colors, e.g. to match the physical number of pens in an ink plotter, or the 16 color limitation of old terminals. but you could just as well set it to 64K if you have 16-bit indexed color entries. I actually had a DECStation that worked that way long ago, but that was long ago in the days before 24bit RGB became a de facto standard.Does recent xterm still limit the size of sixel graphics to x<=1000 ? That seems a more serious limitation than being limited to indexed color, and easier to fix also :-)
On Mon, Jul 20, 2020, 02:36 Ethan Merritt sfeam@users.sourceforge.net
wrote:
The term "truecolor" seems to be misleading here, in both definitions that
have been used in this discussion. The Gnuplot output is always indexed
and using a maximum of 256 colors. That is neither 24 bit, nor is it
directly mapped from component channels.
And as I said below it seems to work fine, so I'm not sure what it is
that you would like to change.
Is it the default color palette size? That is currently based on what is
being emulated (VT240 / VT241 -- 4 colors, VT330 -- 4 grayscale shades,
VT340 -- 16 colors). DEC never made a terminal with more colors that
supported sixel that I'm aware of.
I do agree it is annoying today for people to have to configure this, and
for software to be able to know how many colors are available. Sadly
there's no "native" terminal mode in xterm where 256 colors (or more,
really, 256 is not a lot on modern computers) would make sense as the
default. I guess such a thing could be added (-ti xterm?), and people
could stop using VT340 mode.
Particularly since the underlying hardware is almost certain to support
Again, I'm confused where 24 bits (presumably 8 bits per channel) comes
into it. XTerm works perfectly well on 24 bit TrueColor displays. It also
handles sixelgd "truecolor" output if started with >= 256 colors.
Sixel doesn't have a direct color mode, though as I outlined it could be
added as an extension. There are various ways that could be done and
maintain consistency with the original format. A real non-indexed RGB mode
would be nicer for modern software, but it just doesn't exist (yet).
If I can make a suggestion, adding support for any number of colors would
set term sixelgd truecolor
set palette maxcolors N
test
The output file uses colors with values like 209 even though the image has
far fewer colors in use.
Does recent xterm still limit the size of sixel graphics to x<=1000 ? That
It does, by default. This is controlled by an X resource setting, so this
does not require any code changes. The default limit is 1000x1000 (about
2MB of RAM per image). Making it unbounded is probably not a good idea
because any program could cause out of memory crashes, but the default
limit could easily be increased. Is there a better value that you can
suggest which isn't too much bigger? Would 2000x1000 cover most use cases?
-Ross
Sorry, I've kind of lost the thread here. Comes from trying to pick up in the middle of a train of throught from 4 years ago. I agree that 256 colors is enough for practical purposes. It is possible to get xterm built and configured to handle that but it doesn't seem that most distros ship it built that way.
I think this is the key limitation in the usability of gnuplot+sixel+xterm. I completely understand why xterm works that way, but the world has moved away from configuring program settings via X11 resources. The problem is that by the time you are issuing gnuplot commands, you are already inside an xterm instance that has set limits on its size, color range, etc at startup time. It's too late to have a gnuplot command
set term size 1200,800
change the xterm settings. Similarly it is too late to change the color handling.Ideally xterm would reevaluate and reallocate the data structures used for sixel output based on the current size of the xterm window. I.e. it would increase automatically if you expand the window with a mouse or set it to full screen.
By the same token xterm would ideally provide a mode where each sixel held a 24 bit RGB color value rather than a "bit plane" index limited to MAX_COLOR_REGISTERS, which is 1024 in the xterm-327 source. That would allow TrueColor or DirectColor or whatever you want to call it with no bottleneck in filtering colors through a fixed-size palette. This isn't at all what the original sixel hardware devices did, and gets back to the issue of whether the goal of xterm is to emulate old hardware or to provide a minimalistic interface to current hardware. What niche is it serving? My guess is the most common use for gnuplot+sixel+xterm is to ssh from the terminal session inside an xterm window to a remote machine on which you run gnuplot. A limit of 256 colors is not a big deal. But the user might well want to expand the window to full screen for graphics, so the fixed-in-advance limit on number of pixels is annoying.
[formatting was mangled; fixed in next post]
Last edit: Ross Combs 2020-07-21
It seems that the quoting got completely mangled. Attempting to fix that here.
On Mon, Jul 20, 2020 at 10:25 PM Ethan Merritt sfeam@users.sourceforge.net
wrote:
Honestly I'm not sure 256 is really enough for all situations. I was
playing with sixel-based animations and ran into quality issues with
gradients. But 256 seems to be the maximum that Gnuplot supports
(truecolor mode seems to use exactly 256 colors no matter what palette
maxcolors is set to). Somehow it's also the maximum that most emulators
support, even though it is both more than any DEC hardware supported and
way less than hardware supported when it became a de-facto standard (just
in the last five years). It just seems a bit arbitrary.
I think most distros don't ship xterm in VT340 mode at all, so people are
configuring the terminal some. They could also change the number of colors
and the maximum image size, but I don't think knowledge of those settings
is very widespread. Maybe I'm wrong about this not being common, but for
example the system I'm on doesn't set decTerminalId at all in the global X
resources.
I agree. This is a usability issue. I think I'll look at making a default
mode which is a lot more generous -- accepting ReGIS, sixels, large images,
and large numbers of colors by default.
That's a good idea and should be possible. There's a hook for resizing,
and terminals with huge windows showing large graphics can reasonably be
expected to use a lot of RAM. I'll work on a patch for it.
Moving from 16 to 24 bits per pixel is quite doable, though it uses more
RAM. But inherently sixel is palette-based, so what you are talking about
isn't sixels -- it's some kind of extension to them.
I don't mind (and in fact appreciate) XTerm having features to support
modern software and displays, but I also want it to have accurate emulation
of the original DEC escape codes. These goals don't have to be in conflict
as long as the new things are defined so they don't overlap with the
semantics of the older format (for example, we could add a new sixel header
flag that declares "#" references are RGB color specifiers and not register
numbers). The image quality would go way up, and encoding would be easier
(though the byte stream quite a bit longer).
The other half of that is getting other software to use it. Would a patch
for Gnuplot that used it be accepted? How many other terminal emulators
would implement it?
-Ross
Last edit: Ross Combs 2020-07-21
There may be a misunderstanding. The gd terminal does full 32bit color (ARGB) in truecolor mode. However not all of the gd output formats support it. JPEG does, GIF does not, PNG allows you to choose between indexed mode (256 colors) and truecolor mode. Let's defer sixel output for a moment.
The command
set palette maxcolors <whatever>
does not refer to the color mode of the output device. It is the size of the palette used by gnuplot'spm3d
mode gray->palette mapping. It is true that the gd terminal currently hard-codes a 256 entry palette, but note that this palette is not used in truecolor mode.On to the sixel terminal. The sixel support code is in a separate routine sixel.c adapted from a sixel support library by kmiya. It has a single relevant entry point
gdImageSixel()
. One of the parameters to this routine is a flag that selects what claims to be truecolor mode. Unfortunately I was never able to get this working using either xterm or yaft as a terminal emulator. So as a work-around gnuplot sets the parameter to FALSE even if gnuplot itself is composing the figure in full 32bit truecolor mode, and passes 256 to gdImageSixel as the number of colors to use. This produced acceptable output in both xterm and yaft so that's where I left things 4 years ago.I would be perfectly happy to add a flag or something so that gnuplot does set the truecolor=TRUE flag passed to gdImageSixel. You would then have to figure out why the code produced by sixel.c in that mode is not handled correctly by xterm. That's where I got stuck and gave up, but I assume kmiya had it working for whatever setup he was using so it should be doable.
I see that since then kmiya's code has been picked up and incorporated into a larger library https://github.com/saitoha/libsixel
I have not looked at the code in that updated library to see if it would be useful for gnuplot. If you investigate, let me know!
truecolor
only refers to what libgd uses internally.sixel.c
will always only use up to 256 palette entries. The new patch introduces an new parametermaxPalet
forgdImageSixel()
. So you should be able to limit it to 16 very easily. Btw. in principle the code tries to limit the number of palette entries topalet_max = gdImageColorsTotal(im);
I don't follow you. From its name, I think gdImageColorsTotal() returns the number of colors used in the png image. That number is not a problem by itself. The problem comes from defining too many pen types simultaneously in the sixel output. When I limit the number or pen types to 16 I'm still getting many more colors than that in the output palette (can't tell by eye exacly how many - 64? 128?).
By contrast, using "set palette maxcolors 16" with the unmodified code does not work because the code still generates too many pen numbers even though it uses only 16 colors from the palette. So - two different things.
Can you try to disable the
#define USE_SIXEL_INITPAL 1
insixel.c
? That should reduce the number of palette entries.That does not help. In fact it makes things worse.
The issue is not the number of palette entries as seen by gnuplot. The limit is on the number of pens used by sixel.c. It calls this parameter maxPalet, but that is misleading. It should be maxPen or something like that. You can use a small number of pens to draw a large number of palette colors.
In pseudo-code:
if (TrueColor)
maxPalet = gdMaxColors
else
maxPalet = 16
That gets me nice output in non-truecolor mode, and kind of weird output in truecolor mode.
More testing:
The terminal unnecessarily limits the size of the output canvas.
1) The first line of output includes the optional string q"1;1;XX:YY where XX and YY are carried through from "set term sixel size XX,YY". The problem is when XX or YY exceeds 3 digits the command is rejected and no output is generated. Deleting these parameters from the output stream (i.e. replacing the string with q"1;1) makes it work again. The sixel standard says that XX and YY are ignored in any case so I think it is best to omit them.
2) However, I then hit a limitation somewhere that the output image is truncated on the right side. Probably this is another limitation to x-coordinates <1000 (though it might be 1024 in this case). I don't know which software layer is doing this truncation.
On point (2), the truncation is happening in XTerm.
There is a maximum width and height in XTerm (to avoid huge memory allocations). These can be configured with:
XTerm.VT100*maxGraphicSize: 1024x768
The default if not set is 1000x1000.
Have a look at this modification of your nice patchset.
1) I added in the original Japanese license text and checked the translation
2) I modified the call site in gd.trm so that the default "set term sixelgd" uses only 16 colors, but "set term sixelgd truecolor" requests gdImageSixel to map the resulting 32-bit ARGB image back down into 256 sixel colors. Maybe you have a better suggestion for how to work around the 16-color bottleneck?
3) I updated the HELP section of the modified gd.trm because the cut-and-paste text was still talking about the gif terminal rather than the new sixelgd terminal
To actually use the 256 color sixel output on linux requires a slightly modified xterm. I will send the modification upstream and hopefully it will find its way into standard desktop distributions.
There may be other terminal emulators that already support 256 color sixel, but if so I didn't find them.
I put some screenshots on http://skuld.bmsc.washington.edu/~merritt/gnuplot/#sixel
Credits for this patch really go to kmiya - I just fixed the "transparent" and "crop" options - at least they work with mintty.
The PNG routines weren't really designed to output only 16 colors. The PNG routines always pre-allocate 96(!) palette entries in "indexed" mode. The attached patch limits this number of palette entries to a minimum and fixes "transparent" also when the number of colors in "indexed" mode exceeds 16. Maybe we should also disable antialising of lines and fonts since that easily requires more colors than 16.
And maybe it would be a good idea to add another option to the terminal to set the maximum number of output colors?
mlterm
supports 256 color sixels - at least the version delivered with Ubunt 16.04 does.In my opinion this patch could now go to CVS.
Ah - thanks for the pointer to mlterm. Yes, that one works out of the box. Meanwhile I got acknowledgement back from upstream xterm of the patch I submitted for 256-color sixel support.
I agree this can go into CVS.
Just a couple more comments.
transparent background -
The "set term sixelgd {no}truecolor transparent" option does not work on either xterm or mlterm. In both cases it produces what looks like flood-fill solid black, regardless of the previous contents of the terminal window. This is hardly a show-stopper, since I fail to see a use case for the option anyhow. But for what it's worth, there is a dead-simple transparent sixel backgound option right there in the sixel standard. The very first line of a sixel output stream is normally: CSIq"1;1;xx;yy
The second parameter controls the background, so CSIq"1:2:xx:yy supposedly skips the bit-set operation for all background pixels, yielding transparency. But neither xterm nor mlterm emulate this part of the sixel standard.
n_colors = GD_DEFINED_COLORS -
I don't understand exactly what effect this change is intended to have. The resulting plots with or without this change look the same, but probably I am not testing the case it is aimed at. Anyhow it doesn't hurt anything.
other-
The build/packaging files need to know about the new file term/sixel.c so that it gets included in the distribution package.
Some addtional help text might be useful that points to known-working terminal emulators that support sixel graphics. We can continue to clean up documentation and help text after it goes in.
XTerm does support sixel transparency -- it has since the earliest versions of support for sixel graphics. Checking patch 304 from 2014 (the earliest version I have on hand) shows it:
So if it isn't working, there's either an XTerm bug, or something about the way the background mode is being set that is non-standard.
OK. Now in CVS.
Interestingly "transparent" works here using mintty locally on Windows and remotely using mlterm on Windows logged in on a Linux machine.
Limiting the number of palette entries for sixelgd in indexed mode should indeed have no visual effect. It just avoids unecessary conversions in
gdImageSixel()
, converting an indexed image to truecolor and back. Another way of doing this is to actually count the number of used palette entries instead of relying ongdImageColorsTotal()
.I added one more feature. The "animate" terminal option (already there for gif output) tells the sixelgd terminal to reset each plot to start at the top left of the terminal. That makes successive plots overwrite the same position on the screen, making a nice animate-in-place result if you put the program in a loop. It also work to save the output stream in a file and play back the file later via, e.g. "cat animate.sixel".
Nice one. I had tried to save-and-restore the cursor position by sending appropriate escape sequences, but that somehow did not work.