1use std::ptr;
2
3use skia_bindings::{self as sb, SkImageFilter, SkRect};
4
5use crate::{
6 prelude::*, scalar, Blender, Color, Color4f, ColorChannel, ColorFilter, ColorSpace,
7 CubicResampler, IPoint, IRect, ISize, Image, ImageFilter, Matrix, Picture, Point3, Rect,
8 SamplingOptions, Shader, TileMode, Vector,
9};
10
11/// This is just a convenience type to allow passing [`IRect`]s, [`Rect`]s, and optional references
12/// to those types as a crop rect for the image filter factories. It's not intended to be used
13/// directly.
14#[repr(C)]
15#[derive(Copy, Clone, PartialEq, Debug)]
16pub struct CropRect(Option<Rect>);
17
18impl CropRect {
19 pub const NO_CROP_RECT: CropRect = CropRect(None);
20
21 pub fn rect(&self) -> Option<Rect> {
22 self.0
23 }
24
25 fn native(&self) -> *const SkRect {
26 match &self.0 {
27 None => ptr::null(),
28 Some(r: &Rect) => r.native(),
29 }
30 }
31}
32
33impl Default for CropRect {
34 fn default() -> Self {
35 CropRect::NO_CROP_RECT
36 }
37}
38
39impl From<Option<CropRect>> for CropRect {
40 fn from(opt: Option<CropRect>) -> Self {
41 opt.unwrap_or(Self::NO_CROP_RECT)
42 }
43}
44
45impl From<&CropRect> for CropRect {
46 fn from(cr: &CropRect) -> Self {
47 *cr
48 }
49}
50
51impl From<IRect> for CropRect {
52 fn from(r: IRect) -> Self {
53 Self(Some(Rect::from(r)))
54 }
55}
56
57impl From<&IRect> for CropRect {
58 fn from(r: &IRect) -> Self {
59 Self::from(*r)
60 }
61}
62
63impl From<Rect> for CropRect {
64 fn from(r: Rect) -> Self {
65 Self(Some(r))
66 }
67}
68
69impl From<&Rect> for CropRect {
70 fn from(r: &Rect) -> Self {
71 Self(Some(*r))
72 }
73}
74
75/// Create a filter that implements a custom blend mode. Each output pixel is the result of
76/// combining the corresponding background and foreground pixels using the 4 coefficients:
77/// k1 * foreground * background + k2 * foreground + k3 * background + k4
78/// * `k1`, `k2`, `k3`, `k4` The four coefficients used to combine the foreground and background.
79/// * `enforce_pm_color` - If `true`, the RGB channels will be clamped to the calculated alpha.
80/// * `background` - The background content, using the source bitmap when this is null.
81/// * `foreground` - The foreground content, using the source bitmap when this is null.
82/// * `crop_rect` - Optional rectangle that crops the inputs and output.
83#[allow(clippy::too_many_arguments)]
84pub fn arithmetic(
85 k1: scalar,
86 k2: scalar,
87 k3: scalar,
88 k4: scalar,
89 enforce_pm_color: bool,
90 background: impl Into<Option<ImageFilter>>,
91 foreground: impl Into<Option<ImageFilter>>,
92 crop_rect: impl Into<CropRect>,
93) -> Option<ImageFilter> {
94 ImageFilter::from_ptr(unsafe {
95 sb::C_SkImageFilters_Arithmetic(
96 k1,
97 k2,
98 k3,
99 k4,
100 enforcePMColor:enforce_pm_color,
101 background.into().into_ptr_or_null(),
102 foreground.into().into_ptr_or_null(),
103 cropRect:crop_rect.into().native(),
104 )
105 })
106}
107
108/// This filter takes an [`crate::BlendMode`] and uses it to composite the two filters together.
109/// * `blender` - The blender that defines the compositing operation
110/// * `background` - The Dst pixels used in blending, if null the source bitmap is used.
111/// * `foreground` - The Src pixels used in blending, if null the source bitmap is used.
112/// * `crop_rect`` Optional rectangle to crop input and output.
113pub fn blend(
114 mode: impl Into<Blender>,
115 background: impl Into<Option<ImageFilter>>,
116 foreground: impl Into<Option<ImageFilter>>,
117 crop_rect: impl Into<CropRect>,
118) -> Option<ImageFilter> {
119 ImageFilter::from_ptr(unsafe {
120 sb::C_SkImageFilters_Blend(
121 blender:mode.into().into_ptr(),
122 background.into().into_ptr_or_null(),
123 foreground.into().into_ptr_or_null(),
124 cropRect:crop_rect.into().native(),
125 )
126 })
127}
128
129/// Create a filter that blurs its input by the separate X and Y sigmas. The provided tile mode
130/// is used when the blur kernel goes outside the input image.
131/// * `sigma_x` - The Gaussian sigma value for blurring along the X axis.
132/// * `sigma_y` - The Gaussian sigma value for blurring along the Y axis.
133/// * `tile_mode` - The tile mode applied at edges .
134/// * `input` - The input filter that is blurred, uses source bitmap if this is null.
135/// * `crop_rect` - Optional rectangle that crops the input and output.
136pub fn blur(
137 (sigma_x: f32, sigma_y: f32): (scalar, scalar),
138 tile_mode: impl Into<Option<TileMode>>,
139 input: impl Into<Option<ImageFilter>>,
140 crop_rect: impl Into<CropRect>,
141) -> Option<ImageFilter> {
142 ImageFilter::from_ptr(unsafe {
143 sb::C_SkImageFilters_Blur(
144 sigmaX:sigma_x,
145 sigmaY:sigma_y,
146 tileMode:tile_mode.into().unwrap_or(TileMode::Decal),
147 input.into().into_ptr_or_null(),
148 cropRect:crop_rect.into().native(),
149 )
150 })
151}
152
153pub fn color_filter(
154 cf: impl Into<ColorFilter>,
155 input: impl Into<Option<ImageFilter>>,
156 crop_rect: impl Into<CropRect>,
157) -> Option<ImageFilter> {
158 ImageFilter::from_ptr(unsafe {
159 sb::C_SkImageFilters_ColorFilter(
160 cf.into().into_ptr(),
161 input.into().into_ptr_or_null(),
162 cropRect:crop_rect.into().native(),
163 )
164 })
165}
166
167/// Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are
168/// treated as the source bitmap passed to 'outer', i.e. result = outer(inner(source)).
169/// * `outer` - The outer filter that evaluates the results of inner.
170/// * `inner` - The inner filter that produces the input to outer.
171pub fn compose(
172 outer: impl Into<ImageFilter>,
173 inner: impl Into<ImageFilter>,
174) -> Option<ImageFilter> {
175 ImageFilter::from_ptr(unsafe {
176 sb::C_SkImageFilters_Compose(outer.into().into_ptr(), inner.into().into_ptr())
177 })
178}
179
180/// Create a filter that applies a crop to the result of the 'input' filter. Pixels within the
181/// crop rectangle are unmodified from what 'input' produced. Pixels outside of crop match the
182/// provided [`TileMode`] (defaulting to `Decal`).
183///
184/// * `rect` - The cropping geometry
185/// * `tile_mode` - The tile mode applied to pixels *outside* of 'crop'
186/// * `input` - The input filter that is cropped, uses source image if this is `None`
187pub fn crop(
188 rect: impl AsRef<Rect>,
189 tile_mode: impl Into<Option<TileMode>>,
190 input: impl Into<Option<ImageFilter>>,
191) -> Option<ImageFilter> {
192 ImageFilter::from_ptr(unsafe {
193 sb::C_SkImageFilters_Crop(
194 rect.as_ref().native(),
195 tileMode:tile_mode.into().unwrap_or(TileMode::Decal),
196 input.into().into_ptr_or_null(),
197 )
198 })
199}
200
201/// Create a filter that moves each pixel in its color input based on an (x,y) vector encoded
202/// in its displacement input filter. Two color components of the displacement image are
203/// mapped into a vector as `scale * (color[xChannel], color[yChannel])`, where the channel
204/// selectors are one of R, G, B, or A.
205/// * `x_channel_selector` - RGBA channel that encodes the x displacement per pixel.
206/// * `y_channel_selector` - RGBA channel that encodes the y displacement per pixel.
207/// * `scale` - Scale applied to displacement extracted from image.
208/// * `displacement` - The filter defining the displacement image, or `None` to use source.
209/// * `color` - The filter providing the color pixels to be displaced. If `None`,
210/// it will use the source.
211/// * `crop_rect` - Optional rectangle that crops the color input and output.
212pub fn displacement_map(
213 (x_channel_selector: SkColorChannel, y_channel_selector: SkColorChannel): (ColorChannel, ColorChannel),
214 scale: scalar,
215 displacement: impl Into<Option<ImageFilter>>,
216 color: impl Into<Option<ImageFilter>>,
217 crop_rect: impl Into<CropRect>,
218) -> Option<ImageFilter> {
219 ImageFilter::from_ptr(unsafe {
220 sb::C_SkImageFilters_DisplacementMap(
221 xChannelSelector:x_channel_selector,
222 yChannelSelector:y_channel_selector,
223 scale,
224 displacement.into().into_ptr_or_null(),
225 color.into().into_ptr_or_null(),
226 cropRect:crop_rect.into().native(),
227 )
228 })
229}
230
231/// Create a filter that draws a drop shadow under the input content. This filter produces an
232/// image that includes the inputs' content.
233/// * `offset` - The offset of the shadow.
234/// * `sigma_x` - The blur radius for the shadow, along the X axis.
235/// * `sigma_y` - The blur radius for the shadow, along the Y axis.
236/// * `color` - The color of the drop shadow.
237/// * `color_space` - The color space of the drop shadow color.
238/// * `input` - The input filter, or will use the source bitmap if this is null.
239/// * `crop_rect` - Optional rectangle that crops the input and output.
240pub fn drop_shadow(
241 offset: impl Into<Vector>,
242 (sigma_x: f32, sigma_y: f32): (scalar, scalar),
243 color: impl Into<Color4f>,
244 color_space: impl Into<Option<ColorSpace>>,
245 input: impl Into<Option<ImageFilter>>,
246 crop_rect: impl Into<CropRect>,
247) -> Option<ImageFilter> {
248 let delta = offset.into();
249 ImageFilter::from_ptr(unsafe {
250 sb::C_SkImageFilters_DropShadow(
251 dx:delta.x,
252 dy:delta.y,
253 sigmaX:sigma_x,
254 sigmaY:sigma_y,
255 color.into().native(),
256 colorSpace:color_space.into().into_ptr_or_null(),
257 input.into().into_ptr_or_null(),
258 cropRect:crop_rect.into().native(),
259 )
260 })
261}
262
263/// Create a filter that renders a drop shadow, in exactly the same manner as ::DropShadow,
264/// except that the resulting image does not include the input content. This allows the shadow
265/// and input to be composed by a filter DAG in a more flexible manner.
266/// * `offset` - The offset of the shadow.
267/// * `sigma_x` - The blur radius for the shadow, along the X axis.
268/// * `sigma_y` - The blur radius for the shadow, along the Y axis.
269/// * `color` - The color of the drop shadow.
270/// * `color_space` - The color space of the drop shadow color.
271/// * `input` - The input filter, or will use the source bitmap if this is null.
272/// * `crop_rect` - Optional rectangle that crops the input and output.
273pub fn drop_shadow_only(
274 offset: impl Into<Vector>,
275 (sigma_x: f32, sigma_y: f32): (scalar, scalar),
276 color: impl Into<Color4f>,
277 color_space: impl Into<Option<ColorSpace>>,
278 input: impl Into<Option<ImageFilter>>,
279 crop_rect: impl Into<CropRect>,
280) -> Option<ImageFilter> {
281 let delta = offset.into();
282
283 ImageFilter::from_ptr(unsafe {
284 sb::C_SkImageFilters_DropShadowOnly(
285 dx:delta.x,
286 dy:delta.y,
287 sigmaX:sigma_x,
288 sigmaY:sigma_y,
289 color.into().native(),
290 colorSpace:color_space.into().into_ptr_or_null(),
291 input.into().into_ptr_or_null(),
292 cropRect:crop_rect.into().native(),
293 )
294 })
295}
296
297/// Create a filter that always produces transparent black.
298pub fn empty() -> ImageFilter {
299 ImageFilter::from_ptr(unsafe { sb::C_SkImageFilters_Empty() }).unwrap()
300}
301/// Create a filter that draws the 'src_rect' portion of image into 'dst_rect' using the given
302/// filter quality. Similar to [`crate::Canvas::draw_image_rect()`].
303///
304/// * `image` - The image that is output by the filter, subset by 'srcRect'.
305/// * `src_rect` - The source pixels sampled into 'dstRect'
306/// * `dst_rect` - The local rectangle to draw the image into.
307/// * `sampling` - The sampling to use when drawing the image.
308pub fn image<'a>(
309 image: impl Into<Image>,
310 src_rect: impl Into<Option<&'a Rect>>,
311 dst_rect: impl Into<Option<&'a Rect>>,
312 sampling: impl Into<Option<SamplingOptions>>,
313) -> Option<ImageFilter> {
314 let image = image.into();
315 let image_rect: Rect = Rect::from_iwh(w:image.width(), h:image.height());
316 let src_rect = src_rect.into().unwrap_or(&image_rect);
317 let dst_rect = dst_rect.into().unwrap_or(&image_rect);
318 let sampling_options: SamplingOptions = sampling.into().unwrap_or_else(|| {
319 CubicResampler {
320 b: 1.0 / 3.0,
321 c: 1.0 / 3.0,
322 }
323 .into()
324 });
325
326 ImageFilter::from_ptr(unsafe {
327 sb::C_SkImageFilters_Image(
328 image.into_ptr(),
329 srcRect:src_rect.as_ref().native(),
330 dstRect:dst_rect.as_ref().native(),
331 sampling_options.native(),
332 )
333 })
334}
335
336/// Create a filter that fills 'lens_bounds' with a magnification of the input.
337///
338/// * `lens_bounds` - The outer bounds of the magnifier effect
339/// * `zoom_amount` - The amount of magnification applied to the input image
340/// * `inset` - The size or width of the fish-eye distortion around the magnified content
341/// * `sampling` - The [`SamplingOptions`] applied to the input image when magnified
342/// * `input` - The input filter that is magnified; if null the source bitmap is used
343/// * `crop_rect` - Optional rectangle that crops the input and output.
344pub fn magnifier(
345 lens_bounds: impl AsRef<Rect>,
346 zoom_amount: scalar,
347 inset: scalar,
348 sampling: SamplingOptions,
349 input: impl Into<Option<ImageFilter>>,
350 crop_rect: impl Into<CropRect>,
351) -> Option<ImageFilter> {
352 ImageFilter::from_ptr(unsafe {
353 sb::C_SkImageFilters_Magnifier(
354 lensBounds:lens_bounds.as_ref().native(),
355 zoomAmount:zoom_amount,
356 inset,
357 sampling.native(),
358 input.into().into_ptr_or_null(),
359 cropRect:crop_rect.into().native(),
360 )
361 })
362}
363
364/// Create a filter that applies an NxM image processing kernel to the input image. This can be
365/// used to produce effects such as sharpening, blurring, edge detection, etc.
366/// * `kernel_size` - The kernel size in pixels, in each dimension (N by M).
367/// * `kernel` - The image processing kernel. Must contain N * M elements, in row order.
368/// * `gain` - A scale factor applied to each pixel after convolution. This can be
369/// used to normalize the kernel, if it does not already sum to 1.
370/// * `bias` - A bias factor added to each pixel after convolution.
371/// * `kernel_offset` - An offset applied to each pixel coordinate before convolution.
372/// This can be used to center the kernel over the image
373/// (e.g., a 3x3 kernel should have an offset of {1, 1}).
374/// * `tile_mode` - How accesses outside the image are treated.
375/// * `convolve_alpha` - If `true`, all channels are convolved. If `false`, only the RGB channels
376/// are convolved, and alpha is copied from the source image.
377/// * `input` - The input image filter, if null the source bitmap is used instead.
378/// * `crop_rect` - Optional rectangle to which the output processing will be limited.
379#[allow(clippy::too_many_arguments)]
380pub fn matrix_convolution(
381 kernel_size: impl Into<ISize>,
382 kernel: &[scalar],
383 gain: scalar,
384 bias: scalar,
385 kernel_offset: impl Into<IPoint>,
386 tile_mode: TileMode,
387 convolve_alpha: bool,
388 input: impl Into<Option<ImageFilter>>,
389 crop_rect: impl Into<CropRect>,
390) -> Option<ImageFilter> {
391 let kernel_size = kernel_size.into();
392 assert_eq!(
393 (kernel_size.width * kernel_size.height) as usize,
394 kernel.len()
395 );
396 ImageFilter::from_ptr(unsafe {
397 sb::C_SkImageFilters_MatrixConvolution(
398 kernelSize:kernel_size.native(),
399 kernel.as_ptr(),
400 gain,
401 bias,
402 kernelOffset:kernel_offset.into().native(),
403 tileMode:tile_mode,
404 convolveAlpha:convolve_alpha,
405 input.into().into_ptr_or_null(),
406 cropRect:crop_rect.into().native(),
407 )
408 })
409}
410
411/// Create a filter that transforms the input image by 'matrix'. This matrix transforms the
412/// local space, which means it effectively happens prior to any transformation coming from the
413/// [`crate::Canvas`] initiating the filtering.
414/// * `matrix` - The matrix to apply to the original content.
415/// * `sampling` - How the image will be sampled when it is transformed
416/// * `input` - The image filter to transform, or null to use the source image.
417pub fn matrix_transform(
418 matrix: &Matrix,
419 sampling: impl Into<SamplingOptions>,
420 input: impl Into<Option<ImageFilter>>,
421) -> Option<ImageFilter> {
422 ImageFilter::from_ptr(unsafe {
423 sb::C_SkImageFilters_MatrixTransform(
424 matrix.native(),
425 sampling.into().native(),
426 input.into().into_ptr_or_null(),
427 )
428 })
429}
430
431/// Create a filter that merges the filters together by drawing their results in order
432/// with src-over blending.
433/// * `filters` - The input filter array to merge. Any None
434/// filter pointers will use the source bitmap instead.
435/// * `crop_rect` - Optional rectangle that crops all input filters and the output.
436pub fn merge(
437 filters: impl IntoIterator<Item = Option<ImageFilter>>,
438 crop_rect: impl Into<CropRect>,
439) -> Option<ImageFilter> {
440 let filter_ptrs: Vec<*mut SkImageFilter> =
441 filters.into_iter().map(|f: Option>| f.into_ptr_or_null()).collect();
442 ImageFilter::from_ptr(unsafe {
443 sb::C_SkImageFilters_Merge(
444 filters:filter_ptrs.as_ptr(),
445 count:filter_ptrs.len().try_into().unwrap(),
446 cropRect:crop_rect.into().native(),
447 )
448 })
449}
450
451/// Create a filter that offsets the input filter by the given vector.
452/// * `offset` - The offset in local space that the image is shifted.
453/// * `input` - The input that will be moved, if null the source bitmap is used instead.
454/// * `crop_rect` - Optional rectangle to crop the input and output.
455pub fn offset(
456 offset: impl Into<Vector>,
457 input: impl Into<Option<ImageFilter>>,
458 crop_rect: impl Into<CropRect>,
459) -> Option<ImageFilter> {
460 let delta = offset.into();
461 ImageFilter::from_ptr(unsafe {
462 sb::C_SkImageFilters_Offset(
463 dx:delta.x,
464 dy:delta.y,
465 input.into().into_ptr_or_null(),
466 cropRect:crop_rect.into().native(),
467 )
468 })
469}
470
471/// Create a filter that produces the [`Picture`] as its output, clipped to both 'target_rect' and
472/// the picture's internal cull rect.
473///
474/// * `pic` - The picture that is drawn for the filter output.
475/// * `target_rect` - The drawing region for the picture.
476pub fn picture<'a>(
477 pic: impl Into<Picture>,
478 target_rect: impl Into<Option<&'a Rect>>,
479) -> Option<ImageFilter> {
480 let picture = pic.into();
481 let picture_rect = picture.cull_rect();
482 let target_rect = target_rect.into().unwrap_or(&picture_rect);
483
484 ImageFilter::from_ptr(unsafe {
485 sb::C_SkImageFilters_Picture(pic:picture.into_ptr(), targetRect:target_rect.native())
486 })
487}
488
489pub fn runtime_shader(
490 builder: &RuntimeShaderBuilder,
491 child_shader_name: impl AsRef<str>,
492 input: impl Into<Option<ImageFilter>>,
493) -> Option<ImageFilter> {
494 let child_shader_name: &str = child_shader_name.as_ref();
495 unsafe {
496 ImageFilter::from_ptr(sb::C_SkImageFilters_RuntimeShader(
497 builder.native() as *const _,
498 childShaderName:child_shader_name.as_ptr() as *const _,
499 childShaderNameCount:child_shader_name.len(),
500 input.into().into_ptr_or_null(),
501 ))
502 }
503}
504
505pub use skia_bindings::SkImageFilters_Dither as Dither;
506
507use super::runtime_effect::RuntimeShaderBuilder;
508variant_name!(Dither::Yes);
509
510/// Create a filter that fills the output with the per-pixel evaluation of the [`Shader`]. The
511/// shader is defined in the image filter's local coordinate system, so will automatically
512/// be affected by [`Canvas'`] transform.
513///
514/// Like `image()` and Picture(), this is a leaf filter that can be used to introduce inputs to
515/// a complex filter graph, but should generally be combined with a filter that as at least
516/// one null input to use the implicit source image.
517///
518/// * `shader` - The shader that fills the result image
519pub fn shader(shader: impl Into<Shader>, crop_rect: impl Into<CropRect>) -> Option<ImageFilter> {
520 shader_with_dither(shader, Dither::No, crop_rect)
521}
522
523pub fn shader_with_dither(
524 shader: impl Into<Shader>,
525 dither: Dither,
526 crop_rect: impl Into<CropRect>,
527) -> Option<ImageFilter> {
528 ImageFilter::from_ptr(unsafe {
529 sb::C_SkImageFilters_Shader(shader.into().into_ptr(), dither, cropRect:crop_rect.into().native())
530 })
531}
532
533/// Create a tile image filter.
534/// * `src` - Defines the pixels to tile
535/// * `dst` - Defines the pixel region that the tiles will be drawn to
536/// * `input` - The input that will be tiled, if null the source bitmap is used instead.
537pub fn tile(
538 src: impl AsRef<Rect>,
539 dst: impl AsRef<Rect>,
540 input: impl Into<Option<ImageFilter>>,
541) -> Option<ImageFilter> {
542 ImageFilter::from_ptr(unsafe {
543 sb::C_SkImageFilters_Tile(
544 src.as_ref().native(),
545 dst.as_ref().native(),
546 input.into().into_ptr_or_null(),
547 )
548 })
549}
550
551/// Create a filter that dilates each input pixel's channel values to the max value within the
552/// given radii along the x and y axes.
553/// * `radius_x` - The distance to dilate along the x axis to either side of each pixel.
554/// * `radius_y` - The distance to dilate along the y axis to either side of each pixel.
555/// * `input` - The image filter that is dilated, using source bitmap if this is null.
556/// * `crop_rect` - Optional rectangle that crops the input and output.
557pub fn dilate(
558 (radius_x: f32, radius_y: f32): (scalar, scalar),
559 input: impl Into<Option<ImageFilter>>,
560 crop_rect: impl Into<CropRect>,
561) -> Option<ImageFilter> {
562 ImageFilter::from_ptr(unsafe {
563 sb::C_SkImageFilters_Dilate(
564 radiusX:radius_x,
565 radiusY:radius_y,
566 input.into().into_ptr_or_null(),
567 cropRect:crop_rect.into().native(),
568 )
569 })
570}
571
572/// Create a filter that erodes each input pixel's channel values to the minimum channel value
573/// within the given radii along the x and y axes.
574/// * `radius_x` - The distance to erode along the x axis to either side of each pixel.
575/// * `radius_y` - The distance to erode along the y axis to either side of each pixel.
576/// * `input` - The image filter that is eroded, using source bitmap if this is null.
577/// * `crop_rect` - Optional rectangle that crops the input and output.
578pub fn erode(
579 (radius_x: f32, radius_y: f32): (scalar, scalar),
580 input: impl Into<Option<ImageFilter>>,
581 crop_rect: impl Into<CropRect>,
582) -> Option<ImageFilter> {
583 ImageFilter::from_ptr(unsafe {
584 sb::C_SkImageFilters_Erode(
585 radiusX:radius_x,
586 radiusY:radius_y,
587 input.into().into_ptr_or_null(),
588 cropRect:crop_rect.into().native(),
589 )
590 })
591}
592
593/// Create a filter that calculates the diffuse illumination from a distant light source,
594/// interpreting the alpha channel of the input as the height profile of the surface (to
595/// approximate normal vectors).
596/// * `direction` - The direction to the distance light.
597/// * `light_color` - The color of the diffuse light source.
598/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
599/// * `kd` - Diffuse reflectance coefficient.
600/// * `input` - The input filter that defines surface normals (as alpha), or uses the
601/// source bitmap when null.
602/// * `crop_rect` - Optional rectangle that crops the input and output.
603pub fn distant_lit_diffuse(
604 direction: impl Into<Point3>,
605 light_color: impl Into<Color>,
606 surface_scale: scalar,
607 kd: scalar,
608 input: impl Into<Option<ImageFilter>>,
609 crop_rect: impl Into<CropRect>,
610) -> Option<ImageFilter> {
611 ImageFilter::from_ptr(unsafe {
612 sb::C_SkImageFilters_DistantLitDiffuse(
613 direction.into().native(),
614 lightColor:light_color.into().into_native(),
615 surfaceScale:surface_scale,
616 kd,
617 input.into().into_ptr_or_null(),
618 cropRect:crop_rect.into().native(),
619 )
620 })
621}
622
623/// Create a filter that calculates the diffuse illumination from a point light source, using
624/// alpha channel of the input as the height profile of the surface (to approximate normal
625/// vectors).
626/// * `location` - The location of the point light.
627/// * `light_color` - The color of the diffuse light source.
628/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
629/// * `kd` - Diffuse reflectance coefficient.
630/// * `input` - The input filter that defines surface normals (as alpha), or uses the
631/// source bitmap when `None`.
632/// * `crop_rect` - Optional rectangle that crops the input and output.
633pub fn point_lit_diffuse(
634 location: impl Into<Point3>,
635 light_color: impl Into<Color>,
636 surface_scale: scalar,
637 kd: scalar,
638 input: impl Into<Option<ImageFilter>>,
639 crop_rect: impl Into<CropRect>,
640) -> Option<ImageFilter> {
641 ImageFilter::from_ptr(unsafe {
642 sb::C_SkImageFilters_PointLitDiffuse(
643 direction:location.into().native(),
644 lightColor:light_color.into().into_native(),
645 surfaceScale:surface_scale,
646 kd,
647 input.into().into_ptr_or_null(),
648 cropRect:crop_rect.into().native(),
649 )
650 })
651}
652
653/// Create a filter that calculates the diffuse illumination from a spot light source, using
654/// alpha channel of the input as the height profile of the surface (to approximate normal
655/// vectors). The spot light is restricted to be within 'cutoff_angle' of the vector between
656/// the location and target.
657/// * `location` - The location of the spot light.
658/// * `target` - The location that the spot light is point towards
659/// * `falloff_exponent` - Exponential falloff parameter for illumination outside of `cutoff_angle`
660/// * `cutoff_angle` - Maximum angle from lighting direction that receives full light
661/// * `light_color` - The color of the diffuse light source.
662/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
663/// * `kd` - Diffuse reflectance coefficient.
664/// * `input` - The input filter that defines surface normals (as alpha), or uses the
665/// source bitmap when null.
666/// * `crop_rect` - Optional rectangle that crops the input and output.
667#[allow(clippy::too_many_arguments)]
668pub fn spot_lit_diffuse(
669 location: impl Into<Point3>,
670 target: impl Into<Point3>,
671 falloff_exponent: scalar,
672 cutoff_angle: scalar,
673 light_color: impl Into<Color>,
674 surface_scale: scalar,
675 kd: scalar,
676 input: impl Into<Option<ImageFilter>>,
677 crop_rect: impl Into<CropRect>,
678) -> Option<ImageFilter> {
679 ImageFilter::from_ptr(unsafe {
680 sb::C_SkImageFilters_SpotLitDiffuse(
681 location.into().native(),
682 target.into().native(),
683 specularExponent:falloff_exponent,
684 cutoffAngle:cutoff_angle,
685 lightColor:light_color.into().into_native(),
686 surfaceScale:surface_scale,
687 kd,
688 input.into().into_ptr_or_null(),
689 cropRect:crop_rect.into().native(),
690 )
691 })
692}
693
694/// Create a filter that calculates the specular illumination from a distant light source,
695/// interpreting the alpha channel of the input as the height profile of the surface (to
696/// approximate normal vectors).
697/// * `direction` - The direction to the distance light.
698/// * `light_color` - The color of the specular light source.
699/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
700/// * `ks` - Specular reflectance coefficient.
701/// * `shininess` - The specular exponent determining how shiny the surface is.
702/// * `input` - The input filter that defines surface normals (as alpha), or uses the
703/// source bitmap when `None`.
704/// * `crop_rect` - Optional rectangle that crops the input and output.
705pub fn distant_lit_specular(
706 direction: impl Into<Point3>,
707 light_color: impl Into<Color>,
708 surface_scale: scalar,
709 ks: scalar,
710 shininess: scalar,
711 input: impl Into<Option<ImageFilter>>,
712 crop_rect: impl Into<CropRect>,
713) -> Option<ImageFilter> {
714 ImageFilter::from_ptr(unsafe {
715 sb::C_ImageFilters_DistantLitSpecular(
716 direction.into().native(),
717 lightColor:light_color.into().into_native(),
718 surfaceScale:surface_scale,
719 ks,
720 shininess,
721 input.into().into_ptr_or_null(),
722 cropRect:crop_rect.into().native(),
723 )
724 })
725}
726
727/// Create a filter that calculates the specular illumination from a point light source, using
728/// alpha channel of the input as the height profile of the surface (to approximate normal
729/// vectors).
730/// * `location` - The location of the point light.
731/// * `light_color` - The color of the specular light source.
732/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
733/// * `ks` - Specular reflectance coefficient.
734/// * `shininess` - The specular exponent determining how shiny the surface is.
735/// * `input` - The input filter that defines surface normals (as alpha), or uses the
736/// source bitmap when `None`.
737/// * `crop_rect` - Optional rectangle that crops the input and output.
738pub fn point_lit_specular(
739 location: impl Into<Point3>,
740 light_color: impl Into<Color>,
741 surface_scale: scalar,
742 ks: scalar,
743 shininess: scalar,
744 input: impl Into<Option<ImageFilter>>,
745 crop_rect: impl Into<CropRect>,
746) -> Option<ImageFilter> {
747 ImageFilter::from_ptr(unsafe {
748 sb::C_SkImageFilters_PointLitSpecular(
749 location.into().native(),
750 lightColor:light_color.into().into_native(),
751 surfaceScale:surface_scale,
752 ks,
753 shininess,
754 input.into().into_ptr_or_null(),
755 cropRect:crop_rect.into().native(),
756 )
757 })
758}
759
760/// Create a filter that calculates the specular illumination from a spot light source, using
761/// alpha channel of the input as the height profile of the surface (to approximate normal
762/// vectors). The spot light is restricted to be within 'cutoff_angle' of the vector between
763/// the location and target.
764/// * `location` - The location of the spot light.
765/// * `target` - The location that the spot light is point towards
766/// * `falloff_exponent` - Exponential falloff parameter for illumination outside of `cutoff_angle`
767/// * `cutoff_angle` - Maximum angle from lighting direction that receives full light
768/// * `light_color` - The color of the specular light source.
769/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
770/// * `ks` - Specular reflectance coefficient.
771/// * `shininess` - The specular exponent determining how shiny the surface is.
772/// * `input` - The input filter that defines surface normals (as alpha), or uses the
773/// source bitmap when null.
774/// * `crop_rect` - Optional rectangle that crops the input and output.
775#[allow(clippy::too_many_arguments)]
776pub fn spot_lit_specular(
777 location: impl Into<Point3>,
778 target: impl Into<Point3>,
779 falloff_exponent: scalar,
780 cutoff_angle: scalar,
781 light_color: impl Into<Color>,
782 surface_scale: scalar,
783 ks: scalar,
784 shininess: scalar,
785 input: impl Into<Option<ImageFilter>>,
786 crop_rect: impl Into<CropRect>,
787) -> Option<ImageFilter> {
788 ImageFilter::from_ptr(unsafe {
789 sb::C_SkImageFilters_SpotLitSpecular(
790 location.into().native(),
791 target.into().native(),
792 specularExponent:falloff_exponent,
793 cutoffAngle:cutoff_angle,
794 lightColor:light_color.into().into_native(),
795 surfaceScale:surface_scale,
796 ks,
797 shininess,
798 input.into().into_ptr_or_null(),
799 cropRect:crop_rect.into().native(),
800 )
801 })
802}
803
804impl ImageFilter {
805 /// [`arithmetic()`]
806 pub fn arithmetic<'a>(
807 inputs: impl Into<ArithmeticFPInputs>,
808 background: impl Into<Option<Self>>,
809 foreground: impl Into<Option<Self>>,
810 crop_rect: impl Into<Option<&'a IRect>>,
811 ) -> Option<Self> {
812 let inputs = inputs.into();
813 arithmetic(
814 inputs.k[0],
815 inputs.k[1],
816 inputs.k[2],
817 inputs.k[3],
818 inputs.enforce_pm_color,
819 background,
820 foreground,
821 crop_rect.into().map(|r| r.into()),
822 )
823 }
824
825 /// [`blur()`]
826 pub fn blur<'a>(
827 self,
828 crop_rect: impl Into<Option<&'a IRect>>,
829 sigma: (scalar, scalar),
830 tile_mode: impl Into<Option<crate::TileMode>>,
831 ) -> Option<Self> {
832 blur(sigma, tile_mode, self, crop_rect.into().map(|r| r.into()))
833 }
834
835 /// [`color_filter()`]
836 pub fn color_filter<'a>(
837 self,
838 crop_rect: impl Into<Option<&'a IRect>>,
839 cf: impl Into<ColorFilter>,
840 ) -> Option<Self> {
841 color_filter(cf, self, crop_rect.into().map(|r| r.into()))
842 }
843
844 /// [`compose()`]
845 pub fn compose(outer: impl Into<ImageFilter>, inner: impl Into<ImageFilter>) -> Option<Self> {
846 compose(outer, inner)
847 }
848
849 /// [`crop()`]
850 pub fn crop(
851 rect: impl AsRef<Rect>,
852 tile_mode: impl Into<Option<TileMode>>,
853 input: impl Into<Option<ImageFilter>>,
854 ) -> Option<ImageFilter> {
855 crop(rect, tile_mode, input)
856 }
857
858 /// [`displacement_map()`]
859 pub fn displacement_map_effect<'a>(
860 channel_selectors: (ColorChannel, ColorChannel),
861 scale: scalar,
862 displacement: impl Into<Option<ImageFilter>>,
863 color: impl Into<Option<ImageFilter>>,
864 crop_rect: impl Into<Option<&'a IRect>>,
865 ) -> Option<Self> {
866 displacement_map(
867 channel_selectors,
868 scale,
869 displacement,
870 color,
871 crop_rect.into().map(|r| r.into()),
872 )
873 }
874
875 /// [`distant_lit_diffuse()`]
876 pub fn distant_lit_diffuse_lighting<'a>(
877 self,
878 crop_rect: impl Into<Option<&'a IRect>>,
879 direction: impl Into<Point3>,
880 light_color: impl Into<Color>,
881 surface_scale: scalar,
882 kd: scalar,
883 ) -> Option<Self> {
884 distant_lit_diffuse(
885 direction,
886 light_color,
887 surface_scale,
888 kd,
889 self,
890 crop_rect.into().map(|r| r.into()),
891 )
892 }
893
894 /// [`point_lit_diffuse()`]
895 pub fn point_lit_diffuse_lighting<'a>(
896 self,
897 crop_rect: impl Into<Option<&'a IRect>>,
898 location: impl Into<Point3>,
899 light_color: impl Into<Color>,
900 surface_scale: scalar,
901 kd: scalar,
902 ) -> Option<Self> {
903 point_lit_diffuse(
904 location,
905 light_color,
906 surface_scale,
907 kd,
908 self,
909 crop_rect.into().map(|r| r.into()),
910 )
911 }
912
913 /// [`spot_lit_diffuse()`]
914 #[allow(clippy::too_many_arguments)]
915 pub fn spot_lit_diffuse_lighting<'a>(
916 self,
917 crop_rect: impl Into<Option<&'a IRect>>,
918 location: impl Into<Point3>,
919 target: impl Into<Point3>,
920 specular_exponent: scalar,
921 cutoff_angle: scalar,
922 light_color: impl Into<Color>,
923 surface_scale: scalar,
924 kd: scalar,
925 ) -> Option<Self> {
926 spot_lit_diffuse(
927 location,
928 target,
929 specular_exponent,
930 cutoff_angle,
931 light_color,
932 surface_scale,
933 kd,
934 self,
935 crop_rect.into().map(|r| r.into()),
936 )
937 }
938
939 /// [`distant_lit_specular()`]
940 pub fn distant_lit_specular_lighting<'a>(
941 self,
942 crop_rect: impl Into<Option<&'a IRect>>,
943 direction: impl Into<Point3>,
944 light_color: impl Into<Color>,
945 surface_scale: scalar,
946 ks: scalar,
947 shininess: scalar,
948 ) -> Option<Self> {
949 distant_lit_specular(
950 direction,
951 light_color,
952 surface_scale,
953 ks,
954 shininess,
955 self,
956 crop_rect.into().map(|r| r.into()),
957 )
958 }
959
960 /// [`point_lit_specular()`]
961 pub fn point_lit_specular_lighting<'a>(
962 self,
963 crop_rect: impl Into<Option<&'a IRect>>,
964 location: impl Into<Point3>,
965 light_color: impl Into<Color>,
966 surface_scale: scalar,
967 ks: scalar,
968 shininess: scalar,
969 ) -> Option<Self> {
970 point_lit_specular(
971 location,
972 light_color,
973 surface_scale,
974 ks,
975 shininess,
976 self,
977 crop_rect.into().map(|r| r.into()),
978 )
979 }
980
981 /// [`spot_lit_specular()`]
982 #[allow(clippy::too_many_arguments)]
983 pub fn spot_lit_specular_lighting<'a>(
984 self,
985 crop_rect: impl Into<Option<&'a IRect>>,
986 location: impl Into<Point3>,
987 target: impl Into<Point3>,
988 specular_exponent: scalar,
989 cutoff_angle: scalar,
990 light_color: impl Into<Color>,
991 surface_scale: scalar,
992 ks: scalar,
993 shininess: scalar,
994 ) -> Option<Self> {
995 spot_lit_specular(
996 location,
997 target,
998 specular_exponent,
999 cutoff_angle,
1000 light_color,
1001 surface_scale,
1002 ks,
1003 shininess,
1004 self,
1005 crop_rect.into().map(|r| r.into()),
1006 )
1007 }
1008
1009 /// [`magnifier()`]
1010 pub fn magnifier<'a>(
1011 self,
1012 lens_bounds: impl AsRef<Rect>,
1013 zoom_amount: scalar,
1014 inset: scalar,
1015 sampling_options: SamplingOptions,
1016 crop_rect: impl Into<Option<&'a IRect>>,
1017 ) -> Option<Self> {
1018 magnifier(
1019 lens_bounds,
1020 zoom_amount,
1021 inset,
1022 sampling_options,
1023 self,
1024 crop_rect.into().map(|r| r.into()),
1025 )
1026 }
1027
1028 /// [`matrix_convolution()`]
1029 #[allow(clippy::too_many_arguments)]
1030 pub fn matrix_convolution<'a>(
1031 self,
1032 crop_rect: impl Into<Option<&'a IRect>>,
1033 kernel_size: impl Into<ISize>,
1034 kernel: &[scalar],
1035 gain: scalar,
1036 bias: scalar,
1037 kernel_offset: impl Into<IPoint>,
1038 tile_mode: crate::TileMode,
1039 convolve_alpha: bool,
1040 ) -> Option<Self> {
1041 matrix_convolution(
1042 kernel_size,
1043 kernel,
1044 gain,
1045 bias,
1046 kernel_offset,
1047 tile_mode,
1048 convolve_alpha,
1049 self,
1050 crop_rect.into().map(|r| r.into()),
1051 )
1052 }
1053
1054 /// [`merge()`]
1055 pub fn merge<'a>(
1056 filters: impl IntoIterator<Item = Option<Self>>,
1057 crop_rect: impl Into<Option<&'a IRect>>,
1058 ) -> Option<Self> {
1059 merge(filters, crop_rect.into().map(|r| r.into()))
1060 }
1061
1062 /// [`dilate()`]
1063 pub fn dilate<'a>(
1064 self,
1065 crop_rect: impl Into<Option<&'a IRect>>,
1066 radii: (scalar, scalar),
1067 ) -> Option<Self> {
1068 dilate(radii, self, crop_rect.into().map(|r| r.into()))
1069 }
1070
1071 /// [`erode()`]
1072 pub fn erode<'a>(
1073 self,
1074 crop_rect: impl Into<Option<&'a IRect>>,
1075 radii: (scalar, scalar),
1076 ) -> Option<Self> {
1077 erode(radii, self, crop_rect.into().map(|r| r.into()))
1078 }
1079
1080 /// [`offset()`]
1081 pub fn offset<'a>(
1082 self,
1083 crop_rect: impl Into<Option<&'a IRect>>,
1084 delta: impl Into<Vector>,
1085 ) -> Option<Self> {
1086 offset(delta, self, crop_rect.into().map(|r| r.into()))
1087 }
1088
1089 /// [`self::picture()`]
1090 pub fn from_picture<'a>(
1091 picture: impl Into<Picture>,
1092 crop_rect: impl Into<Option<&'a Rect>>,
1093 ) -> Option<Self> {
1094 self::picture(picture, crop_rect)
1095 }
1096
1097 /// [`tile()`]
1098 pub fn tile(self, src: impl AsRef<Rect>, dst: impl AsRef<Rect>) -> Option<Self> {
1099 tile(src, dst, self)
1100 }
1101}
1102
1103#[derive(Copy, Clone, PartialEq, Debug)]
1104pub struct ArithmeticFPInputs {
1105 pub k: [f32; 4],
1106 pub enforce_pm_color: bool,
1107}
1108
1109impl From<([f32; 4], bool)> for ArithmeticFPInputs {
1110 fn from((k: [f32; 4], enforce_pm_color: bool): ([f32; 4], bool)) -> Self {
1111 ArithmeticFPInputs {
1112 k,
1113 enforce_pm_color,
1114 }
1115 }
1116}
1117
1118impl ArithmeticFPInputs {
1119 pub fn new(k0: f32, k1: f32, k2: f32, k3: f32, enforce_pm_color: bool) -> Self {
1120 Self {
1121 k: [k0, k1, k2, k3],
1122 enforce_pm_color,
1123 }
1124 }
1125}
1126
1127impl Picture {
1128 pub fn as_image_filter<'a>(
1129 &self,
1130 crop_rect: impl Into<Option<&'a Rect>>,
1131 ) -> Option<ImageFilter> {
1132 self.clone().into_image_filter(crop_rect)
1133 }
1134
1135 pub fn into_image_filter<'a>(
1136 self,
1137 crop_rect: impl Into<Option<&'a Rect>>,
1138 ) -> Option<ImageFilter> {
1139 picture(self, target_rect:crop_rect)
1140 }
1141}
1142
1143#[cfg(test)]
1144mod tests {
1145 use super::CropRect;
1146 use crate::{IRect, Rect};
1147
1148 fn cr(crop_rect: impl Into<CropRect>) -> CropRect {
1149 crop_rect.into()
1150 }
1151
1152 #[test]
1153 fn test_crop_conversion_options() {
1154 assert_eq!(cr(None), CropRect::NO_CROP_RECT);
1155 assert_eq!(cr(CropRect::NO_CROP_RECT), CropRect::NO_CROP_RECT);
1156 #[allow(clippy::needless_borrow)]
1157 let cr_ref = cr(CropRect::NO_CROP_RECT);
1158 assert_eq!(cr_ref, CropRect::NO_CROP_RECT);
1159 let irect = IRect {
1160 left: 1,
1161 top: 2,
1162 right: 3,
1163 bottom: 4,
1164 };
1165 assert_eq!(cr(irect), CropRect(Some(Rect::from(irect))));
1166 #[allow(clippy::needless_borrow)]
1167 let cr_by_ref = cr(irect);
1168 assert_eq!(cr_by_ref, CropRect(Some(Rect::from(irect))));
1169 let rect = Rect {
1170 left: 1.0,
1171 top: 2.0,
1172 right: 3.0,
1173 bottom: 4.0,
1174 };
1175 assert_eq!(cr(rect), CropRect(Some(rect)));
1176 #[allow(clippy::needless_borrow)]
1177 let cr_by_ref = cr(rect);
1178 assert_eq!(cr_by_ref, CropRect(Some(rect)));
1179 }
1180
1181 #[test]
1182 fn test_drop_shadow_only() {
1183 let rect = Rect {
1184 left: 1.0,
1185 top: 2.0,
1186 right: 3.0,
1187 bottom: 4.0,
1188 };
1189
1190 let _ = super::drop_shadow_only((10., 10.), (1.0, 1.0), 0x404040, None, None, rect);
1191 }
1192}
1193