cadmus_core/framebuffer/
image.rs

1use super::{Framebuffer, UpdateMode};
2use crate::color::{Color, WHITE};
3use crate::geom::{lerp, Rectangle};
4use anyhow::{format_err, Context, Error};
5use std::fs::File;
6use std::io::BufReader;
7use std::io::Cursor;
8use std::path::Path;
9
10#[derive(Debug, Clone)]
11pub struct Pixmap {
12    pub width: u32,
13    pub height: u32,
14    pub samples: usize,
15    pub data: Vec<u8>,
16}
17
18impl Pixmap {
19    pub fn new(width: u32, height: u32, samples: usize) -> Pixmap {
20        let len = samples * (width * height) as usize;
21        Pixmap {
22            width,
23            height,
24            samples,
25            data: vec![WHITE.gray(); len],
26        }
27    }
28
29    pub fn try_new(width: u32, height: u32, samples: usize) -> Option<Pixmap> {
30        let mut data = Vec::new();
31        let len = samples * (width * height) as usize;
32        data.try_reserve_exact(len).ok()?;
33        data.resize(len, WHITE.gray());
34        Some(Pixmap {
35            width,
36            height,
37            samples,
38            data,
39        })
40    }
41
42    pub fn empty(width: u32, height: u32, samples: usize) -> Pixmap {
43        Pixmap {
44            width,
45            height,
46            samples,
47            data: Vec::new(),
48        }
49    }
50
51    pub fn data(&self) -> &[u8] {
52        &self.data
53    }
54
55    pub fn data_mut(&mut self) -> &mut [u8] {
56        &mut self.data
57    }
58
59    pub fn from_png<P: AsRef<Path>>(path: P) -> Result<Pixmap, Error> {
60        let file = File::open(path.as_ref())?;
61        let decoder = png::Decoder::new(BufReader::new(file));
62        let mut reader = decoder.read_info()?;
63        let info = reader.info();
64        let mut pixmap = Pixmap::new(info.width, info.height, info.color_type.samples());
65        reader.next_frame(pixmap.data_mut())?;
66        Ok(pixmap)
67    }
68
69    /// Decode a PNG-encoded buffer into a pixmap.
70    pub fn from_png_bytes(bytes: &[u8]) -> Result<Pixmap, Error> {
71        let cursor = Cursor::new(bytes);
72        let decoder = png::Decoder::new(cursor);
73        let mut reader = decoder.read_info()?;
74        let info = reader.info();
75        let mut pixmap = Pixmap::new(info.width, info.height, info.color_type.samples());
76        reader.next_frame(pixmap.data_mut())?;
77        Ok(pixmap)
78    }
79
80    /// Encode the pixmap as PNG bytes using the current pixel format.
81    ///
82    /// Returns an error when the pixmap has no pixel data.
83    pub fn to_png_bytes(&self) -> Result<Vec<u8>, Error> {
84        if self.data.is_empty() {
85            return Err(format_err!("nothing to encode"));
86        }
87
88        let (width, height) = self.dims();
89        let mut buffer = Vec::new();
90        {
91            let mut encoder = png::Encoder::new(&mut buffer, width, height);
92            encoder.set_depth(png::BitDepth::Eight);
93            encoder.set_color(if self.samples == 3 {
94                png::ColorType::Rgb
95            } else {
96                png::ColorType::Grayscale
97            });
98
99            let mut writer = encoder
100                .write_header()
101                .context("failed to write PNG header")?;
102            writer
103                .write_image_data(&self.data)
104                .context("failed to write PNG data")?;
105        }
106
107        Ok(buffer)
108    }
109
110    #[inline]
111    pub fn get_pixel(&self, x: u32, y: u32) -> Color {
112        if self.data.is_empty() {
113            return WHITE;
114        }
115        let addr = self.samples * (y * self.width + x) as usize;
116        if self.samples == 1 {
117            Color::Gray(self.data[addr])
118        } else {
119            Color::from_rgb(&self.data[addr..addr + 3])
120        }
121    }
122}
123
124impl Framebuffer for Pixmap {
125    fn set_pixel(&mut self, x: u32, y: u32, color: Color) {
126        if x >= self.width || y >= self.height {
127            return;
128        }
129        if self.data.is_empty() {
130            return;
131        }
132        let addr = self.samples * (y * self.width + x) as usize;
133        if self.samples == 1 {
134            self.data[addr] = color.gray();
135        } else {
136            let rgb = color.rgb();
137            self.data[addr..addr + 3].copy_from_slice(&rgb);
138        }
139    }
140
141    fn set_blended_pixel(&mut self, x: u32, y: u32, color: Color, alpha: f32) {
142        if alpha >= 1.0 {
143            self.set_pixel(x, y, color);
144            return;
145        }
146        if x >= self.width || y >= self.height {
147            return;
148        }
149        if self.data.is_empty() {
150            return;
151        }
152        let addr = self.samples * (y * self.width + x) as usize;
153        if self.samples == 1 {
154            self.data[addr] = lerp(self.data[addr] as f32, color.gray() as f32, alpha) as u8;
155        } else {
156            let rgb = color.rgb();
157            for (i, c) in self.data[addr..addr + 3].iter_mut().enumerate() {
158                *c = lerp(*c as f32, rgb[i] as f32, alpha) as u8;
159            }
160        }
161    }
162
163    fn invert_region(&mut self, rect: &Rectangle) {
164        if self.data.is_empty() {
165            return;
166        }
167        for y in rect.min.y..rect.max.y {
168            for x in rect.min.x..rect.max.x {
169                let addr = self.samples * (y * self.width as i32 + x) as usize;
170                if self.samples == 1 {
171                    self.data[addr] = 255 - self.data[addr];
172                } else {
173                    for c in self.data[addr..addr + 3].iter_mut() {
174                        *c = 255 - *c;
175                    }
176                }
177            }
178        }
179    }
180
181    fn shift_region(&mut self, rect: &Rectangle, drift: u8) {
182        if self.data.is_empty() {
183            return;
184        }
185        for y in rect.min.y..rect.max.y {
186            for x in rect.min.x..rect.max.x {
187                let addr = self.samples * (y * self.width as i32 + x) as usize;
188                if self.samples == 1 {
189                    self.data[addr] = self.data[addr].saturating_sub(drift);
190                } else {
191                    for c in self.data[addr..addr + 3].iter_mut() {
192                        *c = c.saturating_sub(drift);
193                    }
194                }
195            }
196        }
197    }
198
199    fn update(&mut self, _rect: &Rectangle, _mode: UpdateMode) -> Result<u32, Error> {
200        Ok(1)
201    }
202
203    fn wait(&self, _: u32) -> Result<i32, Error> {
204        Ok(1)
205    }
206
207    fn save(&self, path: &str) -> Result<(), Error> {
208        if self.data.is_empty() {
209            return Err(format_err!("nothing to save"));
210        }
211        let (width, height) = self.dims();
212        let file =
213            File::create(path).with_context(|| format!("can't create output file {}", path))?;
214        let mut encoder = png::Encoder::new(file, width, height);
215        encoder.set_depth(png::BitDepth::Eight);
216        encoder.set_color(if self.samples == 3 {
217            png::ColorType::Rgb
218        } else {
219            png::ColorType::Grayscale
220        });
221        let mut writer = encoder
222            .write_header()
223            .with_context(|| format!("can't write PNG header for {}", path))?;
224        writer
225            .write_image_data(&self.data)
226            .with_context(|| format!("can't write PNG data to {}", path))?;
227        Ok(())
228    }
229
230    fn set_rotation(&mut self, _n: i8) -> Result<(u32, u32), Error> {
231        Err(format_err!("unsupported"))
232    }
233
234    fn set_monochrome(&mut self, _enable: bool) {}
235
236    fn set_dithered(&mut self, _enable: bool) {}
237
238    fn set_inverted(&mut self, _enable: bool) {}
239
240    fn monochrome(&self) -> bool {
241        false
242    }
243
244    fn dithered(&self) -> bool {
245        false
246    }
247
248    fn inverted(&self) -> bool {
249        false
250    }
251
252    fn width(&self) -> u32 {
253        self.width
254    }
255
256    fn height(&self) -> u32 {
257        self.height
258    }
259}