use alloc::vec::Vec;
use core::mem;
use crate::color::Rgba;
use crate::error::{Error, Result};
use crate::paint::{Matrix, PaintType};
use thorvg_sys as sys;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ColorStop {
pub offset: f32,
pub color: Rgba,
}
impl ColorStop {
#[must_use]
pub const fn new(offset: f32, color: Rgba) -> Self {
Self { offset, color }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum FillSpread {
Pad,
Reflect,
Repeat,
}
impl FillSpread {
fn to_raw(self) -> sys::Tvg_Stroke_Fill {
match self {
FillSpread::Pad => sys::Tvg_Stroke_Fill::TVG_STROKE_FILL_PAD,
FillSpread::Reflect => sys::Tvg_Stroke_Fill::TVG_STROKE_FILL_REFLECT,
FillSpread::Repeat => sys::Tvg_Stroke_Fill::TVG_STROKE_FILL_REPEAT,
}
}
fn from_raw(s: sys::Tvg_Stroke_Fill) -> Self {
match s {
sys::Tvg_Stroke_Fill::TVG_STROKE_FILL_PAD => FillSpread::Pad,
sys::Tvg_Stroke_Fill::TVG_STROKE_FILL_REFLECT => FillSpread::Reflect,
sys::Tvg_Stroke_Fill::TVG_STROKE_FILL_REPEAT => FillSpread::Repeat,
}
}
}
pub struct LinearGradient<'eng> {
raw: sys::Tvg_Gradient,
_engine: core::marker::PhantomData<&'eng ()>,
}
impl LinearGradient<'_> {
pub(crate) fn new() -> Result<Self> {
let raw = unsafe { sys::tvg_linear_gradient_new() };
if raw.is_null() {
return Err(Error::FailedAllocation);
}
Ok(Self {
raw,
_engine: core::marker::PhantomData,
})
}
pub fn set_bounds(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_linear_gradient_set(self.raw, x1, y1, x2, y2) })
}
pub fn bounds(&self) -> Result<(f32, f32, f32, f32)> {
let (mut x1, mut y1, mut x2, mut y2) = (0.0f32, 0.0f32, 0.0f32, 0.0f32);
Error::from_raw(unsafe {
sys::tvg_linear_gradient_get(
self.raw,
&raw mut x1,
&raw mut y1,
&raw mut x2,
&raw mut y2,
)
})?;
Ok((x1, y1, x2, y2))
}
pub fn set_color_stops(&mut self, stops: &[ColorStop]) -> Result<()> {
set_color_stops_raw(self.raw, stops)
}
pub fn color_stops(&self) -> Result<Vec<ColorStop>> {
get_color_stops_raw(self.raw)
}
pub fn set_spread(&mut self, spread: FillSpread) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_gradient_set_spread(self.raw, spread.to_raw()) })
}
pub fn spread(&self) -> Result<FillSpread> {
get_spread_raw(self.raw)
}
pub fn set_transform(&mut self, m: &Matrix) -> Result<()> {
set_transform_raw(self.raw, m)
}
pub fn transform(&self) -> Result<Matrix> {
get_transform_raw(self.raw)
}
#[deprecated(
since = "0.3.0",
note = "renamed to `transform` for consistency with `Paint::transform`"
)]
pub fn get_transform(&self) -> Result<Matrix> {
self.transform()
}
pub fn gradient_type(&self) -> Result<PaintType> {
get_type_raw(self.raw)
}
pub fn duplicate(&self) -> Option<Self> {
let raw = unsafe { sys::tvg_gradient_duplicate(self.raw) };
if raw.is_null() {
None
} else {
Some(Self {
raw,
_engine: core::marker::PhantomData,
})
}
}
pub(crate) fn into_raw(self) -> sys::Tvg_Gradient {
let raw = self.raw;
mem::forget(self);
raw
}
}
impl Drop for LinearGradient<'_> {
fn drop(&mut self) {
unsafe {
sys::tvg_gradient_del(self.raw);
}
}
}
pub struct RadialGradient<'eng> {
raw: sys::Tvg_Gradient,
_engine: core::marker::PhantomData<&'eng ()>,
}
impl RadialGradient<'_> {
pub(crate) fn new() -> Result<Self> {
let raw = unsafe { sys::tvg_radial_gradient_new() };
if raw.is_null() {
return Err(Error::FailedAllocation);
}
Ok(Self {
raw,
_engine: core::marker::PhantomData,
})
}
pub fn set_radial(
&mut self,
cx: f32,
cy: f32,
r: f32,
fx: f32,
fy: f32,
fr: f32,
) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_radial_gradient_set(self.raw, cx, cy, r, fx, fy, fr) })
}
pub fn radial(&self) -> Result<(f32, f32, f32, f32, f32, f32)> {
let (mut cx, mut cy, mut r, mut fx, mut fy, mut fr) =
(0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32);
Error::from_raw(unsafe {
sys::tvg_radial_gradient_get(
self.raw,
&raw mut cx,
&raw mut cy,
&raw mut r,
&raw mut fx,
&raw mut fy,
&raw mut fr,
)
})?;
Ok((cx, cy, r, fx, fy, fr))
}
pub fn set_color_stops(&mut self, stops: &[ColorStop]) -> Result<()> {
set_color_stops_raw(self.raw, stops)
}
pub fn color_stops(&self) -> Result<Vec<ColorStop>> {
get_color_stops_raw(self.raw)
}
pub fn set_spread(&mut self, spread: FillSpread) -> Result<()> {
Error::from_raw(unsafe { sys::tvg_gradient_set_spread(self.raw, spread.to_raw()) })
}
pub fn spread(&self) -> Result<FillSpread> {
get_spread_raw(self.raw)
}
pub fn set_transform(&mut self, m: &Matrix) -> Result<()> {
set_transform_raw(self.raw, m)
}
pub fn transform(&self) -> Result<Matrix> {
get_transform_raw(self.raw)
}
#[deprecated(
since = "0.3.0",
note = "renamed to `transform` for consistency with `Paint::transform`"
)]
pub fn get_transform(&self) -> Result<Matrix> {
self.transform()
}
pub fn gradient_type(&self) -> Result<PaintType> {
get_type_raw(self.raw)
}
pub fn duplicate(&self) -> Option<Self> {
let raw = unsafe { sys::tvg_gradient_duplicate(self.raw) };
if raw.is_null() {
None
} else {
Some(Self {
raw,
_engine: core::marker::PhantomData,
})
}
}
pub(crate) fn into_raw(self) -> sys::Tvg_Gradient {
let raw = self.raw;
mem::forget(self);
raw
}
}
impl Drop for RadialGradient<'_> {
fn drop(&mut self) {
unsafe {
sys::tvg_gradient_del(self.raw);
}
}
}
pub struct BorrowedLinearGradient<'a> {
raw: sys::Tvg_Gradient,
_life: core::marker::PhantomData<&'a ()>,
}
impl BorrowedLinearGradient<'_> {
pub(crate) unsafe fn from_raw(raw: sys::Tvg_Gradient) -> Self {
Self {
raw,
_life: core::marker::PhantomData,
}
}
pub fn bounds(&self) -> Result<(f32, f32, f32, f32)> {
let (mut x1, mut y1, mut x2, mut y2) = (0.0f32, 0.0f32, 0.0f32, 0.0f32);
Error::from_raw(unsafe {
sys::tvg_linear_gradient_get(
self.raw,
&raw mut x1,
&raw mut y1,
&raw mut x2,
&raw mut y2,
)
})?;
Ok((x1, y1, x2, y2))
}
pub fn color_stops(&self) -> Result<Vec<ColorStop>> {
get_color_stops_raw(self.raw)
}
pub fn spread(&self) -> Result<FillSpread> {
get_spread_raw(self.raw)
}
pub fn transform(&self) -> Result<Matrix> {
get_transform_raw(self.raw)
}
}
impl core::fmt::Debug for BorrowedLinearGradient<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BorrowedLinearGradient")
.finish_non_exhaustive()
}
}
pub struct BorrowedRadialGradient<'a> {
raw: sys::Tvg_Gradient,
_life: core::marker::PhantomData<&'a ()>,
}
impl BorrowedRadialGradient<'_> {
pub(crate) unsafe fn from_raw(raw: sys::Tvg_Gradient) -> Self {
Self {
raw,
_life: core::marker::PhantomData,
}
}
pub fn radial(&self) -> Result<(f32, f32, f32, f32, f32, f32)> {
let (mut cx, mut cy, mut r, mut fx, mut fy, mut fr) =
(0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32, 0.0f32);
Error::from_raw(unsafe {
sys::tvg_radial_gradient_get(
self.raw,
&raw mut cx,
&raw mut cy,
&raw mut r,
&raw mut fx,
&raw mut fy,
&raw mut fr,
)
})?;
Ok((cx, cy, r, fx, fy, fr))
}
pub fn color_stops(&self) -> Result<Vec<ColorStop>> {
get_color_stops_raw(self.raw)
}
pub fn spread(&self) -> Result<FillSpread> {
get_spread_raw(self.raw)
}
pub fn transform(&self) -> Result<Matrix> {
get_transform_raw(self.raw)
}
}
impl core::fmt::Debug for BorrowedRadialGradient<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BorrowedRadialGradient")
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub enum BorrowedGradient<'a> {
Linear(BorrowedLinearGradient<'a>),
Radial(BorrowedRadialGradient<'a>),
}
impl BorrowedGradient<'_> {
pub(crate) unsafe fn from_raw(raw: sys::Tvg_Gradient) -> Result<Self> {
let kind = get_type_raw(raw)?;
Ok(match kind {
PaintType::LinearGradient => {
Self::Linear(unsafe { BorrowedLinearGradient::from_raw(raw) })
}
PaintType::RadialGradient => {
Self::Radial(unsafe { BorrowedRadialGradient::from_raw(raw) })
}
_ => return Err(Error::Unknown),
})
}
pub fn color_stops(&self) -> Result<Vec<ColorStop>> {
match self {
Self::Linear(g) => g.color_stops(),
Self::Radial(g) => g.color_stops(),
}
}
pub fn spread(&self) -> Result<FillSpread> {
match self {
Self::Linear(g) => g.spread(),
Self::Radial(g) => g.spread(),
}
}
pub fn transform(&self) -> Result<Matrix> {
match self {
Self::Linear(g) => g.transform(),
Self::Radial(g) => g.transform(),
}
}
}
#[allow(clippy::cast_possible_truncation)]
fn set_color_stops_raw(raw: sys::Tvg_Gradient, stops: &[ColorStop]) -> Result<()> {
let raw_stops: Vec<sys::Tvg_Color_Stop> = stops
.iter()
.map(|s| sys::Tvg_Color_Stop {
offset: s.offset,
r: s.color.r,
g: s.color.g,
b: s.color.b,
a: s.color.a,
})
.collect();
Error::from_raw(unsafe {
sys::tvg_gradient_set_color_stops(raw, raw_stops.as_ptr(), raw_stops.len() as u32)
})
}
fn get_color_stops_raw(raw: sys::Tvg_Gradient) -> Result<Vec<ColorStop>> {
let mut ptr: *const sys::Tvg_Color_Stop = core::ptr::null();
let mut cnt: u32 = 0;
Error::from_raw(unsafe { sys::tvg_gradient_get_color_stops(raw, &raw mut ptr, &raw mut cnt) })?;
if ptr.is_null() || cnt == 0 {
return Ok(Vec::new());
}
let slice = unsafe { core::slice::from_raw_parts(ptr, cnt as usize) };
Ok(slice
.iter()
.map(|s| ColorStop {
offset: s.offset,
color: Rgba::new(s.r, s.g, s.b, s.a),
})
.collect())
}
fn get_spread_raw(raw: sys::Tvg_Gradient) -> Result<FillSpread> {
let mut spread = sys::Tvg_Stroke_Fill::TVG_STROKE_FILL_PAD;
Error::from_raw(unsafe { sys::tvg_gradient_get_spread(raw, &raw mut spread) })?;
Ok(FillSpread::from_raw(spread))
}
fn set_transform_raw(raw: sys::Tvg_Gradient, m: &Matrix) -> Result<()> {
let rm = sys::Tvg_Matrix {
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,
};
Error::from_raw(unsafe { sys::tvg_gradient_set_transform(raw, &raw const rm) })
}
fn get_type_raw(raw: sys::Tvg_Gradient) -> Result<PaintType> {
let mut t = sys::Tvg_Type::TVG_TYPE_UNDEF;
Error::from_raw(unsafe { sys::tvg_gradient_get_type(raw, &raw mut t) })?;
Ok(PaintType::from_raw(t))
}
fn get_transform_raw(raw: sys::Tvg_Gradient) -> 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_gradient_get_transform(raw, &raw mut m) })?;
Ok(Matrix {
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,
})
}