Skip to main content

sixel_image/
sixel_deserializer.rs

1use sixel_tokenizer::SixelEvent;
2use std::collections::BTreeMap;
3use std::iter;
4
5use crate::{Pixel, SixelColor, SixelImage, DCS, RA};
6
7#[derive(Debug, Clone)]
8pub struct SixelDeserializer {
9    dcs: DCS,
10    ra: Option<RA>,
11    color_registers: BTreeMap<u16, SixelColor>,
12    current_color: u16,
13    sixel_cursor_y: usize,
14    sixel_cursor_x: usize,
15    pixels: Vec<Vec<Pixel>>,
16    max_height: Option<usize>,
17    stop_parsing: bool,
18    got_dcs: bool,
19}
20
21impl SixelDeserializer {
22    pub fn new() -> Self {
23        SixelDeserializer {
24            dcs: DCS {
25                macro_parameter: 0,
26                transparent_bg: false,
27            },
28            ra: None,
29            color_registers: BTreeMap::new(),
30            current_color: 0, // this is totally undefined behaviour and seems like a free for all in general
31            sixel_cursor_y: 0,
32            sixel_cursor_x: 0,
33            pixels: vec![vec![]], // start with one empty line
34            max_height: None,
35            stop_parsing: false,
36            got_dcs: false,
37        }
38    }
39    /// Provide a `max_height` value in pixels, all pixels beyond this max height will not be
40    /// parsed
41    pub fn max_height(mut self, max_height: usize) -> Self {
42        self.max_height = Some(max_height);
43        self
44    }
45    /// Create a new [`SixelImage`] out of the existing state and consume it.
46    pub fn create_image(&mut self) -> Result<SixelImage, &'static str> {
47        if !self.got_dcs {
48            return Err("Corrupted image sequence");
49        }
50        let dcs = std::mem::take(&mut self.dcs);
51        let ra = std::mem::take(&mut self.ra);
52        let pixels = std::mem::take(&mut self.pixels);
53        let color_registers = std::mem::take(&mut self.color_registers);
54        Ok(SixelImage {
55            dcs,
56            ra,
57            pixels,
58            color_registers,
59        })
60    }
61    /// Handle a [`SixelEvent`], changing the internal state to match
62    pub fn handle_event(&mut self, event: SixelEvent) -> Result<(), &'static str> {
63        if !self.got_dcs && !matches!(event, SixelEvent::Dcs { .. }) {
64            return Err("Corrupted image sequence");
65        }
66        if self.stop_parsing {
67            return Ok(());
68        }
69        match event {
70            SixelEvent::ColorIntroducer {
71                color_coordinate_system,
72                color_number,
73            } => {
74                match color_coordinate_system {
75                    Some(color_coordinate_system) => {
76                        // define a color in a register
77                        let color = SixelColor::from(color_coordinate_system);
78                        self.color_registers.insert(color_number, color);
79                    }
80                    None => {
81                        // switch to register number
82                        self.current_color = color_number;
83                    }
84                }
85            }
86            SixelEvent::RasterAttribute { pan, pad, ph, pv } => {
87                if !self.dcs.transparent_bg {
88                    if let Some(pv) = pv {
89                        self.pad_lines_vertically(pv);
90                    }
91                    if let Some(ph) = ph {
92                        self.pad_lines_horizontally(ph);
93                    }
94                }
95                self.ra = Some(RA { pan, pad, ph, pv });
96            }
97            SixelEvent::Data { byte } => {
98                self.make_sure_six_lines_exist_after_cursor();
99                self.add_sixel_byte(byte, 1);
100                self.sixel_cursor_x += 1;
101            }
102            SixelEvent::Repeat {
103                repeat_count,
104                byte_to_repeat,
105            } => {
106                self.make_sure_six_lines_exist_after_cursor();
107                self.add_sixel_byte(byte_to_repeat, repeat_count);
108                self.sixel_cursor_x += repeat_count;
109            }
110            SixelEvent::Dcs {
111                macro_parameter,
112                transparent_background,
113                horizontal_pixel_distance: _,
114            } => {
115                self.got_dcs = true;
116                if let Some(mp) = macro_parameter {
117                    self.dcs.macro_parameter = mp;
118                }
119                if transparent_background == Some(1) {
120                    self.dcs.transparent_bg = true;
121                }
122            }
123            SixelEvent::GotoBeginningOfLine => {
124                self.sixel_cursor_x = 0;
125            }
126            SixelEvent::GotoNextLine => {
127                if let Some(max_height) = self.max_height {
128                    if self.sixel_cursor_y + 12 > max_height {
129                        // 12 because we move the cursor to the top of the sixel column and need to count 6 more down to make sure we don't exceed
130                        self.stop_parsing = true;
131                        return Ok(());
132                    }
133                }
134                self.sixel_cursor_y += 6;
135                self.sixel_cursor_x = 0;
136            }
137            SixelEvent::UnknownSequence(_) => {
138                return Err("Corrupted Sixel sequence");
139            }
140            SixelEvent::End => {}
141        }
142        Ok(())
143    }
144    fn make_sure_six_lines_exist_after_cursor(&mut self) {
145        let lines_to_add = (self.sixel_cursor_y + 6).saturating_sub(self.pixels.len());
146        for _ in 0..lines_to_add {
147            self.pixels.push(vec![]);
148        }
149    }
150    fn add_sixel_byte(&mut self, byte: u8, repeat_count: usize) {
151        let mut pixel_line_index_in_sixel = 0;
152        for bit in SixelPixelIterator::new(byte.saturating_sub(63)) {
153            let current_line = self
154                .pixels
155                .get_mut(self.sixel_cursor_y + pixel_line_index_in_sixel)
156                .unwrap();
157            let new_pixel = Pixel {
158                on: bit,
159                color: self.current_color,
160            };
161            for i in 0..repeat_count {
162                match current_line.get_mut(self.sixel_cursor_x + i) {
163                    Some(pixel_in_current_position) if bit => {
164                        let _ = std::mem::replace(pixel_in_current_position, new_pixel);
165                    }
166                    None => {
167                        current_line.push(new_pixel);
168                    }
169                    _ => {} // bit is off and pixel already exists, so noop
170                }
171            }
172            pixel_line_index_in_sixel += 1;
173        }
174    }
175    fn pad_lines_vertically(&mut self, pad_until: usize) {
176        let empty_pixel = Pixel {
177            on: true,
178            color: self.current_color,
179        };
180        if self.pixels.len() < pad_until {
181            let empty_line = vec![empty_pixel; pad_until];
182            let lines_to_pad = pad_until - self.pixels.len();
183            let line_padding = iter::repeat(empty_line).take(lines_to_pad);
184            self.pixels.extend(line_padding);
185        }
186    }
187    fn pad_lines_horizontally(&mut self, pad_until: usize) {
188        let empty_pixel = Pixel {
189            on: true,
190            color: self.current_color,
191        };
192        for pixel_line in self.pixels.iter_mut() {
193            if pixel_line.len() < pad_until {
194                let pixel_count_to_pad = pad_until - pixel_line.len();
195                let pixel_padding = iter::repeat(empty_pixel).take(pixel_count_to_pad);
196                pixel_line.extend(pixel_padding);
197            }
198        }
199    }
200}
201
202#[derive(Debug, Clone, Copy)]
203struct SixelPixelIterator {
204    sixel_byte: u8,
205    current_mask: u8,
206}
207impl SixelPixelIterator {
208    pub fn new(sixel_byte: u8) -> Self {
209        SixelPixelIterator {
210            sixel_byte,
211            current_mask: 1,
212        }
213    }
214}
215impl Iterator for SixelPixelIterator {
216    type Item = bool;
217    fn next(&mut self) -> Option<Self::Item> {
218        // iterate through the bits in a byte from right (least significant) to left (most
219        // significant), eg. 89 => 1011001 => true, false, false, true, true, false, true
220        let bit = self.sixel_byte & self.current_mask == self.current_mask;
221        self.current_mask <<= 1;
222        if self.current_mask == 128 {
223            None
224        } else {
225            Some(bit)
226        }
227    }
228}