use graphicsmagick_sys::InitializeMagick;
use std::{
borrow::Cow,
ffi::{CStr, CString},
marker::PhantomData,
mem,
ops::{Deref, DerefMut},
os::raw::{c_char, c_double, c_uint, c_void},
ptr::{null, NonNull},
slice::{from_raw_parts, from_raw_parts_mut},
str::Utf8Error,
sync::Once,
thread,
};
static HAS_INITIALIZED: Once = Once::new();
pub fn initialize() {
HAS_INITIALIZED.call_once(|| {
assert_eq!(
thread::current().name(),
Some("main"),
"You have to call `graphicsmagick::initialize` in main thread"
);
unsafe {
InitializeMagick(null());
}
});
}
#[inline]
pub fn has_initialized() -> bool {
HAS_INITIALIZED.is_completed()
}
#[inline]
pub(crate) fn assert_initialized() {
assert!(
has_initialized(),
"You have to call `graphicsmagick::initialize` first of all"
)
}
pub trait MaxRGB {
fn max_rgb() -> Self;
}
impl MaxRGB for c_uint {
fn max_rgb() -> Self {
graphicsmagick_sys::MaxRGB
}
}
impl MaxRGB for c_double {
fn max_rgb() -> Self {
graphicsmagick_sys::MaxRGBDouble
}
}
pub fn max_rgb<T: MaxRGB>() -> T {
<T>::max_rgb()
}
pub(crate) fn str_to_c_string(s: &str) -> CString {
let buf = s.bytes().collect::<Vec<_>>();
unsafe { CString::from_vec_unchecked(buf) }
}
#[derive(Debug)]
#[repr(transparent)]
struct MagickAlloc(*mut c_void);
impl MagickAlloc {
unsafe fn new(ptr: *mut c_void) -> Self {
Self(ptr)
}
}
impl Drop for MagickAlloc {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
graphicsmagick_sys::MagickFree(self.0);
}
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub(crate) struct MagickAutoRelinquish(NonNull<c_void>);
impl MagickAutoRelinquish {
pub(crate) unsafe fn new(ptr: *mut c_void) -> Option<Self> {
NonNull::new(ptr).map(Self)
}
pub(crate) unsafe fn as_c_str(&self) -> &CStr {
CStr::from_ptr(self.0.as_ptr() as *const c_char)
}
}
impl Drop for MagickAutoRelinquish {
fn drop(&mut self) {
unsafe {
graphicsmagick_sys::MagickFree(self.0.as_ptr());
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct MagickCString(MagickAlloc);
impl MagickCString {
pub(crate) unsafe fn new(c: *const c_char) -> Self {
Self(MagickAlloc(c as *mut c_void))
}
pub fn as_ptr(&self) -> *const c_char {
self.0 .0 as *const c_char
}
pub fn as_c_str(&self) -> &CStr {
let ptr = self.as_ptr();
if ptr.is_null() {
unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }
} else {
unsafe { CStr::from_ptr(ptr) }
}
}
pub fn to_str(&self) -> Result<&str, Utf8Error> {
self.as_c_str().to_str()
}
pub fn to_str_lossy(&self) -> Cow<'_, str> {
String::from_utf8_lossy(self.as_c_str().to_bytes())
}
}
pub(crate) trait CStrExt {
unsafe fn from_ptr_checked_on_debug<'a>(ptr: *const c_char) -> &'a CStr {
debug_assert!(!ptr.is_null());
CStr::from_ptr(ptr)
}
}
impl CStrExt for CStr {}
#[derive(Debug)]
pub struct MagickBoxSlice<T> {
alloc: MagickAlloc,
len: usize,
phantom: PhantomData<T>,
}
impl<T> MagickBoxSlice<T> {
pub(crate) unsafe fn new<U>(a: *mut U, len: usize) -> Option<Self> {
assert_eq!(mem::size_of::<U>(), mem::size_of::<T>());
(!a.is_null()).then(|| Self {
alloc: MagickAlloc::new(a as *mut c_void),
len,
phantom: PhantomData,
})
}
pub fn as_ptr(&self) -> *const T {
self.alloc.0 as *const T
}
pub fn as_mut_ptr(&mut self) -> *mut T {
self.alloc.0 as *mut T
}
}
impl<T> Deref for MagickBoxSlice<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { from_raw_parts(self.as_ptr(), self.len) }
}
}
impl<T> DerefMut for MagickBoxSlice<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { from_raw_parts_mut(self.as_mut_ptr(), self.len) }
}
}