| 1 | use crate::{ |
| 2 | prelude::*, AlphaType, Color, Color4f, ColorSpace, ColorType, IPoint, IRect, ISize, ImageInfo, |
| 3 | SamplingOptions, |
| 4 | }; |
| 5 | use skia_bindings::{self as sb, SkPixmap}; |
| 6 | use std::{ffi::c_void, fmt, marker::PhantomData, mem, os::raw, ptr, slice}; |
| 7 | |
| 8 | #[repr (transparent)] |
| 9 | pub struct Pixmap<'a> { |
| 10 | inner: Handle<SkPixmap>, |
| 11 | pd: PhantomData<&'a mut [u8]>, |
| 12 | } |
| 13 | |
| 14 | impl NativeDrop for SkPixmap { |
| 15 | fn drop(&mut self) { |
| 16 | unsafe { sb::C_SkPixmap_destruct(self) } |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | impl Default for Pixmap<'_> { |
| 21 | fn default() -> Self { |
| 22 | Self::from_native_c(pixmap:SkPixmap { |
| 23 | fPixels: ptr::null(), |
| 24 | fRowBytes: 0, |
| 25 | fInfo: construct(|ii: *mut SkImageInfo| unsafe { sb::C_SkImageInfo_Construct(uninitialized:ii) }), |
| 26 | }) |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | impl fmt::Debug for Pixmap<'_> { |
| 31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 32 | f&mut DebugStruct<'_, '_>.debug_struct("Pixmap" ) |
| 33 | .field("row_bytes" , &self.row_bytes()) |
| 34 | .field(name:"info" , self.info()) |
| 35 | .finish() |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | impl<'pixels> Pixmap<'pixels> { |
| 40 | pub fn new(info: &ImageInfo, pixels: &'pixels mut [u8], row_bytes: usize) -> Option<Self> { |
| 41 | if row_bytes < info.min_row_bytes() { |
| 42 | return None; |
| 43 | } |
| 44 | if pixels.len() < info.compute_byte_size(row_bytes) { |
| 45 | return None; |
| 46 | } |
| 47 | |
| 48 | Some(Pixmap::from_native_c(SkPixmap { |
| 49 | fPixels: pixels.as_mut_ptr() as _, |
| 50 | fRowBytes: row_bytes, |
| 51 | fInfo: info.native().clone(), |
| 52 | })) |
| 53 | } |
| 54 | |
| 55 | pub fn reset(&mut self) -> &mut Self { |
| 56 | unsafe { self.native_mut().reset() } |
| 57 | self |
| 58 | } |
| 59 | |
| 60 | // TODO: reset() function that re-borrows pixels? |
| 61 | |
| 62 | pub fn set_color_space(&mut self, color_space: impl Into<Option<ColorSpace>>) -> &mut Self { |
| 63 | unsafe { |
| 64 | sb::C_SkPixmap_setColorSpace(self.native_mut(), color_space.into().into_ptr_or_null()) |
| 65 | } |
| 66 | self |
| 67 | } |
| 68 | |
| 69 | #[must_use ] |
| 70 | pub fn extract_subset(&self, area: impl AsRef<IRect>) -> Option<Self> { |
| 71 | let mut pixmap = Pixmap::default(); |
| 72 | unsafe { |
| 73 | self.native() |
| 74 | .extractSubset(pixmap.native_mut(), area.as_ref().native()) |
| 75 | } |
| 76 | .if_true_some(pixmap) |
| 77 | } |
| 78 | |
| 79 | pub fn info(&self) -> &ImageInfo { |
| 80 | ImageInfo::from_native_ref(&self.native().fInfo) |
| 81 | } |
| 82 | |
| 83 | pub fn row_bytes(&self) -> usize { |
| 84 | self.native().fRowBytes |
| 85 | } |
| 86 | |
| 87 | pub fn addr(&self) -> *const c_void { |
| 88 | self.native().fPixels |
| 89 | } |
| 90 | |
| 91 | pub fn width(&self) -> i32 { |
| 92 | self.info().width() |
| 93 | } |
| 94 | |
| 95 | pub fn height(&self) -> i32 { |
| 96 | self.info().height() |
| 97 | } |
| 98 | |
| 99 | pub fn dimensions(&self) -> ISize { |
| 100 | self.info().dimensions() |
| 101 | } |
| 102 | |
| 103 | pub fn color_type(&self) -> ColorType { |
| 104 | self.info().color_type() |
| 105 | } |
| 106 | |
| 107 | pub fn alpha_type(&self) -> AlphaType { |
| 108 | self.info().alpha_type() |
| 109 | } |
| 110 | |
| 111 | pub fn color_space(&self) -> Option<ColorSpace> { |
| 112 | ColorSpace::from_unshared_ptr(unsafe { self.native().colorSpace() }) |
| 113 | } |
| 114 | |
| 115 | pub fn is_opaque(&self) -> bool { |
| 116 | self.alpha_type().is_opaque() |
| 117 | } |
| 118 | |
| 119 | pub fn bounds(&self) -> IRect { |
| 120 | IRect::from_wh(self.width(), self.height()) |
| 121 | } |
| 122 | |
| 123 | pub fn row_bytes_as_pixels(&self) -> usize { |
| 124 | self.row_bytes() >> self.shift_per_pixel() |
| 125 | } |
| 126 | |
| 127 | pub fn shift_per_pixel(&self) -> usize { |
| 128 | self.info().shift_per_pixel() |
| 129 | } |
| 130 | |
| 131 | pub fn compute_byte_size(&self) -> usize { |
| 132 | self.info().compute_byte_size(self.row_bytes()) |
| 133 | } |
| 134 | |
| 135 | pub fn compute_is_opaque(&self) -> bool { |
| 136 | unsafe { self.native().computeIsOpaque() } |
| 137 | } |
| 138 | |
| 139 | pub fn get_color(&self, p: impl Into<IPoint>) -> Color { |
| 140 | let p = p.into(); |
| 141 | self.assert_pixel_exists(p); |
| 142 | Color::from_native_c(unsafe { self.native().getColor(p.x, p.y) }) |
| 143 | } |
| 144 | |
| 145 | pub fn get_color_4f(&self, p: impl Into<IPoint>) -> Color4f { |
| 146 | let p = p.into(); |
| 147 | self.assert_pixel_exists(p); |
| 148 | Color4f::from_native_c(unsafe { self.native().getColor4f(p.x, p.y) }) |
| 149 | } |
| 150 | |
| 151 | pub fn get_alpha_f(&self, p: impl Into<IPoint>) -> f32 { |
| 152 | let p = p.into(); |
| 153 | self.assert_pixel_exists(p); |
| 154 | unsafe { self.native().getAlphaf(p.x, p.y) } |
| 155 | } |
| 156 | |
| 157 | // Helper to test if the pixel does exist physically in memory. |
| 158 | fn assert_pixel_exists(&self, p: impl Into<IPoint>) { |
| 159 | let p = p.into(); |
| 160 | assert!(!self.addr().is_null()); |
| 161 | assert!(p.x >= 0 && p.x < self.width()); |
| 162 | assert!(p.y >= 0 && p.y < self.height()); |
| 163 | } |
| 164 | |
| 165 | pub fn addr_at(&self, p: impl Into<IPoint>) -> *const c_void { |
| 166 | let p = p.into(); |
| 167 | self.assert_pixel_exists(p); |
| 168 | unsafe { |
| 169 | (self.addr() as *const raw::c_char).add(self.info().compute_offset(p, self.row_bytes())) |
| 170 | as _ |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | // TODO: addr8(), addr16(), addr32(), addr64(), addrF16(), |
| 175 | // addr8_at(), addr16_at(), addr32_at(), addr64_at(), addrF16_at() |
| 176 | |
| 177 | pub fn writable_addr(&self) -> *mut c_void { |
| 178 | self.addr() as _ |
| 179 | } |
| 180 | |
| 181 | pub fn writable_addr_at(&self, p: impl Into<IPoint>) -> *mut c_void { |
| 182 | self.addr_at(p) as _ |
| 183 | } |
| 184 | |
| 185 | // TODO: writable_addr8 |
| 186 | // TODO: writable_addr16 |
| 187 | // TODO: writable_addr32 |
| 188 | // TODO: writable_addr64 |
| 189 | // TODO: writable_addrF16 |
| 190 | |
| 191 | pub fn read_pixels<P>( |
| 192 | &self, |
| 193 | dst_info: &ImageInfo, |
| 194 | pixels: &mut [P], |
| 195 | dst_row_bytes: usize, |
| 196 | src: impl Into<IPoint>, |
| 197 | ) -> bool { |
| 198 | if !dst_info.valid_pixels(dst_row_bytes, pixels) { |
| 199 | return false; |
| 200 | } |
| 201 | |
| 202 | let src = src.into(); |
| 203 | |
| 204 | unsafe { |
| 205 | self.native().readPixels( |
| 206 | dst_info.native(), |
| 207 | pixels.as_mut_ptr() as _, |
| 208 | dst_row_bytes, |
| 209 | src.x, |
| 210 | src.y, |
| 211 | ) |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | /// Access the underlying pixels as a byte array. This is a rust-skia specific function. |
| 216 | pub fn bytes(&self) -> Option<&'pixels [u8]> { |
| 217 | let addr = self.addr().into_option()?; |
| 218 | let len = self.compute_byte_size(); |
| 219 | Some(unsafe { slice::from_raw_parts(addr as *const u8, len) }) |
| 220 | } |
| 221 | |
| 222 | pub fn bytes_mut(&mut self) -> Option<&'pixels mut [u8]> { |
| 223 | let addr = self.writable_addr().into_option()?; |
| 224 | let len = self.compute_byte_size(); |
| 225 | Some(unsafe { slice::from_raw_parts_mut(addr.as_ptr() as *mut u8, len) }) |
| 226 | } |
| 227 | |
| 228 | /// Access the underlying pixels. This is a rust-skia specific function. |
| 229 | /// |
| 230 | /// The `Pixel` type must implement the _unsafe_ trait [`Pixel`] and must return `true` in |
| 231 | /// [`Pixel::matches_color_type()`] when matched against the [`ColorType`] of this Pixmap's |
| 232 | /// pixels. |
| 233 | pub fn pixels<P: Pixel>(&self) -> Option<&'pixels [P]> { |
| 234 | let addr = self.addr().into_option()?; |
| 235 | |
| 236 | let info = self.info(); |
| 237 | let ct = info.color_type(); |
| 238 | let pixel_size = mem::size_of::<P>(); |
| 239 | |
| 240 | if info.bytes_per_pixel() == pixel_size && P::matches_color_type(ct) { |
| 241 | let len = self.compute_byte_size() / pixel_size; |
| 242 | return Some(unsafe { slice::from_raw_parts(addr as *const P, len) }); |
| 243 | } |
| 244 | |
| 245 | None |
| 246 | } |
| 247 | |
| 248 | pub fn read_pixels_to_pixmap(&self, dst: &mut Pixmap, src: impl Into<IPoint>) -> bool { |
| 249 | let Some(dst_bytes) = dst.bytes_mut() else { |
| 250 | return false; |
| 251 | }; |
| 252 | self.read_pixels(dst.info(), dst_bytes, dst.row_bytes(), src) |
| 253 | } |
| 254 | |
| 255 | pub fn scale_pixels(&self, dst: &mut Pixmap, sampling: impl Into<SamplingOptions>) -> bool { |
| 256 | let sampling = sampling.into(); |
| 257 | unsafe { self.native().scalePixels(dst.native(), sampling.native()) } |
| 258 | } |
| 259 | |
| 260 | pub fn erase(&mut self, color: impl Into<Color>, subset: Option<&IRect>) -> bool { |
| 261 | let color = color.into().into_native(); |
| 262 | unsafe { |
| 263 | match subset { |
| 264 | Some(subset) => self.native().erase(color, subset.native()), |
| 265 | None => self.native().erase(color, self.bounds().native()), |
| 266 | } |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | pub fn erase_4f(&mut self, color: impl AsRef<Color4f>, subset: Option<&IRect>) -> bool { |
| 271 | let color = color.as_ref(); |
| 272 | unsafe { |
| 273 | self.native() |
| 274 | .erase1(color.native(), subset.native_ptr_or_null()) |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | fn from_native_c(pixmap: SkPixmap) -> Self { |
| 279 | Self { |
| 280 | inner: Handle::from_native_c(pixmap), |
| 281 | pd: PhantomData, |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | #[must_use ] |
| 286 | pub(crate) fn from_native_ref(n: &SkPixmap) -> &Self { |
| 287 | unsafe { transmute_ref(n) } |
| 288 | } |
| 289 | |
| 290 | #[must_use ] |
| 291 | pub(crate) fn from_native_ptr(np: *const SkPixmap) -> *const Self { |
| 292 | // Should be safe as long `Pixmap` is represented with repr(Transparent). |
| 293 | np as _ |
| 294 | } |
| 295 | |
| 296 | pub(crate) fn native_mut(&mut self) -> &mut SkPixmap { |
| 297 | self.inner.native_mut() |
| 298 | } |
| 299 | |
| 300 | pub(crate) fn native(&self) -> &SkPixmap { |
| 301 | self.inner.native() |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | /// Implement this trait to use a pixel type in [`Handle<Pixmap>::pixels()`]. |
| 306 | /// |
| 307 | /// # Safety |
| 308 | /// |
| 309 | /// This trait is unsafe because external [`Pixel`] implementations may lie about their |
| 310 | /// [`ColorType`] or fail to match the alignment of the pixels stored in [`Handle<Pixmap>`]. |
| 311 | pub unsafe trait Pixel: Copy { |
| 312 | /// `true` if the type matches the color type's format. |
| 313 | fn matches_color_type(ct: ColorType) -> bool; |
| 314 | } |
| 315 | |
| 316 | unsafe impl Pixel for u8 { |
| 317 | fn matches_color_type(ct: ColorType) -> bool { |
| 318 | matches!(ct, ColorType::Alpha8 | ColorType::Gray8) |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | unsafe impl Pixel for [u8; 2] { |
| 323 | fn matches_color_type(ct: ColorType) -> bool { |
| 324 | matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm) |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | unsafe impl Pixel for (u8, u8) { |
| 329 | fn matches_color_type(ct: ColorType) -> bool { |
| 330 | matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm) |
| 331 | } |
| 332 | } |
| 333 | |
| 334 | unsafe impl Pixel for [u8; 4] { |
| 335 | fn matches_color_type(ct: ColorType) -> bool { |
| 336 | matches!( |
| 337 | ct, |
| 338 | ColorType::RGBA8888 | ColorType::RGB888x | ColorType::BGRA8888 |
| 339 | ) |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | unsafe impl Pixel for (u8, u8, u8, u8) { |
| 344 | fn matches_color_type(ct: ColorType) -> bool { |
| 345 | matches!( |
| 346 | ct, |
| 347 | ColorType::RGBA8888 | ColorType::RGB888x | ColorType::BGRA8888 |
| 348 | ) |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | unsafe impl Pixel for [f32; 4] { |
| 353 | fn matches_color_type(ct: ColorType) -> bool { |
| 354 | matches!(ct, ColorType::RGBAF32) |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | unsafe impl Pixel for (f32, f32, f32, f32) { |
| 359 | fn matches_color_type(ct: ColorType) -> bool { |
| 360 | matches!(ct, ColorType::RGBAF32) |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | unsafe impl Pixel for u32 { |
| 365 | fn matches_color_type(ct: ColorType) -> bool { |
| 366 | ct == ColorType::N32 |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | unsafe impl Pixel for Color { |
| 371 | fn matches_color_type(ct: ColorType) -> bool { |
| 372 | ct == ColorType::N32 |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | unsafe impl Pixel for Color4f { |
| 377 | fn matches_color_type(ct: ColorType) -> bool { |
| 378 | ct == ColorType::RGBAF32 |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | #[cfg (test)] |
| 383 | mod tests { |
| 384 | use super::*; |
| 385 | |
| 386 | #[test ] |
| 387 | fn pixmap_mutably_borrows_pixels() { |
| 388 | let mut pixels = [0u8; 2 * 2 * 4]; |
| 389 | let info = ImageInfo::new( |
| 390 | (2, 2), |
| 391 | ColorType::RGBA8888, |
| 392 | AlphaType::Premul, |
| 393 | ColorSpace::new_srgb(), |
| 394 | ); |
| 395 | let mut pixmap = Pixmap::new(&info, &mut pixels, info.min_row_bytes()).unwrap(); |
| 396 | // this must fail to compile: |
| 397 | // let _pixel = pixels[0]; |
| 398 | // use `.bytes()`, or `bytes_mut()` instead. |
| 399 | pixmap.reset(); |
| 400 | } |
| 401 | |
| 402 | #[test ] |
| 403 | fn addr_may_return_null_from_a_default_pixmap() { |
| 404 | let pixmap = Pixmap::default(); |
| 405 | assert!(pixmap.addr().is_null()); |
| 406 | assert!(pixmap.writable_addr().is_null()); |
| 407 | } |
| 408 | } |
| 409 | |