use tiny_skia::{
Color as TsColor, GradientStop as TsGradientStop, LinearGradient, Point, RadialGradient,
Shader, SpreadMode, Transform,
};
use zenith_scene::GradientPaint;
pub(super) fn gradient_shader(
x: f64,
y: f64,
w: f64,
h: f64,
gradient: &GradientPaint,
) -> Option<Shader<'static>> {
let stops: Vec<TsGradientStop> = gradient
.stops
.iter()
.map(|s| {
TsGradientStop::new(
s.offset as f32,
TsColor::from_rgba8(s.color.r, s.color.g, s.color.b, s.color.a),
)
})
.collect();
if gradient.radial {
let cx = x + w * gradient.center_x.unwrap_or(0.5);
let cy = y + h * gradient.center_y.unwrap_or(0.5);
let default_radius = (w / 2.0).hypot(h / 2.0);
let radius = (gradient.radius_frac.unwrap_or(1.0) * default_radius) as f32;
let center = Point::from_xy(cx as f32, cy as f32);
RadialGradient::new(
center,
center,
radius,
stops,
SpreadMode::Pad,
Transform::identity(),
)
} else {
let theta = gradient.angle_deg.to_radians();
let (dir_x, dir_y) = (theta.cos(), theta.sin());
let center = (x + w / 2.0, y + h / 2.0);
let line_len = (w * dir_x).abs() + (h * dir_y).abs();
let half = line_len / 2.0;
let start = Point::from_xy(
(center.0 - dir_x * half) as f32,
(center.1 - dir_y * half) as f32,
);
let end = Point::from_xy(
(center.0 + dir_x * half) as f32,
(center.1 + dir_y * half) as f32,
);
LinearGradient::new(start, end, stops, SpreadMode::Pad, Transform::identity())
}
}