peniko/
brush.rs

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