1use std::ffi::CString;
2
3pub struct ChafaCanvas {
4 symbol_map: *mut chafa_sys::ChafaSymbolMap,
5 config: *mut chafa_sys::ChafaCanvasConfig,
6 canvas: *mut chafa_sys::ChafaCanvas,
7}
8
9impl ChafaCanvas {
10 pub fn new(width: u32, height: u32) -> Self {
11 let symbol_map = unsafe {
12 let symbol_map = chafa_sys::chafa_symbol_map_new();
13 chafa_sys::chafa_symbol_map_add_by_tags(
14 symbol_map,
15 chafa_sys::ChafaSymbolTags_CHAFA_SYMBOL_TAG_ALL,
16 );
17 symbol_map
18 };
19
20 let config = unsafe {
21 let config = chafa_sys::chafa_canvas_config_new();
22 chafa_sys::chafa_canvas_config_set_geometry(config, width as i32, height as i32);
23 chafa_sys::chafa_canvas_config_set_symbol_map(config, symbol_map);
24 config
25 };
26
27 let canvas = unsafe { chafa_sys::chafa_canvas_new(config) };
28
29 Self {
30 symbol_map,
31 config,
32 canvas,
33 }
34 }
35
36 pub fn from_term(src_width: u32, src_height: u32) -> Self {
37 let symbol_map = unsafe {
38 let symbol_map = chafa_sys::chafa_symbol_map_new();
39 chafa_sys::chafa_symbol_map_add_by_tags(
40 symbol_map,
41 chafa_sys::ChafaSymbolTags_CHAFA_SYMBOL_TAG_ALL,
42 );
43 symbol_map
44 };
45
46 let (terminal_size::Width(cols), terminal_size::Height(rows)) =
47 terminal_size::terminal_size()
48 .unwrap_or((terminal_size::Width(100), terminal_size::Height(50)));
49
50 let width_ptr: *mut i32 = &mut (cols as i32);
51 let height_ptr: *mut i32 = &mut (rows as i32);
52
53 unsafe {
54 chafa_sys::chafa_calc_canvas_geometry(
55 src_width as i32,
56 src_height as i32,
57 width_ptr,
58 height_ptr,
59 0.5,
60 false.into(),
61 false.into(),
62 );
63 }
64
65 let config = unsafe {
66 let config = chafa_sys::chafa_canvas_config_new();
67 chafa_sys::chafa_canvas_config_set_geometry(config, *width_ptr, *height_ptr);
68 chafa_sys::chafa_canvas_config_set_symbol_map(config, symbol_map);
69 config
70 };
71
72 let canvas = unsafe { chafa_sys::chafa_canvas_new(config) };
73
74 Self {
75 symbol_map,
76 config,
77 canvas,
78 }
79 }
80
81 pub fn draw(&self, pixels: &[u8], pix_width: u32, pix_height: u32) -> String {
82 let channels = 4; unsafe {
85 chafa_sys::chafa_canvas_draw_all_pixels(
86 self.canvas,
87 chafa_sys::ChafaPixelType_CHAFA_PIXEL_RGBA8_UNASSOCIATED,
88 pixels.as_ptr(),
89 pix_width as i32,
90 pix_height as i32,
91 (pix_width * channels) as i32,
92 );
93 }
94
95 let gstring = unsafe { chafa_sys::chafa_canvas_build_ansi(self.canvas) };
96 let ansistr = unsafe { (*gstring).str_ };
97 let ansistr = unsafe { CString::from_raw(ansistr) };
98 let ansistr = ansistr.to_string_lossy();
99
100 ansistr.to_string()
101 }
102}
103
104impl Drop for ChafaCanvas {
105 fn drop(&mut self) {
106 unsafe {
107 chafa_sys::chafa_canvas_unref(self.canvas);
108 chafa_sys::chafa_canvas_config_unref(self.config);
109 chafa_sys::chafa_symbol_map_unref(self.symbol_map);
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use crate::ChafaCanvas;
117
118 #[test]
119 fn usage_example() {
120 const PIX_WIDTH: usize = 3;
121 const PIX_HEIGHT: usize = 3;
122 const N_CHANNELS: usize = 4;
123
124 let pixels: [u8; PIX_WIDTH * PIX_HEIGHT * N_CHANNELS] = [
125 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00,
126 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff,
127 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff,
128 ];
129
130 let canvas_width = 25;
131 let canvas_height = 10;
132 let canvas = ChafaCanvas::new(canvas_width, canvas_height);
133 let ansi = canvas.draw(&pixels, PIX_WIDTH as u32, PIX_HEIGHT as u32);
134 println!("{ansi}");
135 }
136
137 #[test]
138 fn frectonz_image() {
139 let img = image::open("/home/frectonz/workspace/chafa/chafa-sys/frectonz.png")
140 .expect("failed to open image");
141
142 let canvas = ChafaCanvas::from_term(img.width(), img.height());
143 let pixels = img.to_rgba8();
144 let ansi = canvas.draw(&pixels, img.width(), img.height());
145
146 println!("{ansi}");
147 }
148}