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