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