use alloc::ffi::CString;
use crate::error::{Error, Result};
use crate::paint::Paint;
use thorvg_sys as sys;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum FilterMethod {
Bilinear,
Nearest,
}
impl FilterMethod {
fn to_raw(self) -> sys::Tvg_Filter_Method {
match self {
FilterMethod::Bilinear => sys::Tvg_Filter_Method::TVG_FILTER_METHOD_BILINEAR,
FilterMethod::Nearest => sys::Tvg_Filter_Method::TVG_FILTER_METHOD_NEAREST,
}
}
}
pub struct Picture<'eng> {
raw: sys::Tvg_Paint,
owned: bool,
_engine: core::marker::PhantomData<&'eng ()>,
}
unsafe impl Send for Picture<'_> {}
impl Picture<'_> {
pub(crate) fn new() -> Self {
let raw = unsafe { sys::tvg_picture_new() };
assert!(!raw.is_null(), "failed to create Picture");
Self {
raw,
owned: true,
_engine: core::marker::PhantomData,
}
}
pub(crate) unsafe fn from_raw(raw: sys::Tvg_Paint, owned: bool) -> Self {
Self {
raw,
owned,
_engine: core::marker::PhantomData,
}
}
pub fn load_from_str(&mut self, path: &str) -> Result<()> {
let c_path = CString::new(path).map_err(|_| Error::InvalidArguments)?;
Error::from_raw(unsafe { sys::tvg_picture_load(self.raw, c_path.as_ptr()) })
}
#[cfg(feature = "std")]
pub fn load<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<()> {
self.load_from_str(&path.as_ref().to_string_lossy())
}
#[allow(clippy::cast_possible_truncation)]
pub fn load_data(
&mut self,
data: &[u8],
mimetype: &str,
resource_path: Option<&str>,
copy: bool,
) -> Result<()> {
let c_mime = CString::new(mimetype).map_err(|_| Error::InvalidArguments)?;
let c_rpath = resource_path
.map(|p| CString::new(p).map_err(|_| Error::InvalidArguments))
.transpose()?;
let rpath_ptr = c_rpath.as_ref().map_or(core::ptr::null(), |c| c.as_ptr());
Error::from_raw(unsafe {
sys::tvg_picture_load_data(
self.raw,
data.as_ptr().cast::<core::ffi::c_char>(),
data.len() as u32,
c_mime.as_ptr(),
rpath_ptr,
copy,
)
})
}
pub fn load_raw(
&mut self,
data: &[u32],
w: u32,
h: u32,
cs: crate::ColorSpace,
copy: bool,
) -> Result<()> {
Error::from_raw(unsafe {
sys::tvg_picture_load_raw(self.raw, data.as_ptr(), w, h, cs.to_raw(), copy)
})
}
pub fn set_size(&mut self, w: f32, h: f32) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_picture_set_size(self.raw, w, h) })
}
pub fn size(&self) -> Result<(f32, f32)> {
let (mut w, mut h) = (0.0f32, 0.0f32);
Error::from_raw(unsafe { sys::tvg_picture_get_size(self.raw, &raw mut w, &raw mut h) })?;
Ok((w, h))
}
pub fn set_origin(&mut self, x: f32, y: f32) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_picture_set_origin(self.raw, x, y) })
}
pub fn origin(&self) -> Result<(f32, f32)> {
let (mut x, mut y) = (0.0f32, 0.0f32);
Error::from_raw(unsafe { sys::tvg_picture_get_origin(self.raw, &raw mut x, &raw mut y) })?;
Ok((x, y))
}
pub fn set_filter(&mut self, method: FilterMethod) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_picture_set_filter(self.raw, method.to_raw()) })
}
pub fn set_accessible(&mut self, accessible: bool) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_picture_set_accessible(self.raw, accessible) })
}
pub unsafe fn set_asset_resolver(
&mut self,
resolver: sys::Tvg_Picture_Asset_Resolver,
data: *mut core::ffi::c_void,
) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_picture_set_asset_resolver(self.raw, resolver, data) })
}
pub fn get_paint(&self, id: u32) -> Option<sys::Tvg_Paint> {
let raw = unsafe { sys::tvg_picture_get_paint(self.raw, id) };
if raw.is_null() {
None
} else {
Some(raw)
}
}
}
impl Paint for Picture<'_> {
fn raw(&self) -> sys::Tvg_Paint {
self.raw
}
fn into_raw(mut self) -> sys::Tvg_Paint {
self.owned = false;
self.raw
}
unsafe fn from_raw_paint(raw: sys::Tvg_Paint) -> Self {
Self {
raw,
owned: true,
_engine: core::marker::PhantomData,
}
}
}
impl Drop for Picture<'_> {
fn drop(&mut self) {
if self.owned {
unsafe {
sys::tvg_paint_rel(self.raw);
}
}
}
}
impl core::fmt::Debug for Picture<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Picture").finish_non_exhaustive()
}
}