Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR adds support for sixels to lipgloss. Sixels are a protocol for writing images to the terminal by writing a large blob of ANSI-escaped data. They function by encoding columns of 6 pixels into a single character (in much the same way base64 encodes data 6 bits at a time). Sixel images are paletted, with a palette established at the beginning of the image blob and pixels identifying palette entires by index while writing the pixel data.
Sixels are written one 6-pixel-tall band at a time, one color at a time. For each band, a single color's pixels are written, then a carriage return is written to bring the "cursor" back to the beginning of a band where a new color is selected and pixels written. This continues until the entire band has been drawn, at which time a line break is written to begin the next band.
Supporting sixels requires overcoming a few challenges:
Sixels traditionally supported only 256 colors. Modern terminals that have added sixel support may not have this restriction, but there's no real way of knowing, and anyway, too many colors massively inflate the size of a sixel image. As a result, a 256 color quantization process needs to be undergone on the image to produce the palette.
sixel_palette.go
has a Median Cut implementation for sixels.Sixels do not support alpha, so how should it be handled? When writing the sixel image, we use the param
p2=1
which leaves pixels that are not written as part of the sixel's pixel data the color that is already in the terminal window. For fully-transparent pixels we simply do not write the pixels as part of the pixel data. But what about semi-transparent pixels? InStyle.RenderSixelImage
, if the style has a background color, I use the alpha channel in the palette to mix the background color with the pixel color to produce a semi-transparent effect. Be aware that sixel's color space is only 0-100 rather than the traditional 0-255, so it is sometimes possible for the human eye to distinguish between the background color in the terminal and the background color as drawn with sixels. Generally it looks good as long as the two colors aren't side by side.If there is no background color in the style, the pixels are drawn fully-opaque at the color they appear in the image.
Because colors must be drawn one at a time, we generate the pixel data in two phases: first, we traverse the image and write filled bits to a BitSet. Then, we use the BitSet to generate the pixel data string and store it for later use.
Style.RenderSixelImage
is responsible for generating the palette, image size, and pixel data into a coherent ANSI blob.I chose to not tackle these challenges (though I can if they need to be solved before merge):