Skip to main content

skia_safe/core/
bitmap.rs

1use std::{ffi, fmt, ptr};
2
3use skia_bindings::{self as sb, SkBitmap};
4
5use crate::{
6    AlphaType, Color, Color4f, ColorSpace, ColorType, IPoint, IRect, ISize, Image, ImageInfo,
7    Matrix, Paint, PixelRef, Pixmap, SamplingOptions, Shader, TileMode, prelude::*,
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.
30pub type Bitmap = Handle<SkBitmap>;
31
32impl NativeDrop for SkBitmap {
33    fn drop(&mut self) {
34        unsafe { sb::C_SkBitmap_destruct(self) }
35    }
36}
37
38impl 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
46impl Default for Bitmap {
47    /// See [`Bitmap::new()`].
48    fn default() -> Self {
49        Self::new()
50    }
51}
52
53impl fmt::Debug for Bitmap {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        f.debug_struct("Bitmap")
56            .field("pixmap", self.pixmap())
57            .finish()
58    }
59}
60
61impl 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        if !self.try_alloc_pixels_flags(image_info) {
339            panic!("Bitmap::alloc_pixels_flags failed");
340        }
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        if !self.try_alloc_pixels_info(image_info, row_bytes.into()) {
384            panic!("Bitmap::alloc_pixels_info failed");
385        }
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        if !self.try_alloc_n32_pixels((width, height), is_opaque.into().unwrap_or(false)) {
429            panic!("Bitmap::alloc_n32_pixels_failed");
430        }
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        unsafe {
452            self.native_mut()
453                .installPixels(info.native(), pixels, row_bytes, None, ptr::null_mut())
454        }
455    }
456
457    // TODO: wrap installPixels with SkPixmap&
458
459    // TODO: setPixels()?
460
461    /// Allocates pixel memory with HeapAllocator, and replaces existing [`PixelRef`]. The
462    /// allocation size is determined by [`ImageInfo`] width, height, and [`ColorType`].
463    ///
464    /// Returns `false` if `info().color_type()` is [`ColorType::Unknown`], or allocation fails.
465    #[must_use]
466    pub fn try_alloc_pixels(&mut self) -> bool {
467        unsafe { sb::C_SkBitmap_tryAllocPixels(self.native_mut()) }
468    }
469
470    /// Allocates pixel memory with HeapAllocator, and replaces existing [`PixelRef`]. The
471    /// allocation size is determined by [`ImageInfo`] width, height, and [`ColorType`].
472    ///
473    /// Aborts if `info().color_type()` is [`ColorType::Unknown`], or allocation fails.
474    pub fn alloc_pixels(&mut self) {
475        if !self.try_alloc_pixels() {
476            panic!("Bitmap::alloc_pixels failed");
477        }
478    }
479
480    // TODO: allocPixels(Allocator*)
481
482    // TODO: find a way to return pixel ref without increasing the ref count here?
483
484    /// Returns [`PixelRef`], which contains: pixel base address; its dimensions; and `row_bytes()`,
485    /// the interval from one row to the next. Does not change [`PixelRef`] reference count.
486    /// [`PixelRef`] may be shared by multiple bitmaps.
487    ///
488    /// If [`PixelRef`] has not been set, returns `None`.
489    pub fn pixel_ref(&self) -> Option<PixelRef> {
490        PixelRef::from_unshared_ptr(self.native().fPixelRef.fPtr)
491    }
492
493    /// Returns origin of pixels within [`PixelRef`]. [`Bitmap`] bounds is always contained
494    /// by [`PixelRef`] bounds, which may be the same size or larger. Multiple [`Bitmap`]
495    /// can share the same [`PixelRef`], where each [`Bitmap`] has different bounds.
496    ///
497    /// The returned origin added to [`Bitmap`] dimensions equals or is smaller than the
498    /// [`PixelRef`] dimensions.
499    ///
500    /// Returns `(0, 0)` if [`PixelRef`] is `None`.
501    pub fn pixel_ref_origin(&self) -> IPoint {
502        IPoint::from_native_c(unsafe { sb::C_SkBitmap_pixelRefOrigin(self.native()) })
503    }
504
505    /// Replaces `pixel_ref` and origin in [`Bitmap`]. `offset` specifies the offset within the
506    /// [`PixelRef`] pixels for the top-left corner of the bitmap.
507    ///
508    /// Asserts in debug builds if offset is out of range. Pins offset to legal range in release
509    /// builds.
510    ///
511    /// The caller is responsible for ensuring that the pixels match the [`ColorType`] and
512    /// [`AlphaType`] in [`ImageInfo`].
513    pub fn set_pixel_ref(
514        &mut self,
515        pixel_ref: impl Into<Option<PixelRef>>,
516        offset: impl Into<IPoint>,
517    ) {
518        let offset = offset.into();
519        unsafe {
520            sb::C_SkBitmap_setPixelRef(
521                self.native_mut(),
522                pixel_ref.into().into_ptr_or_null(),
523                offset.x,
524                offset.y,
525            )
526        }
527    }
528
529    /// Returns `true` if [`Bitmap`] can be drawn.
530    pub fn is_ready_to_draw(&self) -> bool {
531        unsafe { sb::C_SkBitmap_readyToDraw(self.native()) }
532    }
533
534    /// Returns a unique value corresponding to the pixels in [`PixelRef`].  
535    /// Returns a different value after `notify_pixels_changed()` has been called.  
536    /// Returns zero if [`PixelRef`] is `None`.
537    ///
538    /// Determines if pixels have changed since last examined.
539    pub fn generation_id(&self) -> u32 {
540        unsafe { self.native().getGenerationID() }
541    }
542
543    /// Marks that pixels in [`PixelRef`] have changed. Subsequent calls to `generation_id()` return
544    /// a different value.
545    pub fn notify_pixels_changed(&self) {
546        unsafe { self.native().notifyPixelsChanged() }
547    }
548
549    /// Replaces pixel values with `c`, interpreted as being in the sRGB [`ColorSpace`]. All pixels
550    /// contained by [`Self::bounds()`] are affected. If the [`Self::color_type()`] is
551    /// [`ColorType::Gray8`] or [`ColorType::RGB565`], then alpha is ignored; RGB is treated as
552    /// opaque. If [`Self::color_type()`] is [`ColorType::Alpha8`], then RGB is ignored.
553    ///
554    /// Input color is ultimately converted to an [`Color4f`], so [`Self::erase_color_4f`] will have
555    /// higher color resolution.
556    pub fn erase_color(&self, c: impl Into<Color>) {
557        unsafe { self.native().eraseColor1(c.into().into_native()) }
558    }
559
560    /// Replaces pixel values with `c`, interpreted as being in the sRGB [`ColorSpace`]. All pixels
561    /// contained by [`Self::bounds()`] are affected. If the [`Self::color_type()`] is
562    /// [`ColorType::Gray8`] or [ColorType::RGB565], then alpha is ignored; RGB is treated as
563    /// opaque. If [`Self::color_type()`] is [`ColorType::Alpha8`], then RGB is ignored.
564    pub fn erase_color_4f(&self, c: impl AsRef<Color4f>) {
565        unsafe { self.native().eraseColor(c.as_ref().into_native()) }
566    }
567
568    /// Replaces pixel values with unpremultiplied color built from `a`, `r`, `g`, and `b`,
569    /// interpreted as being in the sRGB [`ColorSpace`]. All pixels contained by [`Self::bounds()`]
570    /// are affected. If the [`Self::color_type()`] is [`ColorType::Gray8`] or [`ColorType::RGB565`],
571    /// then `a` is ignored; `r`, `g`, and `b` are treated as opaque. If [`Self::color_type()`] is
572    /// [`ColorType::Alpha8`], then `r`, `g`, and `b` are ignored.
573    pub fn erase_argb(&self, a: u8, r: u8, g: u8, b: u8) {
574        unsafe { sb::C_SkBitmap_eraseARGB(self.native(), a.into(), r.into(), g.into(), b.into()) }
575    }
576
577    /// Replaces pixel values inside area with c. interpreted as being in the sRGB [`ColorSpace`].
578    /// If area does not intersect `bounds()`, call has no effect.
579    ///
580    /// If the `color_type()` is [`ColorType::Gray8`] [`ColorType::RGB565`], then alpha is ignored;
581    /// RGB is treated as opaque. If `color_type()` is [`ColorType::Alpha8`], then RGB is ignored.
582    ///
583    /// Input color is ultimately converted to an [`Color4f`], so [`Self::erase_4f`] will have
584    /// higher color resolution.
585    pub fn erase(&self, c: impl Into<Color>, area: impl AsRef<IRect>) {
586        unsafe {
587            self.native()
588                .erase1(c.into().into_native(), area.as_ref().native())
589        }
590    }
591
592    /// Replaces pixel values inside area with c. interpreted as being in the sRGB [`ColorSpace`].
593    /// If area does not intersect `bounds()`, call has no effect.
594    ///
595    /// If the `color_type()` is [`ColorType::Gray8`] [`ColorType::RGB565`], then alpha is ignored;
596    /// RGB is treated as opaque. If `color_type()` is [`ColorType::Alpha8`], then RGB is ignored.
597    pub fn erase_4f(&self, c: impl AsRef<Color4f>, area: impl AsRef<IRect>) {
598        unsafe {
599            self.native()
600                .erase(c.as_ref().into_native(), area.as_ref().native())
601        }
602    }
603
604    /// Returns pixel at `(x, y)` as unpremultiplied color.  
605    /// Returns black with alpha if [`ColorType`] is [`ColorType::Alpha8`]
606    ///
607    /// Input is not validated: out of bounds values of `x` or `y` trigger an `assert()`.
608    ///
609    /// Fails if [`ColorType`] is [`ColorType::Unknown`] or pixel address is `nullptr`.
610    ///
611    /// [`ColorSpace`] in [`ImageInfo`] is ignored. Some color precision may be lost in the
612    /// conversion to unpremultiplied color; original pixel data may have additional precision.
613    pub fn get_color(&self, p: impl Into<IPoint>) -> Color {
614        self.pixmap().get_color(p)
615    }
616
617    /// Returns pixel at `(x, y)` as unpremultiplied color.
618    /// Returns black with alpha if [ColorType] is [ColorType::Alpha8]
619    ///
620    /// Input is not validated: out of bounds values of x or y trigger an `assert()`.
621    ///
622    /// Fails if [ColorType] is [ColorType::Unknown] or pixel address is `None`.
623    ///
624    /// [`ColorSpace`] in [`ImageInfo`] is ignored. Some color precision may be lost in the
625    /// conversion to unpremultiplied color.
626    pub fn get_color_4f(&self, p: impl Into<IPoint>) -> Color4f {
627        self.pixmap().get_color_4f(p)
628    }
629
630    /// Look up the pixel at `(x,y)` and return its alpha component, normalized to `[0..1]`. This is
631    /// roughly equivalent to `get_color().a()`, but can be more efficient (and more precise if the
632    /// pixels store more than 8 bits per component).
633    pub fn get_alpha_f(&self, p: impl Into<IPoint>) -> f32 {
634        self.pixmap().get_alpha_f(p)
635    }
636
637    /// Returns pixel address at `(x, y)`.
638    ///
639    /// Input is not validated: out of bounds values of `x` or `y`, or [`ColorType::Unknown`],
640    /// trigger an `assert()`. Returns `nullptr` if [`ColorType`] is [`ColorType::Unknown`], or
641    /// [`PixelRef`] is `nullptr`.
642    ///
643    /// Performs a lookup of pixel size; for better performance, call one of: `get_addr8()`,
644    /// `get_addr16()`, or `get_addr32()`.
645    pub fn get_addr(&self, p: impl Into<IPoint>) -> *const ffi::c_void {
646        let p = p.into();
647        unsafe { self.native().getAddr(p.x, p.y) }
648    }
649
650    // TODO: get_addr_32(), get_addr_16(), get_addr_8()
651
652    /// Shares [`PixelRef`] with `dst`. Pixels are not copied; [`Bitmap`] and dst point to the same
653    /// pixels; dst [`Self::bounds()`] are set to the intersection of subset and the original
654    /// [`Self::bounds()`].
655    ///
656    /// Subset may be larger than [`Self::bounds()`]. Any area outside of [`Self::bounds()`] is
657    /// ignored.
658    ///
659    /// Any contents of dst are discarded.
660    ///
661    /// Return `false` if:
662    /// - dst is `nullptr`
663    /// - [`PixelRef`] is `nullptr`
664    /// - subset does not intersect [`Self::bounds()`]
665    ///
666    /// example: <https://fiddle.skia.org/c/@Bitmap_extractSubset>
667    pub fn extract_subset(&self, dst: &mut Self, subset: impl AsRef<IRect>) -> bool {
668        unsafe {
669            self.native()
670                .extractSubset(dst.native_mut(), subset.as_ref().native())
671        }
672    }
673
674    /// Copies a [`crate::Rect`] of pixels from [`Bitmap`] to `dst_pixels`. Copy starts at `(src_x,
675    /// src_y)`, and does not exceed [`Bitmap`] `(width(), height())`.
676    ///
677    /// `dst_info` specifies width, height, [ColorType], [`AlphaType`], and [`ColorSpace`] of
678    /// destination.  
679    /// `dst_row_bytes` specifics the gap from one destination row to the next. Returns `true` if
680    /// pixels are copied. Returns `false` if:
681    /// - `dst_info` has no address
682    /// - `dst_row_bytes` is less than `dst_info.min_row_bytes()`
683    /// - [`PixelRef`] is `nullptr`
684    ///
685    /// Pixels are copied only if pixel conversion is possible. If [`Self::color_type()`] is
686    /// [`ColorType::Gray8`], or [`ColorType::Alpha8`]; `dst_info.color_type()` must match. If
687    /// [`Self::color_type()`] is [`ColorType::Gray8`], `dst_info.color_space()` must match. If
688    /// [`Self::alpha_type()`] is [`AlphaType::Opaque`], `dst_info.alpha_type()` must match. If
689    /// [`Self::color_space()`] is `nullptr`, `dst_info.color_space()` must match. Returns `false`
690    /// if pixel conversion is not possible.
691    ///
692    /// `src_x` and `src_y` may be negative to copy only top or left of source. Returns `false` if
693    /// [`Self::width()`] or [`Self::height()`] is zero or negative. Returns `false` if `abs(src_x)`
694    /// `>=` [`Self::width()`], or if `abs(src_y) >=` [`Self::height()`].
695    #[allow(clippy::missing_safety_doc)]
696    pub unsafe fn read_pixels(
697        &self,
698        dst_info: &ImageInfo,
699        dst_pixels: *mut ffi::c_void,
700        dst_row_bytes: usize,
701        src_x: i32,
702        src_y: i32,
703    ) -> bool {
704        unsafe {
705            self.native()
706                .readPixels(dst_info.native(), dst_pixels, dst_row_bytes, src_x, src_y)
707        }
708    }
709
710    // TODO: read_pixels(Pixmap)
711    // TODO: write_pixels(Pixmap)
712
713    /// Sets dst to alpha described by pixels. Returns `false` if `dst` cannot be written to or
714    /// `dst` pixels cannot be allocated.
715    ///
716    /// If `paint` is not `None` and contains [`crate::MaskFilter`], [`crate::MaskFilter`] generates
717    /// mask alpha from [`Bitmap`]. Uses `HeapAllocator` to reserve memory for `dst` [`PixelRef`].
718    /// Returns offset to top-left position for `dst` for alignment with [`Bitmap`]; `(0, 0)` unless
719    /// [crate::MaskFilter] generates mask.
720    pub fn extract_alpha(&self, dst: &mut Self, paint: Option<&Paint>) -> Option<IPoint> {
721        let mut offset = IPoint::default();
722        unsafe {
723            sb::C_SkBitmap_extractAlpha(
724                self.native(),
725                dst.native_mut(),
726                paint.native_ptr_or_null(),
727                offset.native_mut(),
728            )
729        }
730        .then_some(offset)
731    }
732
733    /// Copies [`Bitmap`] pixel address, row bytes, and [`ImageInfo`] to pixmap, if address is
734    /// available, and returns `Some(Pixmap)`. If pixel address is not available, return `None`
735    /// and leave pixmap unchanged.
736    ///
737    /// example: <https://fiddle.skia.org/c/@Bitmap_peekPixels>
738    pub fn peek_pixels(&self) -> Option<Pixmap> {
739        let mut pixmap = Pixmap::default();
740        unsafe { self.native().peekPixels(pixmap.native_mut()) }.then_some(pixmap)
741    }
742
743    /// Make a shader with the specified tiling, matrix and sampling.  
744    /// Defaults to clamp in both X and Y.
745    pub fn to_shader<'a>(
746        &self,
747        tile_modes: impl Into<Option<(TileMode, TileMode)>>,
748        sampling: impl Into<SamplingOptions>,
749        local_matrix: impl Into<Option<&'a Matrix>>,
750    ) -> Option<Shader> {
751        let tile_modes = tile_modes.into();
752        let sampling = sampling.into();
753        let local_matrix = local_matrix.into();
754        Shader::from_ptr(unsafe {
755            let tmx = tile_modes.map(|tm| tm.0).unwrap_or_default();
756            let tmy = tile_modes.map(|tm| tm.1).unwrap_or_default();
757            sb::C_SkBitmap_makeShader(
758                self.native(),
759                tmx,
760                tmy,
761                sampling.native(),
762                local_matrix.native_ptr_or_null(),
763            )
764        })
765    }
766
767    /// Returns a new image from the bitmap. If the bitmap is marked immutable, this will
768    /// share the pixel buffer. If not, it will make a copy of the pixels for the image.
769    pub fn as_image(&self) -> Image {
770        Image::from_ptr(unsafe { sb::C_SkBitmap_asImage(self.native()) }).unwrap()
771    }
772}
773
774#[cfg(test)]
775mod tests {
776    use super::TileMode;
777    use crate::{AlphaType, Bitmap, Canvas, ColorSpace, ColorType, ImageInfo, SamplingOptions};
778
779    #[test]
780    fn create_clone_and_drop() {
781        let bm = Bitmap::new();
782        #[allow(clippy::redundant_clone)]
783        let _bm2 = bm.clone();
784    }
785
786    #[test]
787    fn get_info() {
788        let bm = Bitmap::new();
789        let _info = bm.info();
790    }
791
792    #[test]
793    fn empty_bitmap_shader() {
794        let bm = Bitmap::new();
795        let _shader = bm.to_shader(None, SamplingOptions::default(), None);
796    }
797
798    #[test]
799    fn shader_with_tile_mode() {
800        let bm = Bitmap::new();
801        let _shader = bm.to_shader(
802            (TileMode::Decal, TileMode::Mirror),
803            SamplingOptions::default(),
804            None,
805        );
806    }
807
808    #[test]
809    fn test_get_subset() {
810        let bm = Bitmap::new();
811        let _ = bm.get_subset();
812    }
813
814    #[test]
815    fn test_pixel_ref_origin() {
816        let bm = Bitmap::new();
817        let _ = bm.pixel_ref_origin();
818    }
819
820    /// Test for: <https://github.com/rust-skia/rust-skia/issues/669>
821    #[test]
822    fn cant_get_a_canvas_for_a_non_drawable_bitmap() {
823        let info = ImageInfo::new(
824            (400, 400),
825            ColorType::BGRA8888,
826            AlphaType::Premul,
827            ColorSpace::new_srgb(),
828        );
829        let mut bitmap = Bitmap::new();
830        if !bitmap.set_info(&info, None) {
831            panic!("set_info failed");
832        }
833
834        let canvas = Canvas::from_bitmap(&bitmap, None);
835        assert!(canvas.is_none());
836    }
837}