Skip to main content

cranpose_ui_graphics/
brush.rs

1//! Brush definitions for painting (solid colors, gradients, etc.)
2
3use crate::{color::Color, geometry::Point, render_effect::TileMode};
4
5/// Describes how a shape, path or text is filled: a flat color or a gradient. Compose's `Brush`.
6#[derive(Clone, Debug, PartialEq)]
7pub enum Brush {
8    /// A single flat color. Compose's `SolidColor`.
9    Solid(Color),
10    /// Colors blend along the line from `start` to `end`. Compose's `Brush.linearGradient`.
11    LinearGradient {
12        /// Colors to blend between, in order, from `start` to `end`.
13        colors: Vec<Color>,
14        /// Positions of `colors` along the line from `start` to `end`, from 0.0 to 1.0; `None`
15        /// spaces the colors evenly.
16        stops: Option<Vec<f32>>,
17        /// Where the first color is reached, in logical pixels relative to the top-left corner of
18        /// the painted area. An infinite coordinate means the far edge of that area.
19        start: Point,
20        /// Where the last color is reached, in logical pixels relative to the top-left corner of
21        /// the painted area. An infinite coordinate means the far edge of that area.
22        end: Point,
23        /// How the gradient continues beyond `start` and `end`.
24        tile_mode: TileMode,
25    },
26    /// Colors blend with distance from `center`. Compose's `Brush.radialGradient`.
27    RadialGradient {
28        /// Colors to blend between, in order, from `center` outward.
29        colors: Vec<Color>,
30        /// Positions of `colors` from 0.0 to 1.0; `None` spaces the colors evenly.
31        stops: Option<Vec<f32>>,
32        /// Gradient center in logical pixels, relative to the top-left corner of the painted area.
33        center: Point,
34        /// Distance in logical pixels from `center` at which the last color is reached.
35        radius: f32,
36        /// How the gradient continues beyond `radius`.
37        tile_mode: TileMode,
38    },
39    /// Colors blend by angle around `center`. Compose's `Brush.sweepGradient`.
40    SweepGradient {
41        /// Colors to blend between, in order.
42        colors: Vec<Color>,
43        /// Positions of `colors` from 0.0 to 1.0; `None` spaces the colors evenly.
44        stops: Option<Vec<f32>>,
45        /// Gradient center in logical pixels, relative to the top-left corner of the painted area.
46        center: Point,
47    },
48}
49
50fn split_color_stops(color_stops: Vec<(f32, Color)>) -> (Vec<Color>, Vec<f32>) {
51    let mut colors = Vec::with_capacity(color_stops.len());
52    let mut stops = Vec::with_capacity(color_stops.len());
53    for (stop, color) in color_stops {
54        colors.push(color);
55        stops.push(stop);
56    }
57    (colors, stops)
58}
59
60/// A colour paints as a solid brush wherever a brush is taken, as Compose
61/// takes a `Color` beside each `Brush` overload.
62impl From<Color> for Brush {
63    fn from(color: Color) -> Self {
64        Brush::Solid(color)
65    }
66}
67
68impl Brush {
69    /// Creates a brush that paints one flat color.
70    pub fn solid(color: Color) -> Self {
71        Brush::Solid(color)
72    }
73
74    /// Creates a linear gradient that defaults to Compose semantics:
75    /// start at `(0,0)`, end at `(+inf,+inf)`, and `TileMode::Clamp`.
76    pub fn linear_gradient(colors: Vec<Color>) -> Self {
77        Self::linear_gradient_with_tile_mode(
78            colors,
79            Point { x: 0.0, y: 0.0 },
80            Point {
81                x: f32::INFINITY,
82                y: f32::INFINITY,
83            },
84            TileMode::Clamp,
85        )
86    }
87
88    /// Creates a linear gradient that spreads `colors` evenly from `start` to `end`, clamping
89    /// outside that line.
90    pub fn linear_gradient_range(colors: Vec<Color>, start: Point, end: Point) -> Self {
91        Self::linear_gradient_with_tile_mode(colors, start, end, TileMode::Clamp)
92    }
93
94    /// Creates a linear gradient that spreads `colors` evenly from `start` to `end`, repeating or
95    /// clamping outside that line as `tile_mode` says.
96    pub fn linear_gradient_with_tile_mode(
97        colors: Vec<Color>,
98        start: Point,
99        end: Point,
100        tile_mode: TileMode,
101    ) -> Self {
102        Brush::LinearGradient {
103            colors,
104            stops: None,
105            start,
106            end,
107            tile_mode,
108        }
109    }
110
111    /// Creates a linear gradient from `(position, color)` pairs, with positions from 0.0 at `start`
112    /// to 1.0 at `end`.
113    pub fn linear_gradient_stops(
114        color_stops: Vec<(f32, Color)>,
115        start: Point,
116        end: Point,
117        tile_mode: TileMode,
118    ) -> Self {
119        let (colors, stops) = split_color_stops(color_stops);
120        Brush::LinearGradient {
121            colors,
122            stops: Some(stops),
123            start,
124            end,
125            tile_mode,
126        }
127    }
128
129    /// Creates a vertical gradient that spreads `colors` evenly from `start_y` to `end_y`, clamping
130    /// outside that range.
131    pub fn vertical_gradient(colors: Vec<Color>, start_y: f32, end_y: f32) -> Self {
132        Self::vertical_gradient_tiled(colors, start_y, end_y, TileMode::Clamp)
133    }
134
135    /// Creates a vertical gradient that spreads `colors` evenly from `start_y` to `end_y`,
136    /// repeating or clamping outside that range as `tile_mode` says.
137    pub fn vertical_gradient_tiled(
138        colors: Vec<Color>,
139        start_y: f32,
140        end_y: f32,
141        tile_mode: TileMode,
142    ) -> Self {
143        Self::linear_gradient_with_tile_mode(
144            colors,
145            Point { x: 0.0, y: start_y },
146            Point { x: 0.0, y: end_y },
147            tile_mode,
148        )
149    }
150
151    /// Creates a vertical gradient across the full height of the painted area, clamping at both
152    /// ends.
153    pub fn vertical_gradient_default(colors: Vec<Color>) -> Self {
154        Self::vertical_gradient_tiled(colors, 0.0, f32::INFINITY, TileMode::Clamp)
155    }
156
157    /// Creates a vertical gradient from `(position, color)` pairs, with positions from 0.0 at
158    /// `start_y` to 1.0 at `end_y`.
159    pub fn vertical_gradient_stops(
160        color_stops: Vec<(f32, Color)>,
161        start_y: f32,
162        end_y: f32,
163        tile_mode: TileMode,
164    ) -> Self {
165        Self::linear_gradient_stops(
166            color_stops,
167            Point { x: 0.0, y: start_y },
168            Point { x: 0.0, y: end_y },
169            tile_mode,
170        )
171    }
172
173    /// Creates a horizontal gradient that spreads `colors` evenly from `start_x` to `end_x`,
174    /// clamping outside that range.
175    pub fn horizontal_gradient(colors: Vec<Color>, start_x: f32, end_x: f32) -> Self {
176        Self::horizontal_gradient_tiled(colors, start_x, end_x, TileMode::Clamp)
177    }
178
179    /// Creates a horizontal gradient that spreads `colors` evenly from `start_x` to `end_x`,
180    /// repeating or clamping outside that range as `tile_mode` says.
181    pub fn horizontal_gradient_tiled(
182        colors: Vec<Color>,
183        start_x: f32,
184        end_x: f32,
185        tile_mode: TileMode,
186    ) -> Self {
187        Self::linear_gradient_with_tile_mode(
188            colors,
189            Point { x: start_x, y: 0.0 },
190            Point { x: end_x, y: 0.0 },
191            tile_mode,
192        )
193    }
194
195    /// Creates a horizontal gradient across the full width of the painted area, clamping at both
196    /// ends.
197    pub fn horizontal_gradient_default(colors: Vec<Color>) -> Self {
198        Self::horizontal_gradient_tiled(colors, 0.0, f32::INFINITY, TileMode::Clamp)
199    }
200
201    /// Creates a horizontal gradient from `(position, color)` pairs, with positions from 0.0 at
202    /// `start_x` to 1.0 at `end_x`.
203    pub fn horizontal_gradient_stops(
204        color_stops: Vec<(f32, Color)>,
205        start_x: f32,
206        end_x: f32,
207        tile_mode: TileMode,
208    ) -> Self {
209        Self::linear_gradient_stops(
210            color_stops,
211            Point { x: start_x, y: 0.0 },
212            Point { x: end_x, y: 0.0 },
213            tile_mode,
214        )
215    }
216
217    /// Creates a radial gradient that spreads `colors` evenly from `center` out to `radius`,
218    /// clamping beyond it.
219    pub fn radial_gradient(colors: Vec<Color>, center: Point, radius: f32) -> Self {
220        Self::radial_gradient_tiled(colors, center, radius, TileMode::Clamp)
221    }
222
223    /// Creates a radial gradient that spreads `colors` evenly from `center` out to `radius`,
224    /// repeating or clamping beyond it as `tile_mode` says.
225    pub fn radial_gradient_tiled(
226        colors: Vec<Color>,
227        center: Point,
228        radius: f32,
229        tile_mode: TileMode,
230    ) -> Self {
231        Brush::RadialGradient {
232            colors,
233            stops: None,
234            center,
235            radius,
236            tile_mode,
237        }
238    }
239
240    /// Creates a radial gradient from `(position, color)` pairs, with positions from 0.0 at the
241    /// center to 1.0 at `radius`.
242    pub fn radial_gradient_stops(
243        color_stops: Vec<(f32, Color)>,
244        center: Point,
245        radius: f32,
246        tile_mode: TileMode,
247    ) -> Self {
248        let (colors, stops) = split_color_stops(color_stops);
249        Brush::RadialGradient {
250            colors,
251            stops: Some(stops),
252            center,
253            radius,
254            tile_mode,
255        }
256    }
257
258    /// Creates a sweep gradient that spreads `colors` evenly around `center`.
259    pub fn sweep_gradient(colors: Vec<Color>, center: Point) -> Self {
260        Brush::SweepGradient {
261            colors,
262            stops: None,
263            center,
264        }
265    }
266
267    /// Creates a sweep gradient from `(position, color)` pairs, with positions from 0.0 to 1.0
268    /// around `center`.
269    pub fn sweep_gradient_stops(color_stops: Vec<(f32, Color)>, center: Point) -> Self {
270        let (colors, stops) = split_color_stops(color_stops);
271        Brush::SweepGradient {
272            colors,
273            stops: Some(stops),
274            center,
275        }
276    }
277}
278
279#[cfg(test)]
280#[path = "tests/brush_tests.rs"]
281mod tests;