| 1 | use std::{ffi, fmt, ptr}; |
| 2 | |
| 3 | use skia_bindings::{self as sb, SkBitmap}; |
| 4 | |
| 5 | use crate::{ |
| 6 | prelude::*, AlphaType, Color, Color4f, ColorSpace, ColorType, IPoint, IRect, ISize, Image, |
| 7 | ImageInfo, Matrix, Paint, PixelRef, Pixmap, SamplingOptions, Shader, TileMode, |
| 8 | }; |
| 9 | |
| 10 | /// [`Bitmap`] describes a two-dimensional raster pixel array. [`Bitmap`] is built on [`ImageInfo`], |
| 11 | /// containing integer width and height, [`ColorType`] and [`AlphaType`] describing the pixel |
| 12 | /// format, and [`ColorSpace`] describing the range of colors. [`Bitmap`] points to [`PixelRef`], |
| 13 | /// which describes the physical array of pixels. [`ImageInfo`] bounds may be located anywhere fully |
| 14 | /// inside [PixelRef] bounds. |
| 15 | /// |
| 16 | /// [`Bitmap`] can be drawn using [crate::Canvas]. [`Bitmap`] can be a drawing destination for |
| 17 | /// [`crate::Canvas`] draw member functions. [`Bitmap`] flexibility as a pixel container limits some |
| 18 | /// optimizations available to the target platform. |
| 19 | /// |
| 20 | /// If pixel array is primarily read-only, use [`Image`] for better performance. |
| 21 | /// |
| 22 | /// If pixel array is primarily written to, use [`crate::Surface`] for better performance. |
| 23 | /// |
| 24 | /// Declaring [`Bitmap`] const prevents altering [`ImageInfo`]: the [`Bitmap`] height, width, and so |
| 25 | /// on cannot change. It does not affect [`PixelRef`]: a caller may write its pixels. Declaring |
| 26 | /// [`Bitmap`] const affects [`Bitmap`] configuration, not its contents. |
| 27 | /// |
| 28 | /// [`Bitmap`] is not thread safe. Each thread must have its own copy of [`Bitmap`] fields, although |
| 29 | /// threads may share the underlying pixel array. |
| 30 | pub type Bitmap = Handle<SkBitmap>; |
| 31 | |
| 32 | impl NativeDrop for SkBitmap { |
| 33 | fn drop(&mut self) { |
| 34 | unsafe { sb::C_SkBitmap_destruct(self) } |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | impl NativeClone for SkBitmap { |
| 39 | /// Copies settings from `self` to returned [`Bitmap`]. Shares pixels if `self` has pixels |
| 40 | /// allocated, so both bitmaps reference the same pixels. |
| 41 | fn clone(&self) -> Self { |
| 42 | unsafe { SkBitmap::new1(self) } |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | impl Default for Bitmap { |
| 47 | /// See [`Bitmap::new()`]. |
| 48 | fn default() -> Self { |
| 49 | Self::new() |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | impl fmt::Debug for Bitmap { |
| 54 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 55 | f&mut DebugStruct<'_, '_>.debug_struct("Bitmap" ) |
| 56 | .field(name:"pixmap" , self.pixmap()) |
| 57 | .finish() |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | impl Bitmap { |
| 62 | /// Creates an empty [`Bitmap`] without pixels, with [`ColorType::Unknown`], [`AlphaType::Unknown`], |
| 63 | /// and with a width and height of zero. [`PixelRef`] origin is set to `(0, 0)`. |
| 64 | /// |
| 65 | /// Use [`Self::set_info()`] to associate [`ColorType`], [`AlphaType`], width, and height after |
| 66 | /// [`Bitmap`] has been created. |
| 67 | pub fn new() -> Self { |
| 68 | Self::construct(|bitmap| unsafe { sb::C_SkBitmap_Construct(bitmap) }) |
| 69 | } |
| 70 | |
| 71 | /// Swaps the fields of the two bitmaps. |
| 72 | pub fn swap(&mut self, other: &mut Self) { |
| 73 | unsafe { self.native_mut().swap(other.native_mut()) } |
| 74 | } |
| 75 | |
| 76 | /// Returns a constant reference to the [`Pixmap`] holding the [`Bitmap`] pixel address, row |
| 77 | /// bytes, and [`ImageInfo`]. |
| 78 | pub fn pixmap(&self) -> &Pixmap { |
| 79 | Pixmap::from_native_ref(&self.native().fPixmap) |
| 80 | } |
| 81 | |
| 82 | /// Returns width, height, [`AlphaType`], [ColorType], and [`ColorSpace`]. |
| 83 | pub fn info(&self) -> &ImageInfo { |
| 84 | self.pixmap().info() |
| 85 | } |
| 86 | |
| 87 | /// Returns pixel count in each row. Should be equal or less than `row_bytes()` / |
| 88 | /// `info().bytes_per_pixel()`. |
| 89 | /// |
| 90 | /// May be less than `pixel_ref().width()`. Will not exceed `pixel_ref().width()` less |
| 91 | /// `pixel_ref_origin().x`. |
| 92 | pub fn width(&self) -> i32 { |
| 93 | self.pixmap().width() |
| 94 | } |
| 95 | |
| 96 | /// Returns pixel row count. |
| 97 | /// |
| 98 | /// Maybe be less than `pixel_ref().height()`. Will not exceed `pixel_ref().height()` less |
| 99 | /// `pixel_ref_origin().y`. |
| 100 | pub fn height(&self) -> i32 { |
| 101 | self.pixmap().height() |
| 102 | } |
| 103 | |
| 104 | pub fn color_type(&self) -> ColorType { |
| 105 | self.pixmap().color_type() |
| 106 | } |
| 107 | |
| 108 | pub fn alpha_type(&self) -> AlphaType { |
| 109 | self.pixmap().alpha_type() |
| 110 | } |
| 111 | |
| 112 | /// Returns [`ColorSpace`], the range of colors, associated with [`ImageInfo`]. The returned |
| 113 | /// [`ColorSpace`] is immutable. |
| 114 | pub fn color_space(&self) -> Option<ColorSpace> { |
| 115 | ColorSpace::from_unshared_ptr(unsafe { self.native().colorSpace() }) |
| 116 | } |
| 117 | |
| 118 | /// Returns number of bytes per pixel required by [`ColorType`]. |
| 119 | /// |
| 120 | /// Returns zero if `color_type()` is [`ColorType::Unknown`]. |
| 121 | pub fn bytes_per_pixel(&self) -> usize { |
| 122 | self.info().bytes_per_pixel() |
| 123 | } |
| 124 | |
| 125 | /// Returns number of pixels that fit on row. Should be greater than or equal to `width()`. |
| 126 | pub fn row_bytes_as_pixels(&self) -> usize { |
| 127 | self.pixmap().row_bytes_as_pixels() |
| 128 | } |
| 129 | |
| 130 | /// Returns bit shift converting row bytes to row pixels. |
| 131 | /// |
| 132 | /// Returns zero for [`ColorType::Unknown`]. |
| 133 | pub fn shift_per_pixel(&self) -> usize { |
| 134 | self.pixmap().shift_per_pixel() |
| 135 | } |
| 136 | |
| 137 | /// Returns `true` if either `width()` or `height()` are zero. |
| 138 | /// |
| 139 | /// Does not check if [`PixelRef`] is `None`; call `draws_nothing()` to check `width()`, |
| 140 | /// `height()`, and [`PixelRef`]. |
| 141 | pub fn is_empty(&self) -> bool { |
| 142 | self.info().is_empty() |
| 143 | } |
| 144 | |
| 145 | /// Returns `true` if [`PixelRef`] is `None`. |
| 146 | /// |
| 147 | /// Does not check if `width()` or `height()` are zero; call `draws_nothing()` to check |
| 148 | /// `width()`, `height()`, and [`PixelRef`]. |
| 149 | pub fn is_null(&self) -> bool { |
| 150 | self.native().fPixelRef.fPtr.is_null() |
| 151 | } |
| 152 | |
| 153 | /// Returns `true` if `width()` or `height()` are zero, or if [`PixelRef`] is `None`. |
| 154 | /// |
| 155 | /// If `true`, [`Bitmap`] has no effect when drawn or drawn into. |
| 156 | pub fn draws_nothing(&self) -> bool { |
| 157 | self.is_empty() || self.is_null() |
| 158 | } |
| 159 | |
| 160 | /// Returns row bytes, the interval from one pixel row to the next. Row bytes is at least as |
| 161 | /// large as: `width()` * `info().bytes_per_pixel()`. |
| 162 | /// |
| 163 | /// Returns zero if `color_type()` is [`ColorType::Unknown`], or if row bytes supplied to |
| 164 | /// `set_info()` is not large enough to hold a row of pixels. |
| 165 | pub fn row_bytes(&self) -> usize { |
| 166 | self.pixmap().row_bytes() |
| 167 | } |
| 168 | |
| 169 | /// Sets [`AlphaType`], if `alpha_type` is compatible with [`ColorType`]. Returns `true` unless |
| 170 | /// `alpha_type` is [`AlphaType::Unknown`] and current [`AlphaType`] is not [`AlphaType::Unknown`]. |
| 171 | /// |
| 172 | /// Returns `true` if [`ColorType`] is [`ColorType::Unknown`]. `alpha_type` is ignored, and |
| 173 | /// [`AlphaType`] remains [`AlphaType::Unknown`]. |
| 174 | /// |
| 175 | /// Returns `true` if [`ColorType`] is [`ColorType::RGB565`] or [`ColorType::Gray8`]. |
| 176 | /// `alpha_type` is ignored, and [`AlphaType`] remains [`AlphaType::Opaque`]. |
| 177 | /// |
| 178 | /// If [`ColorType`] is [`ColorType::ARGB4444`], [`ColorType::RGBA8888`], |
| 179 | /// [`ColorType::BGRA8888`], or [`ColorType::RGBAF16`]: returns `true` unless `alpha_type` is |
| 180 | /// [`AlphaType::Unknown`] and [`AlphaType`] is not [`AlphaType::Unknown`]. If [`AlphaType`] is |
| 181 | /// [`AlphaType::Unknown`], `alpha_type` is ignored. |
| 182 | /// |
| 183 | /// If [`ColorType`] is [`ColorType::Alpha8`], returns `true` unless `alpha_type` is |
| 184 | /// [`AlphaType::Unknown`] and [`AlphaType`] is not [`AlphaType::Unknown`]. If [`AlphaType`] is |
| 185 | /// kUnknown_SkAlphaType, `alpha_type` is ignored. If `alpha_type` is [`AlphaType::Unpremul`], |
| 186 | /// it is treated as [`AlphaType::Premul`]. |
| 187 | /// |
| 188 | /// This changes [`AlphaType`] in [`PixelRef`]; all bitmaps sharing [`PixelRef`] are affected. |
| 189 | pub fn set_alpha_type(&mut self, alpha_type: AlphaType) -> bool { |
| 190 | unsafe { self.native_mut().setAlphaType(alpha_type) } |
| 191 | } |
| 192 | |
| 193 | /// Sets the [`ColorSpace`] associated with this [`Bitmap`]. |
| 194 | /// |
| 195 | /// The raw pixel data is not altered by this call; no conversion is |
| 196 | /// performed. |
| 197 | /// |
| 198 | /// This changes [`ColorSpace`] in [`PixelRef`]; all bitmaps sharing [`PixelRef`] |
| 199 | /// are affected. |
| 200 | pub fn set_color_space(&mut self, color_space: impl Into<Option<ColorSpace>>) { |
| 201 | unsafe { |
| 202 | sb::C_SkBitmap_setColorSpace(self.native_mut(), color_space.into().into_ptr_or_null()) |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | /// Returns pixel address, the base address corresponding to the pixel origin. |
| 207 | pub fn pixels(&mut self) -> *mut ffi::c_void { |
| 208 | self.pixmap().writable_addr() |
| 209 | } |
| 210 | |
| 211 | /// Returns minimum memory required for pixel storage. |
| 212 | /// Does not include unused memory on last row when `row_bytes_as_pixels()` exceeds `width()`. |
| 213 | /// Returns [`usize::MAX`] if result does not fit in `usize`. |
| 214 | /// Returns zero if `height()` or `width()` is 0. |
| 215 | /// Returns `height()` times `row_bytes()` if `color_type()` is [`ColorType::Unknown`]. |
| 216 | pub fn compute_byte_size(&self) -> usize { |
| 217 | self.pixmap().compute_byte_size() |
| 218 | } |
| 219 | |
| 220 | /// Returns `true` if pixels can not change. |
| 221 | /// |
| 222 | /// Most immutable [`Bitmap`] checks trigger an assert only on debug builds. |
| 223 | pub fn is_immutable(&self) -> bool { |
| 224 | unsafe { self.native().isImmutable() } |
| 225 | } |
| 226 | |
| 227 | /// Sets internal flag to mark [`Bitmap`] as immutable. Once set, pixels can not change. Any |
| 228 | /// other bitmap sharing the same [`PixelRef`] are also marked as immutable. |
| 229 | /// |
| 230 | /// Once [`PixelRef`] is marked immutable, the setting cannot be cleared. |
| 231 | /// |
| 232 | /// Writing to immutable [`Bitmap`] pixels triggers an assert on debug builds. |
| 233 | pub fn set_immutable(&mut self) { |
| 234 | unsafe { self.native_mut().setImmutable() } |
| 235 | } |
| 236 | |
| 237 | /// Returns `true` if [`AlphaType`] is set to hint that all pixels are opaque; their alpha value |
| 238 | /// is implicitly or explicitly `1.0`. If `true`, and all pixels are not opaque, Skia may draw |
| 239 | /// incorrectly. |
| 240 | /// |
| 241 | /// Does not check if [ColorType] allows alpha, or if any pixel value has transparency. |
| 242 | pub fn is_opaque(&self) -> bool { |
| 243 | self.pixmap().is_opaque() |
| 244 | } |
| 245 | |
| 246 | /// Resets to its initial state; all fields are set to zero, as if [`Bitmap`] had |
| 247 | /// been initialized by [`Bitmap::new()`]. |
| 248 | /// |
| 249 | /// Sets width, height, row bytes to zero; pixel address to `None`; [`ColorType`] to |
| 250 | /// [`ColorType::Unknown`]; and [`AlphaType`] to [`AlphaType::Unknown`]. |
| 251 | /// |
| 252 | /// If [`PixelRef`] is allocated, its reference count is decreased by one, releasing its memory |
| 253 | /// if [`Bitmap`] is the sole owner. |
| 254 | pub fn reset(&mut self) { |
| 255 | unsafe { self.native_mut().reset() } |
| 256 | } |
| 257 | |
| 258 | /// Returns `true `if all pixels are opaque. [`ColorType`] determines how pixels are encoded, and |
| 259 | /// whether pixel describes alpha. Returns `true` for [`ColorType`] without alpha in each pixel; |
| 260 | /// for other [`ColorType`], returns `true` if all pixels have alpha values equivalent to 1.0 or |
| 261 | /// greater. |
| 262 | /// |
| 263 | /// Returns `false` for [`ColorType::Unknown`]. |
| 264 | pub fn compute_is_opaque(bm: &Self) -> bool { |
| 265 | unsafe { sb::C_SkBitmap_ComputeIsOpaque(bm.native()) } |
| 266 | } |
| 267 | |
| 268 | /// Returns `IRect { 0, 0, width(), height() }`. |
| 269 | pub fn bounds(&self) -> IRect { |
| 270 | self.info().bounds() |
| 271 | } |
| 272 | |
| 273 | /// Returns `ISize { width(), height() }`. |
| 274 | pub fn dimensions(&self) -> ISize { |
| 275 | self.info().dimensions() |
| 276 | } |
| 277 | |
| 278 | /// Returns the bounds of this bitmap, offset by its [`PixelRef`] origin. |
| 279 | pub fn get_subset(&self) -> IRect { |
| 280 | let origin = self.pixel_ref_origin(); |
| 281 | IRect::from_xywh(origin.x, origin.y, self.width(), self.height()) |
| 282 | } |
| 283 | |
| 284 | /// Sets width, height, [`AlphaType`], [ColorType], [`ColorSpace`], and optional `row_bytes`. |
| 285 | /// Frees pixels, and returns `true` if successful. |
| 286 | /// |
| 287 | /// `row_bytes` must equal or exceed `image_info.min_row_bytes()`. If `image_info.color_space()` |
| 288 | /// is [`ColorType::Unknown`], `row_bytes` is ignored and treated as zero; for all other |
| 289 | /// [`ColorSpace`] values, `row_bytes` of zero is treated as `image_info.min_row_bytes()`. |
| 290 | /// |
| 291 | /// Calls `reset()` and returns `false` if: |
| 292 | /// - rowBytes exceeds 31 bits |
| 293 | /// - `image_info.width()` is negative |
| 294 | /// - `image_info.height()` is negative |
| 295 | /// - `row_bytes` is positive and less than `image_info.width()` times |
| 296 | /// `image_info.bytes_per_pixel()` |
| 297 | #[must_use ] |
| 298 | pub fn set_info( |
| 299 | &mut self, |
| 300 | image_info: &ImageInfo, |
| 301 | row_bytes: impl Into<Option<usize>>, |
| 302 | ) -> bool { |
| 303 | unsafe { |
| 304 | self.native_mut() |
| 305 | .setInfo(image_info.native(), row_bytes.into().unwrap_or(0)) |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | /// Sets [`ImageInfo`] to info following the rules in `set_info()` and allocates pixel memory. |
| 310 | /// Memory is zeroed. |
| 311 | /// |
| 312 | /// Returns `false` and calls `reset()` if [`ImageInfo`] could not be set, or memory could not |
| 313 | /// be allocated, or memory could not optionally be zeroed. |
| 314 | /// |
| 315 | /// On most platforms, allocating pixel memory may succeed even though there is not sufficient |
| 316 | /// memory to hold pixels; allocation does not take place until the pixels are written to. The |
| 317 | /// actual behavior depends on the platform implementation of `calloc()`. |
| 318 | #[must_use ] |
| 319 | pub fn try_alloc_pixels_flags(&mut self, image_info: &ImageInfo) -> bool { |
| 320 | unsafe { |
| 321 | self.native_mut().tryAllocPixelsFlags( |
| 322 | image_info.native(), |
| 323 | sb::SkBitmap_AllocFlags_kZeroPixels_AllocFlag as _, |
| 324 | ) |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | /// Sets [`ImageInfo`] to info following the rules in `set_info()` and allocates pixel memory. |
| 329 | /// Memory is zeroed. |
| 330 | /// |
| 331 | /// Returns `false` and calls `reset()` if [`ImageInfo`] could not be set, or memory could not be |
| 332 | /// allocated, or memory could not optionally be zeroed. |
| 333 | /// |
| 334 | /// On most platforms, allocating pixel memory may succeed even though there is not sufficient |
| 335 | /// memory to hold pixels; allocation does not take place until the pixels are written to. The |
| 336 | /// actual behavior depends on the platform implementation of `calloc()`. |
| 337 | pub fn alloc_pixels_flags(&mut self, image_info: &ImageInfo) { |
| 338 | self.try_alloc_pixels_flags(image_info) |
| 339 | .into_option() |
| 340 | .expect("Bitmap::alloc_pixels_flags failed" ); |
| 341 | } |
| 342 | |
| 343 | /// Sets [`ImageInfo`] to info following the rules in `set_info()` and allocates pixel memory. |
| 344 | /// `row_bytes` must equal or exceed `info.width()` times `info.bytes_per_pixel()`, or equal |
| 345 | /// `None`. Pass in `None` for `row_bytes` to compute the minimum valid value. |
| 346 | /// |
| 347 | /// Returns `false` and calls `reset()` if [`ImageInfo`] could not be set, or memory could not be |
| 348 | /// allocated. |
| 349 | /// |
| 350 | /// On most platforms, allocating pixel memory may succeed even though there is not sufficient |
| 351 | /// memory to hold pixels; allocation does not take place until the pixels are written to. The |
| 352 | /// actual behavior depends on the platform implementation of `malloc()`. |
| 353 | #[must_use ] |
| 354 | pub fn try_alloc_pixels_info( |
| 355 | &mut self, |
| 356 | image_info: &ImageInfo, |
| 357 | row_bytes: impl Into<Option<usize>>, |
| 358 | ) -> bool { |
| 359 | let row_bytes = row_bytes |
| 360 | .into() |
| 361 | .unwrap_or_else(|| image_info.min_row_bytes()); |
| 362 | unsafe { |
| 363 | self.native_mut() |
| 364 | .tryAllocPixels(image_info.native(), row_bytes) |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | /// Sets [`ImageInfo`] to info following the rules in `set_info()` and allocates pixel memory. |
| 369 | /// `row_bytes` must equal or exceed `info.width()` times `info.bytes_per_pixel()`, or equal |
| 370 | /// `None`. Pass in `None` for `row_bytes` to compute the minimum valid value. |
| 371 | /// |
| 372 | /// Aborts execution if SkImageInfo could not be set, or memory could |
| 373 | /// be allocated. |
| 374 | /// |
| 375 | /// On most platforms, allocating pixel memory may succeed even though there is not sufficient |
| 376 | /// memory to hold pixels; allocation does not take place until the pixels are written to. The |
| 377 | /// actual behavior depends on the platform implementation of `malloc()`. |
| 378 | pub fn alloc_pixels_info( |
| 379 | &mut self, |
| 380 | image_info: &ImageInfo, |
| 381 | row_bytes: impl Into<Option<usize>>, |
| 382 | ) { |
| 383 | self.try_alloc_pixels_info(image_info, row_bytes.into()) |
| 384 | .into_option() |
| 385 | .expect("Bitmap::alloc_pixels_info failed" ); |
| 386 | } |
| 387 | |
| 388 | /// Sets [`ImageInfo`] to width, height, and native color type; and allocates pixel memory. If |
| 389 | /// `is_opaque` is `true`, sets [`ImageInfo`] to [`AlphaType::Opaque`]; otherwise, sets to |
| 390 | /// [`AlphaType::Premul`]. |
| 391 | /// |
| 392 | /// Calls `reset()` and returns `false` if width exceeds 29 bits or is negative, or height is |
| 393 | /// negative. |
| 394 | /// |
| 395 | /// Returns `false` if allocation fails. |
| 396 | /// |
| 397 | /// Use to create [`Bitmap`] that matches [`crate::PMColor`], the native pixel arrangement on |
| 398 | /// the platform. [`Bitmap`] drawn to output device skips converting its pixel format. |
| 399 | #[must_use ] |
| 400 | pub fn try_alloc_n32_pixels( |
| 401 | &mut self, |
| 402 | (width, height): (i32, i32), |
| 403 | is_opaque: impl Into<Option<bool>>, |
| 404 | ) -> bool { |
| 405 | unsafe { |
| 406 | sb::C_SkBitmap_tryAllocN32Pixels( |
| 407 | self.native_mut(), |
| 408 | width, |
| 409 | height, |
| 410 | is_opaque.into().unwrap_or(false), |
| 411 | ) |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | /// Sets [`ImageInfo`] to width, height, and native color type; and allocates pixel memory. If |
| 416 | /// `is_opaque` is `true`, sets [`ImageInfo`] to [`AlphaType::Opaque`]; otherwise, sets to |
| 417 | /// [`AlphaType::Premul`]. |
| 418 | /// |
| 419 | /// Aborts if width exceeds 29 bits or is negative, or height is negative, or allocation fails. |
| 420 | /// |
| 421 | /// Use to create [`Bitmap`] that matches [`crate::PMColor`], the native pixel arrangement on |
| 422 | /// the platform. [`Bitmap`] drawn to output device skips converting its pixel format. |
| 423 | pub fn alloc_n32_pixels( |
| 424 | &mut self, |
| 425 | (width, height): (i32, i32), |
| 426 | is_opaque: impl Into<Option<bool>>, |
| 427 | ) { |
| 428 | self.try_alloc_n32_pixels((width, height), is_opaque.into().unwrap_or(false)) |
| 429 | .into_option() |
| 430 | .expect("Bitmap::alloc_n32_pixels_failed" ) |
| 431 | } |
| 432 | |
| 433 | // TODO: wrap installPixels with releaseProc. |
| 434 | |
| 435 | /// Sets [`ImageInfo`] to info following the rules in `set_info()`, and creates [`PixelRef`] |
| 436 | /// containing `pixels` and `row_bytes`. |
| 437 | /// |
| 438 | /// If [`ImageInfo`] could not be set, or `row_bytes` is less than `info.min_row_bytes(): calls |
| 439 | /// `reset()`, and returns `false`. |
| 440 | /// |
| 441 | /// Otherwise, if pixels equals `ptr::null_mut()`: sets [`ImageInfo`], returns `true`. |
| 442 | /// |
| 443 | /// Caller must ensure that pixels are valid for the lifetime of [`Bitmap`] and [`PixelRef`]. |
| 444 | #[allow (clippy::missing_safety_doc)] |
| 445 | pub unsafe fn install_pixels( |
| 446 | &mut self, |
| 447 | info: &ImageInfo, |
| 448 | pixels: *mut ffi::c_void, |
| 449 | row_bytes: usize, |
| 450 | ) -> bool { |
| 451 | self.native_mut() |
| 452 | .installPixels(info.native(), pixels, row_bytes, None, ptr::null_mut()) |
| 453 | } |
| 454 | |
| 455 | // TODO: wrap installPixels with SkPixmap& |
| 456 | |
| 457 | // TODO: setPixels()? |
| 458 | |
| 459 | /// Allocates pixel memory with HeapAllocator, and replaces existing [`PixelRef`]. The |
| 460 | /// allocation size is determined by [`ImageInfo`] width, height, and [`ColorType`]. |
| 461 | /// |
| 462 | /// Returns `false` if `info().color_type()` is [`ColorType::Unknown`], or allocation fails. |
| 463 | #[must_use ] |
| 464 | pub fn try_alloc_pixels(&mut self) -> bool { |
| 465 | unsafe { sb::C_SkBitmap_tryAllocPixels(self.native_mut()) } |
| 466 | } |
| 467 | |
| 468 | /// Allocates pixel memory with HeapAllocator, and replaces existing [`PixelRef`]. The |
| 469 | /// allocation size is determined by [`ImageInfo`] width, height, and [`ColorType`]. |
| 470 | /// |
| 471 | /// Aborts if `info().color_type()` is [`ColorType::Unknown`], or allocation fails. |
| 472 | pub fn alloc_pixels(&mut self) { |
| 473 | self.try_alloc_pixels() |
| 474 | .into_option() |
| 475 | .expect("Bitmap::alloc_pixels failed" ) |
| 476 | } |
| 477 | |
| 478 | // TODO: allocPixels(Allocator*) |
| 479 | |
| 480 | // TODO: find a way to return pixel ref without increasing the ref count here? |
| 481 | |
| 482 | /// Returns [`PixelRef`], which contains: pixel base address; its dimensions; and `row_bytes()`, |
| 483 | /// the interval from one row to the next. Does not change [`PixelRef`] reference count. |
| 484 | /// [`PixelRef`] may be shared by multiple bitmaps. |
| 485 | /// |
| 486 | /// If [`PixelRef`] has not been set, returns `None`. |
| 487 | pub fn pixel_ref(&self) -> Option<PixelRef> { |
| 488 | PixelRef::from_unshared_ptr(self.native().fPixelRef.fPtr) |
| 489 | } |
| 490 | |
| 491 | /// Returns origin of pixels within [`PixelRef`]. [`Bitmap`] bounds is always contained |
| 492 | /// by [`PixelRef`] bounds, which may be the same size or larger. Multiple [`Bitmap`] |
| 493 | /// can share the same [`PixelRef`], where each [`Bitmap`] has different bounds. |
| 494 | /// |
| 495 | /// The returned origin added to [`Bitmap`] dimensions equals or is smaller than the |
| 496 | /// [`PixelRef`] dimensions. |
| 497 | /// |
| 498 | /// Returns `(0, 0)` if [`PixelRef`] is `None`. |
| 499 | pub fn pixel_ref_origin(&self) -> IPoint { |
| 500 | IPoint::from_native_c(unsafe { sb::C_SkBitmap_pixelRefOrigin(self.native()) }) |
| 501 | } |
| 502 | |
| 503 | /// Replaces `pixel_ref` and origin in [`Bitmap`]. `offset` specifies the offset within the |
| 504 | /// [`PixelRef`] pixels for the top-left corner of the bitmap. |
| 505 | /// |
| 506 | /// Asserts in debug builds if offset is out of range. Pins offset to legal range in release |
| 507 | /// builds. |
| 508 | /// |
| 509 | /// The caller is responsible for ensuring that the pixels match the [`ColorType`] and |
| 510 | /// [`AlphaType`] in [`ImageInfo`]. |
| 511 | pub fn set_pixel_ref( |
| 512 | &mut self, |
| 513 | pixel_ref: impl Into<Option<PixelRef>>, |
| 514 | offset: impl Into<IPoint>, |
| 515 | ) { |
| 516 | let offset = offset.into(); |
| 517 | unsafe { |
| 518 | sb::C_SkBitmap_setPixelRef( |
| 519 | self.native_mut(), |
| 520 | pixel_ref.into().into_ptr_or_null(), |
| 521 | offset.x, |
| 522 | offset.y, |
| 523 | ) |
| 524 | } |
| 525 | } |
| 526 | |
| 527 | /// Returns `true` if [`Bitmap`] can be drawn. |
| 528 | pub fn is_ready_to_draw(&self) -> bool { |
| 529 | unsafe { sb::C_SkBitmap_readyToDraw(self.native()) } |
| 530 | } |
| 531 | |
| 532 | /// Returns a unique value corresponding to the pixels in [`PixelRef`]. |
| 533 | /// Returns a different value after `notify_pixels_changed()` has been called. |
| 534 | /// Returns zero if [`PixelRef`] is `None`. |
| 535 | /// |
| 536 | /// Determines if pixels have changed since last examined. |
| 537 | pub fn generation_id(&self) -> u32 { |
| 538 | unsafe { self.native().getGenerationID() } |
| 539 | } |
| 540 | |
| 541 | /// Marks that pixels in [`PixelRef`] have changed. Subsequent calls to `generation_id()` return |
| 542 | /// a different value. |
| 543 | pub fn notify_pixels_changed(&self) { |
| 544 | unsafe { self.native().notifyPixelsChanged() } |
| 545 | } |
| 546 | |
| 547 | /// Replaces pixel values with `c`, interpreted as being in the sRGB [`ColorSpace`]. All pixels |
| 548 | /// contained by [`bounds(&self)`] are affected. If the [`color_type(&self)`] is |
| 549 | /// [`ColorType::Gray8`] or [`ColorType::RGB565`], then alpha is ignored; RGB is treated as |
| 550 | /// opaque. If [`color_type(&self)`] is [`ColorType::Alpha8`], then RGB is ignored. |
| 551 | /// |
| 552 | /// Input color is ultimately converted to an [`Color4f`], so [`Self::erase_color_4f`] will have |
| 553 | /// higher color resolution. |
| 554 | pub fn erase_color(&self, c: impl Into<Color>) { |
| 555 | unsafe { self.native().eraseColor1(c.into().into_native()) } |
| 556 | } |
| 557 | |
| 558 | /// Replaces pixel values with `c`, interpreted as being in the sRGB [`ColorSpace`]. All pixels |
| 559 | /// contained by [`bounds(&self)`] are affected. If the [`color_type(&self)`] is |
| 560 | /// [`ColorType::Gray8`] or [ColorType::RGB565], then alpha is ignored; RGB is treated as |
| 561 | /// opaque. If [`color_type(&self)`] is [`ColorType::Alpha8`], then RGB is ignored. |
| 562 | pub fn erase_color_4f(&self, c: impl AsRef<Color4f>) { |
| 563 | unsafe { self.native().eraseColor(c.as_ref().into_native()) } |
| 564 | } |
| 565 | |
| 566 | /// Replaces pixel values with unpremultiplied color built from `a`, `r`, `g`, and `b`, |
| 567 | /// interpreted as being in the sRGB [`ColorSpace`]. All pixels contained by [`bounds(&self)`] |
| 568 | /// are affected. If the [`color_type(&self)`] is [`ColorType::Gray8`] or [`ColorType::RGB565`], |
| 569 | /// then `a` is ignored; `r`, `g`, and `b` are treated as opaque. If [`color_type(&self)`] is |
| 570 | /// [`ColorType::Alpha8`], then `r`, `g`, and `b` are ignored. |
| 571 | pub fn erase_argb(&self, a: u8, r: u8, g: u8, b: u8) { |
| 572 | unsafe { sb::C_SkBitmap_eraseARGB(self.native(), a.into(), r.into(), g.into(), b.into()) } |
| 573 | } |
| 574 | |
| 575 | /// Replaces pixel values inside area with c. interpreted as being in the sRGB [`ColorSpace`]. |
| 576 | /// If area does not intersect `bounds()`, call has no effect. |
| 577 | /// |
| 578 | /// If the `color_type()` is [`ColorType::Gray8`] [`ColorType::RGB565`], then alpha is ignored; |
| 579 | /// RGB is treated as opaque. If `color_type()` is [`ColorType::Alpha8`], then RGB is ignored. |
| 580 | /// |
| 581 | /// Input color is ultimately converted to an [`Color4f`], so [`Self::erase_4f`] will have |
| 582 | /// higher color resolution. |
| 583 | pub fn erase(&self, c: impl Into<Color>, area: impl AsRef<IRect>) { |
| 584 | unsafe { |
| 585 | self.native() |
| 586 | .erase1(c.into().into_native(), area.as_ref().native()) |
| 587 | } |
| 588 | } |
| 589 | |
| 590 | /// Replaces pixel values inside area with c. interpreted as being in the sRGB [`ColorSpace`]. |
| 591 | /// If area does not intersect `bounds()`, call has no effect. |
| 592 | /// |
| 593 | /// If the `color_type()` is [`ColorType::Gray8`] [`ColorType::RGB565`], then alpha is ignored; |
| 594 | /// RGB is treated as opaque. If `color_type()` is [`ColorType::Alpha8`], then RGB is ignored. |
| 595 | pub fn erase_4f(&self, c: impl AsRef<Color4f>, area: impl AsRef<IRect>) { |
| 596 | unsafe { |
| 597 | self.native() |
| 598 | .erase(c.as_ref().into_native(), area.as_ref().native()) |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | /// Returns pixel at `(x, y)` as unpremultiplied color. |
| 603 | /// Returns black with alpha if [`ColorType`] is [`ColorType::Alpha8`] |
| 604 | /// |
| 605 | /// Input is not validated: out of bounds values of `x` or `y` trigger an `assert()`. |
| 606 | /// |
| 607 | /// Fails if [`ColorType`] is [`ColorType::Unknown`] or pixel address is `nullptr`. |
| 608 | /// |
| 609 | /// [`ColorSpace`] in [`ImageInfo`] is ignored. Some color precision may be lost in the |
| 610 | /// conversion to unpremultiplied color; original pixel data may have additional precision. |
| 611 | pub fn get_color(&self, p: impl Into<IPoint>) -> Color { |
| 612 | self.pixmap().get_color(p) |
| 613 | } |
| 614 | |
| 615 | /// Returns pixel at `(x, y)` as unpremultiplied color. |
| 616 | /// Returns black with alpha if [ColorType] is [ColorType::Alpha8] |
| 617 | /// |
| 618 | /// Input is not validated: out of bounds values of x or y trigger an `assert()`. |
| 619 | /// |
| 620 | /// Fails if [ColorType] is [ColorType::Unknown] or pixel address is `None`. |
| 621 | /// |
| 622 | /// [`ColorSpace`] in [`ImageInfo`] is ignored. Some color precision may be lost in the |
| 623 | /// conversion to unpremultiplied color. |
| 624 | pub fn get_color_4f(&self, p: impl Into<IPoint>) -> Color4f { |
| 625 | self.pixmap().get_color_4f(p) |
| 626 | } |
| 627 | |
| 628 | /// Look up the pixel at `(x,y)` and return its alpha component, normalized to `[0..1]`. This is |
| 629 | /// roughly equivalent to `get_color().a()`, but can be more efficient (and more precise if the |
| 630 | /// pixels store more than 8 bits per component). |
| 631 | pub fn get_alpha_f(&self, p: impl Into<IPoint>) -> f32 { |
| 632 | self.pixmap().get_alpha_f(p) |
| 633 | } |
| 634 | |
| 635 | /// Returns pixel address at `(x, y)`. |
| 636 | /// |
| 637 | /// Input is not validated: out of bounds values of `x` or `y`, or [`ColorType::Unknown`], |
| 638 | /// trigger an `assert()`. Returns `nullptr` if [`ColorType`] is [`ColorType::Unknown`], or |
| 639 | /// [`PixelRef`] is `nullptr`. |
| 640 | /// |
| 641 | /// Performs a lookup of pixel size; for better performance, call one of: `get_addr8()`, |
| 642 | /// `get_addr16()`, or `get_addr32()`. |
| 643 | pub fn get_addr(&self, p: impl Into<IPoint>) -> *const ffi::c_void { |
| 644 | let p = p.into(); |
| 645 | unsafe { self.native().getAddr(p.x, p.y) } |
| 646 | } |
| 647 | |
| 648 | // TODO: get_addr_32(), get_addr_16(), get_addr_8() |
| 649 | |
| 650 | /// Shares [`PixelRef`] with `dst`. Pixels are not copied; [`Bitmap`] and dst point to the same |
| 651 | /// pixels; dst [`Self::bounds()`] are set to the intersection of subset and the original |
| 652 | /// [`Self::bounds()`]. |
| 653 | /// |
| 654 | /// Subset may be larger than [`Self::bounds()`]. Any area outside of [`Self::bounds()`] is |
| 655 | /// ignored. |
| 656 | /// |
| 657 | /// Any contents of dst are discarded. |
| 658 | /// |
| 659 | /// Return `false` if: |
| 660 | /// - dst is `nullptr` |
| 661 | /// - [`PixelRef`] is `nullptr` |
| 662 | /// - subset does not intersect [`Self::bounds()`] |
| 663 | /// |
| 664 | /// example: <https://fiddle.skia.org/c/@Bitmap_extractSubset> |
| 665 | pub fn extract_subset(&self, dst: &mut Self, subset: impl AsRef<IRect>) -> bool { |
| 666 | unsafe { |
| 667 | self.native() |
| 668 | .extractSubset(dst.native_mut(), subset.as_ref().native()) |
| 669 | } |
| 670 | } |
| 671 | |
| 672 | /// Copies a [`crate::Rect`] of pixels from [`Bitmap`] to `dst_pixels`. Copy starts at `(src_x, |
| 673 | /// src_y)`, and does not exceed [`Bitmap`] `(width(), height())`. |
| 674 | /// |
| 675 | /// `dst_info` specifies width, height, [ColorType], [`AlphaType`], and [`ColorSpace`] of |
| 676 | /// destination. |
| 677 | /// `dst_row_bytes` specifics the gap from one destination row to the next. Returns `true` if |
| 678 | /// pixels are copied. Returns `false` if: |
| 679 | /// - `dst_info` has no address |
| 680 | /// - `dst_row_bytes` is less than `dst_info.min_row_bytes()` |
| 681 | /// - [`PixelRef`] is `nullptr` |
| 682 | /// |
| 683 | /// Pixels are copied only if pixel conversion is possible. If [`Self::color_type()`] is |
| 684 | /// [`ColorType::Gray8`], or [`ColorType::Alpha8`]; `dst_info.color_type()` must match. If |
| 685 | /// [`Self::color_type()`] is [`ColorType::Gray8`], `dst_info.color_space()` must match. If |
| 686 | /// [`Self::alpha_type()`] is [`AlphaType::Opaque`], `dst_info.alpha_type()` must match. If |
| 687 | /// [`Self::color_space()`] is `nullptr`, `dst_info.color_space()` must match. Returns `false` |
| 688 | /// if pixel conversion is not possible. |
| 689 | /// |
| 690 | /// `src_x` and `src_y` may be negative to copy only top or left of source. Returns `false` if |
| 691 | /// [`Self::width()`] or [`Self::height()`] is zero or negative. Returns `false` if `abs(src_x)` |
| 692 | /// `>=` [`Self::width()`], or if `abs(src_y) >=` [`Self::height()`]. |
| 693 | #[allow (clippy::missing_safety_doc)] |
| 694 | pub unsafe fn read_pixels( |
| 695 | &self, |
| 696 | dst_info: &ImageInfo, |
| 697 | dst_pixels: *mut ffi::c_void, |
| 698 | dst_row_bytes: usize, |
| 699 | src_x: i32, |
| 700 | src_y: i32, |
| 701 | ) -> bool { |
| 702 | self.native() |
| 703 | .readPixels(dst_info.native(), dst_pixels, dst_row_bytes, src_x, src_y) |
| 704 | } |
| 705 | |
| 706 | // TODO: read_pixels(Pixmap) |
| 707 | // TODO: write_pixels(Pixmap) |
| 708 | |
| 709 | /// Sets dst to alpha described by pixels. Returns `false` if `dst` cannot be written to or |
| 710 | /// `dst` pixels cannot be allocated. |
| 711 | /// |
| 712 | /// If `paint` is not `None` and contains [`crate::MaskFilter`], [`crate::MaskFilter`] generates |
| 713 | /// mask alpha from [`Bitmap`]. Uses `HeapAllocator` to reserve memory for `dst` [`PixelRef`]. |
| 714 | /// Returns offset to top-left position for `dst` for alignment with [`Bitmap`]; `(0, 0)` unless |
| 715 | /// [crate::MaskFilter] generates mask. |
| 716 | pub fn extract_alpha(&self, dst: &mut Self, paint: Option<&Paint>) -> Option<IPoint> { |
| 717 | let mut offset = IPoint::default(); |
| 718 | unsafe { |
| 719 | sb::C_SkBitmap_extractAlpha( |
| 720 | self.native(), |
| 721 | dst.native_mut(), |
| 722 | paint.native_ptr_or_null(), |
| 723 | offset.native_mut(), |
| 724 | ) |
| 725 | } |
| 726 | .if_true_some(offset) |
| 727 | } |
| 728 | |
| 729 | /// Copies [`Bitmap`] pixel address, row bytes, and [`ImageInfo`] to pixmap, if address is |
| 730 | /// available, and returns [`Some(Pixmap)`]. If pixel address is not available, return `None` |
| 731 | /// and leave pixmap unchanged. |
| 732 | /// |
| 733 | /// example: <https://fiddle.skia.org/c/@Bitmap_peekPixels> |
| 734 | pub fn peek_pixels(&self) -> Option<Pixmap> { |
| 735 | let mut pixmap = Pixmap::default(); |
| 736 | unsafe { self.native().peekPixels(pixmap.native_mut()) }.if_true_some(pixmap) |
| 737 | } |
| 738 | |
| 739 | /// Make a shader with the specified tiling, matrix and sampling. |
| 740 | /// Defaults to clamp in both X and Y. |
| 741 | pub fn to_shader<'a>( |
| 742 | &self, |
| 743 | tile_modes: impl Into<Option<(TileMode, TileMode)>>, |
| 744 | sampling: impl Into<SamplingOptions>, |
| 745 | local_matrix: impl Into<Option<&'a Matrix>>, |
| 746 | ) -> Option<Shader> { |
| 747 | let tile_modes = tile_modes.into(); |
| 748 | let sampling = sampling.into(); |
| 749 | let local_matrix = local_matrix.into(); |
| 750 | Shader::from_ptr(unsafe { |
| 751 | let tmx = tile_modes.map(|tm| tm.0).unwrap_or_default(); |
| 752 | let tmy = tile_modes.map(|tm| tm.1).unwrap_or_default(); |
| 753 | sb::C_SkBitmap_makeShader( |
| 754 | self.native(), |
| 755 | tmx, |
| 756 | tmy, |
| 757 | sampling.native(), |
| 758 | local_matrix.native_ptr_or_null(), |
| 759 | ) |
| 760 | }) |
| 761 | } |
| 762 | |
| 763 | /// Returns a new image from the bitmap. If the bitmap is marked immutable, this will |
| 764 | /// share the pixel buffer. If not, it will make a copy of the pixels for the image. |
| 765 | pub fn as_image(&self) -> Image { |
| 766 | Image::from_ptr(unsafe { sb::C_SkBitmap_asImage(self.native()) }).unwrap() |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | #[cfg (test)] |
| 771 | mod tests { |
| 772 | use super::TileMode; |
| 773 | use crate::{AlphaType, Bitmap, Canvas, ColorSpace, ColorType, ImageInfo, SamplingOptions}; |
| 774 | |
| 775 | #[test ] |
| 776 | fn create_clone_and_drop() { |
| 777 | let bm = Bitmap::new(); |
| 778 | #[allow (clippy::redundant_clone)] |
| 779 | let _bm2 = bm.clone(); |
| 780 | } |
| 781 | |
| 782 | #[test ] |
| 783 | fn get_info() { |
| 784 | let bm = Bitmap::new(); |
| 785 | let _info = bm.info(); |
| 786 | } |
| 787 | |
| 788 | #[test ] |
| 789 | fn empty_bitmap_shader() { |
| 790 | let bm = Bitmap::new(); |
| 791 | let _shader = bm.to_shader(None, SamplingOptions::default(), None); |
| 792 | } |
| 793 | |
| 794 | #[test ] |
| 795 | fn shader_with_tile_mode() { |
| 796 | let bm = Bitmap::new(); |
| 797 | let _shader = bm.to_shader( |
| 798 | (TileMode::Decal, TileMode::Mirror), |
| 799 | SamplingOptions::default(), |
| 800 | None, |
| 801 | ); |
| 802 | } |
| 803 | |
| 804 | #[test ] |
| 805 | fn test_get_subset() { |
| 806 | let bm = Bitmap::new(); |
| 807 | let _ = bm.get_subset(); |
| 808 | } |
| 809 | |
| 810 | #[test ] |
| 811 | fn test_pixel_ref_origin() { |
| 812 | let bm = Bitmap::new(); |
| 813 | let _ = bm.pixel_ref_origin(); |
| 814 | } |
| 815 | |
| 816 | /// Test for: <https://github.com/rust-skia/rust-skia/issues/669> |
| 817 | #[test ] |
| 818 | fn cant_get_a_canvas_for_a_non_drawable_bitmap() { |
| 819 | let info = ImageInfo::new( |
| 820 | (400, 400), |
| 821 | ColorType::BGRA8888, |
| 822 | AlphaType::Premul, |
| 823 | ColorSpace::new_srgb(), |
| 824 | ); |
| 825 | let mut bitmap = Bitmap::new(); |
| 826 | if !bitmap.set_info(&info, None) { |
| 827 | panic!("set_info failed" ); |
| 828 | } |
| 829 | |
| 830 | let canvas = Canvas::from_bitmap(&bitmap, None); |
| 831 | assert!(canvas.is_none()); |
| 832 | } |
| 833 | } |
| 834 | |