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 specific color space.
118    /// By default, all shaders operate in the destination (surface) color space.
119    /// The results of a shader are still always converted to the destination - this
120    /// API has no impact on simple shaders or images. Primarily, it impacts shaders
121    /// that perform mathematical operations, like Blend shaders, or runtime shaders.
122    #[must_use]
123    pub fn with_working_color_space(&self, color_space: impl Into<ColorSpace>) -> Self {
124        Self::from_ptr(unsafe {
125            sb::C_SkShader_makeWithWorkingColorSpace(self.native(), color_space.into().into_ptr())
126        })
127        .unwrap()
128    }
129}
130
131pub mod shaders {
132    use skia_bindings as sb;
133
134    use crate::{
135        prelude::*, Blender, Color, Color4f, ColorSpace, Image, Matrix, Rect, SamplingOptions,
136        Shader, TileMode,
137    };
138
139    pub fn empty() -> Shader {
140        Shader::from_ptr(unsafe { sb::C_SkShaders_Empty() }).unwrap()
141    }
142
143    pub fn color(color: impl Into<Color>) -> Shader {
144        let color = color.into();
145        Shader::from_ptr(unsafe { sb::C_SkShaders_Color(color.into_native()) }).unwrap()
146    }
147
148    pub fn color_in_space(color: impl AsRef<Color4f>, space: impl Into<ColorSpace>) -> Shader {
149        Shader::from_ptr(unsafe {
150            sb::C_SkShaders_Color2(color.as_ref().native(), space.into().into_ptr())
151        })
152        .unwrap()
153    }
154
155    pub fn blend(
156        blender: impl Into<Blender>,
157        dst: impl Into<Shader>,
158        src: impl Into<Shader>,
159    ) -> Shader {
160        Shader::from_ptr(unsafe {
161            sb::C_SkShaders_Blend(
162                blender.into().into_ptr(),
163                dst.into().into_ptr(),
164                src.into().into_ptr(),
165            )
166        })
167        .unwrap()
168    }
169
170    pub fn coord_clamp(shader: impl Into<Shader>, rect: impl AsRef<Rect>) -> Option<Shader> {
171        Shader::from_ptr(unsafe {
172            sb::C_SkShaders_CoordClamp(shader.into().into_ptr(), rect.as_ref().native())
173        })
174    }
175
176    /// Create an [`Shader`] that will sample the 'image'. This is equivalent to [`Image::to_shader`].
177    pub fn image<'a>(
178        image: impl Into<Image>,
179        tm: (TileMode, TileMode),
180        options: &SamplingOptions,
181        matrix: impl Into<Option<&'a Matrix>>,
182    ) -> Option<Shader> {
183        Shader::from_ptr(unsafe {
184            sb::C_SkShaders_Image(
185                image.into().into_ptr(),
186                tm.0,
187                tm.1,
188                options.native(),
189                matrix.into().native_ptr_or_null(),
190            )
191        })
192    }
193
194    /// Create an [`Shader`] that will sample 'image' with minimal processing. This is equivalent to
195    /// [`Image::to_raw_shader`].
196    pub fn raw_image<'a>(
197        image: impl Into<Image>,
198        tm: (TileMode, TileMode),
199        options: &SamplingOptions,
200        matrix: impl Into<Option<&'a Matrix>>,
201    ) -> Option<Shader> {
202        Shader::from_ptr(unsafe {
203            sb::C_SkShaders_RawImage(
204                image.into().into_ptr(),
205                tm.0,
206                tm.1,
207                options.native(),
208                matrix.into().native_ptr_or_null(),
209            )
210        })
211    }
212}