1use std::fmt;
2use std::fmt::Write;
3
4pub use bit_vec::BitVec;
5
6use crate::{BoundingRect, Color, ColorName, ColorType, Field, PointF32, PointF64, PointI32};
7
8#[derive(Debug, Clone, Default)]
10pub struct BinaryImage {
11 pub pixels: BitVec,
12 pub width: usize,
13 pub height: usize,
14}
15
16#[derive(Clone, Default)]
18pub struct ScalerField<T> {
19 field: Field<T>,
20}
21
22pub type MonoImageItem = u16;
24pub type MonoImage = ScalerField<MonoImageItem>;
26
27#[derive(Clone, Default)]
29pub struct ColorImage {
30 pub pixels: Vec<u8>,
31 pub width: usize,
32 pub height: usize,
33}
34
35pub struct ColorImageIter<'a> {
37 im: &'a ColorImage,
38 curr: usize,
39 stop: usize,
40}
41
42impl BinaryImage {
43 pub fn new_w_h(width: usize, height: usize) -> BinaryImage {
44 BinaryImage {
45 pixels: BitVec::from_elem(width * height, false),
46 width,
47 height,
48 }
49 }
50
51 pub fn get_pixel_at(&self, p: PointI32) -> bool {
52 self.get_pixel(p.x as usize, p.y as usize)
53 }
54
55 pub fn get_pixel(&self, x: usize, y: usize) -> bool {
56 let i = y * self.width + x;
57 self.pixels.get(i).unwrap()
58 }
59
60 pub fn get_pixel_at_safe(&self, p: PointI32) -> bool {
61 self.get_pixel_safe(p.x, p.y)
62 }
63
64 pub fn get_pixel_safe(&self, x: i32, y: i32) -> bool {
65 if x >= 0 && x < self.width as i32 &&
66 y >= 0 && y < self.height as i32 {
67 return self.get_pixel(x as usize, y as usize);
68 }
69 false
70 }
71
72 pub fn set_pixel(&mut self, x: usize, y: usize, v: bool) {
73 let i = y * self.width + x;
74 self.pixels.set(i, v);
75 }
76
77 pub fn set_pixel_at(&mut self, p: PointI32, v: bool) {
78 self.set_pixel(p.x as usize, p.y as usize, v);
79 }
80
81 pub fn set_pixel_index(&mut self, i: usize, v: bool) {
82 self.pixels.set(i, v);
83 }
84
85 pub fn set_pixel_safe(&mut self, x: i32, y: i32, v: bool) -> bool {
86 if x >= 0 && x < self.width as i32 &&
87 y >= 0 && y < self.height as i32 {
88 self.set_pixel(x as usize, y as usize, v);
89 return true;
90 }
91 false
92 }
93
94 pub fn set_pixel_at_safe(&mut self, p: PointI32, v: bool) {
95 self.set_pixel_safe(p.x, p.y, v);
96 }
97
98 pub fn bounding_rect(&self) -> BoundingRect {
99 let mut rect = BoundingRect::default();
100 for y in 0..self.height {
101 for x in 0..self.width {
102 if self.get_pixel(x, y) {
103 rect.add_x_y(x as i32, y as i32);
104 }
105 }
106 }
107 rect
108 }
109
110 pub fn area(&self) -> u64 {
111 self.pixels.iter().filter(|x| *x).count() as u64
112 }
113
114 pub fn crop(&self) -> BinaryImage {
116 self.crop_with_rect(self.bounding_rect())
117 }
118
119 pub fn crop_with_rect(&self, rect: BoundingRect) -> BinaryImage {
121 let mut image = BinaryImage::new_w_h(rect.width() as usize, rect.height() as usize);
122 for y in rect.top..rect.bottom {
123 for x in rect.left..rect.right {
124 if self.get_pixel(x as usize, y as usize) {
125 image.set_pixel(
126 x as usize - rect.left as usize,
127 y as usize - rect.top as usize,
128 true,
129 );
130 }
131 }
132 }
133 image
134 }
135
136 pub fn uncrop(&self, new_width: usize, new_height: usize) -> BinaryImage {
138 let xx = (new_width - self.width) >> 1;
139 let yy = (new_height - self.height) >> 1;
140 let mut new_image = BinaryImage::new_w_h(new_width, new_height);
141 for y in 0..self.height {
142 for x in 0..self.width {
143 if self.get_pixel(x, y) {
144 new_image.set_pixel(x + xx, y + yy, true);
145 }
146 }
147 }
148 new_image
149 }
150
151 pub fn from_string(string: &str) -> Self {
152 let mut width = 0;
153 let mut height = 0;
154 for line in string.lines() {
155 if height == 0 {
156 width = line.len();
157 }
158 height += 1;
159 }
160 let mut image = Self::new_w_h(width, height);
161 for (y, line) in string.lines().enumerate() {
162 for (x, c) in line.chars().enumerate() {
163 image.set_pixel(x, y, c == '*');
164 }
165 }
166 image
167 }
168
169 pub fn rotate(&self, angle: f64) -> BinaryImage {
170 let rotated_width = (self.width as f64 * angle.cos().abs() + self.height as f64 * angle.sin().abs()).round() as usize;
171 let rotated_height = (self.width as f64 * angle.sin().abs() + self.height as f64 * angle.cos().abs()).round() as usize;
172 let mut rotated_image = BinaryImage::new_w_h(rotated_width, rotated_height);
173 let origin = PointF64::new(rotated_width as f64 / 2.0, rotated_height as f64 / 2.0);
174 let offset = PointF64::new(
175 (rotated_width as i32 - self.width as i32) as f64 / 2.0,
176 (rotated_height as i32 - self.height as i32) as f64 / 2.0
177 );
178 for y in 0..rotated_image.height {
179 for x in 0..rotated_image.width {
180 let rotated = PointF64::new(x as f64, y as f64).rotate(origin, -angle).translate(-offset);
181 rotated_image.set_pixel(
182 x, y,
183 self.get_pixel_safe(rotated.x.round() as i32, rotated.y.round() as i32)
184 );
185 }
186 }
187 rotated_image
188 }
189
190 pub fn paste_from(&mut self, src: &BinaryImage, offset: PointI32) {
192 for y in 0..src.height {
193 for x in 0..src.width {
194 if src.get_pixel(x, y) {
195 self.set_pixel(
196 x + offset.x as usize,
197 y + offset.y as usize,
198 true
199 );
200 }
201 }
202 }
203 }
204
205 pub fn to_color_image(&self) -> ColorImage {
206 let mut image = ColorImage::new_w_h(self.width, self.height);
207 let black = Color::color(&ColorName::Black);
208 let white = Color::color(&ColorName::White);
209 for y in 0..self.height {
210 for x in 0..self.width {
211 image.set_pixel(x, y, if self.get_pixel(x, y) {
212 &black
213 } else {
214 &white
215 });
216 }
217 }
218 image
219 }
220}
221
222impl fmt::Display for BinaryImage {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 for y in 0..self.height {
225 for x in 0..self.width {
226 f.write_char(if self.get_pixel(x, y) { '*' } else { '-' })?;
227 }
228 f.write_char('\n')?;
229 }
230 Ok(())
231 }
232}
233
234impl<T> ScalerField<T> where T: Default {
235 pub fn new_w_h(width: usize, height: usize) -> Self {
236 Self {
237 field: Field::with_default(width, height),
238 }
239 }
240}
241
242impl<T> ScalerField<T> where T: Clone {
243 pub fn get_pixel(&self, x: usize, y: usize) -> T {
244 self.field.get(self.field.index_at(x, y)).unwrap()
245 }
246
247 pub fn set_pixel(&mut self, x: usize, y: usize, v: T) {
248 self.field.replace(self.field.index_at(x, y), v);
249 }
250}
251
252impl Iterator for ColorImageIter<'_> {
253 type Item = Color;
254
255 fn next(&mut self) -> Option<Color> {
256 if self.curr < self.stop {
257 let res = self.im.get_pixel_at(self.curr);
258 self.curr += 1;
259 Some(res)
260 } else {
261 None
262 }
263 }
264}
265
266impl ColorImage {
267 pub fn new() -> Self {
268 Default::default()
269 }
270
271 pub fn new_w_h(width: usize, height: usize) -> Self {
272 Self {
273 pixels: vec![0; width * height * 4],
274 width,
275 height,
276 }
277 }
278
279 pub fn iter(&self) -> ColorImageIter {
280 ColorImageIter {
281 im: self,
282 curr: 0,
283 stop: self.width * self.height,
284 }
285 }
286
287 pub fn get_pixel(&self, x: usize, y: usize) -> Color {
288 let index = y * self.width + x;
289 self.get_pixel_at(index)
290 }
291
292 pub fn get_pixel_at_point_safe(&self, p: PointI32) -> Option<Color> {
293 self.get_pixel_safe(p.x, p.y)
294 }
295
296 pub fn get_pixel_safe(&self, x: i32, y: i32) -> Option<Color> {
297 if x >= 0 && x < self.width as i32 &&
298 y >= 0 && y < self.height as i32 {
299 return Some(self.get_pixel(x as usize, y as usize));
300 }
301 None
302 }
303
304 pub fn get_pixel_at(&self, index: usize) -> Color {
305 let index = index * 4;
306 let r = self.pixels[index];
307 let g = self.pixels[index + 1];
308 let b = self.pixels[index + 2];
309 let a = self.pixels[index + 3];
310
311 Color::new_rgba(r, g, b, a)
312 }
313
314 pub fn set_pixel(&mut self, x: usize, y: usize, color: &Color) {
315 let index = y * self.width + x;
316 self.set_pixel_at(index, color);
317 }
318
319 pub fn set_pixel_at(&mut self, index: usize, color: &Color) {
320 let index = index * 4;
321 self.pixels[index] = color.r;
322 self.pixels[index + 1] = color.g;
323 self.pixels[index + 2] = color.b;
324 self.pixels[index + 3] = color.a;
325 }
326
327 pub fn to_binary_image<F>(&self, f: F) -> BinaryImage
328 where F: Fn(Color) -> bool {
329 let mut image = BinaryImage::new_w_h(self.width, self.height);
330 for y in 0..self.height {
331 for x in 0..self.width {
332 image.set_pixel(x, y, f(self.get_pixel(x, y)));
333 }
334 }
335 image
336 }
337
338 pub fn sample_pixel_at(&self, p: PointF32) -> Color {
339 bilinear_interpolate(self, p)
340 }
341
342 pub fn sample_pixel_at_safe(&self, p:PointF32) -> Option<Color> {
343 bilinear_interpolate_safe(self, p)
344 }
345}
346
347pub fn bilinear_interpolate_safe(im: &ColorImage, p: PointF32) -> Option<Color> {
348 if p.x.is_sign_negative() || p.y.is_sign_negative() || p.x > (im.width - 1) as f32 || p.y > (im.height - 1) as f32 {
349 None
350 } else {
351 Some(bilinear_interpolate(im, p))
352 }
353}
354
355pub fn bilinear_interpolate(im: &ColorImage, p: PointF32) -> Color {
356 let x_0 = p.x.floor() as usize;
357 let x_1 = p.x.ceil() as usize;
358 let y_0 = p.y.floor() as usize;
359 let y_1 = p.y.ceil() as usize;
360 let c_00 = im.get_pixel(x_0, y_0);
361 let c_01 = im.get_pixel(x_0, y_1);
362 let c_10 = im.get_pixel(x_1, y_0);
363 let c_11 = im.get_pixel(x_1, y_1);
364
365 let interpolate = |channel: usize| {
366 let f_00 = c_00.channel(channel).unwrap() as f32;
367 let f_01 = c_01.channel(channel).unwrap() as f32;
368 let f_10 = c_10.channel(channel).unwrap() as f32;
369 let f_11 = c_11.channel(channel).unwrap() as f32;
370 let x = p.x - p.x.floor();
371 let y = p.y - p.y.floor();
372
373 (f_00 * (1.0 - x) * (1.0 - y) +
374 f_10 * x * (1.0 - y) +
375 f_01 * (1.0 - x) * y +
376 f_11 * x * y) as u8
377 };
378
379 Color::new_rgba(
380 interpolate(0),
381 interpolate(1),
382 interpolate(2),
383 interpolate(3),
384 )
385}
386
387#[cfg(test)]
388mod tests {
389 use super::*;
390
391 #[test]
392 fn binary_image_crop() {
393 let mut image = BinaryImage::new_w_h(4, 4);
394 image.set_pixel(1, 1, true);
395 image.set_pixel(2, 2, true);
396 let crop = image.crop();
397 assert_eq!(crop.width, 2);
398 assert_eq!(crop.height, 2);
399 assert_eq!(crop.get_pixel(0, 0), true);
400 assert_eq!(crop.get_pixel(0, 1), false);
401 assert_eq!(crop.get_pixel(1, 0), false);
402 assert_eq!(crop.get_pixel(1, 1), true);
403 }
404
405 #[test]
406 fn image_as_string() {
407 let mut image = BinaryImage::new_w_h(2,2);
408 image.set_pixel(0,0,true);
409 image.set_pixel(1,1,true);
410 assert_eq!(image.to_string(),
411 "*-\n".to_owned()+
412 "-*\n");
413 let recover = BinaryImage::from_string(&image.to_string());
414 assert_eq!(image.width, recover.width);
415 assert_eq!(image.height, recover.height);
416 for y in 0..image.height {
417 for x in 0..image.width {
418 assert_eq!(image.get_pixel(x, y), recover.get_pixel(x, y));
419 }
420 }
421 }
422
423 #[test]
424 fn rotate_test() {
425 assert_eq!(
426 BinaryImage::from_string(&(
427 "-----------*************---------\n".to_owned()+
428 "---------*****************-------\n"+
429 "-------*********************-----\n"+
430 "-----************************----\n"+
431 "----**************************---\n"+
432 "---****************************--\n"+
433 "--*****************************--\n"+
434 "--******************************-\n"+
435 "-*******************************-\n"+
436 "-********************************\n"+
437 "*********************************\n"+
438 "*********************************\n"+
439 "********************************-\n"+
440 "********************************-\n"+
441 "********************************-\n"+
442 "*******************************--\n"+
443 "-******************************--\n"+
444 "-*****************************---\n"+
445 "--***************************----\n"+
446 "---*************************-----\n"+
447 "----***********************------\n"+
448 "-----*********************-------\n"+
449 "-------*****************---------\n"+
450 "---------************------------\n"
451 )).rotate(1.3962634015954636).to_string(),
452 "-----------------------------\n".to_owned()+
453 "-----------------------------\n"+
454 "-----------****-*------------\n"+
455 "---------**********----------\n"+
456 "-------*************---------\n"+
457 "------***************--------\n"+
458 "-----*****************-------\n"+
459 "-----*******************-----\n"+
460 "-----*******************-----\n"+
461 "----*********************----\n"+
462 "----*********************----\n"+
463 "---***********************---\n"+
464 "---************************--\n"+
465 "--*************************--\n"+
466 "---************************--\n"+
467 "---************************--\n"+
468 "---************************--\n"+
469 "---************************--\n"+
470 "---*************************-\n"+
471 "---*************************-\n"+
472 "----************************-\n"+
473 "----************************-\n"+
474 "----************************-\n"+
475 "----************************-\n"+
476 "----************************-\n"+
477 "-----***********************-\n"+
478 "------*********************--\n"+
479 "------*********************--\n"+
480 "-------*******************---\n"+
481 "--------*****************----\n"+
482 "---------****************----\n"+
483 "-----------**************----\n"+
484 "------------***********------\n"+
485 "---------------******--------\n"+
486 "------------------*----------\n"+
487 "-----------------------------\n"+
488 "-----------------------------\n"
489 );
490 }
491}