skia_safe/core/
pixmap.rs

1use crate::{
2    prelude::*, AlphaType, Color, Color4f, ColorSpace, ColorType, IPoint, IRect, ISize, ImageInfo,
3    SamplingOptions,
4};
5use skia_bindings::{self as sb, SkPixmap};
6use std::{ffi::c_void, fmt, marker::PhantomData, mem, os::raw, ptr, slice};
7
8#[repr(transparent)]
9pub struct Pixmap<'a> {
10    inner: Handle<SkPixmap>,
11    pd: PhantomData<&'a mut [u8]>,
12}
13
14impl NativeDrop for SkPixmap {
15    fn drop(&mut self) {
16        unsafe { sb::C_SkPixmap_destruct(self) }
17    }
18}
19
20impl Default for Pixmap<'_> {
21    fn default() -> Self {
22        Self::from_native_c(SkPixmap {
23            fPixels: ptr::null(),
24            fRowBytes: 0,
25            fInfo: construct(|ii| unsafe { sb::C_SkImageInfo_Construct(ii) }),
26        })
27    }
28}
29
30impl fmt::Debug for Pixmap<'_> {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        f.debug_struct("Pixmap")
33            .field("row_bytes", &self.row_bytes())
34            .field("info", self.info())
35            .finish()
36    }
37}
38
39impl<'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        self.assert_pixel_exists(p);
168        unsafe {
169            (self.addr() as *const raw::c_char).add(self.info().compute_offset(p, self.row_bytes()))
170                as _
171        }
172    }
173
174    // TODO: addr8(), addr16(), addr32(), addr64(), addrF16(),
175    //       addr8_at(), addr16_at(), addr32_at(), addr64_at(), addrF16_at()
176
177    pub fn writable_addr(&self) -> *mut c_void {
178        self.addr() as _
179    }
180
181    pub fn writable_addr_at(&self, p: impl Into<IPoint>) -> *mut c_void {
182        self.addr_at(p) as _
183    }
184
185    // TODO: writable_addr8
186    // TODO: writable_addr16
187    // TODO: writable_addr32
188    // TODO: writable_addr64
189    // TODO: writable_addrF16
190
191    pub fn read_pixels<P>(
192        &self,
193        dst_info: &ImageInfo,
194        pixels: &mut [P],
195        dst_row_bytes: usize,
196        src: impl Into<IPoint>,
197    ) -> bool {
198        if !dst_info.valid_pixels(dst_row_bytes, pixels) {
199            return false;
200        }
201
202        let src = src.into();
203
204        unsafe {
205            self.native().readPixels(
206                dst_info.native(),
207                pixels.as_mut_ptr() as _,
208                dst_row_bytes,
209                src.x,
210                src.y,
211            )
212        }
213    }
214
215    /// Access the underlying pixels as a byte array. This is a rust-skia specific function.
216    pub fn bytes(&self) -> Option<&'pixels [u8]> {
217        let addr = self.addr().into_option()?;
218        let len = self.compute_byte_size();
219        Some(unsafe { slice::from_raw_parts(addr as *const u8, len) })
220    }
221
222    pub fn bytes_mut(&mut self) -> Option<&'pixels mut [u8]> {
223        let addr = self.writable_addr().into_option()?;
224        let len = self.compute_byte_size();
225        Some(unsafe { slice::from_raw_parts_mut(addr.as_ptr() as *mut u8, len) })
226    }
227
228    /// Access the underlying pixels. This is a rust-skia specific function.
229    ///
230    /// The `Pixel` type must implement the _unsafe_ trait [`Pixel`] and must return `true` in
231    /// [`Pixel::matches_color_type()`] when matched against the [`ColorType`] of this Pixmap's
232    /// pixels.
233    pub fn pixels<P: Pixel>(&self) -> Option<&'pixels [P]> {
234        let addr = self.addr().into_option()?;
235
236        let info = self.info();
237        let ct = info.color_type();
238        let pixel_size = mem::size_of::<P>();
239
240        if info.bytes_per_pixel() == pixel_size && P::matches_color_type(ct) {
241            let len = self.compute_byte_size() / pixel_size;
242            return Some(unsafe { slice::from_raw_parts(addr as *const P, len) });
243        }
244
245        None
246    }
247
248    pub fn read_pixels_to_pixmap(&self, dst: &mut Pixmap, src: impl Into<IPoint>) -> bool {
249        let Some(dst_bytes) = dst.bytes_mut() else {
250            return false;
251        };
252        self.read_pixels(dst.info(), dst_bytes, dst.row_bytes(), src)
253    }
254
255    pub fn scale_pixels(&self, dst: &mut Pixmap, sampling: impl Into<SamplingOptions>) -> bool {
256        let sampling = sampling.into();
257        unsafe { self.native().scalePixels(dst.native(), sampling.native()) }
258    }
259
260    pub fn erase(&mut self, color: impl Into<Color>, subset: Option<&IRect>) -> bool {
261        let color = color.into().into_native();
262        unsafe {
263            match subset {
264                Some(subset) => self.native().erase(color, subset.native()),
265                None => self.native().erase(color, self.bounds().native()),
266            }
267        }
268    }
269
270    pub fn erase_4f(&mut self, color: impl AsRef<Color4f>, subset: Option<&IRect>) -> bool {
271        let color = color.as_ref();
272        unsafe {
273            self.native()
274                .erase1(color.native(), subset.native_ptr_or_null())
275        }
276    }
277
278    fn from_native_c(pixmap: SkPixmap) -> Self {
279        Self {
280            inner: Handle::from_native_c(pixmap),
281            pd: PhantomData,
282        }
283    }
284
285    #[must_use]
286    pub(crate) fn from_native_ref(n: &SkPixmap) -> &Self {
287        unsafe { transmute_ref(n) }
288    }
289
290    #[must_use]
291    pub(crate) fn from_native_ptr(np: *const SkPixmap) -> *const Self {
292        // Should be safe as long `Pixmap` is represented with repr(Transparent).
293        np as _
294    }
295
296    pub(crate) fn native_mut(&mut self) -> &mut SkPixmap {
297        self.inner.native_mut()
298    }
299
300    pub(crate) fn native(&self) -> &SkPixmap {
301        self.inner.native()
302    }
303}
304
305/// Implement this trait to use a pixel type in [`Handle<Pixmap>::pixels()`].
306///
307/// # Safety
308///
309/// This trait is unsafe because external [`Pixel`] implementations may lie about their
310/// [`ColorType`] or fail to match the alignment of the pixels stored in [`Handle<Pixmap>`].
311pub unsafe trait Pixel: Copy {
312    /// `true` if the type matches the color type's format.
313    fn matches_color_type(ct: ColorType) -> bool;
314}
315
316unsafe impl Pixel for u8 {
317    fn matches_color_type(ct: ColorType) -> bool {
318        matches!(ct, ColorType::Alpha8 | ColorType::Gray8)
319    }
320}
321
322unsafe impl Pixel for [u8; 2] {
323    fn matches_color_type(ct: ColorType) -> bool {
324        matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm)
325    }
326}
327
328unsafe impl Pixel for (u8, u8) {
329    fn matches_color_type(ct: ColorType) -> bool {
330        matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm)
331    }
332}
333
334unsafe impl Pixel for [u8; 4] {
335    fn matches_color_type(ct: ColorType) -> bool {
336        matches!(
337            ct,
338            ColorType::RGBA8888 | ColorType::RGB888x | ColorType::BGRA8888
339        )
340    }
341}
342
343unsafe impl Pixel for (u8, u8, u8, u8) {
344    fn matches_color_type(ct: ColorType) -> bool {
345        matches!(
346            ct,
347            ColorType::RGBA8888 | ColorType::RGB888x | ColorType::BGRA8888
348        )
349    }
350}
351
352unsafe impl Pixel for [f32; 4] {
353    fn matches_color_type(ct: ColorType) -> bool {
354        matches!(ct, ColorType::RGBAF32)
355    }
356}
357
358unsafe impl Pixel for (f32, f32, f32, f32) {
359    fn matches_color_type(ct: ColorType) -> bool {
360        matches!(ct, ColorType::RGBAF32)
361    }
362}
363
364unsafe impl Pixel for u32 {
365    fn matches_color_type(ct: ColorType) -> bool {
366        ct == ColorType::N32
367    }
368}
369
370unsafe impl Pixel for Color {
371    fn matches_color_type(ct: ColorType) -> bool {
372        ct == ColorType::N32
373    }
374}
375
376unsafe impl Pixel for Color4f {
377    fn matches_color_type(ct: ColorType) -> bool {
378        ct == ColorType::RGBAF32
379    }
380}
381
382#[cfg(test)]
383mod tests {
384    use super::*;
385
386    #[test]
387    fn pixmap_mutably_borrows_pixels() {
388        let mut pixels = [0u8; 2 * 2 * 4];
389        let info = ImageInfo::new(
390            (2, 2),
391            ColorType::RGBA8888,
392            AlphaType::Premul,
393            ColorSpace::new_srgb(),
394        );
395        let mut pixmap = Pixmap::new(&info, &mut pixels, info.min_row_bytes()).unwrap();
396        // this must fail to compile:
397        // let _pixel = pixels[0];
398        // use `.bytes()`, or `bytes_mut()` instead.
399        pixmap.reset();
400    }
401
402    #[test]
403    fn addr_may_return_null_from_a_default_pixmap() {
404        let pixmap = Pixmap::default();
405        assert!(pixmap.addr().is_null());
406        assert!(pixmap.writable_addr().is_null());
407    }
408}