Skip to content

Add dynamic image to sixel api#2

Merged
tinic merged 3 commits into
mainfrom
add-image-to-sixel-api
May 1, 2026
Merged

Add dynamic image to sixel api#2
tinic merged 3 commits into
mainfrom
add-image-to-sixel-api

Conversation

@tinic
Copy link
Copy Markdown
Owner

@tinic tinic commented May 1, 2026

No description provided.

tinic added 3 commits May 1, 2026 15:57
The image<> template requires compile-time W and H, which is awkward
for callers that produce arbitrary-resolution frames at runtime
(terminal previews, decoded video). Add two free functions in
namespace constixel that take RGBA bytes plus runtime width/height
and emit a sixel stream via a char-callback:

  rgba_to_sixel(rgba, w, h, char_out)
      Quantises to constixel's built-in 256-color palette
      (format_8bit::quant). Use when input has more than 256 distinct
      colors or when the caller has no preferred palette.

  rgba_to_sixel(rgba, w, h, palette, char_out)
      Uses a caller-supplied palette of up to 256 sRGB byte triples.
      The built-in quantiser is bypassed; each pixel maps to its
      squared-sRGB-distance nearest entry. Use when the caller already
      knows the exact palette of the source data.

Implementation reuses the existing sixel header/raster/end helpers
in spirit but inlines a runtime-sized encoder loop with run-length
compression matching sixel_image<W,H,...>. No changes to the
compile-time-sized image<> path.
Replaces the freestanding rgba_to_sixel free functions with a proper
sibling of image<F,W,H>:

- runtime_palette: shared <=256-entry palette type (BGR-packed).
- format_8bit_dyn: 8-bit indexed format with runtime W/H carried by
  parameter (vs format_8bit's compile-time W/H). Default palette
  delegates to format_8bit::quant; custom palette uses runtime_palette.
- dynamic_image<Ops>: runtime-sized image, mirrors image<F,W,H>
  surface (blit_RGBA, sixel, sixel_to_cout under CONSTIXEL_ENABLE_COUT).

Custom palette also wired into the existing static image<F,W,H> via
new constructor overloads that take std::span<const std::array<u8,3>>,
gated on the format exposing a runtime_palette typedef. blit_RGBA and
sixel members dispatch through if-constexpr to *_with_palette helpers
when has_custom_palette_ is set; the default code path is unchanged.

format_8bit gains sixel_with_palette and blit_RGBA_with_palette static
methods that delegate to format_8bit_dyn (defined out-of-line to break
the circular header dependency). No duplication of the sixel encoder
between compile-time and runtime image types.
Microsoft Terminal #17887: when P2 is 0 or omitted in the sixel
introducer, an arbitrary number of cell-height rows past the image
get filled with an arbitrary color (uninitialized memory). Setting
P2=1 marks unset pixels as transparent and sidesteps the bug.

For fully-painted images (every pixel covered, as dynamic_image
produces) P2=0 vs P2=1 are visually identical — but only P2=1
renders correctly under WT >=1.22 sixel.
@tinic tinic merged commit 7be545b into main May 1, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant