1use core::ffi::c_void;
4use core::ptr;
5
6use crate::CFError;
7
8use super::drawing::{CGColorSpace, CGImage};
9use super::ffi;
10use super::CGRect;
11
12const BITS_PER_COMPONENT_8: usize = 8;
13const CG_IMAGE_ALPHA_NONE: u32 = 0;
14const CG_IMAGE_ALPHA_PREMULTIPLIED_LAST: u32 = 1;
15const CG_BITMAP_BYTE_ORDER_32_BIG: u32 = 16_384;
16const RGBA8_BITMAP_INFO: u32 = CG_IMAGE_ALPHA_PREMULTIPLIED_LAST | CG_BITMAP_BYTE_ORDER_32_BIG;
17const GRAYSCALE8_BITMAP_INFO: u32 = CG_IMAGE_ALPHA_NONE;
18
19#[derive(Debug)]
24pub struct CGContext {
25 ptr: *mut c_void,
26}
27
28crate::utils::retained::cf_retained!(
29 CGContext,
30 field = ptr,
31 retain = ffi::CGContextRetain,
32 release = ffi::CGContextRelease,
33 drop = null_out,
34);
35
36impl CGContext {
37 #[must_use]
44 pub const unsafe fn from_raw(ptr: *mut c_void) -> Self {
45 Self { ptr }
46 }
47
48 fn new_bitmap(
49 width: usize,
50 height: usize,
51 color_space: &CGColorSpace,
52 bitmap_info: u32,
53 ) -> Result<Self, CFError> {
54 let context = unsafe {
55 ffi::CGBitmapContextCreate(
56 ptr::null_mut(),
57 width,
58 height,
59 BITS_PER_COMPONENT_8,
60 0,
61 color_space.as_ptr(),
62 bitmap_info,
63 )
64 };
65
66 if context.is_null() {
67 Err(CFError::new("CGBitmapContextCreate"))
68 } else {
69 let context = Self { ptr: context };
70 let data = context.data();
71 let len = context.buffer_len();
72 if !data.is_null() && len != 0 {
73 unsafe { data.write_bytes(0, len) };
74 }
75 Ok(context)
76 }
77 }
78
79 pub fn new_rgba8(width: usize, height: usize) -> Result<Self, CFError> {
85 let color_space = CGColorSpace::device_rgb();
86 Self::new_bitmap(width, height, &color_space, RGBA8_BITMAP_INFO)
87 }
88
89 pub fn new_grayscale(width: usize, height: usize) -> Result<Self, CFError> {
95 let color_space = CGColorSpace::device_gray();
96 Self::new_bitmap(width, height, &color_space, GRAYSCALE8_BITMAP_INFO)
97 }
98
99 fn buffer_len(&self) -> usize {
100 self.height().checked_mul(self.bytes_per_row()).unwrap_or(0)
101 }
102
103 #[must_use]
105 pub fn width(&self) -> usize {
106 unsafe { ffi::CGBitmapContextGetWidth(self.ptr) }
107 }
108
109 #[must_use]
111 pub fn height(&self) -> usize {
112 unsafe { ffi::CGBitmapContextGetHeight(self.ptr) }
113 }
114
115 #[must_use]
117 pub fn bytes_per_row(&self) -> usize {
118 unsafe { ffi::CGBitmapContextGetBytesPerRow(self.ptr) }
119 }
120
121 #[must_use]
123 pub fn bits_per_component(&self) -> usize {
124 unsafe { ffi::CGBitmapContextGetBitsPerComponent(self.ptr) }
125 }
126
127 #[must_use]
129 pub fn bits_per_pixel(&self) -> usize {
130 unsafe { ffi::CGBitmapContextGetBitsPerPixel(self.ptr) }
131 }
132
133 #[must_use]
138 pub fn data(&self) -> *mut u8 {
139 unsafe { ffi::CGBitmapContextGetData(self.ptr).cast::<u8>() }
140 }
141
142 #[must_use]
144 pub fn color_space(&self) -> Option<CGColorSpace> {
145 unsafe {
146 let color_space = ffi::CGBitmapContextGetColorSpace(self.ptr);
147 if color_space.is_null() {
148 None
149 } else {
150 Some(CGColorSpace::from_raw(ffi::CGColorSpaceRetain(color_space)))
151 }
152 }
153 }
154
155 #[must_use]
157 pub fn alpha_info(&self) -> u32 {
158 unsafe { ffi::CGBitmapContextGetAlphaInfo(self.ptr) }
159 }
160
161 #[must_use]
170 pub unsafe fn as_bytes(&self) -> &[u8] {
171 let data = self.data();
172 let len = self.buffer_len();
173 if data.is_null() || len == 0 || isize::try_from(len).is_err() {
174 &[]
175 } else {
176 unsafe { std::slice::from_raw_parts(data.cast_const(), len) }
177 }
178 }
179
180 #[must_use]
189 pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] {
190 let data = self.data();
191 let len = self.buffer_len();
192 if data.is_null() || len == 0 || isize::try_from(len).is_err() {
193 &mut []
194 } else {
195 unsafe { std::slice::from_raw_parts_mut(data, len) }
196 }
197 }
198
199 pub fn set_rgb_fill_color(&self, r: f64, g: f64, b: f64, a: f64) {
201 unsafe { ffi::CGContextSetRGBFillColor(self.ptr, r, g, b, a) };
202 }
203
204 pub fn set_rgb_stroke_color(&self, r: f64, g: f64, b: f64, a: f64) {
206 unsafe { ffi::CGContextSetRGBStrokeColor(self.ptr, r, g, b, a) };
207 }
208
209 pub fn set_line_width(&self, w: f64) {
211 unsafe { ffi::CGContextSetLineWidth(self.ptr, w) };
212 }
213
214 pub fn clear_rect(&self, x: f64, y: f64, w: f64, h: f64) {
216 unsafe { ffi::CGContextClearRect(self.ptr, CGRect::new(x, y, w, h)) };
217 }
218
219 pub fn fill_rect(&self, x: f64, y: f64, w: f64, h: f64) {
221 unsafe { ffi::CGContextFillRect(self.ptr, CGRect::new(x, y, w, h)) };
222 }
223
224 pub fn stroke_rect(&self, x: f64, y: f64, w: f64, h: f64) {
226 unsafe { ffi::CGContextStrokeRect(self.ptr, CGRect::new(x, y, w, h)) };
227 }
228
229 pub fn begin_path(&self) {
231 unsafe { ffi::CGContextBeginPath(self.ptr) };
232 }
233
234 pub fn close_path(&self) {
236 unsafe { ffi::CGContextClosePath(self.ptr) };
237 }
238
239 pub fn move_to(&self, x: f64, y: f64) {
241 unsafe { ffi::CGContextMoveToPoint(self.ptr, x, y) };
242 }
243
244 pub fn add_line_to(&self, x: f64, y: f64) {
246 unsafe { ffi::CGContextAddLineToPoint(self.ptr, x, y) };
247 }
248
249 pub fn add_rect(&self, x: f64, y: f64, w: f64, h: f64) {
251 unsafe { ffi::CGContextAddRect(self.ptr, CGRect::new(x, y, w, h)) };
252 }
253
254 pub fn add_ellipse_in_rect(&self, x: f64, y: f64, w: f64, h: f64) {
256 unsafe { ffi::CGContextAddEllipseInRect(self.ptr, CGRect::new(x, y, w, h)) };
257 }
258
259 pub fn fill_path(&self) {
261 unsafe { ffi::CGContextFillPath(self.ptr) };
262 }
263
264 pub fn stroke_path(&self) {
266 unsafe { ffi::CGContextStrokePath(self.ptr) };
267 }
268
269 pub fn draw_image(&self, x: f64, y: f64, w: f64, h: f64, image: &CGImage) {
271 unsafe { ffi::CGContextDrawImage(self.ptr, CGRect::new(x, y, w, h), image.as_ptr()) };
272 }
273
274 pub fn translate(&self, tx: f64, ty: f64) {
276 unsafe { ffi::CGContextTranslateCTM(self.ptr, tx, ty) };
277 }
278
279 pub fn scale(&self, sx: f64, sy: f64) {
281 unsafe { ffi::CGContextScaleCTM(self.ptr, sx, sy) };
282 }
283
284 pub fn rotate(&self, radians: f64) {
286 unsafe { ffi::CGContextRotateCTM(self.ptr, radians) };
287 }
288
289 pub fn save_g_state(&self) {
291 unsafe { ffi::CGContextSaveGState(self.ptr) };
292 }
293
294 pub fn restore_g_state(&self) {
296 unsafe { ffi::CGContextRestoreGState(self.ptr) };
297 }
298
299 #[must_use]
301 pub fn snapshot_to_image(&self) -> Option<CGImage> {
302 let image = unsafe { ffi::CGBitmapContextCreateImage(self.ptr) };
303 if image.is_null() {
304 None
305 } else {
306 Some(unsafe { CGImage::from_raw(image) })
307 }
308 }
309
310 #[must_use]
312 pub const fn as_ptr(&self) -> *mut c_void {
313 self.ptr
314 }
315}