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