1 | // TODO: prefix paint creation functions with make_ or new_ |
2 | // so that they are easier to find when autocompleting |
3 | |
4 | use std::rc::Rc; |
5 | |
6 | use crate::{geometry::Position, Align, Baseline, Color, FillRule, FontId, ImageId, LineCap, LineJoin}; |
7 | |
8 | #[derive (Copy, Clone, Debug, PartialEq, Default)] |
9 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
10 | pub(crate) struct GradientStop(pub f32, pub Color); |
11 | |
12 | // We use MultiStopGradient as a key since we cache them. We either need |
13 | // to define Hash (for HashMap) or Ord for (BTreeMap). |
14 | impl Eq for GradientStop {} |
15 | impl Ord for GradientStop { |
16 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
17 | if (other.0, other.1) < (self.0, self.1) { |
18 | std::cmp::Ordering::Less |
19 | } else if (self.0, self.1) < (other.0, other.1) { |
20 | std::cmp::Ordering::Greater |
21 | } else { |
22 | std::cmp::Ordering::Equal |
23 | } |
24 | } |
25 | } |
26 | |
27 | impl PartialOrd for GradientStop { |
28 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |
29 | Some(self.cmp(other)) |
30 | } |
31 | } |
32 | |
33 | #[derive (Clone, Debug, PartialEq)] |
34 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
35 | pub(crate) struct MultiStopGradient { |
36 | shared_stops: Rc<[GradientStop]>, |
37 | tint: f32, |
38 | } |
39 | |
40 | impl MultiStopGradient { |
41 | pub(crate) fn get(&self, index: usize) -> GradientStop { |
42 | let mut stop: GradientStop = self |
43 | .shared_stops |
44 | .get(index) |
45 | .cloned() |
46 | .unwrap_or_else(|| GradientStop(2.0, Color::black())); |
47 | |
48 | stop.1.a *= self.tint; |
49 | stop |
50 | } |
51 | |
52 | pub(crate) fn pairs(&self) -> impl Iterator<Item = [GradientStop; 2]> + '_ { |
53 | self.shared_stops.as_ref().windows(size:2).map(move |pair: &[GradientStop]| { |
54 | let mut stops: [GradientStop; 2] = [pair[0], pair[1]]; |
55 | stops[0].1.a *= self.tint; |
56 | stops[1].1.a *= self.tint; |
57 | stops |
58 | }) |
59 | } |
60 | } |
61 | |
62 | impl Eq for MultiStopGradient {} |
63 | |
64 | impl PartialOrd for MultiStopGradient { |
65 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |
66 | Some(self.cmp(other)) |
67 | } |
68 | } |
69 | |
70 | impl Ord for MultiStopGradient { |
71 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
72 | if (&other.shared_stops, other.tint) < (&self.shared_stops, self.tint) { |
73 | std::cmp::Ordering::Less |
74 | } else if (&self.shared_stops, self.tint) < (&other.shared_stops, other.tint) { |
75 | std::cmp::Ordering::Greater |
76 | } else { |
77 | std::cmp::Ordering::Equal |
78 | } |
79 | } |
80 | } |
81 | |
82 | #[allow (clippy::large_enum_variant)] |
83 | #[derive (Clone, Debug, PartialEq, PartialOrd)] |
84 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
85 | pub(crate) enum GradientColors { |
86 | TwoStop { start_color: Color, end_color: Color }, |
87 | MultiStop { stops: MultiStopGradient }, |
88 | } |
89 | impl GradientColors { |
90 | fn mul_alpha(&mut self, a: f32) { |
91 | match self { |
92 | GradientColors::TwoStop { start_color, end_color } => { |
93 | start_color.a *= a; |
94 | end_color.a *= a; |
95 | } |
96 | GradientColors::MultiStop { stops, .. } => { |
97 | stops.tint *= a; |
98 | } |
99 | } |
100 | } |
101 | fn from_stops<Stops>(stops: Stops) -> GradientColors |
102 | where |
103 | Stops: IntoIterator<Item = (f32, Color)>, |
104 | { |
105 | let mut stops = stops.into_iter(); |
106 | let first_stop = match stops.next() { |
107 | Some(stop) => stop, |
108 | None => { |
109 | // No stops, we use black. |
110 | return GradientColors::TwoStop { |
111 | start_color: Color::black(), |
112 | end_color: Color::black(), |
113 | }; |
114 | } |
115 | }; |
116 | let second_stop = match stops.next() { |
117 | Some(stop) => stop, |
118 | None => { |
119 | // One stop devolves to a solid color fill (but using the gradient shader variation). |
120 | return GradientColors::TwoStop { |
121 | start_color: first_stop.1, |
122 | end_color: first_stop.1, |
123 | }; |
124 | } |
125 | }; |
126 | |
127 | let maybe_third_stop = stops.next(); |
128 | |
129 | if maybe_third_stop.is_none() && first_stop.0 <= 0.0 && second_stop.0 >= 1.0 { |
130 | // Two stops takes the classic gradient path, so long as the stop positions are at |
131 | // the extents (if the stop positions are inset then we'll fill to them). |
132 | return GradientColors::TwoStop { |
133 | start_color: first_stop.1, |
134 | end_color: second_stop.1, |
135 | }; |
136 | } |
137 | |
138 | // Actual multistop gradient. We copy out the stops and then use a stop with a |
139 | // position > 1.0 as a sentinel. GradientStore ignores stop positions > 1.0 |
140 | // when synthesizing the gradient texture. |
141 | let out_stops = [first_stop, second_stop] |
142 | .into_iter() |
143 | .chain(maybe_third_stop) |
144 | .chain(stops) |
145 | .map(|(stop, color)| GradientStop(stop, color)) |
146 | .collect(); |
147 | GradientColors::MultiStop { |
148 | stops: MultiStopGradient { |
149 | shared_stops: out_stops, |
150 | tint: 1.0, |
151 | }, |
152 | } |
153 | } |
154 | } |
155 | |
156 | #[derive (Clone, Debug)] |
157 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
158 | pub(crate) enum PaintFlavor { |
159 | Color(Color), |
160 | #[cfg_attr (feature = "serde" , serde(skip))] |
161 | Image { |
162 | id: ImageId, |
163 | center: Position, |
164 | width: f32, |
165 | height: f32, |
166 | angle: f32, |
167 | tint: Color, |
168 | }, |
169 | LinearGradient { |
170 | start: Position, |
171 | end: Position, |
172 | colors: GradientColors, |
173 | }, |
174 | BoxGradient { |
175 | pos: Position, |
176 | width: f32, |
177 | height: f32, |
178 | radius: f32, |
179 | feather: f32, |
180 | colors: GradientColors, |
181 | }, |
182 | RadialGradient { |
183 | center: Position, |
184 | in_radius: f32, |
185 | out_radius: f32, |
186 | colors: GradientColors, |
187 | }, |
188 | } |
189 | |
190 | // Convenience method to fetch the GradientColors out of a PaintFlavor |
191 | impl PaintFlavor { |
192 | pub(crate) fn mul_alpha(&mut self, a: f32) { |
193 | match self { |
194 | PaintFlavor::Color(color) => { |
195 | color.a *= a; |
196 | } |
197 | PaintFlavor::Image { tint, .. } => { |
198 | tint.a *= a; |
199 | } |
200 | PaintFlavor::LinearGradient { colors, .. } => { |
201 | colors.mul_alpha(a); |
202 | } |
203 | PaintFlavor::BoxGradient { colors, .. } => { |
204 | colors.mul_alpha(a); |
205 | } |
206 | PaintFlavor::RadialGradient { colors, .. } => { |
207 | colors.mul_alpha(a); |
208 | } |
209 | } |
210 | } |
211 | |
212 | pub(crate) fn gradient_colors(&self) -> Option<&GradientColors> { |
213 | match self { |
214 | PaintFlavor::LinearGradient { colors, .. } => Some(colors), |
215 | PaintFlavor::BoxGradient { colors, .. } => Some(colors), |
216 | PaintFlavor::RadialGradient { colors, .. } => Some(colors), |
217 | _ => None, |
218 | } |
219 | } |
220 | |
221 | /// Returns true if this paint is an untransformed image paint without anti-aliasing at the edges in case of a fill |
222 | pub(crate) fn is_straight_tinted_image(&self, shape_anti_alias: bool) -> bool { |
223 | matches!(self, &PaintFlavor::Image { angle, .. } if angle == 0.0 && !shape_anti_alias) |
224 | } |
225 | } |
226 | |
227 | #[derive (Copy, Clone, Debug)] |
228 | pub(crate) enum GlyphTexture { |
229 | None, |
230 | AlphaMask(ImageId), |
231 | ColorTexture(ImageId), |
232 | } |
233 | |
234 | impl Default for GlyphTexture { |
235 | fn default() -> Self { |
236 | Self::None |
237 | } |
238 | } |
239 | |
240 | #[derive (Clone, Debug)] |
241 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
242 | pub(crate) struct StrokeSettings { |
243 | pub(crate) stencil_strokes: bool, |
244 | pub(crate) miter_limit: f32, |
245 | pub(crate) line_width: f32, |
246 | pub(crate) line_cap_start: LineCap, |
247 | pub(crate) line_cap_end: LineCap, |
248 | pub(crate) line_join: LineJoin, |
249 | } |
250 | |
251 | impl Default for StrokeSettings { |
252 | fn default() -> Self { |
253 | Self { |
254 | stencil_strokes: true, |
255 | miter_limit: 10.0, |
256 | line_width: 1.0, |
257 | line_cap_start: Default::default(), |
258 | line_cap_end: Default::default(), |
259 | line_join: Default::default(), |
260 | } |
261 | } |
262 | } |
263 | |
264 | #[derive (Clone, Debug)] |
265 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
266 | pub(crate) struct TextSettings { |
267 | #[cfg_attr (feature = "serde" , serde(skip))] |
268 | pub(crate) font_ids: [Option<FontId>; 8], |
269 | pub(crate) font_size: f32, |
270 | pub(crate) letter_spacing: f32, |
271 | pub(crate) text_baseline: Baseline, |
272 | pub(crate) text_align: Align, |
273 | } |
274 | |
275 | impl Default for TextSettings { |
276 | fn default() -> Self { |
277 | Self { |
278 | font_ids: Default::default(), |
279 | font_size: 16.0, |
280 | letter_spacing: 0.0, |
281 | text_baseline: Default::default(), |
282 | text_align: Default::default(), |
283 | } |
284 | } |
285 | } |
286 | |
287 | /// Struct controlling how graphical shapes are rendered. |
288 | /// |
289 | /// The Paint struct is a relatively lightweight object which contains all the information needed to |
290 | /// display something on the canvas. Unlike the HTML canvas where the current drawing style is stored |
291 | /// in an internal stack this paint struct is simply passed to the relevant drawing methods on the canvas. |
292 | /// |
293 | /// Clients code can have as many paints as they desire for different use cases and styles. This makes |
294 | /// the internal stack in the [Canvas](struct.Canvas.html) struct much lighter since it only needs to |
295 | /// contain the transform stack and current scissor rectangle. |
296 | /// |
297 | /// # Example |
298 | /// ``` |
299 | /// use femtovg::{Paint, Path, Color, Canvas, renderer::Void}; |
300 | /// |
301 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
302 | /// |
303 | /// let fill_paint = Paint::color(Color::hex("454545" )); |
304 | /// let mut stroke_paint = Paint::color(Color::hex("bababa" )); |
305 | /// stroke_paint.set_line_width(4.0); |
306 | /// |
307 | /// let mut path = Path::new(); |
308 | /// path.rounded_rect(10.0, 10.0, 100.0, 100.0, 20.0); |
309 | /// canvas.fill_path(&path, &fill_paint); |
310 | /// canvas.stroke_path(&path, &stroke_paint); |
311 | /// ``` |
312 | #[derive (Clone, Debug)] |
313 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
314 | pub struct Paint { |
315 | pub(crate) flavor: PaintFlavor, |
316 | pub(crate) shape_anti_alias: bool, |
317 | pub(crate) stroke: StrokeSettings, |
318 | pub(crate) text: TextSettings, |
319 | pub(crate) fill_rule: FillRule, |
320 | } |
321 | |
322 | impl Default for Paint { |
323 | fn default() -> Self { |
324 | Self { |
325 | flavor: PaintFlavor::Color(Color::white()), |
326 | shape_anti_alias: true, |
327 | stroke: StrokeSettings::default(), |
328 | text: TextSettings::default(), |
329 | fill_rule: Default::default(), |
330 | } |
331 | } |
332 | } |
333 | |
334 | impl Paint { |
335 | /// Creates a new solid color paint |
336 | pub fn color(color: Color) -> Self { |
337 | Paint::with_flavor(PaintFlavor::Color(color)) |
338 | } |
339 | |
340 | fn with_flavor(flavor: PaintFlavor) -> Self { |
341 | Paint { |
342 | flavor, |
343 | ..Default::default() |
344 | } |
345 | } |
346 | |
347 | /// Creates a new image pattern paint. |
348 | /// |
349 | /// * `id` - is handle to the image to render |
350 | /// * `cx` `cy` - Specify the top-left location of the image pattern |
351 | /// * `width` `height` - The size of one image |
352 | /// * `angle` - Rotation around the top-left corner |
353 | /// * `alpha` - Transparency applied on the image |
354 | /// |
355 | /// # Example |
356 | /// ``` |
357 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
358 | /// |
359 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
360 | /// |
361 | /// let image_id = canvas.load_image_file("examples/assets/rust-logo.png" , ImageFlags::GENERATE_MIPMAPS).expect("Cannot create image" ); |
362 | /// let fill_paint = Paint::image(image_id, 10.0, 10.0, 85.0, 85.0, 0.0, 1.0); |
363 | /// |
364 | /// let mut path = Path::new(); |
365 | /// path.rect(10.0, 10.0, 85.0, 85.0); |
366 | /// canvas.fill_path(&path, &fill_paint); |
367 | /// ``` |
368 | pub fn image(id: ImageId, cx: f32, cy: f32, width: f32, height: f32, angle: f32, alpha: f32) -> Self { |
369 | Paint::with_flavor(PaintFlavor::Image { |
370 | id, |
371 | center: Position { x: cx, y: cy }, |
372 | width, |
373 | height, |
374 | angle, |
375 | tint: Color::rgbaf(1.0, 1.0, 1.0, alpha), |
376 | }) |
377 | } |
378 | |
379 | /// Like `image`, but allows for adding a tint, or a color which will transform each pixel's |
380 | /// color via channel-wise multiplication. |
381 | pub fn image_tint(id: ImageId, cx: f32, cy: f32, width: f32, height: f32, angle: f32, tint: Color) -> Self { |
382 | Paint::with_flavor(PaintFlavor::Image { |
383 | id, |
384 | center: Position { x: cx, y: cy }, |
385 | width, |
386 | height, |
387 | angle, |
388 | tint, |
389 | }) |
390 | } |
391 | |
392 | /// Creates and returns a linear gradient paint. |
393 | /// |
394 | /// The gradient is transformed by the current transform when it is passed to fill_path() or stroke_path(). |
395 | /// |
396 | /// # Example |
397 | /// ``` |
398 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
399 | /// |
400 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
401 | /// |
402 | /// let bg = Paint::linear_gradient(0.0, 0.0, 0.0, 100.0, Color::rgba(255, 255, 255, 16), Color::rgba(0, 0, 0, 16)); |
403 | /// let mut path = Path::new(); |
404 | /// path.rounded_rect(0.0, 0.0, 100.0, 100.0, 5.0); |
405 | /// canvas.fill_path(&path, &bg); |
406 | /// ``` |
407 | pub fn linear_gradient( |
408 | start_x: f32, |
409 | start_y: f32, |
410 | end_x: f32, |
411 | end_y: f32, |
412 | start_color: Color, |
413 | end_color: Color, |
414 | ) -> Self { |
415 | Paint::with_flavor(PaintFlavor::LinearGradient { |
416 | start: Position { x: start_x, y: start_y }, |
417 | end: Position { x: end_x, y: end_y }, |
418 | colors: GradientColors::TwoStop { start_color, end_color }, |
419 | }) |
420 | } |
421 | /// Creates and returns a linear gradient paint with two or more stops. |
422 | /// |
423 | /// The gradient is transformed by the current transform when it is passed to fill_path() or stroke_path(). |
424 | /// |
425 | /// # Example |
426 | /// ``` |
427 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
428 | /// |
429 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
430 | /// |
431 | /// let bg = Paint::linear_gradient_stops( |
432 | /// 0.0, 0.0, |
433 | /// 0.0, 100.0, |
434 | /// [ |
435 | /// (0.0, Color::rgba(255, 255, 255, 16)), |
436 | /// (0.5, Color::rgba(0, 0, 0, 16)), |
437 | /// (1.0, Color::rgba(255, 0, 0, 16)) |
438 | /// ]); |
439 | /// let mut path = Path::new(); |
440 | /// path.rounded_rect(0.0, 0.0, 100.0, 100.0, 5.0); |
441 | /// canvas.fill_path(&path, &bg); |
442 | /// ``` |
443 | pub fn linear_gradient_stops( |
444 | start_x: f32, |
445 | start_y: f32, |
446 | end_x: f32, |
447 | end_y: f32, |
448 | stops: impl IntoIterator<Item = (f32, Color)>, |
449 | ) -> Self { |
450 | Paint::with_flavor(PaintFlavor::LinearGradient { |
451 | start: Position { x: start_x, y: start_y }, |
452 | end: Position { x: end_x, y: end_y }, |
453 | colors: GradientColors::from_stops(stops), |
454 | }) |
455 | } |
456 | |
457 | #[allow (clippy::too_many_arguments)] |
458 | /// Creates and returns a box gradient. |
459 | /// |
460 | /// Box gradient is a feathered rounded rectangle, it is useful for rendering |
461 | /// drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle, |
462 | /// (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry |
463 | /// the border of the rectangle is. Parameter inner_color specifies the inner color and outer_color the outer color of the gradient. |
464 | /// The gradient is transformed by the current transform when it is passed to fill_path() or stroke_path(). |
465 | /// |
466 | /// # Example |
467 | /// ``` |
468 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
469 | /// |
470 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
471 | /// |
472 | /// let bg = Paint::box_gradient( |
473 | /// 0.0, |
474 | /// 0.0, |
475 | /// 100.0, |
476 | /// 100.0, |
477 | /// 10.0, |
478 | /// 10.0, |
479 | /// Color::rgba(0, 0, 0, 128), |
480 | /// Color::rgba(0, 0, 0, 0), |
481 | /// ); |
482 | /// |
483 | /// let mut path = Path::new(); |
484 | /// path.rounded_rect(0.0, 0.0, 100.0, 100.0, 5.0); |
485 | /// canvas.fill_path(&path, &bg); |
486 | /// ``` |
487 | pub fn box_gradient( |
488 | x: f32, |
489 | y: f32, |
490 | width: f32, |
491 | height: f32, |
492 | radius: f32, |
493 | feather: f32, |
494 | inner_color: Color, |
495 | outer_color: Color, |
496 | ) -> Self { |
497 | Paint::with_flavor(PaintFlavor::BoxGradient { |
498 | pos: Position { x, y }, |
499 | width, |
500 | height, |
501 | radius, |
502 | feather, |
503 | colors: GradientColors::TwoStop { |
504 | start_color: inner_color, |
505 | end_color: outer_color, |
506 | }, |
507 | }) |
508 | } |
509 | |
510 | /// Creates and returns a radial gradient. |
511 | /// |
512 | /// Parameters (cx,cy) specify the center, in_radius and out_radius specify |
513 | /// the inner and outer radius of the gradient, inner_color specifies the start color and outer_color the end color. |
514 | /// The gradient is transformed by the current transform when it is passed to fill_paint() or stroke_paint(). |
515 | /// |
516 | /// # Example |
517 | /// ``` |
518 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
519 | /// |
520 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
521 | /// |
522 | /// let bg = Paint::radial_gradient( |
523 | /// 50.0, |
524 | /// 50.0, |
525 | /// 18.0, |
526 | /// 24.0, |
527 | /// Color::rgba(0, 0, 0, 128), |
528 | /// Color::rgba(0, 0, 0, 0), |
529 | /// ); |
530 | /// |
531 | /// let mut path = Path::new(); |
532 | /// path.circle(50.0, 50.0, 20.0); |
533 | /// canvas.fill_path(&path, &bg); |
534 | /// ``` |
535 | pub fn radial_gradient( |
536 | cx: f32, |
537 | cy: f32, |
538 | in_radius: f32, |
539 | out_radius: f32, |
540 | inner_color: Color, |
541 | outer_color: Color, |
542 | ) -> Self { |
543 | Paint::with_flavor(PaintFlavor::RadialGradient { |
544 | center: Position { x: cx, y: cy }, |
545 | in_radius, |
546 | out_radius, |
547 | colors: GradientColors::TwoStop { |
548 | start_color: inner_color, |
549 | end_color: outer_color, |
550 | }, |
551 | }) |
552 | } |
553 | |
554 | /// Creates and returns a multi-stop radial gradient. |
555 | /// |
556 | /// Parameters (cx,cy) specify the center, in_radius and out_radius specify the inner and outer radius of the gradient, |
557 | /// colors specifies a list of color stops with offsets. The first offset should be 0.0 and the last offset should be 1.0. |
558 | /// |
559 | /// The gradient is transformed by the current transform when it is passed to fill_paint() or stroke_paint(). |
560 | /// |
561 | /// # Example |
562 | /// ``` |
563 | /// use femtovg::{Paint, Path, Color, Canvas, ImageFlags, renderer::Void}; |
564 | /// |
565 | /// let mut canvas = Canvas::new(Void).expect("Cannot create canvas" ); |
566 | /// |
567 | /// let bg = Paint::radial_gradient_stops( |
568 | /// 50.0, |
569 | /// 50.0, |
570 | /// 18.0, |
571 | /// 24.0, |
572 | /// [ |
573 | /// (0.0, Color::rgba(0, 0, 0, 128)), |
574 | /// (0.5, Color::rgba(0, 0, 128, 128)), |
575 | /// (1.0, Color::rgba(0, 128, 0, 128)) |
576 | /// ] |
577 | /// ); |
578 | /// |
579 | /// let mut path = Path::new(); |
580 | /// path.circle(50.0, 50.0, 20.0); |
581 | /// canvas.fill_path(&path, &bg); |
582 | /// ``` |
583 | pub fn radial_gradient_stops( |
584 | cx: f32, |
585 | cy: f32, |
586 | in_radius: f32, |
587 | out_radius: f32, |
588 | stops: impl IntoIterator<Item = (f32, Color)>, |
589 | ) -> Self { |
590 | Paint::with_flavor(PaintFlavor::RadialGradient { |
591 | center: Position { x: cx, y: cy }, |
592 | in_radius, |
593 | out_radius, |
594 | colors: GradientColors::from_stops(stops), |
595 | }) |
596 | } |
597 | |
598 | /// Creates a new solid color paint |
599 | pub fn set_color(&mut self, color: Color) { |
600 | self.flavor = PaintFlavor::Color(color); |
601 | } |
602 | |
603 | /// Returns the paint with a new solid color set to the specified value. |
604 | pub fn with_color(mut self, color: Color) -> Self { |
605 | self.set_color(color); |
606 | self |
607 | } |
608 | |
609 | /// Returns boolean if the shapes drawn with this paint will be antialiased. |
610 | pub fn anti_alias(&self) -> bool { |
611 | self.shape_anti_alias |
612 | } |
613 | |
614 | /// Sets whether shapes drawn with this paint will be anti aliased. Enabled by default. |
615 | pub fn set_anti_alias(&mut self, value: bool) { |
616 | self.shape_anti_alias = value; |
617 | } |
618 | |
619 | /// Returns the paint with anti alias set to the specified value. |
620 | pub fn with_anti_alias(mut self, value: bool) -> Self { |
621 | self.set_anti_alias(value); |
622 | self |
623 | } |
624 | |
625 | /// True if this paint uses higher quality stencil strokes. |
626 | pub fn stencil_strokes(&self) -> bool { |
627 | self.stroke.stencil_strokes |
628 | } |
629 | |
630 | /// Sets whether to use higher quality stencil strokes. |
631 | pub fn set_stencil_strokes(&mut self, value: bool) { |
632 | self.stroke.stencil_strokes = value; |
633 | } |
634 | |
635 | /// Returns the paint with stencil strokes set to the specified value. |
636 | pub fn with_stencil_strokes(mut self, value: bool) -> Self { |
637 | self.set_stencil_strokes(value); |
638 | self |
639 | } |
640 | |
641 | /// Returns the current line width. |
642 | pub fn line_width(&self) -> f32 { |
643 | self.stroke.line_width |
644 | } |
645 | |
646 | /// Sets the line width for shapes stroked with this paint. |
647 | pub fn set_line_width(&mut self, width: f32) { |
648 | self.stroke.line_width = width; |
649 | } |
650 | |
651 | /// Returns the paint with line width set to the specified value. |
652 | pub fn with_line_width(mut self, width: f32) -> Self { |
653 | self.set_line_width(width); |
654 | self |
655 | } |
656 | |
657 | /// Getter for the miter limit |
658 | pub fn miter_limit(&self) -> f32 { |
659 | self.stroke.miter_limit |
660 | } |
661 | |
662 | /// Sets the limit at which a sharp corner is drawn beveled. |
663 | /// |
664 | /// If the miter at a corner exceeds this limit, LineJoin is replaced with LineJoin::Bevel. |
665 | pub fn set_miter_limit(&mut self, limit: f32) { |
666 | self.stroke.miter_limit = limit; |
667 | } |
668 | |
669 | /// Returns the paint with the miter limit set to the specified value. |
670 | pub fn with_miter_limit(mut self, limit: f32) -> Self { |
671 | self.set_miter_limit(limit); |
672 | self |
673 | } |
674 | |
675 | /// Returns the current start line cap for this paint. |
676 | pub fn line_cap_start(&self) -> LineCap { |
677 | self.stroke.line_cap_start |
678 | } |
679 | |
680 | /// Returns the current start line cap for this paint. |
681 | pub fn line_cap_end(&self) -> LineCap { |
682 | self.stroke.line_cap_end |
683 | } |
684 | |
685 | /// Sets how the start and end of the line (cap) is drawn |
686 | /// |
687 | /// By default it's set to LineCap::Butt |
688 | pub fn set_line_cap(&mut self, cap: LineCap) { |
689 | self.stroke.line_cap_start = cap; |
690 | self.stroke.line_cap_end = cap; |
691 | } |
692 | |
693 | /// Returns the paint with line cap set to the specified value. |
694 | pub fn with_line_cap(mut self, cap: LineCap) -> Self { |
695 | self.set_line_cap(cap); |
696 | self |
697 | } |
698 | |
699 | /// Sets how the beggining cap of the line is drawn |
700 | /// |
701 | /// By default it's set to LineCap::Butt |
702 | pub fn set_line_cap_start(&mut self, cap: LineCap) { |
703 | self.stroke.line_cap_start = cap; |
704 | } |
705 | |
706 | /// Returns the paint with the beginning cap of the line set to the specified value. |
707 | pub fn with_line_cap_start(mut self, cap: LineCap) -> Self { |
708 | self.set_line_cap_start(cap); |
709 | self |
710 | } |
711 | |
712 | /// Sets how the end cap of the line is drawn |
713 | /// |
714 | /// By default it's set to LineCap::Butt |
715 | pub fn set_line_cap_end(&mut self, cap: LineCap) { |
716 | self.stroke.line_cap_end = cap; |
717 | } |
718 | |
719 | /// Returns the paint with the beginning cap of the line set to the specified value. |
720 | pub fn with_line_cap_end(mut self, cap: LineCap) -> Self { |
721 | self.set_line_cap_end(cap); |
722 | self |
723 | } |
724 | |
725 | /// Returns the current line join for this paint. |
726 | pub fn line_join(&self) -> LineJoin { |
727 | self.stroke.line_join |
728 | } |
729 | |
730 | /// Sets how sharp path corners are drawn. |
731 | /// |
732 | /// By default it's set to LineJoin::Miter |
733 | pub fn set_line_join(&mut self, join: LineJoin) { |
734 | self.stroke.line_join = join; |
735 | } |
736 | |
737 | /// Returns the paint with the line join set to the specified value. |
738 | pub fn with_line_join(mut self, join: LineJoin) -> Self { |
739 | self.set_line_join(join); |
740 | self |
741 | } |
742 | |
743 | pub fn set_font(&mut self, font_ids: &[FontId]) { |
744 | self.text.font_ids = Default::default(); |
745 | |
746 | for (i, id) in font_ids.iter().take(8).enumerate() { |
747 | self.text.font_ids[i] = Some(*id); |
748 | } |
749 | } |
750 | |
751 | /// Returns the paint with the font set to the specified value. |
752 | pub fn with_font(mut self, font_ids: &[FontId]) -> Self { |
753 | self.set_font(font_ids); |
754 | self |
755 | } |
756 | |
757 | /// Returns the current font size |
758 | /// |
759 | /// Only has effect on canvas text operations |
760 | pub fn font_size(&self) -> f32 { |
761 | self.text.font_size |
762 | } |
763 | |
764 | /// Sets the font size. |
765 | /// |
766 | /// Only has effect on canvas text operations |
767 | pub fn set_font_size(&mut self, size: f32) { |
768 | self.text.font_size = size; |
769 | } |
770 | |
771 | /// Returns the paint with the font size set to the specified value. |
772 | pub fn with_font_size(mut self, size: f32) -> Self { |
773 | self.set_font_size(size); |
774 | self |
775 | } |
776 | |
777 | /// Returns the current letter spacing |
778 | pub fn letter_spacing(&self) -> f32 { |
779 | self.text.letter_spacing |
780 | } |
781 | |
782 | /// Sets the letter spacing for this paint |
783 | /// |
784 | /// Only has effect on canvas text operations |
785 | pub fn set_letter_spacing(&mut self, spacing: f32) { |
786 | self.text.letter_spacing = spacing; |
787 | } |
788 | |
789 | /// Returns the paint with the letter spacing set to the specified value. |
790 | pub fn with_letter_spacing(mut self, spacing: f32) -> Self { |
791 | self.set_letter_spacing(spacing); |
792 | self |
793 | } |
794 | |
795 | /// Returns the current vertical align |
796 | pub fn text_baseline(&self) -> Baseline { |
797 | self.text.text_baseline |
798 | } |
799 | |
800 | /// Sets the text vertical alignment for this paint |
801 | /// |
802 | /// Only has effect on canvas text operations |
803 | pub fn set_text_baseline(&mut self, align: Baseline) { |
804 | self.text.text_baseline = align; |
805 | } |
806 | |
807 | /// Returns the paint with the text vertical alignment set to the specified value. |
808 | pub fn with_text_baseline(mut self, align: Baseline) -> Self { |
809 | self.set_text_baseline(align); |
810 | self |
811 | } |
812 | |
813 | /// Returns the current horizontal align |
814 | pub fn text_align(&self) -> Align { |
815 | self.text.text_align |
816 | } |
817 | |
818 | /// Sets the text horizontal alignment for this paint |
819 | /// |
820 | /// Only has effect on canvas text operations |
821 | pub fn set_text_align(&mut self, align: Align) { |
822 | self.text.text_align = align; |
823 | } |
824 | |
825 | /// Returns the paint with the text horizontal alignment set to the specified value. |
826 | pub fn with_text_align(mut self, align: Align) -> Self { |
827 | self.set_text_align(align); |
828 | self |
829 | } |
830 | |
831 | /// Retrieves the current fill rule setting for this paint |
832 | pub fn fill_rule(&self) -> FillRule { |
833 | self.fill_rule |
834 | } |
835 | |
836 | /// Sets the current rule to be used when filling a path |
837 | /// |
838 | /// <https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule> |
839 | pub fn set_fill_rule(&mut self, rule: FillRule) { |
840 | self.fill_rule = rule; |
841 | } |
842 | |
843 | /// Returns the paint with the rule for filling a path set to the specified value. |
844 | pub fn with_fill_rule(mut self, rule: FillRule) -> Self { |
845 | self.set_fill_rule(rule); |
846 | self |
847 | } |
848 | } |
849 | |