tiny_skia/shaders/
mod.rs

1// Copyright 2006 The Android Open Source Project
2// Copyright 2020 Yevhenii Reizner
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7mod gradient;
8mod linear_gradient;
9mod pattern;
10mod radial_gradient;
11
12use tiny_skia_path::{NormalizedF32, Scalar};
13
14pub use gradient::GradientStop;
15pub use linear_gradient::LinearGradient;
16pub use pattern::{FilterQuality, Pattern, PixmapPaint};
17pub use radial_gradient::RadialGradient;
18
19use crate::{Color, Transform};
20
21use crate::pipeline::RasterPipelineBuilder;
22
23/// A shader spreading mode.
24#[derive(Copy, Clone, PartialEq, Debug)]
25pub enum SpreadMode {
26    /// Replicate the edge color if the shader draws outside of its
27    /// original bounds.
28    Pad,
29
30    /// Repeat the shader's image horizontally and vertically, alternating
31    /// mirror images so that adjacent images always seam.
32    Reflect,
33
34    /// Repeat the shader's image horizontally and vertically.
35    Repeat,
36}
37
38impl Default for SpreadMode {
39    fn default() -> Self {
40        SpreadMode::Pad
41    }
42}
43
44/// A shader specifies the source color(s) for what is being drawn.
45///
46/// If a paint has no shader, then the paint's color is used. If the paint has a
47/// shader, then the shader's color(s) are use instead, but they are
48/// modulated by the paint's alpha. This makes it easy to create a shader
49/// once (e.g. bitmap tiling or gradient) and then change its transparency
50/// without having to modify the original shader. Only the paint's alpha needs
51/// to be modified.
52#[derive(Clone, PartialEq, Debug)]
53pub enum Shader<'a> {
54    /// A solid color shader.
55    SolidColor(Color),
56    /// A linear gradient shader.
57    LinearGradient(LinearGradient),
58    /// A radial gradient shader.
59    RadialGradient(RadialGradient),
60    /// A pattern shader.
61    Pattern(Pattern<'a>),
62}
63
64impl<'a> Shader<'a> {
65    /// Checks if the shader is guaranteed to produce only opaque colors.
66    pub fn is_opaque(&self) -> bool {
67        match self {
68            Shader::SolidColor(ref c) => c.is_opaque(),
69            Shader::LinearGradient(ref g) => g.is_opaque(),
70            Shader::RadialGradient(_) => false,
71            Shader::Pattern(_) => false,
72        }
73    }
74
75    // Unlike Skia, we do not have is_constant, because we don't have Color shaders.
76
77    /// If this returns false, then we draw nothing (do not fall back to shader context)
78    #[must_use]
79    pub(crate) fn push_stages(&self, p: &mut RasterPipelineBuilder) -> bool {
80        match self {
81            Shader::SolidColor(color) => {
82                p.push_uniform_color(color.premultiply());
83                true
84            }
85            Shader::LinearGradient(ref g) => g.push_stages(p),
86            Shader::RadialGradient(ref g) => g.push_stages(p),
87            Shader::Pattern(ref patt) => patt.push_stages(p),
88        }
89    }
90
91    /// Transforms the shader.
92    pub fn transform(&mut self, ts: Transform) {
93        match self {
94            Shader::SolidColor(_) => {}
95            Shader::LinearGradient(g) => {
96                g.base.transform = g.base.transform.post_concat(ts);
97            }
98            Shader::RadialGradient(g) => {
99                g.base.transform = g.base.transform.post_concat(ts);
100            }
101            Shader::Pattern(p) => {
102                p.transform = p.transform.post_concat(ts);
103            }
104        }
105    }
106
107    /// Shifts shader's opacity.
108    ///
109    /// `opacity` will be clamped to the 0..=1 range.
110    ///
111    /// This is roughly the same as Skia's `SkPaint::setAlpha`.
112    ///
113    /// Unlike Skia, we do not support global alpha/opacity, which is in Skia
114    /// is set via the alpha channel of the `SkPaint::fColor4f`.
115    /// Instead, you can shift the opacity of the shader to whatever value you need.
116    ///
117    /// - For `SolidColor` this function will multiply `color.alpha` by `opacity`.
118    /// - For gradients this function will multiply all colors by `opacity`.
119    /// - For `Pattern` this function will multiply `Patter::opacity` by `opacity`.
120    pub fn apply_opacity(&mut self, opacity: f32) {
121        match self {
122            Shader::SolidColor(ref mut c) => {
123                c.apply_opacity(opacity);
124            }
125            Shader::LinearGradient(g) => {
126                g.base.apply_opacity(opacity);
127            }
128            Shader::RadialGradient(g) => {
129                g.base.apply_opacity(opacity);
130            }
131            Shader::Pattern(ref mut p) => {
132                p.opacity = NormalizedF32::new(p.opacity.get() * opacity.bound(0.0, 1.0)).unwrap();
133            }
134        }
135    }
136}