Skip to main content

skia_safe/core/
pixmap.rs

1use crate::{
2    AlphaType, Color, Color4f, ColorSpace, ColorType, IPoint, IRect, ISize, ImageInfo,
3    SamplingOptions, prelude::*,
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        .then_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 is_empty(&self) -> bool {
100        self.info().is_empty()
101    }
102
103    pub fn dimensions(&self) -> ISize {
104        self.info().dimensions()
105    }
106
107    pub fn color_type(&self) -> ColorType {
108        self.info().color_type()
109    }
110
111    pub fn alpha_type(&self) -> AlphaType {
112        self.info().alpha_type()
113    }
114
115    pub fn color_space(&self) -> Option<ColorSpace> {
116        ColorSpace::from_unshared_ptr(unsafe { self.native().colorSpace() })
117    }
118
119    pub fn is_opaque(&self) -> bool {
120        self.alpha_type().is_opaque()
121    }
122
123    pub fn bounds(&self) -> IRect {
124        IRect::from_wh(self.width(), self.height())
125    }
126
127    pub fn row_bytes_as_pixels(&self) -> usize {
128        self.row_bytes() >> self.shift_per_pixel()
129    }
130
131    pub fn shift_per_pixel(&self) -> usize {
132        self.info().shift_per_pixel()
133    }
134
135    pub fn compute_byte_size(&self) -> usize {
136        self.info().compute_byte_size(self.row_bytes())
137    }
138
139    pub fn compute_is_opaque(&self) -> bool {
140        unsafe { self.native().computeIsOpaque() }
141    }
142
143    pub fn get_color(&self, p: impl Into<IPoint>) -> Color {
144        let p = p.into();
145        self.assert_pixel_exists(p);
146        Color::from_native_c(unsafe { self.native().getColor(p.x, p.y) })
147    }
148
149    pub fn get_color_4f(&self, p: impl Into<IPoint>) -> Color4f {
150        let p = p.into();
151        self.assert_pixel_exists(p);
152        Color4f::from_native_c(unsafe { self.native().getColor4f(p.x, p.y) })
153    }
154
155    pub fn get_alpha_f(&self, p: impl Into<IPoint>) -> f32 {
156        let p = p.into();
157        self.assert_pixel_exists(p);
158        unsafe { self.native().getAlphaf(p.x, p.y) }
159    }
160
161    // Helper to test if the pixel does exist physically in memory.
162    fn assert_pixel_exists(&self, p: impl Into<IPoint>) {
163        let p = p.into();
164        assert!(!self.addr().is_null());
165        assert!(p.x >= 0 && p.x < self.width());
166        assert!(p.y >= 0 && p.y < self.height());
167    }
168
169    pub fn addr_at(&self, p: impl Into<IPoint>) -> *const c_void {
170        let p = p.into();
171        self.assert_pixel_exists(p);
172        unsafe {
173            (self.addr() as *const raw::c_char).add(self.info().compute_offset(p, self.row_bytes()))
174                as _
175        }
176    }
177
178    // TODO: addr8(), addr16(), addr32(), addr64(), addrF16(),
179    //       addr8_at(), addr16_at(), addr32_at(), addr64_at(), addrF16_at()
180
181    pub fn writable_addr(&self) -> *mut c_void {
182        self.addr() as _
183    }
184
185    pub fn writable_addr_at(&self, p: impl Into<IPoint>) -> *mut c_void {
186        self.addr_at(p) as _
187    }
188
189    // TODO: writable_addr8
190    // TODO: writable_addr16
191    // TODO: writable_addr32
192    // TODO: writable_addr64
193    // TODO: writable_addrF16
194
195    pub fn read_pixels<P>(
196        &self,
197        dst_info: &ImageInfo,
198        pixels: &mut [P],
199        dst_row_bytes: usize,
200        src: impl Into<IPoint>,
201    ) -> bool {
202        if !dst_info.valid_pixels(dst_row_bytes, pixels) {
203            return false;
204        }
205
206        let src = src.into();
207
208        unsafe {
209            self.native().readPixels(
210                dst_info.native(),
211                pixels.as_mut_ptr() as _,
212                dst_row_bytes,
213                src.x,
214                src.y,
215            )
216        }
217    }
218
219    /// Access the underlying pixels as a byte array. This is a rust-skia specific function.
220    pub fn bytes(&self) -> Option<&'pixels [u8]> {
221        let addr = self.addr().into_non_null()?;
222        let len = self.compute_byte_size();
223        Some(unsafe { slice::from_raw_parts(addr.as_ptr() as *const _, len) })
224    }
225
226    pub fn bytes_mut(&mut self) -> Option<&'pixels mut [u8]> {
227        let addr = self.writable_addr().into_non_null()?;
228        let len = self.compute_byte_size();
229        Some(unsafe { slice::from_raw_parts_mut(addr.as_ptr() as *mut u8, len) })
230    }
231
232    /// Access the underlying pixels. This is a rust-skia specific function.
233    ///
234    /// The `Pixel` type must implement the _unsafe_ trait [`Pixel`] and must return `true` in
235    /// [`Pixel::matches_color_type()`] when matched against the [`ColorType`] of this Pixmap's
236    /// pixels.
237    pub fn pixels<P: Pixel>(&self) -> Option<&'pixels [P]> {
238        let addr = self.addr().into_non_null()?;
239
240        let info = self.info();
241        let ct = info.color_type();
242        let pixel_size = mem::size_of::<P>();
243
244        if info.bytes_per_pixel() == pixel_size && P::matches_color_type(ct) {
245            let len = self.compute_byte_size() / pixel_size;
246            return Some(unsafe { slice::from_raw_parts(addr.as_ptr() as *const _, len) });
247        }
248
249        None
250    }
251
252    pub fn read_pixels_to_pixmap(&self, dst: &mut Pixmap, src: impl Into<IPoint>) -> bool {
253        let Some(dst_bytes) = dst.bytes_mut() else {
254            return false;
255        };
256        self.read_pixels(dst.info(), dst_bytes, dst.row_bytes(), src)
257    }
258
259    pub fn scale_pixels(&self, dst: &mut Pixmap, sampling: impl Into<SamplingOptions>) -> bool {
260        let sampling = sampling.into();
261        unsafe { self.native().scalePixels(dst.native(), sampling.native()) }
262    }
263
264    pub fn erase(&mut self, color: impl Into<Color>, subset: Option<&IRect>) -> bool {
265        let color = color.into().into_native();
266        unsafe {
267            match subset {
268                Some(subset) => self.native().erase(color, subset.native()),
269                None => self.native().erase(color, self.bounds().native()),
270            }
271        }
272    }
273
274    pub fn erase_4f(&mut self, color: impl AsRef<Color4f>, subset: Option<&IRect>) -> bool {
275        let color = color.as_ref();
276        unsafe {
277            self.native()
278                .erase1(color.native(), subset.native_ptr_or_null())
279        }
280    }
281
282    fn from_native_c(pixmap: SkPixmap) -> Self {
283        Self {
284            inner: Handle::from_native_c(pixmap),
285            pd: PhantomData,
286        }
287    }
288
289    #[must_use]
290    pub(crate) fn from_native_ref(n: &SkPixmap) -> &Self {
291        unsafe { transmute_ref(n) }
292    }
293
294    #[must_use]
295    pub(crate) fn from_native_ptr(np: *const SkPixmap) -> *const Self {
296        // Should be safe as long `Pixmap` is represented with repr(Transparent).
297        np as _
298    }
299
300    pub(crate) fn native_mut(&mut self) -> &mut SkPixmap {
301        self.inner.native_mut()
302    }
303
304    pub(crate) fn native(&self) -> &SkPixmap {
305        self.inner.native()
306    }
307}
308
309/// Implement this trait to use a pixel type in [`Handle<Pixmap>::pixels()`].
310///
311/// # Safety
312///
313/// This trait is unsafe because external [`Pixel`] implementations may lie about their
314/// [`ColorType`] or fail to match the alignment of the pixels stored in [`Handle<Pixmap>`].
315pub unsafe trait Pixel: Copy {
316    /// `true` if the type matches the color type's format.
317    fn matches_color_type(ct: ColorType) -> bool;
318}
319
320unsafe impl Pixel for u8 {
321    fn matches_color_type(ct: ColorType) -> bool {
322        matches!(ct, ColorType::Alpha8 | ColorType::Gray8)
323    }
324}
325
326unsafe impl Pixel for [u8; 2] {
327    fn matches_color_type(ct: ColorType) -> bool {
328        matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm)
329    }
330}
331
332unsafe impl Pixel for (u8, u8) {
333    fn matches_color_type(ct: ColorType) -> bool {
334        matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm)
335    }
336}
337
338unsafe impl Pixel for [u8; 4] {
339    fn matches_color_type(ct: ColorType) -> bool {
340        matches!(
341            ct,
342            ColorType::RGBA8888 | ColorType::RGB888x | ColorType::BGRA8888
343        )
344    }
345}
346
347unsafe impl Pixel for (u8, u8, u8, u8) {
348    fn matches_color_type(ct: ColorType) -> bool {
349        matches!(
350            ct,
351            ColorType::RGBA8888 | ColorType::RGB888x | ColorType::BGRA8888
352        )
353    }
354}
355
356unsafe impl Pixel for [f32; 4] {
357    fn matches_color_type(ct: ColorType) -> bool {
358        matches!(ct, ColorType::RGBAF32)
359    }
360}
361
362unsafe impl Pixel for (f32, f32, f32, f32) {
363    fn matches_color_type(ct: ColorType) -> bool {
364        matches!(ct, ColorType::RGBAF32)
365    }
366}
367
368unsafe impl Pixel for u32 {
369    fn matches_color_type(ct: ColorType) -> bool {
370        ct == ColorType::N32
371    }
372}
373
374unsafe impl Pixel for Color {
375    fn matches_color_type(ct: ColorType) -> bool {
376        ct == ColorType::N32
377    }
378}
379
380unsafe impl Pixel for Color4f {
381    fn matches_color_type(ct: ColorType) -> bool {
382        ct == ColorType::RGBAF32
383    }
384}
385
386#[cfg(test)]
387mod tests {
388    use super::*;
389
390    #[test]
391    fn pixmap_mutably_borrows_pixels() {
392        let mut pixels = [0u8; 2 * 2 * 4];
393        let info = ImageInfo::new(
394            (2, 2),
395            ColorType::RGBA8888,
396            AlphaType::Premul,
397            ColorSpace::new_srgb(),
398        );
399        let mut pixmap = Pixmap::new(&info, &mut pixels, info.min_row_bytes()).unwrap();
400        // this must fail to compile:
401        // let _pixel = pixels[0];
402        // use `.bytes()`, or `bytes_mut()` instead.
403        pixmap.reset();
404    }
405
406    #[test]
407    fn addr_may_return_null_from_a_default_pixmap() {
408        let pixmap = Pixmap::default();
409        assert!(pixmap.addr().is_null());
410        assert!(pixmap.writable_addr().is_null());
411    }
412}