sixel_image/
sixel_deserializer.rs

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