tiny_skia/shaders/
sweep_gradient.rs1use alloc::vec::Vec;
7
8use tiny_skia_path::Scalar;
9
10use crate::{ColorSpace, GradientStop, Point, Shader, SpreadMode, Transform};
11
12use super::gradient::{Gradient, DEGENERATE_THRESHOLD};
13use crate::pipeline::{RasterPipelineBuilder, Stage};
14
15#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
16use tiny_skia_path::NoStdFloat;
17
18#[derive(Clone, PartialEq, Debug)]
20pub struct SweepGradient {
21 pub(crate) base: Gradient,
22 t0: f32,
23 t1: f32,
24}
25
26impl SweepGradient {
27 #[allow(clippy::new_ret_no_self)]
29 pub fn new(
30 center: Point,
31 start_angle: f32,
32 end_angle: f32,
33 stops: Vec<GradientStop>,
34 mut mode: SpreadMode,
35 transform: Transform,
36 ) -> Option<Shader<'static>> {
37 if !start_angle.is_finite() || !end_angle.is_finite() || start_angle > end_angle {
38 return None;
39 }
40
41 match stops.as_slice() {
42 [] => return None,
43 [stop] => return Some(Shader::SolidColor(stop.color)),
44 _ => (),
45 }
46 transform.invert()?;
47 if start_angle.is_nearly_equal_within_tolerance(end_angle, DEGENERATE_THRESHOLD) {
48 if mode == SpreadMode::Pad && end_angle > DEGENERATE_THRESHOLD {
49 let front_color = stops.first().unwrap().color;
53 let back_color = stops.last().unwrap().color;
54 let mut new_stops = stops;
55 new_stops.clear();
56 new_stops.extend_from_slice(&[
57 GradientStop::new(0.0, front_color),
58 GradientStop::new(1.0, front_color),
59 GradientStop::new(1.0, back_color),
60 ]);
61 return SweepGradient::new(center, 0.0, end_angle, new_stops, mode, transform);
62 }
63 return None;
66 }
67 if start_angle <= 0.0 && end_angle >= 360.0 {
68 mode = SpreadMode::Pad;
69 }
70 let t0 = start_angle / 360.0;
71 let t1 = end_angle / 360.0;
72 Some(Shader::SweepGradient(SweepGradient {
73 base: Gradient::new(
74 stops,
75 mode,
76 transform,
77 Transform::from_translate(-center.x, -center.y),
78 ),
79 t0,
80 t1,
81 }))
82 }
83
84 pub(crate) fn is_opaque(&self) -> bool {
85 self.base.colors_are_opaque
86 }
87
88 pub(crate) fn push_stages(&self, cs: ColorSpace, p: &mut RasterPipelineBuilder) -> bool {
89 let scale = 1.0 / (self.t1 - self.t0);
90 let bias = -scale * self.t0;
91 p.ctx.two_point_conical_gradient.p0 = scale;
92 p.ctx.two_point_conical_gradient.p1 = bias;
93 self.base.push_stages(
94 p,
95 cs,
96 &|p| {
97 p.push(Stage::XYToUnitAngle);
98 if scale != 1.0 && bias != 0.0 {
99 p.push(Stage::ApplyConcentricScaleBias)
100 }
101 },
102 &|_| {},
103 )
104 }
105}