skia_safe/core/
shader.rs

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