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;