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}
137