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<'a> GradientInfo<'a> {
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:data.as_ptr() as _, length: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&mut DebugStruct<'_, '_>.debug_struct("Shader")
57 .field("is_opaque", &self.is_opaque())
58 .field(name:"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 crate::{prelude::*, Blender, Color, Color4f, ColorSpace, Rect, Shader};
133 use skia_bindings as sb;
134
135 pub fn empty() -> Shader {
136 Shader::from_ptr(unsafe { sb::C_SkShaders_Empty() }).unwrap()
137 }
138
139 pub fn color(color: impl Into<Color>) -> Shader {
140 let color = color.into();
141 Shader::from_ptr(unsafe { sb::C_SkShaders_Color(color.into_native()) }).unwrap()
142 }
143
144 pub fn color_in_space(color: impl AsRef<Color4f>, space: impl Into<ColorSpace>) -> Shader {
145 Shader::from_ptr(unsafe {
146 sb::C_SkShaders_Color2(color.as_ref().native(), space.into().into_ptr())
147 })
148 .unwrap()
149 }
150
151 pub fn blend(
152 blender: impl Into<Blender>,
153 dst: impl Into<Shader>,
154 src: impl Into<Shader>,
155 ) -> Shader {
156 Shader::from_ptr(unsafe {
157 sb::C_SkShaders_Blend(
158 blender.into().into_ptr(),
159 dst.into().into_ptr(),
160 src.into().into_ptr(),
161 )
162 })
163 .unwrap()
164 }
165
166 pub fn coord_clamp(shader: impl Into<Shader>, rect: impl AsRef<Rect>) -> Option<Shader> {
167 Shader::from_ptr(unsafe {
168 sb::C_SkShaders_CoordClamp(shader.into().into_ptr(), rect.as_ref().native())
169 })
170 }
171}
172