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:
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();dotnet runThe 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:
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:
| Mode | Method | Behavior |
|---|---|---|
| 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:
// 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)2
3
4
5
6
7
Use layout size hints to make the image fill available space:
// Fill the parent container
v.KgpImage(pixels, width, height, img => img.Text("fallback"))
.Width(SizeHint.Fill)
.Height(SizeHint.Fill)2
3
4
Z-Ordering
KGP images can be rendered above or below the text layer:
// 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()2
3
4
5
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:
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)2
3
4
5
6
7
8
9
10
11
12
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.