use crate::error::{Error, Result};
use crate::gradient::{LinearGradient, RadialGradient};
use crate::paint::{Paint, Point};
use thorvg_sys as ffi;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum FillRule {
NonZero,
EvenOdd,
}
impl FillRule {
fn to_raw(self) -> ffi::Tvg_Fill_Rule {
match self {
FillRule::NonZero => ffi::Tvg_Fill_Rule::TVG_FILL_RULE_NON_ZERO,
FillRule::EvenOdd => ffi::Tvg_Fill_Rule::TVG_FILL_RULE_EVEN_ODD,
}
}
fn from_raw(r: ffi::Tvg_Fill_Rule) -> Self {
match r {
ffi::Tvg_Fill_Rule::TVG_FILL_RULE_EVEN_ODD => FillRule::EvenOdd,
_ => FillRule::NonZero,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum StrokeCap {
Butt,
Round,
Square,
}
impl StrokeCap {
fn to_raw(self) -> ffi::Tvg_Stroke_Cap {
match self {
StrokeCap::Butt => ffi::Tvg_Stroke_Cap::TVG_STROKE_CAP_BUTT,
StrokeCap::Round => ffi::Tvg_Stroke_Cap::TVG_STROKE_CAP_ROUND,
StrokeCap::Square => ffi::Tvg_Stroke_Cap::TVG_STROKE_CAP_SQUARE,
}
}
fn from_raw(c: ffi::Tvg_Stroke_Cap) -> Self {
match c {
ffi::Tvg_Stroke_Cap::TVG_STROKE_CAP_ROUND => StrokeCap::Round,
ffi::Tvg_Stroke_Cap::TVG_STROKE_CAP_SQUARE => StrokeCap::Square,
_ => StrokeCap::Butt,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum StrokeJoin {
Miter,
Round,
Bevel,
}
impl StrokeJoin {
fn to_raw(self) -> ffi::Tvg_Stroke_Join {
match self {
StrokeJoin::Miter => ffi::Tvg_Stroke_Join::TVG_STROKE_JOIN_MITER,
StrokeJoin::Round => ffi::Tvg_Stroke_Join::TVG_STROKE_JOIN_ROUND,
StrokeJoin::Bevel => ffi::Tvg_Stroke_Join::TVG_STROKE_JOIN_BEVEL,
}
}
fn from_raw(j: ffi::Tvg_Stroke_Join) -> Self {
match j {
ffi::Tvg_Stroke_Join::TVG_STROKE_JOIN_ROUND => StrokeJoin::Round,
ffi::Tvg_Stroke_Join::TVG_STROKE_JOIN_BEVEL => StrokeJoin::Bevel,
_ => StrokeJoin::Miter,
}
}
}
pub struct Shape<'eng> {
raw: ffi::Tvg_Paint,
owned: bool,
_engine: core::marker::PhantomData<&'eng ()>,
}
unsafe impl Send for Shape<'_> {}
impl Shape<'_> {
pub(crate) fn new() -> Self {
let raw = unsafe { ffi::tvg_shape_new() };
assert!(!raw.is_null(), "failed to create Shape");
Self {
raw,
owned: true,
_engine: core::marker::PhantomData,
}
}
pub(crate) unsafe fn from_raw(raw: ffi::Tvg_Paint, owned: bool) -> Self {
Self {
raw,
owned,
_engine: core::marker::PhantomData,
}
}
pub fn reset(&mut self) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_reset(self.raw) })
}
pub fn move_to(&mut self, x: f32, y: f32) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_move_to(self.raw, x, y) })
}
pub fn line_to(&mut self, x: f32, y: f32) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_line_to(self.raw, x, y) })
}
pub fn cubic_to(
&mut self,
cx1: f32,
cy1: f32,
cx2: f32,
cy2: f32,
x: f32,
y: f32,
) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_cubic_to(self.raw, cx1, cy1, cx2, cy2, x, y) })
}
pub fn close(&mut self) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_close(self.raw) })
}
#[allow(clippy::cast_possible_truncation)]
pub fn append_path(&mut self, cmds: &[u8], pts: &[Point]) -> Result<()> {
let raw_pts: alloc::vec::Vec<ffi::Tvg_Point> = pts
.iter()
.map(|p| ffi::Tvg_Point { x: p.x, y: p.y })
.collect();
Error::from_raw(unsafe {
ffi::tvg_shape_append_path(
self.raw,
cmds.as_ptr(),
cmds.len() as u32,
raw_pts.as_ptr(),
raw_pts.len() as u32,
)
})
}
#[allow(clippy::cast_possible_truncation)]
pub fn path(&self) -> Result<(u32, u32)> {
let mut cmds_cnt: u32 = 0;
let mut pts_cnt: u32 = 0;
Error::from_raw(unsafe {
ffi::tvg_shape_get_path(
self.raw,
core::ptr::null_mut(),
&raw mut cmds_cnt,
core::ptr::null_mut(),
&raw mut pts_cnt,
)
})?;
Ok((cmds_cnt, pts_cnt))
}
#[allow(clippy::too_many_arguments)]
pub fn append_rect(
&mut self,
x: f32,
y: f32,
w: f32,
h: f32,
rx: f32,
ry: f32,
cw: bool,
) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_append_rect(self.raw, x, y, w, h, rx, ry, cw) })
}
pub fn append_circle(&mut self, cx: f32, cy: f32, rx: f32, ry: f32, cw: bool) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_append_circle(self.raw, cx, cy, rx, ry, cw) })
}
pub fn set_fill_color(&mut self, r: u8, g: u8, b: u8, a: u8) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_fill_color(self.raw, r, g, b, a) })
}
pub fn fill_color(&self) -> Result<(u8, u8, u8, u8)> {
let (mut r, mut g, mut b, mut a) = (0u8, 0u8, 0u8, 0u8);
Error::from_raw(unsafe {
ffi::tvg_shape_get_fill_color(self.raw, &raw mut r, &raw mut g, &raw mut b, &raw mut a)
})?;
Ok((r, g, b, a))
}
pub fn set_fill_rule(&mut self, rule: FillRule) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_fill_rule(self.raw, rule.to_raw()) })
}
pub fn fill_rule(&self) -> Result<FillRule> {
let mut rule = ffi::Tvg_Fill_Rule::TVG_FILL_RULE_NON_ZERO;
Error::from_raw(unsafe { ffi::tvg_shape_get_fill_rule(self.raw, &raw mut rule) })?;
Ok(FillRule::from_raw(rule))
}
pub fn set_linear_gradient(&mut self, grad: LinearGradient<'_>) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_gradient(self.raw, grad.into_raw()) })
}
pub fn set_radial_gradient(&mut self, grad: RadialGradient<'_>) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_gradient(self.raw, grad.into_raw()) })
}
pub fn gradient_raw(&self) -> Option<ffi::Tvg_Gradient> {
let mut grad: ffi::Tvg_Gradient = core::ptr::null_mut();
let r = unsafe { ffi::tvg_shape_get_gradient(self.raw, &raw mut grad) };
if Error::from_raw(r).is_err() || grad.is_null() {
None
} else {
Some(grad)
}
}
pub fn set_paint_order(&mut self, stroke_first: bool) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_paint_order(self.raw, stroke_first) })
}
pub fn set_stroke_width(&mut self, width: f32) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_stroke_width(self.raw, width) })
}
pub fn stroke_width(&self) -> Result<f32> {
let mut width: f32 = 0.0;
Error::from_raw(unsafe { ffi::tvg_shape_get_stroke_width(self.raw, &raw mut width) })?;
Ok(width)
}
pub fn set_stroke_color(&mut self, r: u8, g: u8, b: u8, a: u8) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_stroke_color(self.raw, r, g, b, a) })
}
pub fn stroke_color(&self) -> Result<(u8, u8, u8, u8)> {
let (mut r, mut g, mut b, mut a) = (0u8, 0u8, 0u8, 0u8);
Error::from_raw(unsafe {
ffi::tvg_shape_get_stroke_color(
self.raw, &raw mut r, &raw mut g, &raw mut b, &raw mut a,
)
})?;
Ok((r, g, b, a))
}
pub fn set_stroke_cap(&mut self, cap: StrokeCap) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_stroke_cap(self.raw, cap.to_raw()) })
}
pub fn stroke_cap(&self) -> Result<StrokeCap> {
let mut cap = ffi::Tvg_Stroke_Cap::TVG_STROKE_CAP_BUTT;
Error::from_raw(unsafe { ffi::tvg_shape_get_stroke_cap(self.raw, &raw mut cap) })?;
Ok(StrokeCap::from_raw(cap))
}
pub fn set_stroke_join(&mut self, join: StrokeJoin) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_stroke_join(self.raw, join.to_raw()) })
}
pub fn stroke_join(&self) -> Result<StrokeJoin> {
let mut join = ffi::Tvg_Stroke_Join::TVG_STROKE_JOIN_MITER;
Error::from_raw(unsafe { ffi::tvg_shape_get_stroke_join(self.raw, &raw mut join) })?;
Ok(StrokeJoin::from_raw(join))
}
pub fn set_stroke_miterlimit(&mut self, miterlimit: f32) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_stroke_miterlimit(self.raw, miterlimit) })
}
pub fn stroke_miterlimit(&self) -> Result<f32> {
let mut ml: f32 = 0.0;
Error::from_raw(unsafe { ffi::tvg_shape_get_stroke_miterlimit(self.raw, &raw mut ml) })?;
Ok(ml)
}
#[allow(clippy::cast_possible_truncation)]
pub fn set_stroke_dash(&mut self, pattern: &[f32], offset: f32) -> Result<()> {
Error::from_raw(unsafe {
ffi::tvg_shape_set_stroke_dash(self.raw, pattern.as_ptr(), pattern.len() as u32, offset)
})
}
pub fn stroke_dash(&self) -> Result<(alloc::vec::Vec<f32>, f32)> {
let mut ptr: *const f32 = core::ptr::null();
let mut cnt: u32 = 0;
let mut offset: f32 = 0.0;
Error::from_raw(unsafe {
ffi::tvg_shape_get_stroke_dash(self.raw, &raw mut ptr, &raw mut cnt, &raw mut offset)
})?;
let pattern = if ptr.is_null() || cnt == 0 {
alloc::vec::Vec::new()
} else {
unsafe { core::slice::from_raw_parts(ptr, cnt as usize) }.to_vec()
};
Ok((pattern, offset))
}
pub fn stroke_gradient_raw(&self) -> Option<ffi::Tvg_Gradient> {
let mut grad: ffi::Tvg_Gradient = core::ptr::null_mut();
let r = unsafe { ffi::tvg_shape_get_stroke_gradient(self.raw, &raw mut grad) };
if Error::from_raw(r).is_err() || grad.is_null() {
None
} else {
Some(grad)
}
}
pub fn set_stroke_gradient(&mut self, grad: LinearGradient<'_>) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_stroke_gradient(self.raw, grad.into_raw()) })
}
pub fn set_trimpath(&mut self, begin: f32, end: f32, simultaneous: bool) -> Result<()> {
Error::from_raw(unsafe { ffi::tvg_shape_set_trimpath(self.raw, begin, end, simultaneous) })
}
}
impl Paint for Shape<'_> {
fn raw(&self) -> ffi::Tvg_Paint {
self.raw
}
fn into_raw(mut self) -> ffi::Tvg_Paint {
self.owned = false;
self.raw
}
unsafe fn from_raw_paint(raw: ffi::Tvg_Paint) -> Self {
Self {
raw,
owned: true,
_engine: core::marker::PhantomData,
}
}
}
impl Drop for Shape<'_> {
fn drop(&mut self) {
if self.owned {
unsafe {
ffi::tvg_paint_rel(self.raw);
}
}
}
}
impl core::fmt::Debug for Shape<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Shape").finish_non_exhaustive()
}
}