skia_safe/core/
image_info.rs

1use crate::{prelude::*, AlphaType, ColorSpace, ColorType, IPoint, IRect, ISize};
2use skia_bindings::{self as sb, SkColorInfo, SkImageInfo};
3use std::{fmt, mem};
4
5pub use skia_bindings::SkYUVColorSpace as YUVColorSpace;
6variant_name!(YUVColorSpace::JPEG);
7
8pub type ColorInfo = Handle<SkColorInfo>;
9unsafe_send_sync!(ColorInfo);
10
11impl NativeDrop for SkColorInfo {
12    fn drop(&mut self) {
13        unsafe { sb::C_SkColorInfo_destruct(self) }
14    }
15}
16
17impl NativeClone for SkColorInfo {
18    fn clone(&self) -> Self {
19        unsafe {
20            construct(|color_info| {
21                sb::C_SkColorInfo_Construct(color_info);
22                sb::C_SkColorInfo_Copy(self, color_info);
23            })
24        }
25    }
26}
27
28impl NativePartialEq for SkColorInfo {
29    fn eq(&self, rhs: &Self) -> bool {
30        unsafe { sb::C_SkColorInfo_Equals(self, rhs) }
31    }
32}
33
34impl Default for ColorInfo {
35    fn default() -> Self {
36        Self::construct(|color_info| unsafe { sb::C_SkColorInfo_Construct(color_info) })
37    }
38}
39
40impl fmt::Debug for ColorInfo {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        f.debug_struct("ColorInfo")
43            .field("color_space", &self.color_space())
44            .field("color_type", &self.color_type())
45            .field("alpha_type", &self.alpha_type())
46            .field("is_opaque", &self.is_opaque())
47            .field("is_gamma_close_to_srgb", &self.is_gamma_close_to_srgb())
48            .field("bytes_per_pixel", &self.bytes_per_pixel())
49            .field("shift_per_pixel", &self.shift_per_pixel())
50            .finish()
51    }
52}
53
54impl ColorInfo {
55    pub fn new(ct: ColorType, at: AlphaType, cs: impl Into<Option<ColorSpace>>) -> Self {
56        Self::construct(|color_info| unsafe {
57            sb::C_SkColorInfo_Construct2(
58                color_info,
59                ct.into_native(),
60                at,
61                cs.into().into_ptr_or_null(),
62            )
63        })
64    }
65
66    pub fn color_space(&self) -> Option<ColorSpace> {
67        ColorSpace::from_unshared_ptr(unsafe { self.native().colorSpace() })
68    }
69
70    pub fn color_type(&self) -> ColorType {
71        ColorType::from_native_c(self.native().fColorType)
72    }
73
74    pub fn alpha_type(&self) -> AlphaType {
75        self.native().fAlphaType
76    }
77
78    pub fn is_opaque(&self) -> bool {
79        self.alpha_type().is_opaque() || self.color_type().is_always_opaque()
80    }
81
82    pub fn is_gamma_close_to_srgb(&self) -> bool {
83        unsafe { self.native().gammaCloseToSRGB() }
84    }
85
86    #[must_use]
87    pub fn with_alpha_type(&self, new_alpha_type: AlphaType) -> Self {
88        Self::construct(|ci| unsafe {
89            sb::C_SkColorInfo_makeAlphaType(self.native(), new_alpha_type, ci)
90        })
91    }
92
93    #[must_use]
94    pub fn with_color_type(&self, new_color_type: ColorType) -> Self {
95        Self::construct(|ci| unsafe {
96            sb::C_SkColorInfo_makeColorType(self.native(), new_color_type.into_native(), ci)
97        })
98    }
99
100    #[must_use]
101    pub fn with_color_space(&self, cs: impl Into<Option<ColorSpace>>) -> Self {
102        let color_space: Option<ColorSpace> = cs.into();
103        Self::construct(|ci| unsafe {
104            sb::C_SkColorInfo_makeColorSpace(self.native(), color_space.into_ptr_or_null(), ci)
105        })
106    }
107
108    pub fn bytes_per_pixel(&self) -> usize {
109        unsafe { self.native().bytesPerPixel().try_into().unwrap() }
110    }
111
112    pub fn shift_per_pixel(&self) -> usize {
113        unsafe { self.native().shiftPerPixel().try_into().unwrap() }
114    }
115}
116
117pub type ImageInfo = Handle<SkImageInfo>;
118unsafe_send_sync!(ImageInfo);
119
120impl NativeDrop for SkImageInfo {
121    fn drop(&mut self) {
122        unsafe { sb::C_SkImageInfo_destruct(self) }
123    }
124}
125
126impl NativeClone for SkImageInfo {
127    fn clone(&self) -> Self {
128        unsafe {
129            construct(|image_info| {
130                sb::C_SkImageInfo_Construct(image_info);
131                sb::C_SkImageInfo_Copy(self, image_info);
132            })
133        }
134    }
135}
136
137impl NativePartialEq for SkImageInfo {
138    fn eq(&self, rhs: &Self) -> bool {
139        unsafe { sb::C_SkImageInfo_Equals(self, rhs) }
140    }
141}
142
143impl Default for Handle<SkImageInfo> {
144    fn default() -> Self {
145        Self::construct(|image_info| unsafe { sb::C_SkImageInfo_Construct(image_info) })
146    }
147}
148
149impl fmt::Debug for ImageInfo {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        f.debug_struct("ImageInfo")
152            .field("color_info", self.color_info())
153            .field("dimensions", &self.dimensions())
154            .finish()
155    }
156}
157
158impl ImageInfo {
159    pub fn new(
160        dimensions: impl Into<ISize>,
161        ct: ColorType,
162        at: AlphaType,
163        cs: impl Into<Option<ColorSpace>>,
164    ) -> Self {
165        let dimensions = dimensions.into();
166        ImageInfo::construct(|ii| unsafe {
167            sb::C_SkImageInfo_Make(
168                dimensions.width,
169                dimensions.height,
170                ct.into_native(),
171                at,
172                cs.into().into_ptr_or_null(),
173                ii,
174            )
175        })
176    }
177
178    pub fn from_color_info(dimensions: impl Into<ISize>, color_info: ColorInfo) -> Self {
179        // TODO: (perf) actually move of color_info.
180        Self::new(
181            dimensions,
182            color_info.color_type(),
183            color_info.alpha_type(),
184            color_info.color_space(),
185        )
186    }
187
188    pub fn new_n32(
189        dimensions: impl Into<ISize>,
190        at: AlphaType,
191        cs: impl Into<Option<ColorSpace>>,
192    ) -> ImageInfo {
193        let dimensions = dimensions.into();
194        Self::construct(|ii| unsafe {
195            sb::C_SkImageInfo_MakeN32(
196                dimensions.width,
197                dimensions.height,
198                at,
199                cs.into().into_ptr_or_null(),
200                ii,
201            )
202        })
203    }
204
205    pub fn new_s32(dimensions: impl Into<ISize>, at: AlphaType) -> ImageInfo {
206        let dimensions = dimensions.into();
207        Self::construct(|ii| unsafe {
208            sb::C_SkImageInfo_MakeS32(dimensions.width, dimensions.height, at, ii)
209        })
210    }
211
212    pub fn new_n32_premul(
213        dimensions: impl Into<ISize>,
214        cs: impl Into<Option<ColorSpace>>,
215    ) -> ImageInfo {
216        let dimensions = dimensions.into();
217        Self::construct(|ii| unsafe {
218            sb::C_SkImageInfo_MakeN32Premul(
219                dimensions.width,
220                dimensions.height,
221                cs.into().into_ptr_or_null(),
222                ii,
223            )
224        })
225    }
226
227    pub fn new_a8(dimensions: impl Into<ISize>) -> ImageInfo {
228        let dimensions = dimensions.into();
229        Self::construct(|ii| unsafe {
230            sb::C_SkImageInfo_MakeA8(dimensions.width, dimensions.height, ii)
231        })
232    }
233
234    pub fn new_unknown(dimensions: Option<ISize>) -> ImageInfo {
235        let dimensions = dimensions.unwrap_or_default();
236        Self::construct(|ii| unsafe {
237            sb::C_SkImageInfo_MakeUnknown(dimensions.width, dimensions.height, ii)
238        })
239    }
240
241    pub fn width(&self) -> i32 {
242        self.dimensions().width
243    }
244
245    pub fn height(&self) -> i32 {
246        self.dimensions().height
247    }
248
249    pub fn color_type(&self) -> ColorType {
250        self.color_info().color_type()
251    }
252
253    pub fn alpha_type(&self) -> AlphaType {
254        self.color_info().alpha_type()
255    }
256
257    pub fn color_space(&self) -> Option<ColorSpace> {
258        ColorSpace::from_unshared_ptr(unsafe { self.native().colorSpace() })
259    }
260
261    pub fn is_empty(&self) -> bool {
262        self.dimensions().is_empty()
263    }
264
265    pub fn color_info(&self) -> &ColorInfo {
266        Handle::from_native_ref(&self.native().fColorInfo)
267    }
268
269    pub fn is_opaque(&self) -> bool {
270        self.color_info().is_opaque()
271    }
272
273    pub fn dimensions(&self) -> ISize {
274        ISize::from_native_c(self.native().fDimensions)
275    }
276
277    pub fn bounds(&self) -> IRect {
278        IRect::from_size(self.dimensions())
279    }
280
281    pub fn is_gamma_close_to_srgb(&self) -> bool {
282        self.color_info().is_gamma_close_to_srgb()
283    }
284
285    #[must_use]
286    pub fn with_dimensions(&self, new_dimensions: impl Into<ISize>) -> Self {
287        Self::from_color_info(new_dimensions, self.color_info().clone())
288    }
289
290    #[must_use]
291    pub fn with_alpha_type(&self, new_alpha_type: AlphaType) -> Self {
292        Self::from_color_info(
293            self.dimensions(),
294            self.color_info().with_alpha_type(new_alpha_type),
295        )
296    }
297
298    #[must_use]
299    pub fn with_color_type(&self, new_color_type: ColorType) -> Self {
300        Self::from_color_info(
301            self.dimensions(),
302            self.color_info().with_color_type(new_color_type),
303        )
304    }
305
306    #[must_use]
307    pub fn with_color_space(&self, new_color_space: impl Into<Option<ColorSpace>>) -> Self {
308        Self::construct(|ii| unsafe {
309            sb::C_SkImageInfo_makeColorSpace(
310                self.native(),
311                new_color_space.into().into_ptr_or_null(),
312                ii,
313            )
314        })
315    }
316
317    pub fn bytes_per_pixel(&self) -> usize {
318        self.color_info().bytes_per_pixel()
319    }
320
321    pub fn shift_per_pixel(&self) -> usize {
322        self.color_info().shift_per_pixel()
323    }
324
325    pub fn min_row_bytes(&self) -> usize {
326        usize::try_from(self.width()).unwrap() * self.bytes_per_pixel()
327    }
328
329    pub fn compute_offset(&self, point: impl Into<IPoint>, row_bytes: usize) -> usize {
330        let point = point.into();
331        unsafe { self.native().computeOffset(point.x, point.y, row_bytes) }
332    }
333
334    pub fn compute_byte_size(&self, row_bytes: usize) -> usize {
335        unsafe { self.native().computeByteSize(row_bytes) }
336    }
337
338    pub fn compute_min_byte_size(&self) -> usize {
339        self.compute_byte_size(self.min_row_bytes())
340    }
341
342    pub fn valid_row_bytes(&self, row_bytes: usize) -> bool {
343        if row_bytes < self.min_row_bytes() {
344            return false;
345        }
346        let shift = self.shift_per_pixel();
347        let aligned_row_bytes = row_bytes >> shift << shift;
348        aligned_row_bytes == row_bytes
349    }
350
351    pub fn reset(&mut self) -> &mut Self {
352        unsafe { sb::C_SkImageInfo_reset(self.native_mut()) };
353        self
354    }
355
356    /// Returns `true` if the `row_bytes` are valid for [ImageInfo] _and_ an image would fit into
357    /// `pixels`.
358    pub(crate) fn valid_pixels<P>(&self, row_bytes: usize, pixels: &[P]) -> bool {
359        self.valid_row_bytes(row_bytes)
360            && mem::size_of_val(pixels) >= self.compute_byte_size(row_bytes)
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use crate::prelude::*;
367    use crate::{AlphaType, ColorSpace, ImageInfo};
368    use std::mem;
369
370    #[test]
371    fn ref_cnt_in_relation_to_color_space() {
372        let cs = ColorSpace::new_srgb();
373        let before = cs.native().ref_cnt();
374        {
375            let ii = ImageInfo::new_n32((10, 10), AlphaType::Premul, Some(cs.clone()));
376            // one for the capture in image info
377            assert_eq!(before + 1, cs.native().ref_cnt());
378            let cs2 = ii.color_space();
379            // and one for the returned one.
380            assert_eq!(before + 2, cs.native().ref_cnt());
381            drop(cs2);
382        }
383        assert_eq!(before, cs.native().ref_cnt())
384    }
385
386    #[test]
387    fn size_of_val_actually_counts_slices_bytes() {
388        let x: [u16; 4] = Default::default();
389        assert_eq!(mem::size_of_val(&x), 8);
390    }
391}