1 | use crate::{ |
2 | prelude::*, AlphaType, Color, Color4f, ColorSpace, ColorType, IPoint, IRect, ISize, ImageInfo, |
3 | SamplingOptions, |
4 | }; |
5 | use skia_bindings::{self as sb, SkPixmap}; |
6 | use std::{ffi::c_void, fmt, marker::PhantomData, mem, os::raw, ptr, slice}; |
7 | |
8 | #[repr (transparent)] |
9 | pub struct Pixmap<'a> { |
10 | inner: Handle<SkPixmap>, |
11 | pd: PhantomData<&'a mut [u8]>, |
12 | } |
13 | |
14 | impl NativeDrop for SkPixmap { |
15 | fn drop(&mut self) { |
16 | unsafe { sb::C_SkPixmap_destruct(self) } |
17 | } |
18 | } |
19 | |
20 | impl Default for Pixmap<'_> { |
21 | fn default() -> Self { |
22 | Self::from_native_c(pixmap:SkPixmap { |
23 | fPixels: ptr::null(), |
24 | fRowBytes: 0, |
25 | fInfo: construct(|ii: *mut SkImageInfo| unsafe { sb::C_SkImageInfo_Construct(uninitialized:ii) }), |
26 | }) |
27 | } |
28 | } |
29 | |
30 | impl fmt::Debug for Pixmap<'_> { |
31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
32 | f&mut DebugStruct<'_, '_>.debug_struct("Pixmap" ) |
33 | .field("row_bytes" , &self.row_bytes()) |
34 | .field(name:"info" , self.info()) |
35 | .finish() |
36 | } |
37 | } |
38 | |
39 | impl<'pixels> Pixmap<'pixels> { |
40 | pub fn new(info: &ImageInfo, pixels: &'pixels mut [u8], row_bytes: usize) -> Option<Self> { |
41 | if row_bytes < info.min_row_bytes() { |
42 | return None; |
43 | } |
44 | if pixels.len() < info.compute_byte_size(row_bytes) { |
45 | return None; |
46 | } |
47 | |
48 | Some(Pixmap::from_native_c(SkPixmap { |
49 | fPixels: pixels.as_mut_ptr() as _, |
50 | fRowBytes: row_bytes, |
51 | fInfo: info.native().clone(), |
52 | })) |
53 | } |
54 | |
55 | pub fn reset(&mut self) -> &mut Self { |
56 | unsafe { self.native_mut().reset() } |
57 | self |
58 | } |
59 | |
60 | // TODO: reset() function that re-borrows pixels? |
61 | |
62 | pub fn set_color_space(&mut self, color_space: impl Into<Option<ColorSpace>>) -> &mut Self { |
63 | unsafe { |
64 | sb::C_SkPixmap_setColorSpace(self.native_mut(), color_space.into().into_ptr_or_null()) |
65 | } |
66 | self |
67 | } |
68 | |
69 | #[must_use ] |
70 | pub fn extract_subset(&self, area: impl AsRef<IRect>) -> Option<Self> { |
71 | let mut pixmap = Pixmap::default(); |
72 | unsafe { |
73 | self.native() |
74 | .extractSubset(pixmap.native_mut(), area.as_ref().native()) |
75 | } |
76 | .if_true_some(pixmap) |
77 | } |
78 | |
79 | pub fn info(&self) -> &ImageInfo { |
80 | ImageInfo::from_native_ref(&self.native().fInfo) |
81 | } |
82 | |
83 | pub fn row_bytes(&self) -> usize { |
84 | self.native().fRowBytes |
85 | } |
86 | |
87 | pub fn addr(&self) -> *const c_void { |
88 | self.native().fPixels |
89 | } |
90 | |
91 | pub fn width(&self) -> i32 { |
92 | self.info().width() |
93 | } |
94 | |
95 | pub fn height(&self) -> i32 { |
96 | self.info().height() |
97 | } |
98 | |
99 | pub fn dimensions(&self) -> ISize { |
100 | self.info().dimensions() |
101 | } |
102 | |
103 | pub fn color_type(&self) -> ColorType { |
104 | self.info().color_type() |
105 | } |
106 | |
107 | pub fn alpha_type(&self) -> AlphaType { |
108 | self.info().alpha_type() |
109 | } |
110 | |
111 | pub fn color_space(&self) -> Option<ColorSpace> { |
112 | ColorSpace::from_unshared_ptr(unsafe { self.native().colorSpace() }) |
113 | } |
114 | |
115 | pub fn is_opaque(&self) -> bool { |
116 | self.alpha_type().is_opaque() |
117 | } |
118 | |
119 | pub fn bounds(&self) -> IRect { |
120 | IRect::from_wh(self.width(), self.height()) |
121 | } |
122 | |
123 | pub fn row_bytes_as_pixels(&self) -> usize { |
124 | self.row_bytes() >> self.shift_per_pixel() |
125 | } |
126 | |
127 | pub fn shift_per_pixel(&self) -> usize { |
128 | self.info().shift_per_pixel() |
129 | } |
130 | |
131 | pub fn compute_byte_size(&self) -> usize { |
132 | self.info().compute_byte_size(self.row_bytes()) |
133 | } |
134 | |
135 | pub fn compute_is_opaque(&self) -> bool { |
136 | unsafe { self.native().computeIsOpaque() } |
137 | } |
138 | |
139 | pub fn get_color(&self, p: impl Into<IPoint>) -> Color { |
140 | let p = p.into(); |
141 | self.assert_pixel_exists(p); |
142 | Color::from_native_c(unsafe { self.native().getColor(p.x, p.y) }) |
143 | } |
144 | |
145 | pub fn get_color_4f(&self, p: impl Into<IPoint>) -> Color4f { |
146 | let p = p.into(); |
147 | self.assert_pixel_exists(p); |
148 | Color4f::from_native_c(unsafe { self.native().getColor4f(p.x, p.y) }) |
149 | } |
150 | |
151 | pub fn get_alpha_f(&self, p: impl Into<IPoint>) -> f32 { |
152 | let p = p.into(); |
153 | self.assert_pixel_exists(p); |
154 | unsafe { self.native().getAlphaf(p.x, p.y) } |
155 | } |
156 | |
157 | // Helper to test if the pixel does exist physically in memory. |
158 | fn assert_pixel_exists(&self, p: impl Into<IPoint>) { |
159 | let p = p.into(); |
160 | assert!(!self.addr().is_null()); |
161 | assert!(p.x >= 0 && p.x < self.width()); |
162 | assert!(p.y >= 0 && p.y < self.height()); |
163 | } |
164 | |
165 | pub fn addr_at(&self, p: impl Into<IPoint>) -> *const c_void { |
166 | let p = p.into(); |
167 | unsafe { |
168 | (self.addr() as *const raw::c_char).add(self.info().compute_offset(p, self.row_bytes())) |
169 | as _ |
170 | } |
171 | } |
172 | |
173 | // TODO: addr8(), addr16(), addr32(), addr64(), addrF16(), |
174 | // addr8_at(), addr16_at(), addr32_at(), addr64_at(), addrF16_at() |
175 | |
176 | pub fn writable_addr(&self) -> *mut c_void { |
177 | self.addr() as _ |
178 | } |
179 | |
180 | pub fn writable_addr_at(&self, p: impl Into<IPoint>) -> *mut c_void { |
181 | self.addr_at(p) as _ |
182 | } |
183 | |
184 | // TODO: writable_addr8 |
185 | // TODO: writable_addr16 |
186 | // TODO: writable_addr32 |
187 | // TODO: writable_addr64 |
188 | // TODO: writable_addrF16 |
189 | |
190 | pub fn read_pixels<P>( |
191 | &self, |
192 | dst_info: &ImageInfo, |
193 | pixels: &mut [P], |
194 | dst_row_bytes: usize, |
195 | src: impl Into<IPoint>, |
196 | ) -> bool { |
197 | if !dst_info.valid_pixels(dst_row_bytes, pixels) { |
198 | return false; |
199 | } |
200 | |
201 | let src = src.into(); |
202 | |
203 | unsafe { |
204 | self.native().readPixels( |
205 | dst_info.native(), |
206 | pixels.as_mut_ptr() as _, |
207 | dst_row_bytes, |
208 | src.x, |
209 | src.y, |
210 | ) |
211 | } |
212 | } |
213 | |
214 | /// Access the underlying pixels as a byte array. This is a rust-skia specific function. |
215 | pub fn bytes(&self) -> Option<&'pixels [u8]> { |
216 | let addr = self.addr().into_option()?; |
217 | let len = self.compute_byte_size(); |
218 | return Some(unsafe { slice::from_raw_parts(addr as *const u8, len) }); |
219 | } |
220 | |
221 | pub fn bytes_mut(&mut self) -> Option<&'pixels mut [u8]> { |
222 | let addr = self.writable_addr().into_option()?; |
223 | let len = self.compute_byte_size(); |
224 | return Some(unsafe { slice::from_raw_parts_mut(addr.as_ptr() as *mut u8, len) }); |
225 | } |
226 | |
227 | /// Access the underlying pixels. This is a rust-skia specific function. |
228 | /// |
229 | /// The `Pixel` type must implement the _unsafe_ trait [`Pixel`] and must return `true` in |
230 | /// [`Pixel::matches_color_type()`] when matched against the [`ColorType`] of this Pixmap's |
231 | /// pixels. |
232 | pub fn pixels<P: Pixel>(&self) -> Option<&'pixels [P]> { |
233 | let addr = self.addr().into_option()?; |
234 | |
235 | let info = self.info(); |
236 | let ct = info.color_type(); |
237 | let pixel_size = mem::size_of::<P>(); |
238 | |
239 | if info.bytes_per_pixel() == pixel_size && P::matches_color_type(ct) { |
240 | let len = self.compute_byte_size() / pixel_size; |
241 | return Some(unsafe { slice::from_raw_parts(addr as *const P, len) }); |
242 | } |
243 | |
244 | None |
245 | } |
246 | |
247 | pub fn read_pixels_to_pixmap(&self, dst: &mut Pixmap, src: impl Into<IPoint>) -> bool { |
248 | let Some(dst_bytes) = dst.bytes_mut() else { |
249 | return false; |
250 | }; |
251 | self.read_pixels(dst.info(), dst_bytes, dst.row_bytes(), src) |
252 | } |
253 | |
254 | pub fn scale_pixels(&self, dst: &mut Pixmap, sampling: impl Into<SamplingOptions>) -> bool { |
255 | let sampling = sampling.into(); |
256 | unsafe { self.native().scalePixels(dst.native(), sampling.native()) } |
257 | } |
258 | |
259 | pub fn erase(&mut self, color: impl Into<Color>, subset: Option<&IRect>) -> bool { |
260 | let color = color.into().into_native(); |
261 | unsafe { |
262 | match subset { |
263 | Some(subset) => self.native().erase(color, subset.native()), |
264 | None => self.native().erase(color, self.bounds().native()), |
265 | } |
266 | } |
267 | } |
268 | |
269 | pub fn erase_4f(&mut self, color: impl AsRef<Color4f>, subset: Option<&IRect>) -> bool { |
270 | let color = color.as_ref(); |
271 | unsafe { |
272 | self.native() |
273 | .erase1(color.native(), subset.native_ptr_or_null()) |
274 | } |
275 | } |
276 | |
277 | fn from_native_c(pixmap: SkPixmap) -> Self { |
278 | Self { |
279 | inner: Handle::from_native_c(pixmap), |
280 | pd: PhantomData, |
281 | } |
282 | } |
283 | |
284 | #[must_use ] |
285 | pub(crate) fn from_native_ref(n: &SkPixmap) -> &Self { |
286 | unsafe { transmute_ref(n) } |
287 | } |
288 | |
289 | #[must_use ] |
290 | pub(crate) fn from_native_ptr(np: *const SkPixmap) -> *const Self { |
291 | // Should be safe as long `Pixmap` is represented with repr(Transparent). |
292 | np as _ |
293 | } |
294 | |
295 | pub(crate) fn native_mut(&mut self) -> &mut SkPixmap { |
296 | self.inner.native_mut() |
297 | } |
298 | |
299 | pub(crate) fn native(&self) -> &SkPixmap { |
300 | self.inner.native() |
301 | } |
302 | } |
303 | |
304 | /// Implement this trait to use a pixel type in [`Handle<Pixmap>::pixels()`]. |
305 | /// |
306 | /// # Safety |
307 | /// |
308 | /// This trait is unsafe because external [`Pixel`] implementations may lie about their |
309 | /// [`ColorType`] or fail to match the alignment of the pixels stored in [`Handle<Pixmap>`]. |
310 | pub unsafe trait Pixel: Copy { |
311 | /// `true` if the type matches the color type's format. |
312 | fn matches_color_type(ct: ColorType) -> bool; |
313 | } |
314 | |
315 | unsafe impl Pixel for u8 { |
316 | fn matches_color_type(ct: ColorType) -> bool { |
317 | matches!(ct, ColorType::Alpha8 | ColorType::Gray8) |
318 | } |
319 | } |
320 | |
321 | unsafe impl Pixel for [u8; 2] { |
322 | fn matches_color_type(ct: ColorType) -> bool { |
323 | matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm) |
324 | } |
325 | } |
326 | |
327 | unsafe impl Pixel for (u8, u8) { |
328 | fn matches_color_type(ct: ColorType) -> bool { |
329 | matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm) |
330 | } |
331 | } |
332 | |
333 | unsafe impl Pixel for [u8; 4] { |
334 | fn matches_color_type(ct: ColorType) -> bool { |
335 | matches!( |
336 | ct, |
337 | ColorType::RGBA8888 | ColorType::RGB888x | ColorType::BGRA8888 |
338 | ) |
339 | } |
340 | } |
341 | |
342 | unsafe impl Pixel for (u8, u8, u8, u8) { |
343 | fn matches_color_type(ct: ColorType) -> bool { |
344 | matches!( |
345 | ct, |
346 | ColorType::RGBA8888 | ColorType::RGB888x | ColorType::BGRA8888 |
347 | ) |
348 | } |
349 | } |
350 | |
351 | unsafe impl Pixel for [f32; 4] { |
352 | fn matches_color_type(ct: ColorType) -> bool { |
353 | matches!(ct, ColorType::RGBAF32) |
354 | } |
355 | } |
356 | |
357 | unsafe impl Pixel for (f32, f32, f32, f32) { |
358 | fn matches_color_type(ct: ColorType) -> bool { |
359 | matches!(ct, ColorType::RGBAF32) |
360 | } |
361 | } |
362 | |
363 | unsafe impl Pixel for u32 { |
364 | fn matches_color_type(ct: ColorType) -> bool { |
365 | ct == ColorType::N32 |
366 | } |
367 | } |
368 | |
369 | unsafe impl Pixel for Color { |
370 | fn matches_color_type(ct: ColorType) -> bool { |
371 | ct == ColorType::N32 |
372 | } |
373 | } |
374 | |
375 | unsafe impl Pixel for Color4f { |
376 | fn matches_color_type(ct: ColorType) -> bool { |
377 | ct == ColorType::RGBAF32 |
378 | } |
379 | } |
380 | |
381 | #[cfg (test)] |
382 | mod tests { |
383 | use super::*; |
384 | |
385 | #[test ] |
386 | fn pixmap_mutably_borrows_pixels() { |
387 | let mut pixels = [0u8; 2 * 2 * 4]; |
388 | let info = ImageInfo::new( |
389 | (2, 2), |
390 | ColorType::RGBA8888, |
391 | AlphaType::Premul, |
392 | ColorSpace::new_srgb(), |
393 | ); |
394 | let mut pixmap = Pixmap::new(&info, &mut pixels, info.min_row_bytes()).unwrap(); |
395 | // this must fail to compile: |
396 | // let _pixel = pixels[0]; |
397 | // use `.bytes()`, or `bytes_mut()` instead. |
398 | pixmap.reset(); |
399 | } |
400 | |
401 | #[test ] |
402 | fn addr_may_return_null_from_a_default_pixmap() { |
403 | let pixmap = Pixmap::default(); |
404 | assert!(pixmap.addr().is_null()); |
405 | assert!(pixmap.writable_addr().is_null()); |
406 | assert!(pixmap.addr_at((10, 10)).is_null()); |
407 | assert!(pixmap.writable_addr_at((10, 10)).is_null()); |
408 | } |
409 | } |
410 | |