cadmus_core/framebuffer/
image.rs1use 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 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 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}