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(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
30impl 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
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 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>`].
310pub 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
315unsafe impl Pixel for u8 {
316 fn matches_color_type(ct: ColorType) -> bool {
317 matches!(ct, ColorType::Alpha8 | ColorType::Gray8)
318 }
319}
320
321unsafe impl Pixel for [u8; 2] {
322 fn matches_color_type(ct: ColorType) -> bool {
323 matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm)
324 }
325}
326
327unsafe impl Pixel for (u8, u8) {
328 fn matches_color_type(ct: ColorType) -> bool {
329 matches!(ct, ColorType::R8G8UNorm | ColorType::A16UNorm)
330 }
331}
332
333unsafe 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
342unsafe 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
351unsafe impl Pixel for [f32; 4] {
352 fn matches_color_type(ct: ColorType) -> bool {
353 matches!(ct, ColorType::RGBAF32)
354 }
355}
356
357unsafe impl Pixel for (f32, f32, f32, f32) {
358 fn matches_color_type(ct: ColorType) -> bool {
359 matches!(ct, ColorType::RGBAF32)
360 }
361}
362
363unsafe impl Pixel for u32 {
364 fn matches_color_type(ct: ColorType) -> bool {
365 ct == ColorType::N32
366 }
367}
368
369unsafe impl Pixel for Color {
370 fn matches_color_type(ct: ColorType) -> bool {
371 ct == ColorType::N32
372 }
373}
374
375unsafe impl Pixel for Color4f {
376 fn matches_color_type(ct: ColorType) -> bool {
377 ct == ColorType::RGBAF32
378 }
379}
380
381#[cfg(test)]
382mod 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