use core::ffi::c_void;
use core::ptr;
use crate::CFError;
use super::drawing::{CGColorSpace, CGImage};
use super::ffi;
use super::CGRect;
const BITS_PER_COMPONENT_8: usize = 8;
const CG_IMAGE_ALPHA_NONE: u32 = 0;
const CG_IMAGE_ALPHA_PREMULTIPLIED_LAST: u32 = 1;
const CG_BITMAP_BYTE_ORDER_32_BIG: u32 = 16_384;
const RGBA8_BITMAP_INFO: u32 = CG_IMAGE_ALPHA_PREMULTIPLIED_LAST | CG_BITMAP_BYTE_ORDER_32_BIG;
const GRAYSCALE8_BITMAP_INFO: u32 = CG_IMAGE_ALPHA_NONE;
#[derive(Debug)]
pub struct CGContext {
ptr: *mut c_void,
}
impl Drop for CGContext {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { ffi::CGContextRelease(self.ptr) };
self.ptr = ptr::null_mut();
}
}
}
impl Clone for CGContext {
fn clone(&self) -> Self {
Self {
ptr: unsafe { ffi::CGContextRetain(self.ptr) },
}
}
}
impl CGContext {
#[must_use]
pub const unsafe fn from_raw(ptr: *mut c_void) -> Self {
Self { ptr }
}
fn new_bitmap(
width: usize,
height: usize,
color_space: &CGColorSpace,
bitmap_info: u32,
) -> Result<Self, CFError> {
let context = unsafe {
ffi::CGBitmapContextCreate(
ptr::null_mut(),
width,
height,
BITS_PER_COMPONENT_8,
0,
color_space.as_ptr(),
bitmap_info,
)
};
if context.is_null() {
Err(CFError::new("CGBitmapContextCreate"))
} else {
Ok(Self { ptr: context })
}
}
pub fn new_rgba8(width: usize, height: usize) -> Result<Self, CFError> {
let color_space = CGColorSpace::device_rgb();
Self::new_bitmap(width, height, &color_space, RGBA8_BITMAP_INFO)
}
pub fn new_grayscale(width: usize, height: usize) -> Result<Self, CFError> {
let color_space = CGColorSpace::device_gray();
Self::new_bitmap(width, height, &color_space, GRAYSCALE8_BITMAP_INFO)
}
fn buffer_len(&self) -> usize {
self.height().checked_mul(self.bytes_per_row()).unwrap_or(0)
}
#[must_use]
pub fn width(&self) -> usize {
unsafe { ffi::CGBitmapContextGetWidth(self.ptr) }
}
#[must_use]
pub fn height(&self) -> usize {
unsafe { ffi::CGBitmapContextGetHeight(self.ptr) }
}
#[must_use]
pub fn bytes_per_row(&self) -> usize {
unsafe { ffi::CGBitmapContextGetBytesPerRow(self.ptr) }
}
#[must_use]
pub fn bits_per_component(&self) -> usize {
unsafe { ffi::CGBitmapContextGetBitsPerComponent(self.ptr) }
}
#[must_use]
pub fn bits_per_pixel(&self) -> usize {
unsafe { ffi::CGBitmapContextGetBitsPerPixel(self.ptr) }
}
#[must_use]
pub fn data(&self) -> *mut u8 {
unsafe { ffi::CGBitmapContextGetData(self.ptr).cast::<u8>() }
}
#[must_use]
pub fn color_space(&self) -> Option<CGColorSpace> {
unsafe {
let color_space = ffi::CGBitmapContextGetColorSpace(self.ptr);
if color_space.is_null() {
None
} else {
Some(CGColorSpace::from_raw(ffi::CGColorSpaceRetain(color_space)))
}
}
}
#[must_use]
pub fn alpha_info(&self) -> u32 {
unsafe { ffi::CGBitmapContextGetAlphaInfo(self.ptr) }
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
let data = self.data();
let len = self.buffer_len();
if data.is_null() || len == 0 {
&[]
} else {
unsafe { std::slice::from_raw_parts(data.cast_const(), len) }
}
}
#[must_use]
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
let data = self.data();
let len = self.buffer_len();
if data.is_null() || len == 0 {
&mut []
} else {
unsafe { std::slice::from_raw_parts_mut(data, len) }
}
}
pub fn set_rgb_fill_color(&self, r: f64, g: f64, b: f64, a: f64) {
unsafe { ffi::CGContextSetRGBFillColor(self.ptr, r, g, b, a) };
}
pub fn set_rgb_stroke_color(&self, r: f64, g: f64, b: f64, a: f64) {
unsafe { ffi::CGContextSetRGBStrokeColor(self.ptr, r, g, b, a) };
}
pub fn set_line_width(&self, w: f64) {
unsafe { ffi::CGContextSetLineWidth(self.ptr, w) };
}
pub fn clear_rect(&self, x: f64, y: f64, w: f64, h: f64) {
unsafe { ffi::CGContextClearRect(self.ptr, CGRect::new(x, y, w, h)) };
}
pub fn fill_rect(&self, x: f64, y: f64, w: f64, h: f64) {
unsafe { ffi::CGContextFillRect(self.ptr, CGRect::new(x, y, w, h)) };
}
pub fn stroke_rect(&self, x: f64, y: f64, w: f64, h: f64) {
unsafe { ffi::CGContextStrokeRect(self.ptr, CGRect::new(x, y, w, h)) };
}
pub fn begin_path(&self) {
unsafe { ffi::CGContextBeginPath(self.ptr) };
}
pub fn close_path(&self) {
unsafe { ffi::CGContextClosePath(self.ptr) };
}
pub fn move_to(&self, x: f64, y: f64) {
unsafe { ffi::CGContextMoveToPoint(self.ptr, x, y) };
}
pub fn add_line_to(&self, x: f64, y: f64) {
unsafe { ffi::CGContextAddLineToPoint(self.ptr, x, y) };
}
pub fn add_rect(&self, x: f64, y: f64, w: f64, h: f64) {
unsafe { ffi::CGContextAddRect(self.ptr, CGRect::new(x, y, w, h)) };
}
pub fn add_ellipse_in_rect(&self, x: f64, y: f64, w: f64, h: f64) {
unsafe { ffi::CGContextAddEllipseInRect(self.ptr, CGRect::new(x, y, w, h)) };
}
pub fn fill_path(&self) {
unsafe { ffi::CGContextFillPath(self.ptr) };
}
pub fn stroke_path(&self) {
unsafe { ffi::CGContextStrokePath(self.ptr) };
}
pub fn draw_image(&self, x: f64, y: f64, w: f64, h: f64, image: &CGImage) {
unsafe { ffi::CGContextDrawImage(self.ptr, CGRect::new(x, y, w, h), image.as_ptr()) };
}
pub fn translate(&self, tx: f64, ty: f64) {
unsafe { ffi::CGContextTranslateCTM(self.ptr, tx, ty) };
}
pub fn scale(&self, sx: f64, sy: f64) {
unsafe { ffi::CGContextScaleCTM(self.ptr, sx, sy) };
}
pub fn rotate(&self, radians: f64) {
unsafe { ffi::CGContextRotateCTM(self.ptr, radians) };
}
pub fn save_g_state(&self) {
unsafe { ffi::CGContextSaveGState(self.ptr) };
}
pub fn restore_g_state(&self) {
unsafe { ffi::CGContextRestoreGState(self.ptr) };
}
#[must_use]
pub fn snapshot_to_image(&self) -> Option<CGImage> {
let image = unsafe { ffi::CGBitmapContextCreateImage(self.ptr) };
if image.is_null() {
None
} else {
Some(unsafe { CGImage::from_raw(image) })
}
}
#[must_use]
pub const fn as_ptr(&self) -> *mut c_void {
self.ptr
}
}