sixel_image/
lib.rs

1//! This library provides an interface for querying, manipulating and serializing sixel data.
2//!
3//! There are several methods provided here to do this:
4//!
5//! 1. If you already have all the serialized sixel bytes, construct [`SixelImage`] directly
6//! 2. If you'd like to parse bytes in real time "on the wire", use [`SixelDeserializer`]
7//!
8//! # Example
9//! ```rust
10//! use std::io::Read;
11//! use std::io::BufReader;
12//! use std::fs::File;
13//! use sixel_image::SixelImage;
14//! 
15//! fn main() {
16//!     let f = File::open("/home/aram/Downloads/lady-of-shalott.six").unwrap();
17//!     let mut reader = BufReader::new(f);
18//!     let mut buffer = Vec::new();
19//!     reader.read_to_end(&mut buffer).unwrap();
20//!     let sixel_image = SixelImage::new(&buffer).unwrap();
21//!     let serialized = sixel_image.serialize();
22//!     println!("{}", serialized);
23//! }
24//! ```
25
26mod sixel_serializer;
27mod sixel_deserializer;
28
29pub use sixel_serializer::SixelSerializer;
30pub use sixel_deserializer::SixelDeserializer;
31
32use std::fmt;
33use std::collections::BTreeMap;
34use sixel_tokenizer::{ColorCoordinateSystem, Parser};
35
36#[derive(Debug, Clone)]
37pub struct SixelImage {
38    color_registers: BTreeMap<u16, SixelColor>,
39    pixels: Vec<Vec<Pixel>>,
40}
41
42impl SixelImage {
43    /// Constructs a new `SixelImage` out of an existing slice of serialized sixel bytes
44    pub fn new(bytes: &[u8]) -> Result<Self, &'static str> {
45        let mut parser = Parser::new();
46        let mut sixel_deserializer = SixelDeserializer::new();
47        for byte in bytes {
48            let mut handle_result = Ok(());
49            parser.advance(&byte, |sixel_event| {
50                handle_result = sixel_deserializer.handle_event(sixel_event);
51            });
52            handle_result?
53        }
54        let sixel_image = sixel_deserializer.create_image();
55        sixel_image
56    }
57    /// Returns the (height, width) of the image in pixels
58    pub fn pixel_size(&self) -> (usize, usize) { // (height, width) in pixels
59        let width = self.pixels.first().map(|first_line| first_line.len()).unwrap_or(0);
60        let height = self.pixels.len();
61        (height, width)
62    }
63    /// Serializes the whole image, returning a stringified sixel representation of it
64    pub fn serialize(&self) -> String {
65        let sixel_serializer = SixelSerializer::new(&self.color_registers, &self.pixels);
66        let serialized_image = sixel_serializer.serialize();
67        serialized_image
68    }
69    /// Serializes a specific rectangle of this image without manipulating the image itself, x/y
70    /// coordinates as well as width height are in pixels
71    pub fn serialize_range(&self, start_x_index: usize, start_y_index: usize, width: usize, height: usize) -> String {
72        let sixel_serializer = SixelSerializer::new(&self.color_registers, &self.pixels);
73        let serialized_image = sixel_serializer.serialize_range(start_x_index, start_y_index, width, height);
74        serialized_image
75    }
76    /// Manipulates the image in-place, cutting out a rectangle with the specified coordinates. If
77    /// the rectangle exceeds the image, it will be partially cut out. All x/y and width/height
78    /// coordinates are in pixels
79    pub fn cut_out(&mut self, start_x_index: usize, start_y_index: usize, width: usize, height: usize) {
80        for row in self.pixels.iter_mut().skip(start_y_index).take(height) {
81            for pixel in row.iter_mut().skip(start_x_index).take(width) {
82                pixel.on = false;
83            }
84        }
85    }
86}
87
88#[derive(Clone, Copy)]
89pub struct Pixel {
90    on: bool,
91    color: u16, 
92}
93
94impl fmt::Debug for Pixel {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        if self.on {
97            write!(f, "{}", self.color)
98        } else {
99            write!(f, "x")
100        }
101    }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105pub enum SixelColor {
106    Rgb(u8, u8, u8), // 0-100
107    Hsl(u16, u8, u8), // 0-360, 0-100, 0-100
108}
109
110impl From<ColorCoordinateSystem> for SixelColor {
111    fn from(item: ColorCoordinateSystem) -> Self {
112        match item {
113            ColorCoordinateSystem::HLS(x, y, z) => SixelColor::Hsl(x as u16, y as u8, z as u8),
114            ColorCoordinateSystem::RGB(x, y, z) => SixelColor::Rgb(x as u8, y as u8, z as u8),
115        }
116    }
117}
118
119#[cfg(test)]
120mod tests;