// Copyright 2006 The Android Open Source Project // Copyright 2020 Yevhenii Reizner // // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use alloc::vec; use alloc::vec::Vec; use core::convert::TryFrom; use core::num::NonZeroUsize; use tiny_skia_path::{IntSize, ScreenIntRect}; use crate::{Color, IntRect}; use crate::color::PremultipliedColorU8; #[cfg(feature = "png-format")] use crate::color::{premultiply_u8, ALPHA_U8_OPAQUE}; /// Number of bytes per pixel. pub const BYTES_PER_PIXEL: usize = 4; /// A container that owns premultiplied RGBA pixels. /// /// The data is not aligned, therefore width == stride. #[derive(Clone, PartialEq)] pub struct Pixmap { data: Vec, size: IntSize, } impl Pixmap { /// Allocates a new pixmap. /// /// A pixmap is filled with transparent black by default, aka (0, 0, 0, 0). /// /// Zero size in an error. /// /// Pixmap's width is limited by i32::MAX/4. pub fn new(width: u32, height: u32) -> Option { let size = IntSize::from_wh(width, height)?; let data_len = data_len_for_size(size)?; // We cannot check that allocation was successful yet. // We have to wait for https://github.com/rust-lang/rust/issues/48043 Some(Pixmap { data: vec![0; data_len], size, }) } /// Creates a new pixmap by taking ownership over an image buffer /// (premultiplied RGBA pixels). /// /// The size needs to match the data provided. /// /// Pixmap's width is limited by i32::MAX/4. pub fn from_vec(data: Vec, size: IntSize) -> Option { let data_len = data_len_for_size(size)?; if data.len() != data_len { return None; } Some(Pixmap { data, size }) } /// Decodes a PNG data into a `Pixmap`. /// /// Only 8-bit images are supported. /// Index PNGs are not supported. #[cfg(feature = "png-format")] pub fn decode_png(data: &[u8]) -> Result { fn make_custom_png_error(msg: &str) -> png::DecodingError { std::io::Error::new(std::io::ErrorKind::Other, msg).into() } let mut decoder = png::Decoder::new(data); decoder.set_transformations(png::Transformations::normalize_to_color8()); let mut reader = decoder.read_info()?; let mut img_data = vec![0; reader.output_buffer_size()]; let info = reader.next_frame(&mut img_data)?; if info.bit_depth != png::BitDepth::Eight { return Err(make_custom_png_error("unsupported bit depth")); } let size = IntSize::from_wh(info.width, info.height) .ok_or_else(|| make_custom_png_error("invalid image size"))?; let data_len = data_len_for_size(size).ok_or_else(|| make_custom_png_error("image is too big"))?; img_data = match info.color_type { png::ColorType::Rgb => { let mut rgba_data = Vec::with_capacity(data_len); for rgb in img_data.chunks(3) { rgba_data.push(rgb[0]); rgba_data.push(rgb[1]); rgba_data.push(rgb[2]); rgba_data.push(ALPHA_U8_OPAQUE); } rgba_data } png::ColorType::Rgba => img_data, png::ColorType::Grayscale => { let mut rgba_data = Vec::with_capacity(data_len); for gray in img_data { rgba_data.push(gray); rgba_data.push(gray); rgba_data.push(gray); rgba_data.push(ALPHA_U8_OPAQUE); } rgba_data } png::ColorType::GrayscaleAlpha => { let mut rgba_data = Vec::with_capacity(data_len); for slice in img_data.chunks(2) { let gray = slice[0]; let alpha = slice[1]; rgba_data.push(gray); rgba_data.push(gray); rgba_data.push(gray); rgba_data.push(alpha); } rgba_data } png::ColorType::Indexed => { return Err(make_custom_png_error("indexed PNG is not supported")); } }; // Premultiply alpha. // // We cannon use RasterPipeline here, which is faster, // because it produces slightly different results. // Seems like Skia does the same. // // Also, in our tests unsafe version (no bound checking) // had roughly the same performance. So we keep the safe one. for pixel in img_data.as_mut_slice().chunks_mut(BYTES_PER_PIXEL) { let a = pixel[3]; pixel[0] = premultiply_u8(pixel[0], a); pixel[1] = premultiply_u8(pixel[1], a); pixel[2] = premultiply_u8(pixel[2], a); } Pixmap::from_vec(img_data, size) .ok_or_else(|| make_custom_png_error("failed to create a pixmap")) } /// Loads a PNG file into a `Pixmap`. /// /// Only 8-bit images are supported. /// Index PNGs are not supported. #[cfg(feature = "png-format")] pub fn load_png>(path: P) -> Result { // `png::Decoder` is generic over input, which means that it will instance // two copies: one for `&[]` and one for `File`. Which will simply bloat the code. // Therefore we're using only one type for input. let data = std::fs::read(path)?; Self::decode_png(&data) } /// Encodes pixmap into a PNG data. #[cfg(feature = "png-format")] pub fn encode_png(&self) -> Result, png::EncodingError> { self.as_ref().encode_png() } /// Saves pixmap as a PNG file. #[cfg(feature = "png-format")] pub fn save_png>(&self, path: P) -> Result<(), png::EncodingError> { self.as_ref().save_png(path) } /// Returns a container that references Pixmap's data. pub fn as_ref(&self) -> PixmapRef { PixmapRef { data: &self.data, size: self.size, } } /// Returns a container that references Pixmap's data. pub fn as_mut(&mut self) -> PixmapMut { PixmapMut { data: &mut self.data, size: self.size, } } /// Returns pixmap's width. #[inline] pub fn width(&self) -> u32 { self.size.width() } /// Returns pixmap's height. #[inline] pub fn height(&self) -> u32 { self.size.height() } /// Returns pixmap's size. #[allow(dead_code)] pub(crate) fn size(&self) -> IntSize { self.size } /// Fills the entire pixmap with a specified color. pub fn fill(&mut self, color: Color) { let c = color.premultiply().to_color_u8(); for p in self.as_mut().pixels_mut() { *p = c; } } /// Returns the internal data. /// /// Byteorder: RGBA pub fn data(&self) -> &[u8] { self.data.as_slice() } /// Returns the mutable internal data. /// /// Byteorder: RGBA pub fn data_mut(&mut self) -> &mut [u8] { self.data.as_mut_slice() } /// Returns a pixel color. /// /// Returns `None` when position is out of bounds. pub fn pixel(&self, x: u32, y: u32) -> Option { let idx = self.width().checked_mul(y)?.checked_add(x)?; self.pixels().get(idx as usize).cloned() } /// Returns a mutable slice of pixels. pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] { bytemuck::cast_slice_mut(self.data_mut()) } /// Returns a slice of pixels. pub fn pixels(&self) -> &[PremultipliedColorU8] { bytemuck::cast_slice(self.data()) } /// Consumes the internal data. /// /// Byteorder: RGBA pub fn take(self) -> Vec { self.data } /// Returns a copy of the pixmap that intersects the `rect`. /// /// Returns `None` when `Pixmap`'s rect doesn't contain `rect`. pub fn clone_rect(&self, rect: IntRect) -> Option { self.as_ref().clone_rect(rect) } } impl core::fmt::Debug for Pixmap { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("Pixmap") .field("data", &"...") .field("width", &self.size.width()) .field("height", &self.size.height()) .finish() } } /// A container that references premultiplied RGBA pixels. /// /// Can be created from `Pixmap` or from a user provided data. /// /// The data is not aligned, therefore width == stride. #[derive(Clone, Copy, PartialEq)] pub struct PixmapRef<'a> { data: &'a [u8], size: IntSize, } impl<'a> PixmapRef<'a> { /// Creates a new `PixmapRef` from bytes. /// /// The size must be at least `size.width() * size.height() * BYTES_PER_PIXEL`. /// Zero size in an error. Width is limited by i32::MAX/4. /// /// The `data` is assumed to have premultiplied RGBA pixels (byteorder: RGBA). pub fn from_bytes(data: &'a [u8], width: u32, height: u32) -> Option { let size = IntSize::from_wh(width, height)?; let data_len = data_len_for_size(size)?; if data.len() < data_len { return None; } Some(PixmapRef { data, size }) } /// Creates a new `Pixmap` from the current data. /// /// Clones the underlying data. pub fn to_owned(&self) -> Pixmap { Pixmap { data: self.data.to_vec(), size: self.size, } } /// Returns pixmap's width. #[inline] pub fn width(&self) -> u32 { self.size.width() } /// Returns pixmap's height. #[inline] pub fn height(&self) -> u32 { self.size.height() } /// Returns pixmap's size. pub(crate) fn size(&self) -> IntSize { self.size } /// Returns pixmap's rect. pub(crate) fn rect(&self) -> ScreenIntRect { self.size.to_screen_int_rect(0, 0) } /// Returns the internal data. /// /// Byteorder: RGBA pub fn data(&self) -> &'a [u8] { self.data } /// Returns a pixel color. /// /// Returns `None` when position is out of bounds. pub fn pixel(&self, x: u32, y: u32) -> Option { let idx = self.width().checked_mul(y)?.checked_add(x)?; self.pixels().get(idx as usize).cloned() } /// Returns a slice of pixels. pub fn pixels(&self) -> &'a [PremultipliedColorU8] { bytemuck::cast_slice(self.data()) } // TODO: add rows() iterator /// Returns a copy of the pixmap that intersects the `rect`. /// /// Returns `None` when `Pixmap`'s rect doesn't contain `rect`. pub fn clone_rect(&self, rect: IntRect) -> Option { // TODO: to ScreenIntRect? let rect = self.rect().to_int_rect().intersect(&rect)?; let mut new = Pixmap::new(rect.width(), rect.height())?; { let old_pixels = self.pixels(); let mut new_mut = new.as_mut(); let new_pixels = new_mut.pixels_mut(); // TODO: optimize for y in 0..rect.height() { for x in 0..rect.width() { let old_idx = (y + rect.y() as u32) * self.width() + (x + rect.x() as u32); let new_idx = y * rect.width() + x; new_pixels[new_idx as usize] = old_pixels[old_idx as usize]; } } } Some(new) } /// Encodes pixmap into a PNG data. #[cfg(feature = "png-format")] pub fn encode_png(&self) -> Result, png::EncodingError> { // Skia uses skcms here, which is somewhat similar to RasterPipeline. // Sadly, we have to copy the pixmap here, because of demultiplication. // Not sure how to avoid this. // TODO: remove allocation let mut tmp_pixmap = self.to_owned(); // Demultiply alpha. // // RasterPipeline is 15% faster here, but produces slightly different results // due to rounding. So we stick with this method for now. for pixel in tmp_pixmap.pixels_mut() { let c = pixel.demultiply(); *pixel = PremultipliedColorU8::from_rgba_unchecked(c.red(), c.green(), c.blue(), c.alpha()); } let mut data = Vec::new(); { let mut encoder = png::Encoder::new(&mut data, self.width(), self.height()); encoder.set_color(png::ColorType::Rgba); encoder.set_depth(png::BitDepth::Eight); let mut writer = encoder.write_header()?; writer.write_image_data(&tmp_pixmap.data)?; } Ok(data) } /// Saves pixmap as a PNG file. #[cfg(feature = "png-format")] pub fn save_png>(&self, path: P) -> Result<(), png::EncodingError> { let data = self.encode_png()?; std::fs::write(path, data)?; Ok(()) } } impl core::fmt::Debug for PixmapRef<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("PixmapRef") .field("data", &"...") .field("width", &self.size.width()) .field("height", &self.size.height()) .finish() } } /// A container that references mutable premultiplied RGBA pixels. /// /// Can be created from `Pixmap` or from a user provided data. /// /// The data is not aligned, therefore width == stride. #[derive(PartialEq)] pub struct PixmapMut<'a> { data: &'a mut [u8], size: IntSize, } impl<'a> PixmapMut<'a> { /// Creates a new `PixmapMut` from bytes. /// /// The size must be at least `size.width() * size.height() * BYTES_PER_PIXEL`. /// Zero size in an error. Width is limited by i32::MAX/4. /// /// The `data` is assumed to have premultiplied RGBA pixels (byteorder: RGBA). pub fn from_bytes(data: &'a mut [u8], width: u32, height: u32) -> Option { let size = IntSize::from_wh(width, height)?; let data_len = data_len_for_size(size)?; if data.len() < data_len { return None; } Some(PixmapMut { data, size }) } /// Creates a new `Pixmap` from the current data. /// /// Clones the underlying data. pub fn to_owned(&self) -> Pixmap { Pixmap { data: self.data.to_vec(), size: self.size, } } /// Returns a container that references Pixmap's data. pub fn as_ref(&self) -> PixmapRef { PixmapRef { data: self.data, size: self.size, } } /// Returns pixmap's width. #[inline] pub fn width(&self) -> u32 { self.size.width() } /// Returns pixmap's height. #[inline] pub fn height(&self) -> u32 { self.size.height() } /// Returns pixmap's size. pub(crate) fn size(&self) -> IntSize { self.size } /// Fills the entire pixmap with a specified color. pub fn fill(&mut self, color: Color) { let c = color.premultiply().to_color_u8(); for p in self.pixels_mut() { *p = c; } } /// Returns the mutable internal data. /// /// Byteorder: RGBA pub fn data_mut(&mut self) -> &mut [u8] { self.data } /// Returns a mutable slice of pixels. pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] { bytemuck::cast_slice_mut(self.data_mut()) } /// Creates `SubPixmapMut` that contains the whole `PixmapMut`. pub(crate) fn as_subpixmap(&mut self) -> SubPixmapMut { SubPixmapMut { size: self.size(), real_width: self.width() as usize, data: &mut self.data, } } /// Returns a mutable reference to the pixmap region that intersects the `rect`. /// /// Returns `None` when `Pixmap`'s rect doesn't contain `rect`. pub(crate) fn subpixmap(&mut self, rect: IntRect) -> Option { let rect = self.size.to_int_rect(0, 0).intersect(&rect)?; let row_bytes = self.width() as usize * BYTES_PER_PIXEL; let offset = rect.top() as usize * row_bytes + rect.left() as usize * BYTES_PER_PIXEL; Some(SubPixmapMut { size: rect.size(), real_width: self.width() as usize, data: &mut self.data[offset..], }) } } impl core::fmt::Debug for PixmapMut<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("PixmapMut") .field("data", &"...") .field("width", &self.size.width()) .field("height", &self.size.height()) .finish() } } /// A `PixmapMut` subregion. /// /// Unlike `PixmapMut`, contains `real_width` which references the parent `PixmapMut` width. /// This way we can operate on a `PixmapMut` subregion without reallocations. /// Primarily required because of `DrawTiler`. /// /// We cannot implement it in `PixmapMut` directly, because it will brake `fill`, `data_mut` /// `pixels_mut` and other similar methods. /// This is because `SubPixmapMut.data` references more "data" than it actually allowed to access. /// On the other hand, `PixmapMut.data` can access all it's data and it's stored linearly. pub struct SubPixmapMut<'a> { pub data: &'a mut [u8], pub size: IntSize, pub real_width: usize, } impl<'a> SubPixmapMut<'a> { /// Returns a mutable slice of pixels. pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] { bytemuck::cast_slice_mut(self.data) } } /// Returns minimum bytes per row as usize. /// /// Pixmap's maximum value for row bytes must fit in 31 bits. fn min_row_bytes(size: IntSize) -> Option { let w = i32::try_from(size.width()).ok()?; let w = w.checked_mul(BYTES_PER_PIXEL as i32)?; NonZeroUsize::new(w as usize) } /// Returns storage size required by pixel array. fn compute_data_len(size: IntSize, row_bytes: usize) -> Option { let h = size.height().checked_sub(1)?; let h = (h as usize).checked_mul(row_bytes)?; let w = (size.width() as usize).checked_mul(BYTES_PER_PIXEL)?; h.checked_add(w) } fn data_len_for_size(size: IntSize) -> Option { let row_bytes = min_row_bytes(size)?; compute_data_len(size, row_bytes.get()) }