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::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}