peniko/
brush.rs

1// Copyright 2022 the Peniko Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::ImageBrushRef;
5
6use super::{Gradient, ImageBrush};
7
8use color::{AlphaColor, ColorSpace, DynamicColor, OpaqueColor, Srgb};
9
10/// Describes the color content of a filled or stroked shape.
11///
12/// See also [`BrushRef`] which can be used to avoid allocations.
13#[derive(Copy, Clone, Debug, PartialEq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub enum Brush<I = ImageBrush, G = Gradient> {
16    /// Solid color brush.
17    Solid(AlphaColor<Srgb>),
18    /// Gradient brush.
19    Gradient(G),
20    /// Image brush.
21    Image(I),
22}
23
24impl<CS: ColorSpace, I, G> From<AlphaColor<CS>> for Brush<I, G> {
25    fn from(c: AlphaColor<CS>) -> Self {
26        Self::Solid(c.convert())
27    }
28}
29
30impl<I, G> From<DynamicColor> for Brush<I, G> {
31    fn from(c: DynamicColor) -> Self {
32        Self::Solid(c.to_alpha_color::<Srgb>())
33    }
34}
35
36impl<CS: ColorSpace, I, G> From<OpaqueColor<CS>> for Brush<I, G> {
37    fn from(c: OpaqueColor<CS>) -> Self {
38        Self::Solid(c.with_alpha(1.).convert())
39    }
40}
41
42impl<CS: ColorSpace, I, G> From<&AlphaColor<CS>> for Brush<I, G> {
43    fn from(c: &AlphaColor<CS>) -> Self {
44        Self::Solid((*c).convert())
45    }
46}
47
48impl<I, G> From<&DynamicColor> for Brush<I, G> {
49    fn from(c: &DynamicColor) -> Self {
50        Self::Solid((*c).to_alpha_color::<Srgb>())
51    }
52}
53
54impl<CS: ColorSpace, I, G> From<&OpaqueColor<CS>> for Brush<I, G> {
55    fn from(c: &OpaqueColor<CS>) -> Self {
56        Self::Solid((*c).with_alpha(1.).convert())
57    }
58}
59
60impl<I> From<Gradient> for Brush<I, Gradient> {
61    fn from(g: Gradient) -> Self {
62        Self::Gradient(g)
63    }
64}
65
66impl<G, D> From<ImageBrush<D>> for Brush<ImageBrush<D>, G> {
67    fn from(value: ImageBrush<D>) -> Self {
68        Self::Image(value)
69    }
70}
71
72impl<I, G> Default for Brush<I, G> {
73    fn default() -> Self {
74        Self::Solid(AlphaColor::<Srgb>::TRANSPARENT)
75    }
76}
77
78impl Brush {
79    /// Returns the brush with the alpha component set to `alpha`.
80    #[must_use]
81    pub fn with_alpha(self, alpha: f32) -> Self {
82        match self {
83            Self::Solid(color) => color.with_alpha(alpha).into(),
84            Self::Gradient(gradient) => gradient.with_alpha(alpha).into(),
85            Self::Image(image) => image.with_alpha(alpha).into(),
86        }
87    }
88
89    /// Returns the brush with the alpha component multiplied by `alpha`.
90    /// The behaviour of this transformation is undefined if `alpha` is negative.
91    ///
92    /// If any resulting alphas would overflow, these currently saturate (to opaque).
93    #[must_use]
94    #[doc(alias = "with_alpha_factor")]
95    #[track_caller]
96    pub fn multiply_alpha(self, alpha: f32) -> Self {
97        debug_assert!(
98            alpha.is_finite() && alpha >= 0.0,
99            "A non-finite or negative alpha ({alpha}) is meaningless."
100        );
101        if alpha == 1.0 {
102            self
103        } else {
104            match self {
105                Self::Solid(color) => color.multiply_alpha(alpha).into(),
106                Self::Gradient(gradient) => gradient.multiply_alpha(alpha).into(),
107                Self::Image(image) => image.multiply_alpha(alpha).into(),
108            }
109        }
110    }
111}
112
113/// Reference to a [brush](Brush).
114///
115/// This is useful for methods that would like to accept brushes by reference. Defining
116/// the type as `impl Into<BrushRef>` allows accepting types like `&Gradient`
117/// directly without cloning or allocating.
118pub type BrushRef<'a> = Brush<ImageBrushRef<'a>, &'a Gradient>;
119
120impl BrushRef<'_> {
121    /// Converts the reference to an owned brush.
122    #[must_use]
123    pub fn to_owned(&self) -> Brush {
124        match self {
125            Self::Solid(color) => Brush::Solid(*color),
126            Self::Gradient(gradient) => Brush::Gradient((*gradient).clone()),
127            Self::Image(image) => Brush::Image(image.to_owned()),
128        }
129    }
130}
131
132impl<'a> From<&'a Gradient> for BrushRef<'a> {
133    fn from(gradient: &'a Gradient) -> Self {
134        Self::Gradient(gradient)
135    }
136}
137
138impl<'a> From<&'a ImageBrush> for BrushRef<'a> {
139    fn from(image: &'a ImageBrush) -> Self {
140        Self::Image(image.as_ref())
141    }
142}
143
144impl<'a> From<&'a Brush> for BrushRef<'a> {
145    fn from(brush: &'a Brush) -> Self {
146        match brush {
147            Brush::Solid(color) => Self::Solid(*color),
148            Brush::Gradient(gradient) => Self::Gradient(gradient),
149            Brush::Image(image) => Self::Image(image.as_ref()),
150        }
151    }
152}
153
154/// Defines how a brush is extended when the content does not
155/// fill a shape.
156#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
157#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
158#[repr(u8)]
159pub enum Extend {
160    /// Extends the image by repeating the edge color of the brush.
161    #[default]
162    Pad = 0,
163    /// Extends the image by repeating the brush.
164    Repeat = 1,
165    /// Extends the image by reflecting the brush.
166    Reflect = 2,
167}