embedded_graphics/primitives/
styled.rs

1use crate::{
2    draw_target::DrawTarget,
3    geometry::{Dimensions, Point},
4    pixelcolor::PixelColor,
5    primitives::OffsetOutline,
6    primitives::{PrimitiveStyle, Rectangle},
7    transform::Transform,
8    Drawable,
9};
10
11/// Styled.
12#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
13#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
14pub struct Styled<T, S> {
15    /// Primitive.
16    pub primitive: T,
17    /// Style.
18    pub style: S,
19}
20
21impl<T, S> Styled<T, S> {
22    /// Creates a styled.
23    pub const fn new(primitive: T, style: S) -> Self {
24        Self { primitive, style }
25    }
26}
27
28impl<T: OffsetOutline, C: PixelColor> Styled<T, PrimitiveStyle<C>> {
29    /// Returns the fill area.
30    ///
31    /// The returned primitive coincides with the area that would be filled by calling [`draw`]
32    /// on this styled primitive.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use embedded_graphics::{
38    ///     pixelcolor::Rgb888,
39    ///     prelude::*,
40    ///     primitives::{Circle, PrimitiveStyleBuilder},
41    /// };
42    ///
43    /// let style = PrimitiveStyleBuilder::new()
44    ///     .fill_color(Rgb888::RED)
45    ///     .stroke_color(Rgb888::GREEN)
46    ///     .stroke_width(6)
47    ///     .build();
48    ///
49    /// let center = Point::new(10, 20);
50    /// let diameter = 10;
51    ///
52    /// let circle = Circle::with_center(center, diameter).into_styled(style);
53    ///
54    /// assert_eq!(circle.fill_area(), Circle::with_center(center, diameter - style.stroke_width));
55    /// ```
56    ///
57    /// [`draw`]: crate::Drawable::draw
58    pub fn fill_area(&self) -> T {
59        self.style.fill_area(&self.primitive)
60    }
61
62    /// Returns the stroke area.
63    ///
64    /// The outer edge of the returned primitive coincides with the outer edge of the stroke that
65    /// would be drawn by calling [`draw`] on this styled primitive.
66    ///
67    /// # Examples
68    ///
69    /// This example tests if a point lies on the stroke. Because the stoke area surrounds the
70    /// stoke and fill an additional test is required to check that the point is not inside the fill
71    /// area.
72    ///
73    /// ```
74    /// use embedded_graphics::{
75    ///     pixelcolor::Rgb888,
76    ///     prelude::*,
77    ///     primitives::{Circle, PrimitiveStyle},
78    /// };
79    ///
80    /// let style = PrimitiveStyle::with_stroke(Rgb888::RED, 6);
81    ///
82    /// let center = Point::new(10, 20);
83    /// let diameter = 10;
84    ///
85    /// let circle = Circle::with_center(center, diameter).into_styled(style);
86    ///
87    /// // A point lies on the stroke if it is inside the stroke area but not in the fill area.
88    /// let is_on_stroke = |p| circle.stroke_area().contains(p) && !circle.fill_area().contains(p);
89    ///
90    /// assert!(is_on_stroke(center + Size::new(0, diameter / 2)));
91    /// assert!(!is_on_stroke(center));
92    /// ```
93    ///
94    /// [`draw`]: crate::Drawable::draw
95    pub fn stroke_area(&self) -> T {
96        self.style.stroke_area(&self.primitive)
97    }
98}
99
100impl<T: StyledPixels<S>, S> Styled<T, S> {
101    /// Returns an iterator over the pixels in this styled primitive.
102    pub fn pixels(&self) -> T::Iter {
103        self.primitive.pixels(&self.style)
104    }
105}
106
107impl<T: StyledDimensions<S>, S> Dimensions for Styled<T, S> {
108    fn bounding_box(&self) -> Rectangle {
109        self.primitive.styled_bounding_box(&self.style)
110    }
111}
112
113impl<T: StyledDrawable<S>, S> Drawable for Styled<T, S> {
114    type Color = T::Color;
115    type Output = T::Output;
116
117    fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
118    where
119        D: DrawTarget<Color = Self::Color>,
120    {
121        self.primitive.draw_styled(&self.style, target)
122    }
123}
124
125impl<T: Transform, S: Clone> Transform for Styled<T, S> {
126    fn translate(&self, by: Point) -> Self {
127        Self {
128            primitive: self.primitive.translate(by),
129            style: self.style.clone(),
130        }
131    }
132
133    fn translate_mut(&mut self, by: Point) -> &mut Self {
134        self.primitive.translate_mut(by);
135
136        self
137    }
138}
139
140/// Styled drawable.
141pub trait StyledDrawable<S> {
142    /// Color type.
143    type Color: PixelColor;
144    /// Output type.
145    type Output;
146
147    /// Draws the primitive using the given style.
148    fn draw_styled<D>(&self, style: &S, target: &mut D) -> Result<Self::Output, D::Error>
149    where
150        D: DrawTarget<Color = Self::Color>;
151}
152
153/// Styled dimensions.
154pub trait StyledDimensions<S> {
155    /// Returns the bounding box using the given style.
156    fn styled_bounding_box(&self, style: &S) -> Rectangle;
157}
158
159/// Styled drawable.
160pub trait StyledPixels<S> {
161    /// Iterator type.
162    type Iter;
163
164    /// Returns an iterator over all pixels in this styled primitive.
165    fn pixels(&self, style: &S) -> Self::Iter;
166}