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 #[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 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}