skia_safe/effects/
runtime_effect.rs

1use crate::{
2    interop::{self, AsStr},
3    prelude::*,
4    Blender, ColorFilter, Data, Matrix, Shader,
5};
6use core::ffi;
7use sb::{SkFlattenable, SkRuntimeEffect_Child};
8use skia_bindings::{
9    self as sb, ShaderBuilderUniformResult, SkRefCntBase, SkRuntimeEffect, SkRuntimeEffect_Uniform,
10};
11use std::{fmt, marker::PhantomData, ops::DerefMut, ptr};
12
13pub type Uniform = Handle<SkRuntimeEffect_Uniform>;
14unsafe_send_sync!(Uniform);
15
16#[deprecated(since = "0.35.0", note = "Use Uniform instead")]
17pub type Variable = Uniform;
18
19impl NativeDrop for SkRuntimeEffect_Uniform {
20    fn drop(&mut self) {
21        panic!("native type SkRuntimeEffect::Uniform can't be owned by Rust");
22    }
23}
24
25impl fmt::Debug for Uniform {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        self.native().fmt(f)
28    }
29}
30
31impl Uniform {
32    pub fn name(&self) -> &str {
33        self.native().name.as_str()
34    }
35
36    pub fn offset(&self) -> usize {
37        self.native().offset
38    }
39
40    pub fn ty(&self) -> uniform::Type {
41        self.native().type_
42    }
43
44    pub fn count(&self) -> i32 {
45        self.native().count
46    }
47
48    pub fn flags(&self) -> uniform::Flags {
49        uniform::Flags::from_bits(self.native().flags).unwrap()
50    }
51
52    pub fn is_array(&self) -> bool {
53        self.flags().contains(uniform::Flags::ARRAY)
54    }
55
56    pub fn is_color(&self) -> bool {
57        self.flags().contains(uniform::Flags::COLOR)
58    }
59
60    pub fn size_in_bytes(&self) -> usize {
61        unsafe { self.native().sizeInBytes() }
62    }
63}
64
65pub mod uniform {
66    use skia_bindings as sb;
67
68    pub use sb::SkRuntimeEffect_Uniform_Type as Type;
69    variant_name!(Type::Float2x2);
70
71    bitflags! {
72        #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
73        pub struct Flags : u32 {
74            const ARRAY = sb::SkRuntimeEffect_Uniform_Flags_kArray_Flag as _;
75            const COLOR = sb::SkRuntimeEffect_Uniform_Flags_kColor_Flag as _;
76            const VERTEX = sb::SkRuntimeEffect_Uniform_Flags_kVertex_Flag as _;
77            const FRAGMENT = sb::SkRuntimeEffect_Uniform_Flags_kFragment_Flag as _;
78            const HALF_PRECISION = sb::SkRuntimeEffect_Uniform_Flags_kHalfPrecision_Flag as _;
79        }
80    }
81}
82
83pub use sb::SkRuntimeEffect_ChildType as ChildType;
84variant_name!(ChildType::Shader);
85
86#[deprecated(since = "0.41.0", note = "Use Child")]
87pub type Varying = Child;
88
89pub type Child = Handle<SkRuntimeEffect_Child>;
90unsafe_send_sync!(Child);
91
92impl NativeDrop for SkRuntimeEffect_Child {
93    fn drop(&mut self) {
94        panic!("native type SkRuntimeEffect::Child can't be owned in Rust");
95    }
96}
97
98impl fmt::Debug for Child {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        f.debug_struct("Child")
101            .field("name", &self.name())
102            .field("type", &self.ty())
103            .field("index", &self.index())
104            .finish()
105    }
106}
107
108impl Child {
109    pub fn name(&self) -> &str {
110        self.native().name.as_str()
111    }
112
113    pub fn ty(&self) -> ChildType {
114        self.native().type_
115    }
116
117    pub fn index(&self) -> usize {
118        self.native().index.try_into().unwrap()
119    }
120}
121
122pub type RuntimeEffect = RCHandle<SkRuntimeEffect>;
123
124impl NativeRefCountedBase for SkRuntimeEffect {
125    type Base = SkRefCntBase;
126}
127
128#[repr(C)]
129#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
130pub struct Options<'a> {
131    pub force_unoptimized: bool,
132    pub name: &'a str,
133}
134
135impl fmt::Debug for RuntimeEffect {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        f.debug_struct("RuntimeEffect")
138            .field("uniform_size", &self.uniform_size())
139            .field("uniforms", &self.uniforms())
140            .field("children", &self.children())
141            .field("allow_shader", &self.allow_shader())
142            .field("allow_color_filter", &self.allow_color_filter())
143            .field("allow_blender", &self.allow_blender())
144            .finish()
145    }
146}
147
148impl RuntimeEffect {
149    pub fn make_for_color_filter(
150        sksl: impl AsRef<str>,
151        options: Option<&Options<'_>>,
152    ) -> Result<RuntimeEffect, String> {
153        let str = interop::String::from_str(sksl);
154        let options = options.copied().unwrap_or_default();
155        let options = Self::construct_native_options(&options);
156        let mut error = interop::String::default();
157        RuntimeEffect::from_ptr(unsafe {
158            sb::C_SkRuntimeEffect_MakeForColorFilter(str.native(), &options, error.native_mut())
159        })
160        .ok_or_else(|| error.to_string())
161    }
162
163    pub fn make_for_shader(
164        sksl: impl AsRef<str>,
165        options: Option<&Options<'_>>,
166    ) -> Result<RuntimeEffect, String> {
167        let str = interop::String::from_str(sksl);
168        let options = options.copied().unwrap_or_default();
169        let options = Self::construct_native_options(&options);
170        let mut error = interop::String::default();
171        RuntimeEffect::from_ptr(unsafe {
172            sb::C_SkRuntimeEffect_MakeForShader(str.native(), &options, error.native_mut())
173        })
174        .ok_or_else(|| error.to_string())
175    }
176
177    pub fn make_for_blender(
178        sksl: impl AsRef<str>,
179        options: Option<&Options<'_>>,
180    ) -> Result<RuntimeEffect, String> {
181        let str = interop::String::from_str(sksl);
182        let options = options.copied().unwrap_or_default();
183        let options = Self::construct_native_options(&options);
184        let mut error = interop::String::default();
185        RuntimeEffect::from_ptr(unsafe {
186            sb::C_SkRuntimeEffect_MakeForBlender(str.native(), &options, error.native_mut())
187        })
188        .ok_or_else(|| error.to_string())
189    }
190
191    fn construct_native_options(options: &Options<'_>) -> sb::SkRuntimeEffect_Options {
192        construct(|opt| unsafe {
193            sb::C_SkRuntimeEffect_Options_Construct(
194                opt,
195                options.force_unoptimized,
196                options.name.as_ptr() as *const ffi::c_char,
197                options.name.len(),
198            )
199        })
200    }
201
202    pub fn make_shader<'a>(
203        &self,
204        uniforms: impl Into<Data>,
205        children: &[ChildPtr],
206        local_matrix: impl Into<Option<&'a Matrix>>,
207    ) -> Option<Shader> {
208        let mut children: Vec<_> = children
209            .iter()
210            .map(|child_ptr| child_ptr.native())
211            .collect();
212        let children_ptr = children
213            .first_mut()
214            .map(|c| c.deref_mut() as *mut _)
215            .unwrap_or(ptr::null_mut());
216        Shader::from_ptr(unsafe {
217            sb::C_SkRuntimeEffect_makeShader(
218                self.native(),
219                uniforms.into().into_ptr(),
220                children_ptr,
221                children.len(),
222                local_matrix.into().native_ptr_or_null(),
223            )
224        })
225    }
226
227    pub fn make_color_filter<'a>(
228        &self,
229        inputs: impl Into<Data>,
230        children: impl Into<Option<&'a [ChildPtr]>>,
231    ) -> Option<ColorFilter> {
232        let mut children: Vec<_> = children
233            .into()
234            .map(|c| c.iter().map(|child_ptr| child_ptr.native()).collect())
235            .unwrap_or_default();
236        let children_ptr = children
237            .first_mut()
238            .map(|c| c.deref_mut() as *mut _)
239            .unwrap_or(ptr::null_mut());
240        ColorFilter::from_ptr(unsafe {
241            sb::C_SkRuntimeEffect_makeColorFilter(
242                self.native(),
243                inputs.into().into_ptr(),
244                children_ptr,
245                children.len(),
246            )
247        })
248    }
249
250    pub fn make_blender<'a>(
251        &self,
252        uniforms: impl Into<Data>,
253        children: impl Into<Option<&'a [ChildPtr]>>,
254    ) -> Option<Blender> {
255        let mut children: Vec<_> = children
256            .into()
257            .map(|c| c.iter().map(|child_ptr| child_ptr.native()).collect())
258            .unwrap_or_default();
259        let children_ptr = children
260            .first_mut()
261            .map(|c| c.deref_mut() as *mut _)
262            .unwrap_or(ptr::null_mut());
263        Blender::from_ptr(unsafe {
264            sb::C_SkRuntimeEffect_makeBlender(
265                self.native(),
266                uniforms.into().into_ptr(),
267                children_ptr,
268                children.len(),
269            )
270        })
271    }
272
273    // TODO: wrap MakeTraced
274
275    pub fn source(&self) -> &str {
276        let mut len = 0;
277        let ptr = unsafe { sb::C_SkRuntimeEffect_source(self.native(), &mut len) };
278        std::str::from_utf8(unsafe { safer::from_raw_parts(ptr, len) }).unwrap()
279    }
280
281    #[deprecated(since = "0.35.0", note = "Use uniform_size() instead")]
282    pub fn input_size(&self) -> usize {
283        self.uniform_size()
284    }
285
286    pub fn uniform_size(&self) -> usize {
287        unsafe { self.native().uniformSize() }
288    }
289
290    #[deprecated(since = "0.35.0", note = "Use uniforms() instead")]
291    pub fn inputs(&self) -> &[Uniform] {
292        self.uniforms()
293    }
294
295    pub fn uniforms(&self) -> &[Uniform] {
296        unsafe {
297            let mut count: usize = 0;
298            let ptr = sb::C_SkRuntimeEffect_uniforms(self.native(), &mut count);
299            safer::from_raw_parts(Uniform::from_native_ptr(ptr), count)
300        }
301    }
302
303    pub fn children(&self) -> &[Child] {
304        unsafe {
305            let mut count: usize = 0;
306            let ptr = sb::C_SkRuntimeEffect_children(self.native(), &mut count);
307            safer::from_raw_parts(Child::from_native_ptr(ptr), count)
308        }
309    }
310
311    #[deprecated(since = "0.35.0", note = "Use find_uniform()")]
312    pub fn find_input(&self, name: impl AsRef<str>) -> Option<&Uniform> {
313        self.find_uniform(name)
314    }
315
316    pub fn find_uniform(&self, name: impl AsRef<str>) -> Option<&Uniform> {
317        let name = name.as_ref().as_bytes();
318        unsafe { sb::C_SkRuntimeEffect_findUniform(self.native(), name.as_ptr() as _, name.len()) }
319            .into_option()
320            .map(|ptr| Uniform::from_native_ref(unsafe { &*ptr }))
321    }
322
323    pub fn find_child(&self, name: impl AsRef<str>) -> Option<&Child> {
324        let name = name.as_ref().as_bytes();
325        unsafe { sb::C_SkRuntimeEffect_findChild(self.native(), name.as_ptr() as _, name.len()) }
326            .into_option()
327            .map(|ptr| Child::from_native_ref(unsafe { &*ptr }))
328    }
329
330    pub fn allow_shader(&self) -> bool {
331        unsafe { sb::C_SkRuntimeEffect_allowShader(self.native()) }
332    }
333
334    pub fn allow_color_filter(&self) -> bool {
335        unsafe { sb::C_SkRuntimeEffect_allowColorFilter(self.native()) }
336    }
337
338    pub fn allow_blender(&self) -> bool {
339        unsafe { sb::C_SkRuntimeEffect_allowBlender(self.native()) }
340    }
341}
342
343#[derive(Clone, Debug)]
344pub enum ChildPtr {
345    Shader(Shader),
346    ColorFilter(ColorFilter),
347    Blender(Blender),
348}
349
350impl From<Shader> for ChildPtr {
351    fn from(shader: Shader) -> Self {
352        Self::Shader(shader)
353    }
354}
355
356impl From<ColorFilter> for ChildPtr {
357    fn from(color_filter: ColorFilter) -> Self {
358        Self::ColorFilter(color_filter)
359    }
360}
361
362impl From<Blender> for ChildPtr {
363    fn from(blender: Blender) -> Self {
364        Self::Blender(blender)
365    }
366}
367
368// TODO: Create `ChildPtr` from a Flattenable?
369
370impl ChildPtr {
371    pub fn ty(&self) -> ChildType {
372        match self {
373            ChildPtr::Shader(_) => ChildType::Shader,
374            ChildPtr::ColorFilter(_) => ChildType::ColorFilter,
375            ChildPtr::Blender(_) => ChildType::Blender,
376        }
377    }
378
379    // We are treating [`ChildPtr`]s as a _reference_ to a smart pointer: no reference counters are
380    // changed (no drop() is called either).
381    //
382    // Skia will copy the pointers and increase the reference counters if it uses the actual
383    // objects.
384    pub(self) fn native(&self) -> Borrows<sb::SkRuntimeEffect_ChildPtr> {
385        let flattenable: *mut SkFlattenable = match self {
386            // casting to &T &mut T is UB, so we don't use the base() indirection and directly cast
387            // to a pointer.
388            ChildPtr::Shader(shader) => unsafe { shader.native_mut_force() as _ },
389            ChildPtr::ColorFilter(color_filter) => unsafe { color_filter.native_mut_force() as _ },
390            ChildPtr::Blender(blender) => unsafe { blender.native_mut_force() as _ },
391        };
392
393        sb::SkRuntimeEffect_ChildPtr {
394            fChild: sb::sk_sp {
395                fPtr: flattenable,
396                _phantom_0: PhantomData,
397            },
398        }
399        .borrows(self)
400    }
401}
402
403// TODO: wrap SkRuntimeEffectBuilder, SkRuntimeColorFilterBuilder,
404// SkRuntimeBlendBuilder
405
406pub type RuntimeShaderBuilder = Handle<sb::SkRuntimeShaderBuilder>;
407unsafe_send_sync!(RuntimeShaderBuilder);
408
409#[derive(Debug)]
410pub enum ShaderBuilderError {
411    UniformSizeNotSupported,
412}
413impl fmt::Display for ShaderBuilderError {
414    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415        match self {
416            ShaderBuilderError::UniformSizeNotSupported => write!(f, "Unsupported uniform size"),
417        }
418    }
419}
420impl std::error::Error for ShaderBuilderError {}
421
422impl NativeDrop for sb::SkRuntimeShaderBuilder {
423    fn drop(&mut self) {
424        unsafe {
425            sb::C_SkRuntimeShaderBuilder_destruct(self);
426        }
427    }
428}
429
430impl RuntimeShaderBuilder {
431    pub fn new(effect: RuntimeEffect) -> Self {
432        Self::construct(|builder| unsafe {
433            let effect: *mut SkRuntimeEffect = effect.into_ptr() as _;
434            sb::C_SkRuntimeShaderBuilder_Construct(builder, effect)
435        })
436    }
437
438    pub fn make_shader(&self, local_matrix: &Matrix) -> Option<Shader> {
439        unsafe {
440            let instance = self.native_mut_force();
441            let shader =
442                sb::C_SkRuntimeShaderBuilder_makeShader(instance, local_matrix.native() as _);
443            Shader::from_ptr(shader)
444        }
445    }
446    /// Set float uniform values by name.
447    ///
448    /// Supported types are `float`, `float2`, `float3`, `float4`, `float2x2`, `float3x3`, `float4x4`.
449    ///
450    /// The data array must have the correct length for the corresponding uniform type:
451    /// - `float`: `[f32; 1]`
452    /// - `float2`: `[f32; 2]`
453    /// - `float3`: `[f32; 3]`
454    /// - `float4`: `[f32; 4]`
455    /// - `float2x2`: `[f32; 4]`
456    /// - `float3x3`: `[f32; 9]`
457    /// - `float4x4`: `[f32; 16]`
458    ///
459    pub fn set_uniform_float(
460        &mut self,
461        name: impl AsRef<str>,
462        data: &[f32],
463    ) -> Result<(), ShaderBuilderError> {
464        let name = name.as_ref();
465        let result = unsafe {
466            sb::C_SkRuntimeShaderBuilder_setUniformFloat(
467                self.native_mut() as _,
468                name.as_bytes().as_ptr() as _,
469                name.len(),
470                data.as_ptr() as _,
471                data.len(),
472            )
473        };
474        match result {
475            ShaderBuilderUniformResult::Ok => Ok(()),
476            ShaderBuilderUniformResult::Error => Err(ShaderBuilderError::UniformSizeNotSupported),
477        }
478    }
479    /// Set int uniform values by name.
480    ///
481    /// Supported types are `int`, `int2`, `int3`, `int4`.
482    ///
483    /// The data array must have the correct length for the corresponding uniform type:
484    /// - `int`: `[i32; 1]`
485    /// - `int2`: `[i32; 2]`
486    /// - `int3`: `[i32; 3]`
487    /// - `int4`: `[i32; 4]`
488    ///
489    ///
490    pub fn set_uniform_int(
491        &mut self,
492        name: impl AsRef<str>,
493        data: &[i32],
494    ) -> Result<(), ShaderBuilderError> {
495        let name = name.as_ref();
496        let result = unsafe {
497            sb::C_SkRuntimeShaderBuilder_setUniformInt(
498                self.native_mut() as _,
499                name.as_bytes().as_ptr() as _,
500                name.len(),
501                data.as_ptr() as _,
502                data.len(),
503            )
504        };
505        match result {
506            ShaderBuilderUniformResult::Ok => Ok(()),
507            ShaderBuilderUniformResult::Error => Err(ShaderBuilderError::UniformSizeNotSupported),
508        }
509    }
510}
511
512#[cfg(test)]
513mod tests {
514    use super::*;
515
516    // <https://github.com/rust-skia/rust-skia/discussions/1133>
517    #[allow(unused)]
518    fn none_cases_compile() {
519        RuntimeEffect::make_for_color_filter("", None);
520        RuntimeEffect::make_for_shader("", None);
521        RuntimeEffect::make_for_blender("", None);
522    }
523}