1 | use crate::{ |
2 | prelude::*, AlphaType, Color, Color4f, ColorSpace, ColorType, IPoint, IRect, ISize, Image, |
3 | ImageInfo, Matrix, Paint, PixelRef, Pixmap, SamplingOptions, Shader, TileMode, |
4 | }; |
5 | use skia_bindings::{self as sb, SkBitmap}; |
6 | use 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. |
28 | pub type Bitmap = Handle<SkBitmap>; |
29 | |
30 | impl NativeDrop for SkBitmap { |
31 | fn drop(&mut self) { |
32 | unsafe { sb::C_SkBitmap_destruct(self) } |
33 | } |
34 | } |
35 | |
36 | impl 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 | |
44 | impl Default for Bitmap { |
45 | /// See [`Bitmap::new()`]. |
46 | fn default() -> Self { |
47 | Self::new() |
48 | } |
49 | } |
50 | |
51 | impl 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 | |
59 | impl 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)] |
756 | mod 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 | |