use crate::color::ColorSpace;
use crate::error::{Error, Result};
use crate::paint::Paint;
use thorvg_sys as sys;
mod sealed {
pub trait Sealed {}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum EngineOption {
None,
#[default]
Default,
SmartRender,
Aliased,
}
impl EngineOption {
pub(crate) fn to_raw(self) -> sys::Tvg_Engine_Option {
match self {
EngineOption::None => sys::Tvg_Engine_Option::TVG_ENGINE_OPTION_NONE,
EngineOption::Default => sys::Tvg_Engine_Option::TVG_ENGINE_OPTION_DEFAULT,
EngineOption::SmartRender => sys::Tvg_Engine_Option::TVG_ENGINE_OPTION_SMART_RENDER,
EngineOption::Aliased => sys::Tvg_Engine_Option::TVG_ENGINE_OPTION_ALIASED,
}
}
}
pub trait Canvas: sealed::Sealed {
fn add<P: Paint>(&mut self, paint: P) -> Result<()>;
fn insert<P: Paint, Q: Paint>(&mut self, target: P, at: &Q) -> Result<()>;
fn remove<P: Paint>(&mut self, paint: &P) -> Result<()>;
fn clear(&mut self) -> Result<()>;
fn update(&mut self) -> Result<()>;
fn draw(&mut self, clear: bool) -> Result<()>;
fn sync(&mut self) -> Result<()>;
fn set_viewport(&mut self, x: i32, y: i32, w: i32, h: i32) -> Result<()>;
fn render(&mut self) -> Result<()>;
}
macro_rules! impl_canvas {
(
$(#[$meta:meta])*
$ty:ident, $create_fn:ident
) => {
$(#[$meta])*
pub struct $ty<'eng> {
raw: sys::Tvg_Canvas,
_engine: core::marker::PhantomData<&'eng ()>,
}
unsafe impl Send for $ty<'_> {}
impl $ty<'_> {
pub(crate) fn new(option: EngineOption) -> Result<Self> {
let raw = unsafe { sys::$create_fn(option.to_raw()) };
if raw.is_null() {
return Err(Error::Unknown);
}
Ok(Self {
raw,
_engine: core::marker::PhantomData,
})
}
pub fn add<P: Paint>(&mut self, paint: P) -> Result<()> {
let raw_paint = paint.into_raw();
Error::from_raw(unsafe { sys::tvg_canvas_add(self.raw, raw_paint) })
}
pub fn insert<P: Paint, Q: Paint>(&mut self, target: P, at: &Q) -> Result<()> {
Error::from_raw(unsafe {
sys::tvg_canvas_insert(self.raw, target.into_raw(), at.raw())
})
}
pub fn remove<P: Paint>(&mut self, paint: &P) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_canvas_remove(self.raw, paint.raw()) })
}
pub fn clear(&mut self) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_canvas_remove(self.raw, core::ptr::null_mut()) })
}
pub fn update(&mut self) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_canvas_update(self.raw) })
}
pub fn draw(&mut self, clear: bool) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_canvas_draw(self.raw, clear) })
}
pub fn sync(&mut self) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_canvas_sync(self.raw) })
}
pub fn set_viewport(&mut self, x: i32, y: i32, w: i32, h: i32) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_canvas_set_viewport(self.raw, x, y, w, h) })
}
pub fn render(&mut self) -> Result<()> {
self.update()?;
self.draw(true)?;
self.sync()
}
}
impl sealed::Sealed for $ty<'_> {}
impl Canvas for $ty<'_> {
fn add<P: Paint>(&mut self, paint: P) -> Result<()> {
$ty::add(self, paint)
}
fn insert<P: Paint, Q: Paint>(&mut self, target: P, at: &Q) -> Result<()> {
$ty::insert(self, target, at)
}
fn remove<P: Paint>(&mut self, paint: &P) -> Result<()> {
$ty::remove(self, paint)
}
fn clear(&mut self) -> Result<()> {
$ty::clear(self)
}
fn update(&mut self) -> Result<()> {
$ty::update(self)
}
fn draw(&mut self, clear: bool) -> Result<()> {
$ty::draw(self, clear)
}
fn sync(&mut self) -> Result<()> {
$ty::sync(self)
}
fn set_viewport(&mut self, x: i32, y: i32, w: i32, h: i32) -> Result<()> {
$ty::set_viewport(self, x, y, w, h)
}
fn render(&mut self) -> Result<()> {
$ty::render(self)
}
}
impl core::fmt::Debug for $ty<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(stringify!($ty)).finish_non_exhaustive()
}
}
impl Drop for $ty<'_> {
fn drop(&mut self) {
unsafe {
sys::tvg_canvas_destroy(self.raw);
}
}
}
};
}
impl_canvas! {
SwCanvas, tvg_swcanvas_create
}
impl SwCanvas<'_> {
pub unsafe fn set_target(
&mut self,
buffer: &mut [u32],
stride: u32,
width: u32,
height: u32,
colorspace: ColorSpace,
) -> Result<()> {
if stride < width {
return Err(Error::InvalidArguments);
}
let needed = u64::from(stride).checked_mul(u64::from(height));
let Some(needed) = needed else {
return Err(Error::InvalidArguments);
};
if (buffer.len() as u64) < needed {
return Err(Error::InvalidArguments);
}
let result = unsafe {
sys::tvg_swcanvas_set_target(
self.raw,
buffer.as_mut_ptr(),
stride,
width,
height,
colorspace.to_raw(),
)
};
Error::from_raw(result)
}
}
impl_canvas! {
GlCanvas, tvg_glcanvas_create
}
impl GlCanvas<'_> {
pub unsafe fn set_target(&mut self, target: GlTarget) -> Result<()> {
let GlTarget {
display,
surface,
context,
id,
width,
height,
colorspace,
} = target;
Error::from_raw(unsafe {
sys::tvg_glcanvas_set_target(
self.raw,
display,
surface,
context,
id,
width,
height,
colorspace.to_raw(),
)
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct GlTarget {
pub display: *mut core::ffi::c_void,
pub surface: *mut core::ffi::c_void,
pub context: *mut core::ffi::c_void,
pub id: i32,
pub width: u32,
pub height: u32,
pub colorspace: ColorSpace,
}
impl GlTarget {
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn new(
display: *mut core::ffi::c_void,
surface: *mut core::ffi::c_void,
context: *mut core::ffi::c_void,
id: i32,
width: u32,
height: u32,
colorspace: ColorSpace,
) -> Self {
Self {
display,
surface,
context,
id,
width,
height,
colorspace,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum WgTargetType {
Surface = 0,
Texture = 1,
}
impl_canvas! {
WgCanvas, tvg_wgcanvas_create
}
impl WgCanvas<'_> {
pub unsafe fn set_target(&mut self, target: WgTarget) -> Result<()> {
let WgTarget {
device,
instance,
target: target_handle,
width,
height,
colorspace,
target_type,
} = target;
Error::from_raw(unsafe {
sys::tvg_wgcanvas_set_target(
self.raw,
device,
instance,
target_handle,
width,
height,
colorspace.to_raw(),
target_type as i32,
)
})
}
pub unsafe fn set_target_with_context(&mut self, target: WgContextTarget) -> Result<()> {
let WgContextTarget {
context,
target: target_handle,
width,
height,
colorspace,
target_type,
} = target;
let ctx = sys::Tvg_WgContext {
instance: context.instance,
adapter: context.adapter,
device: context.device,
};
Error::from_raw(unsafe {
sys::tvg_wgcanvas_set_target_with_context(
self.raw,
&raw const ctx,
target_handle,
width,
height,
colorspace.to_raw(),
target_type as i32,
)
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct WgContext {
pub instance: *mut core::ffi::c_void,
pub adapter: *mut core::ffi::c_void,
pub device: *mut core::ffi::c_void,
}
impl WgContext {
#[must_use]
pub fn new(
instance: *mut core::ffi::c_void,
adapter: *mut core::ffi::c_void,
device: *mut core::ffi::c_void,
) -> Self {
Self {
instance,
adapter,
device,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct WgContextTarget {
pub context: WgContext,
pub target: *mut core::ffi::c_void,
pub width: u32,
pub height: u32,
pub colorspace: ColorSpace,
pub target_type: WgTargetType,
}
impl WgContextTarget {
#[must_use]
pub fn new(
context: WgContext,
target: *mut core::ffi::c_void,
width: u32,
height: u32,
colorspace: ColorSpace,
target_type: WgTargetType,
) -> Self {
Self {
context,
target,
width,
height,
colorspace,
target_type,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct WgTarget {
pub device: *mut core::ffi::c_void,
pub instance: *mut core::ffi::c_void,
pub target: *mut core::ffi::c_void,
pub width: u32,
pub height: u32,
pub colorspace: ColorSpace,
pub target_type: WgTargetType,
}
impl WgTarget {
#[must_use]
#[allow(clippy::too_many_arguments)]
pub fn new(
device: *mut core::ffi::c_void,
instance: *mut core::ffi::c_void,
target: *mut core::ffi::c_void,
width: u32,
height: u32,
colorspace: ColorSpace,
target_type: WgTargetType,
) -> Self {
Self {
device,
instance,
target,
width,
height,
colorspace,
target_type,
}
}
}