Skip to content

KgpImage

Display pixel-based images in terminals that support the Kitty Graphics Protocol (KGP). When the terminal does not support KGP, a fallback widget is rendered instead.

Terminal Support

KGP is supported by Kitty, WezTerm, and other terminals implementing the protocol. Your application should always provide a meaningful fallback for terminals without graphics support.

Basic Usage

Create a KGP image from raw RGBA32 pixel data with a fallback builder:

csharp
using Hex1b;

// Generate a simple gradient image (RGBA32 format: 4 bytes per pixel)
var width = 64;
var height = 32;
var pixels = new byte[width * height * 4];
for (int y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        var i = (y * width + x) * 4;
        pixels[i]     = (byte)(x * 255 / width);   // R
        pixels[i + 1] = (byte)(y * 255 / height);  // G
        pixels[i + 2] = 128;                        // B
        pixels[i + 3] = 255;                        // A
    }
}

await using var terminal = Hex1bTerminal.CreateBuilder()
    .WithHex1bApp((app, options) => ctx => ctx.VStack(v => [
        v.Text("KGP Image Demo"),
        v.KgpImage(pixels, width, height,
            img => img.Text("Terminal does not support graphics"))
    ]))
    .Build();

await terminal.RunAsync();

The image data must be in RGBA32 format — 4 bytes per pixel (red, green, blue, alpha), row-major order. You must also provide the pixel dimensions so the protocol can transmit the image correctly.

Fallback Content

Every KgpImage requires a fallback builder. It produces the widget displayed when the terminal does not support KGP, whether that's a simple text label or a richer widget tree:

csharp

Always Provide Useful Fallbacks

Don't use empty strings or placeholder text. The fallback is what most terminal users will see — make it informative. Consider using ASCII art, a description of the image, or alternative UI elements.

Stretch Modes

Control how the image fills its allocated area using stretch modes:

csharp
ModeMethodBehavior
Stretch.Stretched()Fills the area completely, distorting the aspect ratio. This is the default.
Fit.Fit()Scales to fit within the area while preserving aspect ratio. May leave empty space.
Fill.Fill()Scales to cover the entire area, preserving aspect ratio. Excess is cropped.
None.NaturalSize()Displays at native pixel-to-cell dimensions without any scaling.

Sizing

By default, the image size is calculated from the pixel dimensions (roughly 10 pixels per column, 20 pixels per row). You can override the display size in character cells:

csharp
// Explicit size in character cells
v.KgpImage(pixels, width, height, img => img.Text("fallback"))
    .WithWidth(20)
    .WithHeight(10)

// Or set size at creation time
v.KgpImage(pixels, width, height, img => img.Text("fallback"), width: 20, height: 10)

Use layout size hints to make the image fill available space:

csharp
// Fill the parent container
v.KgpImage(pixels, width, height, img => img.Text("fallback"))
    .Width(SizeHint.Fill)
    .Height(SizeHint.Fill)

Z-Ordering

KGP images can be rendered above or below the text layer:

csharp
// Render behind text (default)
v.KgpImage(pixels, width, height, img => img.Text("fallback")).BelowText()

// Render on top of text
v.KgpImage(pixels, width, height, img => img.Text("fallback")).AboveText()

When placed below text, the image acts as a background — any text widgets overlapping the image area will be visible on top.

Loading Images from Files

For real-world usage, you'll typically load images from files. Use a library like SkiaSharp or ImageSharp to decode images into RGBA pixel data:

csharp
using SkiaSharp;

// Load and decode an image file
using var bitmap = SKBitmap.Decode("photo.png");
var pixels = bitmap.Bytes; // RGBA32 data
var w = bitmap.Width;
var h = bitmap.Height;

// Use in your widget tree
v.KgpImage(pixels, w, h, img => img.Text("Photo description"))
    .Fit()
    .Width(SizeHint.Fill)

Using with Surfaces

KGP images integrate with the Surface compositing system. When images are rendered inside surface layers, the framework automatically handles:

  • Clipping — Images are cropped to their container bounds
  • Occlusion — Text or other widgets in higher layers correctly obscure portions of the image
  • Multi-placement — A single image may be split into multiple visible fragments when partially occluded

This means KGP images work correctly inside scrollable areas, bordered containers, windows, and any other layout widget.

  • Surface — Low-level rendering with layered compositing
  • Text — For text-based alternatives to images

Released under the MIT License.