Skip to main content

core_graphics2/
image.rs

1use std::ptr::{null, null_mut};
2
3use bitflags::bitflags;
4use cfg_if::cfg_if;
5use core_foundation::{
6    base::{CFTypeID, TCFType},
7    impl_CFTypeDescription, impl_TCFType,
8    string::{CFString, CFStringRef},
9};
10use libc::{c_void, size_t};
11#[cfg(feature = "objc")]
12use objc2::encode::{Encoding, RefEncode};
13
14use crate::{
15    base::CGFloat,
16    color_space::{CGColorRenderingIntent, CGColorSpace, CGColorSpaceRef},
17    data_provider::{CGDataProvider, CGDataProviderRef},
18    geometry::CGRect,
19};
20
21#[repr(C)]
22pub struct __CGImage(c_void);
23
24pub type CGImageRef = *mut __CGImage;
25
26pub const kCGImageAlphaNone: u32 = 0;
27pub const kCGImageAlphaPremultipliedLast: u32 = 1;
28pub const kCGImageAlphaPremultipliedFirst: u32 = 2;
29pub const kCGImageAlphaLast: u32 = 3;
30pub const kCGImageAlphaFirst: u32 = 4;
31pub const kCGImageAlphaNoneSkipLast: u32 = 5;
32pub const kCGImageAlphaNoneSkipFirst: u32 = 6;
33pub const kCGImageAlphaOnly: u32 = 7;
34
35pub const kCGImageByteOrderMask: u32 = 0x7000;
36pub const kCGImageByteOrderDefault: u32 = 0 << 12;
37pub const kCGImageByteOrder16Little: u32 = 1 << 12;
38pub const kCGImageByteOrder32Little: u32 = 2 << 12;
39pub const kCGImageByteOrder16Big: u32 = 3 << 12;
40pub const kCGImageByteOrder32Big: u32 = 4 << 12;
41
42pub const kCGImagePixelFormatMask: u32 = 0xF000;
43pub const kCGImagePixelFormatPacked: u32 = 0 << 16;
44pub const kCGImagePixelFormatRGB555: u32 = 1 << 16;
45pub const kCGImagePixelFormatRGB565: u32 = 2 << 16;
46pub const kCGImagePixelFormatRGB101010: u32 = 3 << 16;
47pub const kCGImagePixelFormatRGBCIF10: u32 = 4 << 16;
48
49cfg_if! {
50    if #[cfg(target_endian = "big")] {
51        pub const kCGImageByteOrder16Host: u32 = kCGImageByteOrder16Big;
52        pub const kCGImageByteOrder32Host: u32 = kCGImageByteOrder32Big;
53    } else {
54        pub const kCGImageByteOrder16Host: u32 = kCGImageByteOrder16Little;
55        pub const kCGImageByteOrder32Host: u32 = kCGImageByteOrder32Little;
56    }
57}
58
59extern "C" {
60    pub fn CGImageGetTypeID() -> CFTypeID;
61    pub fn CGImageCreate(
62        width: size_t,
63        height: size_t,
64        bitsPerComponent: size_t,
65        bitsPerPixel: size_t,
66        bytesPerRow: size_t,
67        space: CGColorSpaceRef,
68        bitmapInfo: u32,
69        provider: CGDataProviderRef,
70        decode: *const CGFloat,
71        shouldInterpolate: bool,
72        intent: CGColorRenderingIntent,
73    ) -> CGImageRef;
74    pub fn CGImageMaskCreate(
75        width: size_t,
76        height: size_t,
77        bitsPerComponent: size_t,
78        bitsPerPixel: size_t,
79        bytesPerRow: size_t,
80        provider: CGDataProviderRef,
81        decode: *const CGFloat,
82        shouldInterpolate: bool,
83    ) -> CGImageRef;
84    pub fn CGImageCreateCopy(image: CGImageRef) -> CGImageRef;
85    pub fn CGImageCreateWithJPEGDataProvider(
86        provider: CGDataProviderRef,
87        decode: *const CGFloat,
88        shouldInterpolate: bool,
89        intent: CGColorRenderingIntent,
90    ) -> CGImageRef;
91    pub fn CGImageCreateWithPNGDataProvider(
92        provider: CGDataProviderRef,
93        decode: *const CGFloat,
94        shouldInterpolate: bool,
95        intent: CGColorRenderingIntent,
96    ) -> CGImageRef;
97    pub fn CGImageCreateWithImageInRect(image: CGImageRef, rect: CGRect) -> CGImageRef;
98    pub fn CGImageCreateWithMask(image: CGImageRef, mask: CGImageRef) -> CGImageRef;
99    pub fn CGImageCreateWithMaskingColors(image: CGImageRef, components: *const CGFloat) -> CGImageRef;
100    pub fn CGImageCreateCopyWithColorSpace(image: CGImageRef, space: CGColorSpaceRef) -> CGImageRef;
101    pub fn CGImageRetain(image: CGImageRef) -> CGImageRef;
102    pub fn CGImageRelease(image: CGImageRef);
103    pub fn CGImageIsMask(image: CGImageRef) -> bool;
104    pub fn CGImageGetWidth(image: CGImageRef) -> size_t;
105    pub fn CGImageGetHeight(image: CGImageRef) -> size_t;
106    pub fn CGImageGetBitsPerComponent(image: CGImageRef) -> size_t;
107    pub fn CGImageGetBitsPerPixel(image: CGImageRef) -> size_t;
108    pub fn CGImageGetBytesPerRow(image: CGImageRef) -> size_t;
109    pub fn CGImageGetColorSpace(image: CGImageRef) -> CGColorSpaceRef;
110    pub fn CGImageGetAlphaInfo(image: CGImageRef) -> CGImageAlphaInfo;
111    pub fn CGImageGetDataProvider(image: CGImageRef) -> CGDataProviderRef;
112    pub fn CGImageGetDecode(image: CGImageRef) -> *const CGFloat;
113    pub fn CGImageGetShouldInterpolate(image: CGImageRef) -> bool;
114    pub fn CGImageGetRenderingIntent(image: CGImageRef) -> CGColorRenderingIntent;
115    pub fn CGImageGetBitmapInfo(image: CGImageRef) -> CGBitmapInfo;
116    pub fn CGImageGetByteOrderInfo(image: CGImageRef) -> CGImageByteOrderInfo;
117    pub fn CGImageGetPixelFormatInfo(image: CGImageRef) -> CGImagePixelFormatInfo;
118    pub fn CGImageGetUTType(image: CGImageRef) -> CFStringRef;
119}
120
121#[repr(u32)]
122#[derive(Clone, Copy, Debug, Eq, PartialEq)]
123pub enum CGImageAlphaInfo {
124    #[doc(alias = "kCGImageAlphaNone")]
125    AlphaNone               = kCGImageAlphaNone,
126    #[doc(alias = "kCGImageAlphaPremultipliedLast")]
127    AlphaPremultipliedLast  = kCGImageAlphaPremultipliedLast,
128    #[doc(alias = "kCGImageAlphaPremultipliedFirst")]
129    AlphaPremultipliedFirst = kCGImageAlphaPremultipliedFirst,
130    #[doc(alias = "kCGImageAlphaLast")]
131    AlphaLast               = kCGImageAlphaLast,
132    #[doc(alias = "kCGImageAlphaFirst")]
133    AlphaFirst              = kCGImageAlphaFirst,
134    #[doc(alias = "kCGImageAlphaNoneSkipLast")]
135    AlphaNoneSkipLast       = kCGImageAlphaNoneSkipLast,
136    #[doc(alias = "kCGImageAlphaNoneSkipFirst")]
137    AlphaNoneSkipFirst      = kCGImageAlphaNoneSkipFirst,
138    #[doc(alias = "kCGImageAlphaOnly")]
139    AlphaOnly               = kCGImageAlphaOnly,
140}
141
142#[repr(u32)]
143#[derive(Clone, Copy, Debug, Eq, PartialEq)]
144pub enum CGImageByteOrderInfo {
145    #[doc(alias = "kCGImageByteOrderMask")]
146    ByteOrderMask     = kCGImageByteOrderMask,
147    #[doc(alias = "kCGImageByteOrderDefault")]
148    ByteOrderDefault  = kCGImageByteOrderDefault,
149    #[doc(alias = "kCGImageByteOrder16Little")]
150    ByteOrder16Little = kCGImageByteOrder16Little,
151    #[doc(alias = "kCGImageByteOrder32Little")]
152    ByteOrder32Little = kCGImageByteOrder32Little,
153    #[doc(alias = "kCGImageByteOrder16Big")]
154    ByteOrder16Big    = kCGImageByteOrder16Big,
155    #[doc(alias = "kCGImageByteOrder32Big")]
156    ByteOrder32Big    = kCGImageByteOrder32Big,
157}
158
159#[repr(u32)]
160#[derive(Clone, Copy, Debug, Eq, PartialEq)]
161pub enum CGImagePixelFormatInfo {
162    #[doc(alias = "kCGImagePixelFormatMask")]
163    PixelFormatMask      = kCGImagePixelFormatMask,
164    #[doc(alias = "kCGImagePixelFormatPacked")]
165    PixelFormatPacked    = kCGImagePixelFormatPacked,
166    #[doc(alias = "kCGImagePixelFormatRGB")]
167    PixelFormatRGB555    = kCGImagePixelFormatRGB555,
168    #[doc(alias = "kCGImagePixelFormatRGB")]
169    PixelFormatRGB565    = kCGImagePixelFormatRGB565,
170    #[doc(alias = "kCGImagePixelFormatRGB")]
171    PixelFormatRGB101010 = kCGImagePixelFormatRGB101010,
172    #[doc(alias = "kCGImagePixelFormatRGB")]
173    PixelFormatRGBCIF10  = kCGImagePixelFormatRGBCIF10,
174}
175
176bitflags! {
177    #[repr(C)]
178    #[derive(Clone, Copy, Debug, Default, PartialEq)]
179    pub struct CGBitmapInfo: u32 {
180        #[doc(alias = "kCGBitmapAlphaInfoMask")]
181        const AlphaInfoMask = 0x1F;
182        const AlphaNone = kCGImageAlphaNone;
183        const AlphaPremultipliedLast = kCGImageAlphaPremultipliedLast;
184        const AlphaPremultipliedFirst = kCGImageAlphaPremultipliedFirst;
185        const AlphaLast = kCGImageAlphaLast;
186        const AlphaFirst = kCGImageAlphaFirst;
187        const AlphaNoneSkipLast = kCGImageAlphaNoneSkipLast;
188        const AlphaNoneSkipFirst = kCGImageAlphaNoneSkipFirst;
189        const AlphaOnly = kCGImageAlphaOnly;
190        #[doc(alias = "kCGBitmapFloatInfoMask")]
191        const FloatInfoMask = 0xF00;
192        #[doc(alias = "kCGBitmapFloatComponents")]
193        const FloatComponents = 1 << 8;
194        #[doc(alias = "kCGBitmapByteOrderMask")]
195        const ByteOrderMask = kCGImageByteOrderMask;
196        #[doc(alias = "kCGBitmapByteOrderDefault")]
197        const ByteOrderDefault = kCGImageByteOrderDefault;
198        #[doc(alias = "kCGBitmapByteOrder16Little")]
199        const ByteOrder16Little = kCGImageByteOrder16Little;
200        #[doc(alias = "kCGBitmapByteOrder32Little")]
201        const ByteOrder32Little = kCGImageByteOrder32Little;
202        #[doc(alias = "kCGBitmapByteOrder16Big")]
203        const ByteOrder16Big = kCGImageByteOrder16Big;
204        #[doc(alias = "kCGBitmapByteOrder32Big")]
205        const ByteOrder32Big = kCGImageByteOrder32Big;
206        #[doc(alias = "kCGBitmapByteOrder16Host")]
207        const ByteOrder16Host = kCGImageByteOrder16Host;
208        #[doc(alias = "kCGBitmapByteOrder32Host")]
209        const ByteOrder32Host = kCGImageByteOrder32Host;
210        const PixelFormatMask = kCGImagePixelFormatMask;
211        const PixelFormatPacked = kCGImagePixelFormatPacked;
212        const PixelFormatRGB555 = kCGImagePixelFormatRGB555;
213        const PixelFormatRGB565 = kCGImagePixelFormatRGB565;
214        const PixelFormatRGB101010 = kCGImagePixelFormatRGB101010;
215        const PixelFormatRGBCIF10 = kCGImagePixelFormatRGBCIF10;
216    }
217}
218
219pub struct CGImage(CGImageRef);
220
221impl Drop for CGImage {
222    fn drop(&mut self) {
223        unsafe { CGImageRelease(self.0) }
224    }
225}
226
227impl_TCFType!(CGImage, CGImageRef, CGImageGetTypeID);
228impl_CFTypeDescription!(CGImage);
229
230impl CGImage {
231    pub fn new(
232        width: size_t,
233        height: size_t,
234        bits_per_component: size_t,
235        bits_per_pixel: size_t,
236        bytes_per_row: size_t,
237        space: Option<&CGColorSpace>,
238        bitmap_info: u32,
239        provider: Option<&CGDataProvider>,
240        decode: Option<&[CGFloat]>,
241        should_interpolate: bool,
242        intent: CGColorRenderingIntent,
243    ) -> Option<Self> {
244        unsafe {
245            let image = CGImageCreate(
246                width,
247                height,
248                bits_per_component,
249                bits_per_pixel,
250                bytes_per_row,
251                space.map_or(null_mut(), |s| s.as_concrete_TypeRef()),
252                bitmap_info,
253                provider.map_or(null(), |p| p.as_concrete_TypeRef()),
254                decode.map_or(null(), |d| d.as_ptr()),
255                should_interpolate,
256                intent,
257            );
258            if image.is_null() {
259                None
260            } else {
261                Some(TCFType::wrap_under_create_rule(image))
262            }
263        }
264    }
265
266    pub fn new_mask(
267        width: size_t,
268        height: size_t,
269        bits_per_component: size_t,
270        bits_per_pixel: size_t,
271        bytes_per_row: size_t,
272        provider: Option<&CGDataProvider>,
273        decode: Option<&[CGFloat]>,
274        should_interpolate: bool,
275    ) -> Option<Self> {
276        unsafe {
277            let image = CGImageMaskCreate(
278                width,
279                height,
280                bits_per_component,
281                bits_per_pixel,
282                bytes_per_row,
283                provider.map_or(null(), |p| p.as_concrete_TypeRef()),
284                decode.map_or(null(), |d| d.as_ptr()),
285                should_interpolate,
286            );
287            if image.is_null() {
288                None
289            } else {
290                Some(TCFType::wrap_under_create_rule(image))
291            }
292        }
293    }
294
295    pub fn from_jpeg_data_provider(
296        provider: &CGDataProvider,
297        decode: Option<&[CGFloat]>,
298        should_interpolate: bool,
299        intent: CGColorRenderingIntent,
300    ) -> Option<Self> {
301        unsafe {
302            let image =
303                CGImageCreateWithJPEGDataProvider(provider.as_concrete_TypeRef(), decode.map_or(null(), |d| d.as_ptr()), should_interpolate, intent);
304            if image.is_null() {
305                None
306            } else {
307                Some(TCFType::wrap_under_create_rule(image))
308            }
309        }
310    }
311
312    pub fn from_png_data_provider(
313        provider: &CGDataProvider,
314        decode: Option<&[CGFloat]>,
315        should_interpolate: bool,
316        intent: CGColorRenderingIntent,
317    ) -> Option<Self> {
318        unsafe {
319            let image =
320                CGImageCreateWithPNGDataProvider(provider.as_concrete_TypeRef(), decode.map_or(null(), |d| d.as_ptr()), should_interpolate, intent);
321            if image.is_null() {
322                None
323            } else {
324                Some(TCFType::wrap_under_create_rule(image))
325            }
326        }
327    }
328
329    pub fn new_copy(&self) -> Option<Self> {
330        unsafe {
331            let image = CGImageCreateCopy(self.as_concrete_TypeRef());
332            if image.is_null() {
333                None
334            } else {
335                Some(TCFType::wrap_under_create_rule(image))
336            }
337        }
338    }
339
340    pub fn new_copy_with_color_space(&self, space: &CGColorSpace) -> Option<Self> {
341        unsafe {
342            let image = CGImageCreateCopyWithColorSpace(self.as_concrete_TypeRef(), space.as_concrete_TypeRef());
343            if image.is_null() {
344                None
345            } else {
346                Some(TCFType::wrap_under_create_rule(image))
347            }
348        }
349    }
350
351    pub fn new_with_mask(&self, mask: &CGImage) -> Option<Self> {
352        unsafe {
353            let image = CGImageCreateWithMask(self.as_concrete_TypeRef(), mask.as_concrete_TypeRef());
354            if image.is_null() {
355                None
356            } else {
357                Some(TCFType::wrap_under_create_rule(image))
358            }
359        }
360    }
361
362    pub fn new_with_masking_colors(&self, components: &[CGFloat]) -> Option<Self> {
363        unsafe {
364            let color_space = self.color_space()?;
365            let num_components = color_space.number_of_components();
366            if components.len() != num_components * 2 {
367                return None;
368            }
369            let image = CGImageCreateWithMaskingColors(self.as_concrete_TypeRef(), components.as_ptr());
370            if image.is_null() {
371                None
372            } else {
373                Some(TCFType::wrap_under_create_rule(image))
374            }
375        }
376    }
377
378    pub fn cropped(&self, rect: CGRect) -> Option<Self> {
379        unsafe {
380            let image = CGImageCreateWithImageInRect(self.as_concrete_TypeRef(), rect);
381            if image.is_null() {
382                None
383            } else {
384                Some(TCFType::wrap_under_create_rule(image))
385            }
386        }
387    }
388
389    pub fn is_mask(&self) -> bool {
390        unsafe { CGImageIsMask(self.as_concrete_TypeRef()) }
391    }
392
393    pub fn width(&self) -> size_t {
394        unsafe { CGImageGetWidth(self.as_concrete_TypeRef()) }
395    }
396
397    pub fn height(&self) -> size_t {
398        unsafe { CGImageGetHeight(self.as_concrete_TypeRef()) }
399    }
400
401    pub fn bits_per_component(&self) -> size_t {
402        unsafe { CGImageGetBitsPerComponent(self.as_concrete_TypeRef()) }
403    }
404
405    pub fn bits_per_pixel(&self) -> size_t {
406        unsafe { CGImageGetBitsPerPixel(self.as_concrete_TypeRef()) }
407    }
408
409    pub fn bytes_per_row(&self) -> size_t {
410        unsafe { CGImageGetBytesPerRow(self.as_concrete_TypeRef()) }
411    }
412
413    pub fn color_space(&self) -> Option<CGColorSpace> {
414        unsafe {
415            let space = CGImageGetColorSpace(self.as_concrete_TypeRef());
416            if space.is_null() {
417                None
418            } else {
419                Some(TCFType::wrap_under_get_rule(space))
420            }
421        }
422    }
423
424    pub fn alpha_info(&self) -> CGImageAlphaInfo {
425        unsafe { CGImageGetAlphaInfo(self.as_concrete_TypeRef()) }
426    }
427
428    pub fn data_provider(&self) -> Option<CGDataProvider> {
429        unsafe {
430            let provider = CGImageGetDataProvider(self.as_concrete_TypeRef());
431            if provider.is_null() {
432                None
433            } else {
434                Some(TCFType::wrap_under_get_rule(provider))
435            }
436        }
437    }
438
439    pub fn should_interpolate(&self) -> bool {
440        unsafe { CGImageGetShouldInterpolate(self.as_concrete_TypeRef()) }
441    }
442
443    pub fn rendering_intent(&self) -> CGColorRenderingIntent {
444        unsafe { CGImageGetRenderingIntent(self.as_concrete_TypeRef()) }
445    }
446
447    pub fn bitmap_info(&self) -> CGBitmapInfo {
448        unsafe { CGImageGetBitmapInfo(self.as_concrete_TypeRef()) }
449    }
450
451    pub fn byte_order_info(&self) -> CGImageByteOrderInfo {
452        unsafe { CGImageGetByteOrderInfo(self.as_concrete_TypeRef()) }
453    }
454
455    pub fn pixel_format_info(&self) -> CGImagePixelFormatInfo {
456        unsafe { CGImageGetPixelFormatInfo(self.as_concrete_TypeRef()) }
457    }
458
459    pub fn ut_type(&self) -> Option<CFString> {
460        unsafe {
461            let ut_type = CGImageGetUTType(self.as_concrete_TypeRef());
462            if ut_type.is_null() {
463                None
464            } else {
465                Some(TCFType::wrap_under_get_rule(ut_type))
466            }
467        }
468    }
469}
470
471#[cfg(feature = "objc")]
472unsafe impl RefEncode for __CGImage {
473    const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct("CGImage", &[]));
474}