1use alloc::vec;
7use alloc::vec::Vec;
8use peniko::color::Rgba8;
9
10use crate::peniko::color::PremulRgba8;
11
12#[cfg(feature = "png")]
13extern crate std;
14
15#[derive(Debug, Clone)]
17pub struct Pixmap {
18 width: u16,
20 height: u16,
22 buf: Vec<PremulRgba8>,
24}
25
26impl Pixmap {
27 pub fn new(width: u16, height: u16) -> Self {
29 let buf = vec![PremulRgba8::from_u32(0); width as usize * height as usize];
30 Self { width, height, buf }
31 }
32
33 pub fn from_parts(data: Vec<PremulRgba8>, width: u16, height: u16) -> Self {
43 assert_eq!(
44 data.len(),
45 usize::from(width) * usize::from(height),
46 "Expected `data` to have length of exactly `width * height`"
47 );
48 Self {
49 width,
50 height,
51 buf: data,
52 }
53 }
54
55 pub fn resize(&mut self, width: u16, height: u16) {
62 self.width = width;
63 self.height = height;
64 self.buf.resize(
65 usize::from(width) * usize::from(height),
66 PremulRgba8::from_u32(0),
67 );
68 }
69
70 pub fn shrink_to_fit(&mut self) {
72 self.buf.shrink_to_fit();
73 }
74
75 pub fn capacity(&self) -> usize {
80 self.buf.capacity()
81 }
82
83 pub fn width(&self) -> u16 {
85 self.width
86 }
87
88 pub fn height(&self) -> u16 {
90 self.height
91 }
92
93 pub fn multiply_alpha(&mut self, alpha: u8) {
95 #[expect(
96 clippy::cast_possible_truncation,
97 reason = "cannot overflow in this case"
98 )]
99 let multiply = |component| ((u16::from(alpha) * u16::from(component)) / 255) as u8;
100
101 for pixel in self.data_mut() {
102 *pixel = PremulRgba8 {
103 r: multiply(pixel.r),
104 g: multiply(pixel.g),
105 b: multiply(pixel.b),
106 a: multiply(pixel.a),
107 };
108 }
109 }
110
111 #[cfg(feature = "png")]
113 pub fn from_png(data: impl std::io::Read) -> Result<Self, png::DecodingError> {
114 let mut decoder = png::Decoder::new(data);
115 decoder.set_transformations(
116 png::Transformations::normalize_to_color8() | png::Transformations::ALPHA,
117 );
118
119 let mut reader = decoder.read_info()?;
120 let mut pixmap = {
121 let info = reader.info();
122 let width: u16 = info
123 .width
124 .try_into()
125 .map_err(|_| png::DecodingError::LimitsExceeded)?;
126 let height: u16 = info
127 .height
128 .try_into()
129 .map_err(|_| png::DecodingError::LimitsExceeded)?;
130 Self::new(width, height)
131 };
132
133 let (color_type, bit_depth) = reader.output_color_type();
136 debug_assert_eq!(
137 bit_depth,
138 png::BitDepth::Eight,
139 "normalize_to_color8 means the bit depth is always 8."
140 );
141
142 match color_type {
143 png::ColorType::Rgb | png::ColorType::Grayscale => {
144 unreachable!("We set a transformation to always convert to alpha")
145 }
146 png::ColorType::Indexed => {
147 unreachable!("Transformation should have expanded indexed images")
148 }
149 png::ColorType::Rgba => {
150 debug_assert_eq!(
151 pixmap.data_as_u8_slice().len(),
152 reader.output_buffer_size(),
153 "The pixmap buffer should have the same number of bytes as the image."
154 );
155 reader.next_frame(pixmap.data_as_u8_slice_mut())?;
156 }
157 png::ColorType::GrayscaleAlpha => {
158 debug_assert_eq!(
159 pixmap.data().len() * 2,
160 reader.output_buffer_size(),
161 "The pixmap buffer should have twice the number of bytes of the grayscale image."
162 );
163 let mut grayscale_data = vec![0; reader.output_buffer_size()];
164 reader.next_frame(&mut grayscale_data)?;
165
166 for (grayscale_pixel, pixmap_pixel) in
167 grayscale_data.chunks_exact(2).zip(pixmap.data_mut())
168 {
169 let [gray, alpha] = grayscale_pixel.try_into().unwrap();
170 *pixmap_pixel = PremulRgba8 {
171 r: gray,
172 g: gray,
173 b: gray,
174 a: alpha,
175 };
176 }
177 }
178 };
179
180 for pixel in pixmap.data_mut() {
181 let alpha = u16::from(pixel.a);
182 #[expect(
183 clippy::cast_possible_truncation,
184 reason = "Overflow should be impossible."
185 )]
186 let premultiply = |e: u8| ((u16::from(e) * alpha) / 255) as u8;
187 pixel.r = premultiply(pixel.r);
188 pixel.g = premultiply(pixel.g);
189 pixel.b = premultiply(pixel.b);
190 }
191
192 Ok(pixmap)
193 }
194
195 #[cfg(feature = "png")]
197 pub fn into_png(self) -> Result<Vec<u8>, png::EncodingError> {
198 let mut data = Vec::new();
199 let mut encoder = png::Encoder::new(&mut data, self.width as u32, self.height as u32);
200 encoder.set_color(png::ColorType::Rgba);
201 encoder.set_depth(png::BitDepth::Eight);
202 let mut writer = encoder.write_header()?;
203 writer.write_image_data(bytemuck::cast_slice(&self.take_unpremultiplied()))?;
204 writer.finish().map(|_| data)
205 }
206
207 pub fn data(&self) -> &[PremulRgba8] {
211 &self.buf
212 }
213
214 pub fn data_mut(&mut self) -> &mut [PremulRgba8] {
218 &mut self.buf
219 }
220
221 pub fn data_as_u8_slice(&self) -> &[u8] {
226 bytemuck::cast_slice(&self.buf)
227 }
228
229 pub fn data_as_u8_slice_mut(&mut self) -> &mut [u8] {
234 bytemuck::cast_slice_mut(&mut self.buf)
235 }
236
237 #[inline(always)]
241 pub fn sample(&self, x: u16, y: u16) -> PremulRgba8 {
242 let idx = self.width as usize * y as usize + x as usize;
243 self.buf[idx]
244 }
245
246 #[inline(always)]
249 pub fn sample_idx(&self, idx: u32) -> PremulRgba8 {
250 self.buf[idx as usize]
251 }
252
253 pub fn take(self) -> Vec<PremulRgba8> {
257 self.buf
258 }
259
260 pub fn take_unpremultiplied(self) -> Vec<Rgba8> {
266 self.buf
267 .into_iter()
268 .map(|PremulRgba8 { r, g, b, a }| {
269 let alpha = 255.0 / f32::from(a);
270 if a != 0 {
271 #[expect(clippy::cast_possible_truncation, reason = "deliberate quantization")]
272 let unpremultiply = |component| (f32::from(component) * alpha + 0.5) as u8;
273 Rgba8 {
274 r: unpremultiply(r),
275 g: unpremultiply(g),
276 b: unpremultiply(b),
277 a,
278 }
279 } else {
280 Rgba8 { r, g, b, a }
281 }
282 })
283 .collect()
284 }
285}