Skip to main content

skia_safe/core/
shader.rs

1use std::fmt;
2
3use skia_bindings::{self as sb, SkFlattenable, SkRefCntBase, SkShader};
4
5use crate::{prelude::*, ColorFilter, ColorSpace, Image, Matrix, NativeFlattenable, TileMode};
6
7pub type Shader = RCHandle<SkShader>;
8unsafe_send_sync!(Shader);
9require_type_equality!(sb::SkShader_INHERITED, SkFlattenable);
10
11impl NativeBase<SkRefCntBase> for SkShader {}
12impl NativeBase<SkFlattenable> for SkShader {}
13
14impl NativeRefCountedBase for SkShader {
15    type Base = SkRefCntBase;
16    fn ref_counted_base(&self) -> &Self::Base {
17        self.base()
18    }
19}
20
21impl NativeFlattenable for SkShader {
22    fn native_flattenable(&self) -> &SkFlattenable {
23        self.base()
24    }
25
26    fn native_deserialize(data: &[u8]) -> *mut Self {
27        unsafe { sb::C_SkShader_Deserialize(data.as_ptr() as _, data.len()) }
28    }
29}
30
31impl Default for Shader {
32    fn default() -> Self {
33        shaders::empty()
34    }
35}
36
37impl fmt::Debug for Shader {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.debug_struct("Shader")
40            .field("is_opaque", &self.is_opaque())
41            .field("image", &self.image())
42            .finish()
43    }
44}
45
46/// Shaders specify the source color(s) for what is being drawn. If a paint
47/// has no shader, then the paint's color is used. If the paint has a
48/// shader, then the shader's color(s) are use instead, but they are
49/// modulated by the paint's alpha. This makes it easy to create a shader
50/// once (e.g. bitmap tiling or gradient) and then change its transparency
51/// w/o having to modify the original shader... only the paint's alpha needs
52/// to be modified.
53impl Shader {
54    /// Returns `true` if the shader is guaranteed to produce only opaque
55    /// colors, subject to the [`crate::Paint`] using the shader to apply an opaque
56    /// alpha value. Subclasses should override this to allow some
57    /// optimizations.
58    pub fn is_opaque(&self) -> bool {
59        unsafe { sb::C_SkShader_isOpaque(self.native()) }
60    }
61    /// Returns iff this shader is backed by a single [`Image`].
62    /// If not, returns `None`.
63    pub fn image(&self) -> Option<(Image, Matrix, (TileMode, TileMode))> {
64        unsafe {
65            let mut matrix = Matrix::default();
66            let mut tile_mode = [TileMode::default(); 2];
67            let image = Image::from_unshared_ptr(
68                self.native()
69                    .isAImage(matrix.native_mut(), tile_mode.as_mut_ptr()),
70            );
71            #[allow(clippy::tuple_array_conversions)]
72            image.map(|i| (i, matrix, (tile_mode[0], tile_mode[1])))
73        }
74    }
75
76    pub fn is_a_image(&self) -> bool {
77        unsafe { sb::C_SkShader_isAImage(self.native()) }
78    }
79
80    /// Return a shader that will apply the specified `local_matrix` to this shader.
81    /// The specified matrix will be applied before any matrix associated with this shader.
82    #[must_use]
83    pub fn with_local_matrix(&self, matrix: &Matrix) -> Self {
84        Self::from_ptr(unsafe {
85            sb::C_SkShader_makeWithLocalMatrix(self.native(), matrix.native())
86        })
87        .unwrap()
88    }
89
90    /// Create a new shader that produces the same colors as invoking this shader and then applying
91    /// the color filter.
92    #[must_use]
93    pub fn with_color_filter(&self, color_filter: impl Into<ColorFilter>) -> Self {
94        Self::from_ptr(unsafe {
95            sb::C_SkShader_makeWithColorFilter(self.native(), color_filter.into().into_ptr())
96        })
97        .unwrap()
98    }
99
100    /// Return a shader that will compute this shader in a context such that any child shaders
101    /// return RGBA values converted to the `input_cs` colorspace.
102    ///
103    /// It is then assumed that the RGBA values returned by this shader have been transformed into
104    /// `output_cs` by the shader being wrapped.  By default, shaders are assumed to return values
105    /// in the destination colorspace and premultiplied. Using a different `output_cs` than `input_cs`
106    /// allows custom shaders to replace the color management Skia normally performs w/o forcing
107    /// authors to otherwise manipulate surface/image color info to avoid unnecessary or incorrect
108    /// work.
109    ///
110    /// If the shader is not performing colorspace conversion but needs to operate in the `input_cs`
111    /// then it should have `output_cs` be the same as `input_cs`. Regardless of the `output_cs` here,
112    /// the RGBA values of the returned [`Shader`] are always converted from `output_cs` to the
113    /// destination surface color space.
114    ///
115    /// A `None` `input_cs` is assumed to be the destination CS.
116    /// A `None` `output_cs` is assumed to be the `input_cs`.
117    #[must_use]
118    pub fn with_working_color_space(
119        &self,
120        input_cs: impl Into<Option<ColorSpace>>,
121        output_cs: impl Into<Option<ColorSpace>>,
122    ) -> Self {
123        Self::from_ptr(unsafe {
124            sb::C_SkShader_makeWithWorkingColorSpace(
125                self.native(),
126                input_cs.into().into_ptr_or_null(),
127                output_cs.into().into_ptr_or_null(),
128            )
129        })
130        .unwrap()
131    }
132}
133
134pub mod shaders {
135    use skia_bindings as sb;
136
137    use crate::{
138        prelude::*, Blender, Color, Color4f, ColorSpace, Image, Matrix, Rect, SamplingOptions,
139        Shader, TileMode,
140    };
141
142    pub fn empty() -> Shader {
143        Shader::from_ptr(unsafe { sb::C_SkShaders_Empty() }).unwrap()
144    }
145
146    pub fn color(color: impl Into<Color>) -> Shader {
147        let color = color.into();
148        Shader::from_ptr(unsafe { sb::C_SkShaders_Color(color.into_native()) }).unwrap()
149    }
150
151    pub fn color_in_space(color: impl AsRef<Color4f>, space: impl Into<ColorSpace>) -> Shader {
152        Shader::from_ptr(unsafe {
153            sb::C_SkShaders_Color2(color.as_ref().native(), space.into().into_ptr())
154        })
155        .unwrap()
156    }
157
158    pub fn blend(
159        blender: impl Into<Blender>,
160        dst: impl Into<Shader>,
161        src: impl Into<Shader>,
162    ) -> Shader {
163        Shader::from_ptr(unsafe {
164            sb::C_SkShaders_Blend(
165                blender.into().into_ptr(),
166                dst.into().into_ptr(),
167                src.into().into_ptr(),
168            )
169        })
170        .unwrap()
171    }
172
173    pub fn coord_clamp(shader: impl Into<Shader>, rect: impl AsRef<Rect>) -> Option<Shader> {
174        Shader::from_ptr(unsafe {
175            sb::C_SkShaders_CoordClamp(shader.into().into_ptr(), rect.as_ref().native())
176        })
177    }
178
179    /// Create an [`Shader`] that will sample the 'image'. This is equivalent to [`Image::to_shader`].
180    pub fn image<'a>(
181        image: impl Into<Image>,
182        tm: (TileMode, TileMode),
183        options: &SamplingOptions,
184        matrix: impl Into<Option<&'a Matrix>>,
185    ) -> Option<Shader> {
186        Shader::from_ptr(unsafe {
187            sb::C_SkShaders_Image(
188                image.into().into_ptr(),
189                tm.0,
190                tm.1,
191                options.native(),
192                matrix.into().native_ptr_or_null(),
193            )
194        })
195    }
196
197    /// Create an [`Shader`] that will sample 'image' with minimal processing. This is equivalent to
198    /// [`Image::to_raw_shader`].
199    pub fn raw_image<'a>(
200        image: impl Into<Image>,
201        tm: (TileMode, TileMode),
202        options: &SamplingOptions,
203        matrix: impl Into<Option<&'a Matrix>>,
204    ) -> Option<Shader> {
205        Shader::from_ptr(unsafe {
206            sb::C_SkShaders_RawImage(
207                image.into().into_ptr(),
208                tm.0,
209                tm.1,
210                options.native(),
211                matrix.into().native_ptr_or_null(),
212            )
213        })
214    }
215
216    // Re-export gradient shader factory functions from effects::gradient::shaders
217    pub use crate::effects::gradient::shaders::{
218        linear_gradient, radial_gradient, sweep_gradient, two_point_conical_gradient,
219    };
220}