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)]
21pub struct CGContext {
22 ptr: *mut c_void,
23}
24
25impl Drop for CGContext {
26 fn drop(&mut self) {
27 if !self.ptr.is_null() {
28 unsafe { ffi::CGContextRelease(self.ptr) };
29 self.ptr = ptr::null_mut();
30 }
31 }
32}
33
34impl Clone for CGContext {
35 fn clone(&self) -> Self {
36 Self {
37 ptr: unsafe { ffi::CGContextRetain(self.ptr) },
38 }
39 }
40}
41
42impl CGContext {
43 #[must_use]
50 pub const unsafe fn from_raw(ptr: *mut c_void) -> Self {
51 Self { ptr }
52 }
53
54 fn new_bitmap(
55 width: usize,
56 height: usize,
57 color_space: &CGColorSpace,
58 bitmap_info: u32,
59 ) -> Result<Self, CFError> {
60 let context = unsafe {
61 ffi::CGBitmapContextCreate(
62 ptr::null_mut(),
63 width,
64 height,
65 BITS_PER_COMPONENT_8,
66 0,
67 color_space.as_ptr(),
68 bitmap_info,
69 )
70 };
71
72 if context.is_null() {
73 Err(CFError::new("CGBitmapContextCreate"))
74 } else {
75 Ok(Self { ptr: 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]
135 pub fn data(&self) -> *mut u8 {
136 unsafe { ffi::CGBitmapContextGetData(self.ptr).cast::<u8>() }
137 }
138
139 #[must_use]
141 pub fn color_space(&self) -> Option<CGColorSpace> {
142 unsafe {
143 let color_space = ffi::CGBitmapContextGetColorSpace(self.ptr);
144 if color_space.is_null() {
145 None
146 } else {
147 Some(CGColorSpace::from_raw(ffi::CGColorSpaceRetain(color_space)))
148 }
149 }
150 }
151
152 #[must_use]
154 pub fn alpha_info(&self) -> u32 {
155 unsafe { ffi::CGBitmapContextGetAlphaInfo(self.ptr) }
156 }
157
158 #[must_use]
160 pub fn as_bytes(&self) -> &[u8] {
161 let data = self.data();
162 let len = self.buffer_len();
163 if data.is_null() || len == 0 {
164 &[]
165 } else {
166 unsafe { std::slice::from_raw_parts(data.cast_const(), len) }
167 }
168 }
169
170 #[must_use]
172 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
173 let data = self.data();
174 let len = self.buffer_len();
175 if data.is_null() || len == 0 {
176 &mut []
177 } else {
178 unsafe { std::slice::from_raw_parts_mut(data, len) }
179 }
180 }
181
182 pub fn set_rgb_fill_color(&self, r: f64, g: f64, b: f64, a: f64) {
184 unsafe { ffi::CGContextSetRGBFillColor(self.ptr, r, g, b, a) };
185 }
186
187 pub fn set_rgb_stroke_color(&self, r: f64, g: f64, b: f64, a: f64) {
189 unsafe { ffi::CGContextSetRGBStrokeColor(self.ptr, r, g, b, a) };
190 }
191
192 pub fn set_line_width(&self, w: f64) {
194 unsafe { ffi::CGContextSetLineWidth(self.ptr, w) };
195 }
196
197 pub fn clear_rect(&self, x: f64, y: f64, w: f64, h: f64) {
199 unsafe { ffi::CGContextClearRect(self.ptr, CGRect::new(x, y, w, h)) };
200 }
201
202 pub fn fill_rect(&self, x: f64, y: f64, w: f64, h: f64) {
204 unsafe { ffi::CGContextFillRect(self.ptr, CGRect::new(x, y, w, h)) };
205 }
206
207 pub fn stroke_rect(&self, x: f64, y: f64, w: f64, h: f64) {
209 unsafe { ffi::CGContextStrokeRect(self.ptr, CGRect::new(x, y, w, h)) };
210 }
211
212 pub fn begin_path(&self) {
214 unsafe { ffi::CGContextBeginPath(self.ptr) };
215 }
216
217 pub fn close_path(&self) {
219 unsafe { ffi::CGContextClosePath(self.ptr) };
220 }
221
222 pub fn move_to(&self, x: f64, y: f64) {
224 unsafe { ffi::CGContextMoveToPoint(self.ptr, x, y) };
225 }
226
227 pub fn add_line_to(&self, x: f64, y: f64) {
229 unsafe { ffi::CGContextAddLineToPoint(self.ptr, x, y) };
230 }
231
232 pub fn add_rect(&self, x: f64, y: f64, w: f64, h: f64) {
234 unsafe { ffi::CGContextAddRect(self.ptr, CGRect::new(x, y, w, h)) };
235 }
236
237 pub fn add_ellipse_in_rect(&self, x: f64, y: f64, w: f64, h: f64) {
239 unsafe { ffi::CGContextAddEllipseInRect(self.ptr, CGRect::new(x, y, w, h)) };
240 }
241
242 pub fn fill_path(&self) {
244 unsafe { ffi::CGContextFillPath(self.ptr) };
245 }
246
247 pub fn stroke_path(&self) {
249 unsafe { ffi::CGContextStrokePath(self.ptr) };
250 }
251
252 pub fn draw_image(&self, x: f64, y: f64, w: f64, h: f64, image: &CGImage) {
254 unsafe { ffi::CGContextDrawImage(self.ptr, CGRect::new(x, y, w, h), image.as_ptr()) };
255 }
256
257 pub fn translate(&self, tx: f64, ty: f64) {
259 unsafe { ffi::CGContextTranslateCTM(self.ptr, tx, ty) };
260 }
261
262 pub fn scale(&self, sx: f64, sy: f64) {
264 unsafe { ffi::CGContextScaleCTM(self.ptr, sx, sy) };
265 }
266
267 pub fn rotate(&self, radians: f64) {
269 unsafe { ffi::CGContextRotateCTM(self.ptr, radians) };
270 }
271
272 pub fn save_g_state(&self) {
274 unsafe { ffi::CGContextSaveGState(self.ptr) };
275 }
276
277 pub fn restore_g_state(&self) {
279 unsafe { ffi::CGContextRestoreGState(self.ptr) };
280 }
281
282 #[must_use]
284 pub fn snapshot_to_image(&self) -> Option<CGImage> {
285 let image = unsafe { ffi::CGBitmapContextCreateImage(self.ptr) };
286 if image.is_null() {
287 None
288 } else {
289 Some(unsafe { CGImage::from_raw(image) })
290 }
291 }
292
293 #[must_use]
295 pub const fn as_ptr(&self) -> *mut c_void {
296 self.ptr
297 }
298}