1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use crate::{color, Color, Point, Size};
// TODO rename this trait
/// Represents a 2D matrix of pixels (an image)
pub trait Bitmap: Clone {
/// Create a new image with a specified size and filled with one color
fn new(size: Size<i32>, color: Color) -> Self;
/// Get the [`Size`] of the image
fn size(&self) -> Size<i32>;
/// Get the width of the image in pixels
fn width(&self) -> i32;
/// Get the height of the image in pixels
fn height(&self) -> i32;
/// Get the pixel color at a certain [`Point`]
fn pixel(&self, point: Point<i32>) -> Color;
/// Set the pixel at a certain [`Point`] to a specified color
fn set_pixel(&mut self, point: Point<i32>, color: Color);
/// Get this image's representation as a slice of bytes
fn bytes(&self) -> &[u8];
/// Create a new image from a slice of bytes and a [`Size`]
fn from_parts(size: Size<i32>, bytes: &[u8]) -> Self;
/// Set this image pixels based on another, but respecting the size of this
/// one, i.e. ignoring pixels out of bounds
fn set_from(&mut self, other: &Self);
// TODO: use this and next method to save/load files
/// Get the the PNG representation of this image as a sequence of bytes
fn png_bytes(&self) -> Vec<u8> {
let img = image::RgbaImage::from_raw(
self.width() as u32,
self.height() as u32,
self.bytes().to_owned(),
)
.expect("Failed to generate image from bytes");
let vec = Vec::<u8>::new();
let mut vec = std::io::Cursor::new(vec);
img.write_to(&mut vec, image::ImageOutputFormat::Png)
.unwrap(); // never fails
vec.into_inner()
}
/// Create a new image from a sequence of bytes read from an image file
/// (e.g. PNG or JPG)
fn from_file_bytes(bytes: Vec<u8>) -> Self {
let reader = image::io::Reader::new(std::io::Cursor::new(bytes))
.with_guessed_format()
.unwrap(); // never fails
let img = reader.decode().expect("failed to decode image");
let img = img.into_rgba8();
let size: Size<i32> = (img.width() as i32, img.height() as i32).into();
let mut bitmap = Self::new(size, color::TRANSPARENT);
for (x, y, pixel) in img.enumerate_pixels() {
let color = Color::new(pixel.0[0], pixel.0[1], pixel.0[2], pixel.0[3]);
bitmap.set_pixel((x as i32, y as i32).into(), color);
}
bitmap
}
}
#[cfg(test)]
pub use test::TestImage;
#[cfg(test)]
mod test {
use super::*;
// TODO implementing this showed that our trait is not that generic. We were
// forced to add this `bytes` field which defeats the purpose of having a
// vec of vec of color as storage. We should improve this.
/// Terribly slow but easy to use image type just for tests' sake
#[derive(Clone)]
pub struct TestImage {
size: Size<i32>,
pixels: Vec<Vec<Color>>, // vec of rows of pixels
bytes: Vec<u8>,
}
impl TestImage {
fn update_bytes(&mut self) {
self.bytes = self
.pixels
.iter()
.map(|row| {
row.iter()
.map(|c| [c.r, c.g, c.b, c.a])
.flatten()
.collect::<Vec<_>>()
})
.flatten()
.collect();
}
}
impl Bitmap for TestImage {
fn new(size: Size<i32>, color: Color) -> Self {
let pixels = vec![vec![color; size.x as usize]; size.y as usize];
let mut img = Self {
size,
pixels,
bytes: Vec::new(),
};
img.update_bytes();
img
}
fn size(&self) -> Size<i32> {
self.size
}
fn width(&self) -> i32 {
self.size.x
}
fn height(&self) -> i32 {
self.size.y
}
fn pixel(&self, point: Point<i32>) -> Color {
self.pixels[point.y as usize][point.x as usize]
}
fn set_pixel(&mut self, point: Point<i32>, color: Color) {
self.pixels[point.y as usize][point.x as usize] = color;
self.update_bytes();
}
fn bytes(&self) -> &[u8] {
&self.bytes
}
fn from_parts(size: Size<i32>, bytes: &[u8]) -> Self {
todo!()
}
fn set_from(&mut self, other: &Self) {
self.pixels = other.pixels.clone();
if self.pixels.len() >= self.size.y as usize {
self.pixels = self.pixels[0..self.size.y as usize].to_vec();
} else {
while self.pixels.len() < self.size.y as usize {
self.pixels
.push(vec![color::TRANSPARENT; self.size.x as usize]);
}
}
if self.pixels[0].len() >= self.size.x as usize {
for row in self.pixels.iter_mut() {
*row = row[0..self.size.x as usize].to_vec();
}
} else {
for row in self.pixels.iter_mut() {
while row.len() < self.size.x as usize {
row.push(color::TRANSPARENT);
}
}
}
}
}
}