1 | // Copyright 2006 The Android Open Source Project |
2 | // Copyright 2020 Yevhenii Reizner |
3 | // |
4 | // Use of this source code is governed by a BSD-style license that can be |
5 | // found in the LICENSE file. |
6 | |
7 | use alloc::vec; |
8 | use alloc::vec::Vec; |
9 | |
10 | use core::convert::TryFrom; |
11 | use core::num::NonZeroUsize; |
12 | |
13 | use tiny_skia_path::IntSize; |
14 | |
15 | use crate::{Color, IntRect}; |
16 | |
17 | use crate::color::PremultipliedColorU8; |
18 | use crate::geom::{IntSizeExt, ScreenIntRect}; |
19 | |
20 | #[cfg (feature = "png-format" )] |
21 | use crate::color::{premultiply_u8, ALPHA_U8_OPAQUE}; |
22 | |
23 | /// Number of bytes per pixel. |
24 | pub const BYTES_PER_PIXEL: usize = 4; |
25 | |
26 | /// A container that owns premultiplied RGBA pixels. |
27 | /// |
28 | /// The data is not aligned, therefore width == stride. |
29 | #[derive (Clone, PartialEq)] |
30 | pub struct Pixmap { |
31 | data: Vec<u8>, |
32 | size: IntSize, |
33 | } |
34 | |
35 | impl Pixmap { |
36 | /// Allocates a new pixmap. |
37 | /// |
38 | /// A pixmap is filled with transparent black by default, aka (0, 0, 0, 0). |
39 | /// |
40 | /// Zero size in an error. |
41 | /// |
42 | /// Pixmap's width is limited by i32::MAX/4. |
43 | pub fn new(width: u32, height: u32) -> Option<Self> { |
44 | let size = IntSize::from_wh(width, height)?; |
45 | let data_len = data_len_for_size(size)?; |
46 | |
47 | // We cannot check that allocation was successful yet. |
48 | // We have to wait for https://github.com/rust-lang/rust/issues/48043 |
49 | |
50 | Some(Pixmap { |
51 | data: vec![0; data_len], |
52 | size, |
53 | }) |
54 | } |
55 | |
56 | /// Creates a new pixmap by taking ownership over an image buffer |
57 | /// (premultiplied RGBA pixels). |
58 | /// |
59 | /// The size needs to match the data provided. |
60 | /// |
61 | /// Pixmap's width is limited by i32::MAX/4. |
62 | pub fn from_vec(data: Vec<u8>, size: IntSize) -> Option<Self> { |
63 | let data_len = data_len_for_size(size)?; |
64 | if data.len() != data_len { |
65 | return None; |
66 | } |
67 | |
68 | Some(Pixmap { data, size }) |
69 | } |
70 | |
71 | /// Decodes a PNG data into a `Pixmap`. |
72 | /// |
73 | /// Only 8-bit images are supported. |
74 | /// Index PNGs are not supported. |
75 | #[cfg (feature = "png-format" )] |
76 | pub fn decode_png(data: &[u8]) -> Result<Self, png::DecodingError> { |
77 | fn make_custom_png_error(msg: &str) -> png::DecodingError { |
78 | std::io::Error::new(std::io::ErrorKind::Other, msg).into() |
79 | } |
80 | |
81 | let mut decoder = png::Decoder::new(data); |
82 | decoder.set_transformations(png::Transformations::normalize_to_color8()); |
83 | let mut reader = decoder.read_info()?; |
84 | let mut img_data = vec![0; reader.output_buffer_size()]; |
85 | let info = reader.next_frame(&mut img_data)?; |
86 | |
87 | if info.bit_depth != png::BitDepth::Eight { |
88 | return Err(make_custom_png_error("unsupported bit depth" )); |
89 | } |
90 | |
91 | let size = IntSize::from_wh(info.width, info.height) |
92 | .ok_or_else(|| make_custom_png_error("invalid image size" ))?; |
93 | let data_len = |
94 | data_len_for_size(size).ok_or_else(|| make_custom_png_error("image is too big" ))?; |
95 | |
96 | img_data = match info.color_type { |
97 | png::ColorType::Rgb => { |
98 | let mut rgba_data = Vec::with_capacity(data_len); |
99 | for rgb in img_data.chunks(3) { |
100 | rgba_data.push(rgb[0]); |
101 | rgba_data.push(rgb[1]); |
102 | rgba_data.push(rgb[2]); |
103 | rgba_data.push(ALPHA_U8_OPAQUE); |
104 | } |
105 | |
106 | rgba_data |
107 | } |
108 | png::ColorType::Rgba => img_data, |
109 | png::ColorType::Grayscale => { |
110 | let mut rgba_data = Vec::with_capacity(data_len); |
111 | for gray in img_data { |
112 | rgba_data.push(gray); |
113 | rgba_data.push(gray); |
114 | rgba_data.push(gray); |
115 | rgba_data.push(ALPHA_U8_OPAQUE); |
116 | } |
117 | |
118 | rgba_data |
119 | } |
120 | png::ColorType::GrayscaleAlpha => { |
121 | let mut rgba_data = Vec::with_capacity(data_len); |
122 | for slice in img_data.chunks(2) { |
123 | let gray = slice[0]; |
124 | let alpha = slice[1]; |
125 | rgba_data.push(gray); |
126 | rgba_data.push(gray); |
127 | rgba_data.push(gray); |
128 | rgba_data.push(alpha); |
129 | } |
130 | |
131 | rgba_data |
132 | } |
133 | png::ColorType::Indexed => { |
134 | return Err(make_custom_png_error("indexed PNG is not supported" )); |
135 | } |
136 | }; |
137 | |
138 | // Premultiply alpha. |
139 | // |
140 | // We cannon use RasterPipeline here, which is faster, |
141 | // because it produces slightly different results. |
142 | // Seems like Skia does the same. |
143 | // |
144 | // Also, in our tests unsafe version (no bound checking) |
145 | // had roughly the same performance. So we keep the safe one. |
146 | for pixel in img_data.as_mut_slice().chunks_mut(BYTES_PER_PIXEL) { |
147 | let a = pixel[3]; |
148 | pixel[0] = premultiply_u8(pixel[0], a); |
149 | pixel[1] = premultiply_u8(pixel[1], a); |
150 | pixel[2] = premultiply_u8(pixel[2], a); |
151 | } |
152 | |
153 | Pixmap::from_vec(img_data, size) |
154 | .ok_or_else(|| make_custom_png_error("failed to create a pixmap" )) |
155 | } |
156 | |
157 | /// Loads a PNG file into a `Pixmap`. |
158 | /// |
159 | /// Only 8-bit images are supported. |
160 | /// Index PNGs are not supported. |
161 | #[cfg (feature = "png-format" )] |
162 | pub fn load_png<P: AsRef<std::path::Path>>(path: P) -> Result<Self, png::DecodingError> { |
163 | // `png::Decoder` is generic over input, which means that it will instance |
164 | // two copies: one for `&[]` and one for `File`. Which will simply bloat the code. |
165 | // Therefore we're using only one type for input. |
166 | let data = std::fs::read(path)?; |
167 | Self::decode_png(&data) |
168 | } |
169 | |
170 | /// Encodes pixmap into a PNG data. |
171 | #[cfg (feature = "png-format" )] |
172 | pub fn encode_png(&self) -> Result<Vec<u8>, png::EncodingError> { |
173 | self.as_ref().encode_png() |
174 | } |
175 | |
176 | /// Saves pixmap as a PNG file. |
177 | #[cfg (feature = "png-format" )] |
178 | pub fn save_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), png::EncodingError> { |
179 | self.as_ref().save_png(path) |
180 | } |
181 | |
182 | /// Returns a container that references Pixmap's data. |
183 | pub fn as_ref(&self) -> PixmapRef { |
184 | PixmapRef { |
185 | data: &self.data, |
186 | size: self.size, |
187 | } |
188 | } |
189 | |
190 | /// Returns a container that references Pixmap's data. |
191 | pub fn as_mut(&mut self) -> PixmapMut { |
192 | PixmapMut { |
193 | data: &mut self.data, |
194 | size: self.size, |
195 | } |
196 | } |
197 | |
198 | /// Returns pixmap's width. |
199 | #[inline ] |
200 | pub fn width(&self) -> u32 { |
201 | self.size.width() |
202 | } |
203 | |
204 | /// Returns pixmap's height. |
205 | #[inline ] |
206 | pub fn height(&self) -> u32 { |
207 | self.size.height() |
208 | } |
209 | |
210 | /// Returns pixmap's size. |
211 | #[allow (dead_code)] |
212 | pub(crate) fn size(&self) -> IntSize { |
213 | self.size |
214 | } |
215 | |
216 | /// Fills the entire pixmap with a specified color. |
217 | pub fn fill(&mut self, color: Color) { |
218 | let c = color.premultiply().to_color_u8(); |
219 | for p in self.as_mut().pixels_mut() { |
220 | *p = c; |
221 | } |
222 | } |
223 | |
224 | /// Returns the internal data. |
225 | /// |
226 | /// Byteorder: RGBA |
227 | pub fn data(&self) -> &[u8] { |
228 | self.data.as_slice() |
229 | } |
230 | |
231 | /// Returns the mutable internal data. |
232 | /// |
233 | /// Byteorder: RGBA |
234 | pub fn data_mut(&mut self) -> &mut [u8] { |
235 | self.data.as_mut_slice() |
236 | } |
237 | |
238 | /// Returns a pixel color. |
239 | /// |
240 | /// Returns `None` when position is out of bounds. |
241 | pub fn pixel(&self, x: u32, y: u32) -> Option<PremultipliedColorU8> { |
242 | let idx = self.width().checked_mul(y)?.checked_add(x)?; |
243 | self.pixels().get(idx as usize).cloned() |
244 | } |
245 | |
246 | /// Returns a mutable slice of pixels. |
247 | pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] { |
248 | bytemuck::cast_slice_mut(self.data_mut()) |
249 | } |
250 | |
251 | /// Returns a slice of pixels. |
252 | pub fn pixels(&self) -> &[PremultipliedColorU8] { |
253 | bytemuck::cast_slice(self.data()) |
254 | } |
255 | |
256 | /// Consumes the internal data. |
257 | /// |
258 | /// Byteorder: RGBA |
259 | pub fn take(self) -> Vec<u8> { |
260 | self.data |
261 | } |
262 | |
263 | /// Returns a copy of the pixmap that intersects the `rect`. |
264 | /// |
265 | /// Returns `None` when `Pixmap`'s rect doesn't contain `rect`. |
266 | pub fn clone_rect(&self, rect: IntRect) -> Option<Pixmap> { |
267 | self.as_ref().clone_rect(rect) |
268 | } |
269 | } |
270 | |
271 | impl core::fmt::Debug for Pixmap { |
272 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
273 | f&mut DebugStruct<'_, '_>.debug_struct("Pixmap" ) |
274 | .field("data" , &"..." ) |
275 | .field("width" , &self.size.width()) |
276 | .field(name:"height" , &self.size.height()) |
277 | .finish() |
278 | } |
279 | } |
280 | |
281 | /// A container that references premultiplied RGBA pixels. |
282 | /// |
283 | /// Can be created from `Pixmap` or from a user provided data. |
284 | /// |
285 | /// The data is not aligned, therefore width == stride. |
286 | #[derive (Clone, Copy, PartialEq)] |
287 | pub struct PixmapRef<'a> { |
288 | data: &'a [u8], |
289 | size: IntSize, |
290 | } |
291 | |
292 | impl<'a> PixmapRef<'a> { |
293 | /// Creates a new `PixmapRef` from bytes. |
294 | /// |
295 | /// The size must be at least `size.width() * size.height() * BYTES_PER_PIXEL`. |
296 | /// Zero size in an error. Width is limited by i32::MAX/4. |
297 | /// |
298 | /// The `data` is assumed to have premultiplied RGBA pixels (byteorder: RGBA). |
299 | pub fn from_bytes(data: &'a [u8], width: u32, height: u32) -> Option<Self> { |
300 | let size = IntSize::from_wh(width, height)?; |
301 | let data_len = data_len_for_size(size)?; |
302 | if data.len() < data_len { |
303 | return None; |
304 | } |
305 | |
306 | Some(PixmapRef { data, size }) |
307 | } |
308 | |
309 | /// Creates a new `Pixmap` from the current data. |
310 | /// |
311 | /// Clones the underlying data. |
312 | pub fn to_owned(&self) -> Pixmap { |
313 | Pixmap { |
314 | data: self.data.to_vec(), |
315 | size: self.size, |
316 | } |
317 | } |
318 | |
319 | /// Returns pixmap's width. |
320 | #[inline ] |
321 | pub fn width(&self) -> u32 { |
322 | self.size.width() |
323 | } |
324 | |
325 | /// Returns pixmap's height. |
326 | #[inline ] |
327 | pub fn height(&self) -> u32 { |
328 | self.size.height() |
329 | } |
330 | |
331 | /// Returns pixmap's size. |
332 | pub(crate) fn size(&self) -> IntSize { |
333 | self.size |
334 | } |
335 | |
336 | /// Returns pixmap's rect. |
337 | pub(crate) fn rect(&self) -> ScreenIntRect { |
338 | self.size.to_screen_int_rect(0, 0) |
339 | } |
340 | |
341 | /// Returns the internal data. |
342 | /// |
343 | /// Byteorder: RGBA |
344 | pub fn data(&self) -> &'a [u8] { |
345 | self.data |
346 | } |
347 | |
348 | /// Returns a pixel color. |
349 | /// |
350 | /// Returns `None` when position is out of bounds. |
351 | pub fn pixel(&self, x: u32, y: u32) -> Option<PremultipliedColorU8> { |
352 | let idx = self.width().checked_mul(y)?.checked_add(x)?; |
353 | self.pixels().get(idx as usize).cloned() |
354 | } |
355 | |
356 | /// Returns a slice of pixels. |
357 | pub fn pixels(&self) -> &'a [PremultipliedColorU8] { |
358 | bytemuck::cast_slice(self.data()) |
359 | } |
360 | |
361 | // TODO: add rows() iterator |
362 | |
363 | /// Returns a copy of the pixmap that intersects the `rect`. |
364 | /// |
365 | /// Returns `None` when `Pixmap`'s rect doesn't contain `rect`. |
366 | pub fn clone_rect(&self, rect: IntRect) -> Option<Pixmap> { |
367 | // TODO: to ScreenIntRect? |
368 | |
369 | let rect = self.rect().to_int_rect().intersect(&rect)?; |
370 | let mut new = Pixmap::new(rect.width(), rect.height())?; |
371 | { |
372 | let old_pixels = self.pixels(); |
373 | let mut new_mut = new.as_mut(); |
374 | let new_pixels = new_mut.pixels_mut(); |
375 | |
376 | // TODO: optimize |
377 | for y in 0..rect.height() { |
378 | for x in 0..rect.width() { |
379 | let old_idx = (y + rect.y() as u32) * self.width() + (x + rect.x() as u32); |
380 | let new_idx = y * rect.width() + x; |
381 | new_pixels[new_idx as usize] = old_pixels[old_idx as usize]; |
382 | } |
383 | } |
384 | } |
385 | |
386 | Some(new) |
387 | } |
388 | |
389 | /// Encodes pixmap into a PNG data. |
390 | #[cfg (feature = "png-format" )] |
391 | pub fn encode_png(&self) -> Result<Vec<u8>, png::EncodingError> { |
392 | // Skia uses skcms here, which is somewhat similar to RasterPipeline. |
393 | |
394 | // Sadly, we have to copy the pixmap here, because of demultiplication. |
395 | // Not sure how to avoid this. |
396 | // TODO: remove allocation |
397 | let mut tmp_pixmap = self.to_owned(); |
398 | |
399 | // Demultiply alpha. |
400 | // |
401 | // RasterPipeline is 15% faster here, but produces slightly different results |
402 | // due to rounding. So we stick with this method for now. |
403 | for pixel in tmp_pixmap.pixels_mut() { |
404 | let c = pixel.demultiply(); |
405 | *pixel = |
406 | PremultipliedColorU8::from_rgba_unchecked(c.red(), c.green(), c.blue(), c.alpha()); |
407 | } |
408 | |
409 | let mut data = Vec::new(); |
410 | { |
411 | let mut encoder = png::Encoder::new(&mut data, self.width(), self.height()); |
412 | encoder.set_color(png::ColorType::Rgba); |
413 | encoder.set_depth(png::BitDepth::Eight); |
414 | let mut writer = encoder.write_header()?; |
415 | writer.write_image_data(&tmp_pixmap.data)?; |
416 | } |
417 | |
418 | Ok(data) |
419 | } |
420 | |
421 | /// Saves pixmap as a PNG file. |
422 | #[cfg (feature = "png-format" )] |
423 | pub fn save_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), png::EncodingError> { |
424 | let data = self.encode_png()?; |
425 | std::fs::write(path, data)?; |
426 | Ok(()) |
427 | } |
428 | } |
429 | |
430 | impl core::fmt::Debug for PixmapRef<'_> { |
431 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
432 | f&mut DebugStruct<'_, '_>.debug_struct("PixmapRef" ) |
433 | .field("data" , &"..." ) |
434 | .field("width" , &self.size.width()) |
435 | .field(name:"height" , &self.size.height()) |
436 | .finish() |
437 | } |
438 | } |
439 | |
440 | /// A container that references mutable premultiplied RGBA pixels. |
441 | /// |
442 | /// Can be created from `Pixmap` or from a user provided data. |
443 | /// |
444 | /// The data is not aligned, therefore width == stride. |
445 | #[derive (PartialEq)] |
446 | pub struct PixmapMut<'a> { |
447 | data: &'a mut [u8], |
448 | size: IntSize, |
449 | } |
450 | |
451 | impl<'a> PixmapMut<'a> { |
452 | /// Creates a new `PixmapMut` from bytes. |
453 | /// |
454 | /// The size must be at least `size.width() * size.height() * BYTES_PER_PIXEL`. |
455 | /// Zero size in an error. Width is limited by i32::MAX/4. |
456 | /// |
457 | /// The `data` is assumed to have premultiplied RGBA pixels (byteorder: RGBA). |
458 | pub fn from_bytes(data: &'a mut [u8], width: u32, height: u32) -> Option<Self> { |
459 | let size = IntSize::from_wh(width, height)?; |
460 | let data_len = data_len_for_size(size)?; |
461 | if data.len() < data_len { |
462 | return None; |
463 | } |
464 | |
465 | Some(PixmapMut { data, size }) |
466 | } |
467 | |
468 | /// Creates a new `Pixmap` from the current data. |
469 | /// |
470 | /// Clones the underlying data. |
471 | pub fn to_owned(&self) -> Pixmap { |
472 | Pixmap { |
473 | data: self.data.to_vec(), |
474 | size: self.size, |
475 | } |
476 | } |
477 | |
478 | /// Returns a container that references Pixmap's data. |
479 | pub fn as_ref(&self) -> PixmapRef { |
480 | PixmapRef { |
481 | data: self.data, |
482 | size: self.size, |
483 | } |
484 | } |
485 | |
486 | /// Returns pixmap's width. |
487 | #[inline ] |
488 | pub fn width(&self) -> u32 { |
489 | self.size.width() |
490 | } |
491 | |
492 | /// Returns pixmap's height. |
493 | #[inline ] |
494 | pub fn height(&self) -> u32 { |
495 | self.size.height() |
496 | } |
497 | |
498 | /// Returns pixmap's size. |
499 | pub(crate) fn size(&self) -> IntSize { |
500 | self.size |
501 | } |
502 | |
503 | /// Fills the entire pixmap with a specified color. |
504 | pub fn fill(&mut self, color: Color) { |
505 | let c = color.premultiply().to_color_u8(); |
506 | for p in self.pixels_mut() { |
507 | *p = c; |
508 | } |
509 | } |
510 | |
511 | /// Returns the mutable internal data. |
512 | /// |
513 | /// Byteorder: RGBA |
514 | pub fn data_mut(&mut self) -> &mut [u8] { |
515 | self.data |
516 | } |
517 | |
518 | /// Returns a mutable slice of pixels. |
519 | pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] { |
520 | bytemuck::cast_slice_mut(self.data_mut()) |
521 | } |
522 | |
523 | /// Creates `SubPixmapMut` that contains the whole `PixmapMut`. |
524 | pub(crate) fn as_subpixmap(&mut self) -> SubPixmapMut { |
525 | SubPixmapMut { |
526 | size: self.size(), |
527 | real_width: self.width() as usize, |
528 | data: self.data, |
529 | } |
530 | } |
531 | |
532 | /// Returns a mutable reference to the pixmap region that intersects the `rect`. |
533 | /// |
534 | /// Returns `None` when `Pixmap`'s rect doesn't contain `rect`. |
535 | pub(crate) fn subpixmap(&mut self, rect: IntRect) -> Option<SubPixmapMut> { |
536 | let rect = self.size.to_int_rect(0, 0).intersect(&rect)?; |
537 | let row_bytes = self.width() as usize * BYTES_PER_PIXEL; |
538 | let offset = rect.top() as usize * row_bytes + rect.left() as usize * BYTES_PER_PIXEL; |
539 | |
540 | Some(SubPixmapMut { |
541 | size: rect.size(), |
542 | real_width: self.width() as usize, |
543 | data: &mut self.data[offset..], |
544 | }) |
545 | } |
546 | } |
547 | |
548 | impl core::fmt::Debug for PixmapMut<'_> { |
549 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
550 | f&mut DebugStruct<'_, '_>.debug_struct("PixmapMut" ) |
551 | .field("data" , &"..." ) |
552 | .field("width" , &self.size.width()) |
553 | .field(name:"height" , &self.size.height()) |
554 | .finish() |
555 | } |
556 | } |
557 | |
558 | /// A `PixmapMut` subregion. |
559 | /// |
560 | /// Unlike `PixmapMut`, contains `real_width` which references the parent `PixmapMut` width. |
561 | /// This way we can operate on a `PixmapMut` subregion without reallocations. |
562 | /// Primarily required because of `DrawTiler`. |
563 | /// |
564 | /// We cannot implement it in `PixmapMut` directly, because it will brake `fill`, `data_mut` |
565 | /// `pixels_mut` and other similar methods. |
566 | /// This is because `SubPixmapMut.data` references more "data" than it actually allowed to access. |
567 | /// On the other hand, `PixmapMut.data` can access all it's data and it's stored linearly. |
568 | pub struct SubPixmapMut<'a> { |
569 | pub data: &'a mut [u8], |
570 | pub size: IntSize, |
571 | pub real_width: usize, |
572 | } |
573 | |
574 | impl<'a> SubPixmapMut<'a> { |
575 | /// Returns a mutable slice of pixels. |
576 | pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] { |
577 | bytemuck::cast_slice_mut(self.data) |
578 | } |
579 | } |
580 | |
581 | /// Returns minimum bytes per row as usize. |
582 | /// |
583 | /// Pixmap's maximum value for row bytes must fit in 31 bits. |
584 | fn min_row_bytes(size: IntSize) -> Option<NonZeroUsize> { |
585 | let w: i32 = i32::try_from(size.width()).ok()?; |
586 | let w: i32 = w.checked_mul(BYTES_PER_PIXEL as i32)?; |
587 | NonZeroUsize::new(w as usize) |
588 | } |
589 | |
590 | /// Returns storage size required by pixel array. |
591 | fn compute_data_len(size: IntSize, row_bytes: usize) -> Option<usize> { |
592 | let h: u32 = size.height().checked_sub(1)?; |
593 | let h: usize = (h as usize).checked_mul(row_bytes)?; |
594 | |
595 | let w: usize = (size.width() as usize).checked_mul(BYTES_PER_PIXEL)?; |
596 | |
597 | h.checked_add(w) |
598 | } |
599 | |
600 | fn data_len_for_size(size: IntSize) -> Option<usize> { |
601 | let row_bytes: NonZero = min_row_bytes(size)?; |
602 | compute_data_len(size, row_bytes:row_bytes.get()) |
603 | } |
604 | |