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