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: *mut SkColorInfo| {
21 sb::C_SkColorInfo_Construct(uninitialized:color_info);
22 sb::C_SkColorInfo_Copy(self, to: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: *mut SkColorInfo| unsafe { sb::C_SkColorInfo_Construct(uninitialized:color_info) })
37 }
38}
39
40impl fmt::Debug for ColorInfo {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 f&mut DebugStruct<'_, '_>.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(name:"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: *mut SkImageInfo| {
130 sb::C_SkImageInfo_Construct(uninitialized:image_info);
131 sb::C_SkImageInfo_Copy(self, to: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: *mut SkImageInfo| unsafe { sb::C_SkImageInfo_Construct(uninitialized:image_info) })
146 }
147}
148
149impl fmt::Debug for ImageInfo {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 f&mut DebugStruct<'_, '_>.debug_struct("ImageInfo")
152 .field("color_info", self.color_info())
153 .field(name:"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)]
365
366mod tests {
367 use crate::prelude::*;
368 use crate::{AlphaType, ColorSpace, ImageInfo};
369 use std::mem;
370
371 #[test]
372 fn ref_cnt_in_relation_to_color_space() {
373 let cs = ColorSpace::new_srgb();
374 let before = cs.native().ref_cnt();
375 {
376 let ii = ImageInfo::new_n32((10, 10), AlphaType::Premul, Some(cs.clone()));
377 // one for the capture in image info
378 assert_eq!(before + 1, cs.native().ref_cnt());
379 let cs2 = ii.color_space();
380 // and one for the returned one.
381 assert_eq!(before + 2, cs.native().ref_cnt());
382 drop(cs2);
383 }
384 assert_eq!(before, cs.native().ref_cnt())
385 }
386
387 #[test]
388 fn size_of_val_actually_counts_slices_bytes() {
389 let x: [u16; 4] = Default::default();
390 assert_eq!(mem::size_of_val(&x), 8);
391 }
392}
393