skia_safe/core/bitmap.rs
1use std::{ffi, fmt, ptr};
2
3use skia_bindings::{self as sb, SkBitmap};
4
5use 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.
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 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)]
771mod 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}