Skip to main content

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    #[cfg_attr(feature = "tracing", tracing::instrument(skip(bytes)))]
71    pub fn from_png_bytes(bytes: &[u8]) -> Result<Pixmap, Error> {
72        let cursor = Cursor::new(bytes);
73        let decoder = png::Decoder::new(cursor);
74        let mut reader = decoder.read_info()?;
75        let info = reader.info();
76        let mut pixmap = Pixmap::new(info.width, info.height, info.color_type.samples());
77        reader.next_frame(pixmap.data_mut())?;
78        Ok(pixmap)
79    }
80
81    /// Encode the pixmap as PNG bytes using the current pixel format.
82    ///
83    /// Returns an error when the pixmap has no pixel data.
84    pub fn to_png_bytes(&self) -> Result<Vec<u8>, Error> {
85        if self.data.is_empty() {
86            return Err(format_err!("nothing to encode"));
87        }
88
89        let (width, height) = self.dims();
90        let mut buffer = Vec::new();
91        {
92            let mut encoder = png::Encoder::new(&mut buffer, width, height);
93            encoder.set_depth(png::BitDepth::Eight);
94            encoder.set_color(if self.samples == 3 {
95                png::ColorType::Rgb
96            } else {
97                png::ColorType::Grayscale
98            });
99
100            let mut writer = encoder
101                .write_header()
102                .context("failed to write PNG header")?;
103            writer
104                .write_image_data(&self.data)
105                .context("failed to write PNG data")?;
106        }
107
108        Ok(buffer)
109    }
110
111    #[inline]
112    pub fn get_pixel(&self, x: u32, y: u32) -> Color {
113        if self.data.is_empty() {
114            return WHITE;
115        }
116        let addr = self.samples * (y * self.width + x) as usize;
117        if self.samples == 1 {
118            Color::Gray(self.data[addr])
119        } else {
120            Color::from_rgb(&self.data[addr..addr + 3])
121        }
122    }
123}
124
125impl Framebuffer for Pixmap {
126    fn set_pixel(&mut self, x: u32, y: u32, color: Color) {
127        if x >= self.width || y >= self.height {
128            return;
129        }
130        if self.data.is_empty() {
131            return;
132        }
133        let addr = self.samples * (y * self.width + x) as usize;
134        if self.samples == 1 {
135            self.data[addr] = color.gray();
136        } else {
137            let rgb = color.rgb();
138            self.data[addr..addr + 3].copy_from_slice(&rgb);
139        }
140    }
141
142    fn set_blended_pixel(&mut self, x: u32, y: u32, color: Color, alpha: f32) {
143        if alpha >= 1.0 {
144            self.set_pixel(x, y, color);
145            return;
146        }
147        if x >= self.width || y >= self.height {
148            return;
149        }
150        if self.data.is_empty() {
151            return;
152        }
153        let addr = self.samples * (y * self.width + x) as usize;
154        if self.samples == 1 {
155            self.data[addr] = lerp(self.data[addr] as f32, color.gray() as f32, alpha) as u8;
156        } else {
157            let rgb = color.rgb();
158            for (i, c) in self.data[addr..addr + 3].iter_mut().enumerate() {
159                *c = lerp(*c as f32, rgb[i] as f32, alpha) as u8;
160            }
161        }
162    }
163
164    fn invert_region(&mut self, rect: &Rectangle) {
165        if self.data.is_empty() {
166            return;
167        }
168        for y in rect.min.y..rect.max.y {
169            for x in rect.min.x..rect.max.x {
170                let addr = self.samples * (y * self.width as i32 + x) as usize;
171                if self.samples == 1 {
172                    self.data[addr] = 255 - self.data[addr];
173                } else {
174                    for c in self.data[addr..addr + 3].iter_mut() {
175                        *c = 255 - *c;
176                    }
177                }
178            }
179        }
180    }
181
182    fn shift_region(&mut self, rect: &Rectangle, drift: u8) {
183        if self.data.is_empty() {
184            return;
185        }
186        for y in rect.min.y..rect.max.y {
187            for x in rect.min.x..rect.max.x {
188                let addr = self.samples * (y * self.width as i32 + x) as usize;
189                if self.samples == 1 {
190                    self.data[addr] = self.data[addr].saturating_sub(drift);
191                } else {
192                    for c in self.data[addr..addr + 3].iter_mut() {
193                        *c = c.saturating_sub(drift);
194                    }
195                }
196            }
197        }
198    }
199
200    fn update(&mut self, _rect: &Rectangle, _mode: UpdateMode) -> Result<u32, Error> {
201        Ok(1)
202    }
203
204    fn wait(&self, _: u32) -> Result<i32, Error> {
205        Ok(1)
206    }
207
208    fn save(&self, path: &str) -> Result<(), Error> {
209        if self.data.is_empty() {
210            return Err(format_err!("nothing to save"));
211        }
212        let (width, height) = self.dims();
213        let file =
214            File::create(path).with_context(|| format!("can't create output file {}", path))?;
215        let mut encoder = png::Encoder::new(file, width, height);
216        encoder.set_depth(png::BitDepth::Eight);
217        encoder.set_color(if self.samples == 3 {
218            png::ColorType::Rgb
219        } else {
220            png::ColorType::Grayscale
221        });
222        let mut writer = encoder
223            .write_header()
224            .with_context(|| format!("can't write PNG header for {}", path))?;
225        writer
226            .write_image_data(&self.data)
227            .with_context(|| format!("can't write PNG data to {}", path))?;
228        Ok(())
229    }
230
231    fn set_rotation(&mut self, _n: i8) -> Result<(u32, u32), Error> {
232        Err(format_err!("unsupported"))
233    }
234
235    fn set_monochrome(&mut self, _enable: bool) {}
236
237    fn set_dithered(&mut self, _enable: bool) {}
238
239    fn set_inverted(&mut self, _enable: bool) {}
240
241    fn monochrome(&self) -> bool {
242        false
243    }
244
245    fn dithered(&self) -> bool {
246        false
247    }
248
249    fn inverted(&self) -> bool {
250        false
251    }
252
253    fn width(&self) -> u32 {
254        self.width
255    }
256
257    fn height(&self) -> u32 {
258        self.height
259    }
260}