1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! Inline terminal image demo.
//!
//! gilt auto-selects the best protocol your terminal supports:
//! Kitty graphics → iTerm2 (OSC 1337) → halfblock `▀` (works everywhere)
//!
//! Run it:
//! cargo run --example image
//! Procedural gradient via halfblock or Kitty — no extra features needed.
//!
//! cargo run --example image --features inline-images
//! Also enables the iTerm2 protocol (PNG-encoded) and loading real files.
//!
//! cargo run --example image --features inline-images -- path/to/photo.png
//! Display an actual image file.
//!
//! Terminal not auto-detected (e.g. an embedded terminal)? Force a protocol:
//! GILT_IMAGE_PROTOCOL=kitty cargo run --example image
//! (use kitty | iterm | sixel | halfblock — handy for testing what your
//! terminal supports; if it shows garbage, that protocol isn't supported.)
use gilt::console::Console;
use gilt::console_caps::ConsoleCapabilities;
use gilt::image::Image;
use gilt::rule::Rule;
/// Linear interpolation across three Dracula stops: purple → pink → cyan.
fn gradient_stop(t: f32) -> (u8, u8, u8) {
let stops = [(189u8, 147u8, 249u8), (255, 121, 198), (139, 233, 253)];
let (seg, local) = if t < 0.5 {
(0, t / 0.5)
} else {
(1, (t - 0.5) / 0.5)
};
let (a, b) = (stops[seg], stops[seg + 1]);
let lerp = |x: u8, y: u8| (x as f32 + (y as f32 - x as f32) * local).round() as u8;
(lerp(a.0, b.0), lerp(a.1, b.1), lerp(a.2, b.2))
}
/// Build a colourful `w × h` RGBA image with no external file:
/// a diagonal Dracula gradient with a bright disc in the centre.
fn gradient_image(w: u32, h: u32) -> Image {
let mut rgba = Vec::with_capacity((w * h * 4) as usize);
let (cx, cy) = (w as f32 / 2.0, h as f32 / 2.0);
let radius = w.min(h) as f32 * 0.34;
for y in 0..h {
for x in 0..w {
let t = (x as f32 + y as f32) / (w as f32 + h as f32);
let (mut r, mut g, mut b) = gradient_stop(t);
// bright yellow disc (#f1fa8c) in the middle
let (dx, dy) = (x as f32 - cx, y as f32 - cy);
if (dx * dx + dy * dy).sqrt() < radius {
(r, g, b) = (241, 250, 140);
}
rgba.extend_from_slice(&[r, g, b, 255]);
}
}
Image::from_rgba(w, h, rgba)
}
fn main() {
let mut console = Console::default();
// Optional override for terminals gilt can't auto-detect (embedded terminals,
// SSH, etc.): GILT_IMAGE_PROTOCOL=kitty|iterm|halfblock.
if let Ok(force) = std::env::var("GILT_IMAGE_PROTOCOL") {
let base = console.capabilities().clone();
let caps = match force.to_ascii_lowercase().as_str() {
"kitty" => ConsoleCapabilities {
kitty: true,
iterm: false,
sixel: false,
..base
},
"iterm" => ConsoleCapabilities {
kitty: false,
iterm: true,
sixel: false,
..base
},
"sixel" => ConsoleCapabilities {
kitty: false,
iterm: false,
sixel: true,
..base
},
"halfblock" => ConsoleCapabilities {
kitty: false,
iterm: false,
sixel: false,
..base
},
other => {
eprintln!(
"GILT_IMAGE_PROTOCOL={other:?} unrecognized (use kitty|iterm|sixel|halfblock)"
);
base
}
};
console.set_capabilities(caps);
}
// Which protocol will this terminal use? (read caps before borrowing mutably)
let (kitty, iterm, sixel) = {
let caps = console.capabilities();
(caps.kitty, caps.iterm, caps.sixel)
};
let protocol = if kitty {
"Kitty graphics protocol"
} else if iterm {
"iTerm2 inline images (OSC 1337)"
} else if sixel {
"Sixel graphics"
} else {
"halfblock (▀) — the universal fallback"
};
console.print(&Rule::with_title("gilt — inline images"));
console.print_text(&format!("This terminal will use: [bold cyan]{protocol}[/]"));
console.print_text("");
// A procedural image — always works, no file or feature required.
console.print_text("[bold]Procedural gradient[/] (Image::from_rgba):");
console.print(&gradient_image(160, 160).width(40));
console.print_text("");
// Optionally load a real image file (needs the `inline-images` feature).
#[cfg(feature = "inline-images")]
{
match std::env::args().nth(1) {
Some(path) => {
console.print(&Rule::with_title("file"));
match Image::from_path(&path) {
Ok(img) => console.print(&img.width(64)),
Err(e) => {
console.print_text(&format!("[bold red]could not load {path}:[/] {e}"))
}
}
}
None => {
console.print_text(
"[dim]tip: pass a path to display a real image file:[/]\n\
[dim] cargo run --example image --features inline-images -- photo.png[/]",
);
}
}
}
#[cfg(not(feature = "inline-images"))]
{
console.print_text(
"[dim]build with [/][dim cyan]--features inline-images[/][dim] to load image files \
and enable the iTerm2 protocol.[/]",
);
}
}