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