| 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.get()) |
| 603 | } |
| 604 | |