use crate::error::{Error, Result};
use crate::shape::Shape;
use thorvg_sys as sys;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Matrix {
pub e11: f32,
pub e12: f32,
pub e13: f32,
pub e21: f32,
pub e22: f32,
pub e23: f32,
pub e31: f32,
pub e32: f32,
pub e33: f32,
}
impl Matrix {
pub const IDENTITY: Self = Self {
e11: 1.0,
e12: 0.0,
e13: 0.0,
e21: 0.0,
e22: 1.0,
e23: 0.0,
e31: 0.0,
e32: 0.0,
e33: 1.0,
};
#[must_use]
fn multiply(self, rhs: Matrix) -> Matrix {
Matrix {
e11: self.e11 * rhs.e11 + self.e12 * rhs.e21 + self.e13 * rhs.e31,
e12: self.e11 * rhs.e12 + self.e12 * rhs.e22 + self.e13 * rhs.e32,
e13: self.e11 * rhs.e13 + self.e12 * rhs.e23 + self.e13 * rhs.e33,
e21: self.e21 * rhs.e11 + self.e22 * rhs.e21 + self.e23 * rhs.e31,
e22: self.e21 * rhs.e12 + self.e22 * rhs.e22 + self.e23 * rhs.e32,
e23: self.e21 * rhs.e13 + self.e22 * rhs.e23 + self.e23 * rhs.e33,
e31: self.e31 * rhs.e11 + self.e32 * rhs.e21 + self.e33 * rhs.e31,
e32: self.e31 * rhs.e12 + self.e32 * rhs.e22 + self.e33 * rhs.e32,
e33: self.e31 * rhs.e13 + self.e32 * rhs.e23 + self.e33 * rhs.e33,
}
}
#[must_use]
pub fn translate(self, x: f32, y: f32) -> Matrix {
Matrix {
e13: x,
e23: y,
..Matrix::IDENTITY
}
.multiply(self)
}
#[must_use]
pub fn scale(self, sx: f32, sy: f32) -> Matrix {
Matrix {
e11: sx,
e22: sy,
..Matrix::IDENTITY
}
.multiply(self)
}
#[must_use]
pub fn rotate(self, degrees: f32) -> Matrix {
let r = degrees.to_radians();
let (s, c) = (libm::sinf(r), libm::cosf(r));
Matrix {
e11: c,
e12: -s,
e21: s,
e22: c,
..Matrix::IDENTITY
}
.multiply(self)
}
fn to_raw(self) -> sys::Tvg_Matrix {
sys::Tvg_Matrix {
e11: self.e11,
e12: self.e12,
e13: self.e13,
e21: self.e21,
e22: self.e22,
e23: self.e23,
e31: self.e31,
e32: self.e32,
e33: self.e33,
}
}
fn from_raw(m: sys::Tvg_Matrix) -> Self {
Self {
e11: m.e11,
e12: m.e12,
e13: m.e13,
e21: m.e21,
e22: m.e22,
e23: m.e23,
e31: m.e31,
e32: m.e32,
e33: m.e33,
}
}
}
impl Default for Matrix {
fn default() -> Self {
Self::IDENTITY
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Point {
pub x: f32,
pub y: f32,
}
impl Point {
#[must_use]
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
#[non_exhaustive]
pub enum BlendMethod {
Normal = 0,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
ColorBurn,
HardLight,
SoftLight,
Difference,
Exclusion,
Hue,
Saturation,
Color,
Luminosity,
Add,
}
impl BlendMethod {
fn to_raw(self) -> sys::Tvg_Blend_Method {
match self {
Self::Normal => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_NORMAL,
Self::Multiply => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_MULTIPLY,
Self::Screen => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_SCREEN,
Self::Overlay => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_OVERLAY,
Self::Darken => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_DARKEN,
Self::Lighten => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_LIGHTEN,
Self::ColorDodge => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_COLORDODGE,
Self::ColorBurn => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_COLORBURN,
Self::HardLight => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_HARDLIGHT,
Self::SoftLight => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_SOFTLIGHT,
Self::Difference => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_DIFFERENCE,
Self::Exclusion => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_EXCLUSION,
Self::Hue => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_HUE,
Self::Saturation => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_SATURATION,
Self::Color => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_COLOR,
Self::Luminosity => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_LUMINOSITY,
Self::Add => sys::Tvg_Blend_Method::TVG_BLEND_METHOD_ADD,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
#[non_exhaustive]
pub enum MaskMethod {
None = 0,
Alpha,
InvAlpha,
Luma,
InvLuma,
Add,
Subtract,
Intersect,
Difference,
Lighten,
Darken,
}
impl MaskMethod {
fn to_raw(self) -> sys::Tvg_Mask_Method {
match self {
Self::None => sys::Tvg_Mask_Method::TVG_MASK_METHOD_NONE,
Self::Alpha => sys::Tvg_Mask_Method::TVG_MASK_METHOD_ALPHA,
Self::InvAlpha => sys::Tvg_Mask_Method::TVG_MASK_METHOD_INVERSE_ALPHA,
Self::Luma => sys::Tvg_Mask_Method::TVG_MASK_METHOD_LUMA,
Self::InvLuma => sys::Tvg_Mask_Method::TVG_MASK_METHOD_INVERSE_LUMA,
Self::Add => sys::Tvg_Mask_Method::TVG_MASK_METHOD_ADD,
Self::Subtract => sys::Tvg_Mask_Method::TVG_MASK_METHOD_SUBTRACT,
Self::Intersect => sys::Tvg_Mask_Method::TVG_MASK_METHOD_INTERSECT,
Self::Difference => sys::Tvg_Mask_Method::TVG_MASK_METHOD_DIFFERENCE,
Self::Lighten => sys::Tvg_Mask_Method::TVG_MASK_METHOD_LIGHTEN,
Self::Darken => sys::Tvg_Mask_Method::TVG_MASK_METHOD_DARKEN,
}
}
pub(crate) fn from_raw(m: sys::Tvg_Mask_Method) -> Self {
match m {
sys::Tvg_Mask_Method::TVG_MASK_METHOD_ALPHA => Self::Alpha,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_INVERSE_ALPHA => Self::InvAlpha,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_LUMA => Self::Luma,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_INVERSE_LUMA => Self::InvLuma,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_ADD => Self::Add,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_SUBTRACT => Self::Subtract,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_INTERSECT => Self::Intersect,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_DIFFERENCE => Self::Difference,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_LIGHTEN => Self::Lighten,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_DARKEN => Self::Darken,
sys::Tvg_Mask_Method::TVG_MASK_METHOD_NONE => Self::None,
}
}
}
pub struct BorrowedPaint<'a> {
raw: sys::Tvg_Paint,
_life: core::marker::PhantomData<&'a ()>,
}
impl BorrowedPaint<'_> {
pub(crate) unsafe fn from_raw(raw: sys::Tvg_Paint) -> Self {
Self {
raw,
_life: core::marker::PhantomData,
}
}
pub fn raw(&self) -> sys::Tvg_Paint {
self.raw
}
pub fn paint_type(&self) -> Result<PaintType> {
let mut t = sys::Tvg_Type::TVG_TYPE_UNDEF;
Error::from_raw(unsafe { sys::tvg_paint_get_type(self.raw, &raw mut t) })?;
Ok(PaintType::from_raw(t))
}
pub fn id(&self) -> u32 {
unsafe { sys::tvg_paint_get_id(self.raw) }
}
pub fn opacity(&self) -> Result<u8> {
let mut o: u8 = 0;
Error::from_raw(unsafe { sys::tvg_paint_get_opacity(self.raw, &raw mut o) })?;
Ok(o)
}
pub fn bounds(&self) -> Result<(f32, f32, f32, f32)> {
let (mut x, mut y, mut w, mut h) = (0f32, 0f32, 0f32, 0f32);
Error::from_raw(unsafe {
sys::tvg_paint_get_aabb(self.raw, &raw mut x, &raw mut y, &raw mut w, &raw mut h)
})?;
Ok((x, y, w, h))
}
}
impl core::fmt::Debug for BorrowedPaint<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BorrowedPaint").finish_non_exhaustive()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum PaintType {
Undefined,
Shape,
Scene,
Picture,
Text,
LinearGradient,
RadialGradient,
}
impl PaintType {
pub(crate) fn from_raw(t: sys::Tvg_Type) -> Self {
match t {
sys::Tvg_Type::TVG_TYPE_SHAPE => Self::Shape,
sys::Tvg_Type::TVG_TYPE_SCENE => Self::Scene,
sys::Tvg_Type::TVG_TYPE_PICTURE => Self::Picture,
sys::Tvg_Type::TVG_TYPE_TEXT => Self::Text,
sys::Tvg_Type::TVG_TYPE_LINEAR_GRAD => Self::LinearGradient,
sys::Tvg_Type::TVG_TYPE_RADIAL_GRAD => Self::RadialGradient,
sys::Tvg_Type::TVG_TYPE_UNDEF => Self::Undefined,
}
}
}
pub(crate) mod sealed {
pub trait Sealed {}
}
pub trait Paint: sealed::Sealed {
fn raw(&self) -> sys::Tvg_Paint;
fn into_raw(self) -> sys::Tvg_Paint;
fn set_opacity(&mut self, opacity: u8) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_paint_set_opacity(self.raw(), opacity) })
}
fn opacity(&self) -> Result<u8> {
let mut opacity: u8 = 0;
Error::from_raw(unsafe { sys::tvg_paint_get_opacity(self.raw(), &raw mut opacity) })?;
Ok(opacity)
}
fn set_visible(&mut self, visible: bool) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_paint_set_visible(self.raw(), visible) })
}
fn visible(&self) -> bool {
unsafe { sys::tvg_paint_get_visible(self.raw()) }
}
fn scale(&mut self, factor: f32) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_paint_scale(self.raw(), factor) })
}
fn rotate(&mut self, degree: f32) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_paint_rotate(self.raw(), degree) })
}
fn translate(&mut self, x: f32, y: f32) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_paint_translate(self.raw(), x, y) })
}
fn set_transform(&mut self, m: &Matrix) -> Result<()> {
let raw_m = m.to_raw();
Error::from_raw(unsafe { sys::tvg_paint_set_transform(self.raw(), &raw const raw_m) })
}
fn transform(&self) -> Result<Matrix> {
let mut m = sys::Tvg_Matrix {
e11: 0.0,
e12: 0.0,
e13: 0.0,
e21: 0.0,
e22: 0.0,
e23: 0.0,
e31: 0.0,
e32: 0.0,
e33: 0.0,
};
Error::from_raw(unsafe { sys::tvg_paint_get_transform(self.raw(), &raw mut m) })?;
Ok(Matrix::from_raw(m))
}
fn bounds(&self) -> Result<(f32, f32, f32, f32)> {
let mut x: f32 = 0.0;
let mut y: f32 = 0.0;
let mut w: f32 = 0.0;
let mut h: f32 = 0.0;
Error::from_raw(unsafe {
sys::tvg_paint_get_aabb(self.raw(), &raw mut x, &raw mut y, &raw mut w, &raw mut h)
})?;
Ok((x, y, w, h))
}
fn bounds_obb(&self) -> Result<[Point; 4]> {
let mut pts = [sys::Tvg_Point { x: 0.0, y: 0.0 }; 4];
Error::from_raw(unsafe { sys::tvg_paint_get_obb(self.raw(), pts.as_mut_ptr()) })?;
Ok([
Point {
x: pts[0].x,
y: pts[0].y,
},
Point {
x: pts[1].x,
y: pts[1].y,
},
Point {
x: pts[2].x,
y: pts[2].y,
},
Point {
x: pts[3].x,
y: pts[3].y,
},
])
}
fn set_clip(&mut self, clipper: Shape<'_>) -> Result<()> {
let raw = clipper.raw();
let r = Error::from_raw(unsafe { sys::tvg_paint_set_clip(self.raw(), raw) });
if r.is_ok() {
let _ = clipper.into_raw();
}
r
}
fn clip(&self) -> Option<Shape<'_>> {
let raw = unsafe { sys::tvg_paint_get_clip(self.raw()) };
if raw.is_null() {
None
} else {
Some(unsafe { Shape::from_raw(raw, false) })
}
}
fn set_mask<P: Paint>(&mut self, mask: P, method: MaskMethod) -> Result<()> {
let raw = mask.raw();
let r = Error::from_raw(unsafe {
sys::tvg_paint_set_mask_method(self.raw(), raw, method.to_raw())
});
if r.is_ok() {
let _ = mask.into_raw();
}
r
}
fn mask(&self) -> Option<(BorrowedPaint<'_>, MaskMethod)> {
let mut target: sys::Tvg_Paint = core::ptr::null_mut();
let mut method = sys::Tvg_Mask_Method::TVG_MASK_METHOD_NONE;
let r = unsafe {
sys::tvg_paint_get_mask_method(
self.raw(),
(&raw mut target).cast::<sys::_Tvg_Paint>(),
&raw mut method,
)
};
if Error::from_raw(r).is_err() || target.is_null() {
return None;
}
Some((
unsafe { BorrowedPaint::from_raw(target) },
MaskMethod::from_raw(method),
))
}
fn set_blend(&mut self, method: BlendMethod) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_paint_set_blend_method(self.raw(), method.to_raw()) })
}
fn set_id(&mut self, id: u32) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_paint_set_id(self.raw(), id) })
}
fn id(&self) -> u32 {
unsafe { sys::tvg_paint_get_id(self.raw()) }
}
fn paint_type(&self) -> Result<PaintType> {
let mut t = sys::Tvg_Type::TVG_TYPE_UNDEF;
Error::from_raw(unsafe { sys::tvg_paint_get_type(self.raw(), &raw mut t) })?;
Ok(PaintType::from_raw(t))
}
fn intersects(&self, x: i32, y: i32, w: i32, h: i32) -> bool {
unsafe { sys::tvg_paint_intersects(self.raw(), x, y, w, h) }
}
fn duplicate(&self) -> Option<Self>
where
Self: Sized,
{
let raw = unsafe { sys::tvg_paint_duplicate(self.raw()) };
if raw.is_null() {
None
} else {
Some(unsafe { Self::from_raw_paint(raw) })
}
}
unsafe fn from_raw_paint(raw: sys::Tvg_Paint) -> Self
where
Self: Sized;
}