1use std::ptr;
2
3use skia_bindings::{self as sb, SkImageFilter, SkRect};
4
5use crate::{
6 prelude::*, scalar, Blender, Color, ColorChannel, ColorFilter, CubicResampler, IPoint, IRect,
7 ISize, Image, ImageFilter, Matrix, Picture, Point3, Rect, SamplingOptions, Shader, TileMode,
8 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:background.into().into_ptr_or_null(),
102 foreground: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:background.into().into_ptr_or_null(),
123 foreground: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: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:cf.into().into_ptr(),
161 input: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:outer.into().into_ptr(), inner: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:rect.as_ref().native(),
195 tileMode:tile_mode.into().unwrap_or(TileMode::Decal),
196 input: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:displacement.into().into_ptr_or_null(),
225 color: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/// * `input` - The input filter, or will use the source bitmap if this is null.
238/// * `crop_rect` - Optional rectangle that crops the input and output.
239pub fn drop_shadow(
240 offset: impl Into<Vector>,
241 (sigma_x: f32, sigma_y: f32): (scalar, scalar),
242 color: impl Into<Color>,
243 input: impl Into<Option<ImageFilter>>,
244 crop_rect: impl Into<CropRect>,
245) -> Option<ImageFilter> {
246 let delta = offset.into();
247 let color = color.into();
248 ImageFilter::from_ptr(unsafe {
249 sb::C_SkImageFilters_DropShadow(
250 dx:delta.x,
251 dy:delta.y,
252 sigmaX:sigma_x,
253 sigmaY:sigma_y,
254 color:color.into_native(),
255 input:input.into().into_ptr_or_null(),
256 cropRect:crop_rect.into().native(),
257 )
258 })
259}
260
261/// Create a filter that renders a drop shadow, in exactly the same manner as ::DropShadow,
262/// except that the resulting image does not include the input content. This allows the shadow
263/// and input to be composed by a filter DAG in a more flexible manner.
264/// * `offset` - The offset of the shadow.
265/// * `sigma_x` - The blur radius for the shadow, along the X axis.
266/// * `sigma_y` - The blur radius for the shadow, along the Y axis.
267/// * `color` - The color of the drop shadow.
268/// * `input` - The input filter, or will use the source bitmap if this is null.
269/// * `crop_rect` - Optional rectangle that crops the input and output.
270pub fn drop_shadow_only(
271 offset: impl Into<Vector>,
272 (sigma_x: f32, sigma_y: f32): (scalar, scalar),
273 color: impl Into<Color>,
274 input: impl Into<Option<ImageFilter>>,
275 crop_rect: impl Into<CropRect>,
276) -> Option<ImageFilter> {
277 let delta = offset.into();
278 let color = color.into();
279 ImageFilter::from_ptr(unsafe {
280 sb::C_SkImageFilters_DropShadowOnly(
281 dx:delta.x,
282 dy:delta.y,
283 sigmaX:sigma_x,
284 sigmaY:sigma_y,
285 color:color.into_native(),
286 input:input.into().into_ptr_or_null(),
287 cropRect:crop_rect.into().native(),
288 )
289 })
290}
291
292/// Create a filter that always produces transparent black.
293pub fn empty() -> ImageFilter {
294 ImageFilter::from_ptr(unsafe { sb::C_SkImageFilters_Empty() }).unwrap()
295}
296/// Create a filter that draws the 'src_rect' portion of image into 'dst_rect' using the given
297/// filter quality. Similar to [`crate::Canvas::draw_image_rect()`].
298///
299/// * `image` - The image that is output by the filter, subset by 'srcRect'.
300/// * `src_rect` - The source pixels sampled into 'dstRect'
301/// * `dst_rect` - The local rectangle to draw the image into.
302/// * `sampling` - The sampling to use when drawing the image.
303pub fn image<'a>(
304 image: impl Into<Image>,
305 src_rect: impl Into<Option<&'a Rect>>,
306 dst_rect: impl Into<Option<&'a Rect>>,
307 sampling: impl Into<Option<SamplingOptions>>,
308) -> Option<ImageFilter> {
309 let image = image.into();
310 let image_rect: Rect = Rect::from_iwh(w:image.width(), h:image.height());
311 let src_rect = src_rect.into().unwrap_or(&image_rect);
312 let dst_rect = dst_rect.into().unwrap_or(&image_rect);
313 let sampling_options: SamplingOptions = sampling.into().unwrap_or_else(|| {
314 CubicResampler {
315 b: 1.0 / 3.0,
316 c: 1.0 / 3.0,
317 }
318 .into()
319 });
320
321 ImageFilter::from_ptr(unsafe {
322 sb::C_SkImageFilters_Image(
323 image:image.into_ptr(),
324 srcRect:src_rect.as_ref().native(),
325 dstRect:dst_rect.as_ref().native(),
326 sampling:sampling_options.native(),
327 )
328 })
329}
330
331/// Create a filter that fills 'lens_bounds' with a magnification of the input.
332///
333/// * `lens_bounds` - The outer bounds of the magnifier effect
334/// * `zoom_amount` - The amount of magnification applied to the input image
335/// * `inset` - The size or width of the fish-eye distortion around the magnified content
336/// * `sampling` - The [`SamplingOptions`] applied to the input image when magnified
337/// * `input` - The input filter that is magnified; if null the source bitmap is used
338/// * `crop_rect` - Optional rectangle that crops the input and output.
339pub fn magnifier(
340 lens_bounds: impl AsRef<Rect>,
341 zoom_amount: scalar,
342 inset: scalar,
343 sampling: SamplingOptions,
344 input: impl Into<Option<ImageFilter>>,
345 crop_rect: impl Into<CropRect>,
346) -> Option<ImageFilter> {
347 ImageFilter::from_ptr(unsafe {
348 sb::C_SkImageFilters_Magnifier(
349 lensBounds:lens_bounds.as_ref().native(),
350 zoomAmount:zoom_amount,
351 inset,
352 sampling:sampling.native(),
353 input:input.into().into_ptr_or_null(),
354 cropRect:crop_rect.into().native(),
355 )
356 })
357}
358
359/// Create a filter that applies an NxM image processing kernel to the input image. This can be
360/// used to produce effects such as sharpening, blurring, edge detection, etc.
361/// * `kernel_size` - The kernel size in pixels, in each dimension (N by M).
362/// * `kernel` - The image processing kernel. Must contain N * M elements, in row order.
363/// * `gain` - A scale factor applied to each pixel after convolution. This can be
364/// used to normalize the kernel, if it does not already sum to 1.
365/// * `bias` - A bias factor added to each pixel after convolution.
366/// * `kernel_offset` - An offset applied to each pixel coordinate before convolution.
367/// This can be used to center the kernel over the image
368/// (e.g., a 3x3 kernel should have an offset of {1, 1}).
369/// * `tile_mode` - How accesses outside the image are treated.
370/// * `convolve_alpha` - If `true`, all channels are convolved. If `false`, only the RGB channels
371/// are convolved, and alpha is copied from the source image.
372/// * `input` - The input image filter, if null the source bitmap is used instead.
373/// * `crop_rect` - Optional rectangle to which the output processing will be limited.
374#[allow(clippy::too_many_arguments)]
375pub fn matrix_convolution(
376 kernel_size: impl Into<ISize>,
377 kernel: &[scalar],
378 gain: scalar,
379 bias: scalar,
380 kernel_offset: impl Into<IPoint>,
381 tile_mode: TileMode,
382 convolve_alpha: bool,
383 input: impl Into<Option<ImageFilter>>,
384 crop_rect: impl Into<CropRect>,
385) -> Option<ImageFilter> {
386 let kernel_size = kernel_size.into();
387 assert_eq!(
388 (kernel_size.width * kernel_size.height) as usize,
389 kernel.len()
390 );
391 ImageFilter::from_ptr(unsafe {
392 sb::C_SkImageFilters_MatrixConvolution(
393 kernelSize:kernel_size.native(),
394 kernel:kernel.as_ptr(),
395 gain,
396 bias,
397 kernelOffset:kernel_offset.into().native(),
398 tileMode:tile_mode,
399 convolveAlpha:convolve_alpha,
400 input:input.into().into_ptr_or_null(),
401 cropRect:crop_rect.into().native(),
402 )
403 })
404}
405
406/// Create a filter that transforms the input image by 'matrix'. This matrix transforms the
407/// local space, which means it effectively happens prior to any transformation coming from the
408/// [`crate::Canvas`] initiating the filtering.
409/// * `matrix` - The matrix to apply to the original content.
410/// * `sampling` - How the image will be sampled when it is transformed
411/// * `input` - The image filter to transform, or null to use the source image.
412pub fn matrix_transform(
413 matrix: &Matrix,
414 sampling: impl Into<SamplingOptions>,
415 input: impl Into<Option<ImageFilter>>,
416) -> Option<ImageFilter> {
417 ImageFilter::from_ptr(unsafe {
418 sb::C_SkImageFilters_MatrixTransform(
419 matrix:matrix.native(),
420 sampling:sampling.into().native(),
421 input:input.into().into_ptr_or_null(),
422 )
423 })
424}
425
426/// Create a filter that merges the filters together by drawing their results in order
427/// with src-over blending.
428/// * `filters` - The input filter array to merge. Any None
429/// filter pointers will use the source bitmap instead.
430/// * `crop_rect` - Optional rectangle that crops all input filters and the output.
431pub fn merge(
432 filters: impl IntoIterator<Item = Option<ImageFilter>>,
433 crop_rect: impl Into<CropRect>,
434) -> Option<ImageFilter> {
435 let filter_ptrs: Vec<*mut SkImageFilter> =
436 filters.into_iter().map(|f: Option>| f.into_ptr_or_null()).collect();
437 ImageFilter::from_ptr(unsafe {
438 sb::C_SkImageFilters_Merge(
439 filters:filter_ptrs.as_ptr(),
440 count:filter_ptrs.len().try_into().unwrap(),
441 cropRect:crop_rect.into().native(),
442 )
443 })
444}
445
446/// Create a filter that offsets the input filter by the given vector.
447/// * `offset` - The offset in local space that the image is shifted.
448/// * `input` - The input that will be moved, if null the source bitmap is used instead.
449/// * `crop_rect` - Optional rectangle to crop the input and output.
450pub fn offset(
451 offset: impl Into<Vector>,
452 input: impl Into<Option<ImageFilter>>,
453 crop_rect: impl Into<CropRect>,
454) -> Option<ImageFilter> {
455 let delta = offset.into();
456 ImageFilter::from_ptr(unsafe {
457 sb::C_SkImageFilters_Offset(
458 dx:delta.x,
459 dy:delta.y,
460 input:input.into().into_ptr_or_null(),
461 cropRect:crop_rect.into().native(),
462 )
463 })
464}
465
466/// Create a filter that produces the [`Picture`] as its output, clipped to both 'target_rect' and
467/// the picture's internal cull rect.
468///
469/// * `pic` - The picture that is drawn for the filter output.
470/// * `target_rect` - The drawing region for the picture.
471pub fn picture<'a>(
472 pic: impl Into<Picture>,
473 target_rect: impl Into<Option<&'a Rect>>,
474) -> Option<ImageFilter> {
475 let picture = pic.into();
476 let picture_rect = picture.cull_rect();
477 let target_rect = target_rect.into().unwrap_or(&picture_rect);
478
479 ImageFilter::from_ptr(unsafe {
480 sb::C_SkImageFilters_Picture(pic:picture.into_ptr(), targetRect:target_rect.native())
481 })
482}
483
484// TODO: RuntimeShader
485
486pub use skia_bindings::SkImageFilters_Dither as Dither;
487variant_name!(Dither::Yes);
488
489/// Create a filter that fills the output with the per-pixel evaluation of the [`Shader`]. The
490/// shader is defined in the image filter's local coordinate system, so will automatically
491/// be affected by [`Canvas'`] transform.
492///
493/// Like `image()` and Picture(), this is a leaf filter that can be used to introduce inputs to
494/// a complex filter graph, but should generally be combined with a filter that as at least
495/// one null input to use the implicit source image.
496///
497/// * `shader` - The shader that fills the result image
498pub fn shader(shader: impl Into<Shader>, crop_rect: impl Into<CropRect>) -> Option<ImageFilter> {
499 shader_with_dither(shader, Dither::No, crop_rect)
500}
501
502pub fn shader_with_dither(
503 shader: impl Into<Shader>,
504 dither: Dither,
505 crop_rect: impl Into<CropRect>,
506) -> Option<ImageFilter> {
507 ImageFilter::from_ptr(unsafe {
508 sb::C_SkImageFilters_Shader(shader:shader.into().into_ptr(), dither, cropRect:crop_rect.into().native())
509 })
510}
511
512/// Create a tile image filter.
513/// * `src` - Defines the pixels to tile
514/// * `dst` - Defines the pixel region that the tiles will be drawn to
515/// * `input` - The input that will be tiled, if null the source bitmap is used instead.
516pub fn tile(
517 src: impl AsRef<Rect>,
518 dst: impl AsRef<Rect>,
519 input: impl Into<Option<ImageFilter>>,
520) -> Option<ImageFilter> {
521 ImageFilter::from_ptr(unsafe {
522 sb::C_SkImageFilters_Tile(
523 src:src.as_ref().native(),
524 dst:dst.as_ref().native(),
525 input:input.into().into_ptr_or_null(),
526 )
527 })
528}
529
530/// Create a filter that dilates each input pixel's channel values to the max value within the
531/// given radii along the x and y axes.
532/// * `radius_x` - The distance to dilate along the x axis to either side of each pixel.
533/// * `radius_y` - The distance to dilate along the y axis to either side of each pixel.
534/// * `input` - The image filter that is dilated, using source bitmap if this is null.
535/// * `crop_rect` - Optional rectangle that crops the input and output.
536pub fn dilate(
537 (radius_x: f32, radius_y: f32): (scalar, scalar),
538 input: impl Into<Option<ImageFilter>>,
539 crop_rect: impl Into<CropRect>,
540) -> Option<ImageFilter> {
541 ImageFilter::from_ptr(unsafe {
542 sb::C_SkImageFilters_Dilate(
543 radiusX:radius_x,
544 radiusY:radius_y,
545 input:input.into().into_ptr_or_null(),
546 cropRect:crop_rect.into().native(),
547 )
548 })
549}
550
551/// Create a filter that erodes each input pixel's channel values to the minimum channel value
552/// within the given radii along the x and y axes.
553/// * `radius_x` - The distance to erode along the x axis to either side of each pixel.
554/// * `radius_y` - The distance to erode along the y axis to either side of each pixel.
555/// * `input` - The image filter that is eroded, using source bitmap if this is null.
556/// * `crop_rect` - Optional rectangle that crops the input and output.
557pub fn erode(
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_Erode(
564 radiusX:radius_x,
565 radiusY:radius_y,
566 input:input.into().into_ptr_or_null(),
567 cropRect:crop_rect.into().native(),
568 )
569 })
570}
571
572/// Create a filter that calculates the diffuse illumination from a distant light source,
573/// interpreting the alpha channel of the input as the height profile of the surface (to
574/// approximate normal vectors).
575/// * `direction` - The direction to the distance light.
576/// * `light_color` - The color of the diffuse light source.
577/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
578/// * `kd` - Diffuse reflectance coefficient.
579/// * `input` - The input filter that defines surface normals (as alpha), or uses the
580/// source bitmap when null.
581/// * `crop_rect` - Optional rectangle that crops the input and output.
582pub fn distant_lit_diffuse(
583 direction: impl Into<Point3>,
584 light_color: impl Into<Color>,
585 surface_scale: scalar,
586 kd: scalar,
587 input: impl Into<Option<ImageFilter>>,
588 crop_rect: impl Into<CropRect>,
589) -> Option<ImageFilter> {
590 ImageFilter::from_ptr(unsafe {
591 sb::C_SkImageFilters_DistantLitDiffuse(
592 direction:direction.into().native(),
593 lightColor:light_color.into().into_native(),
594 surfaceScale:surface_scale,
595 kd,
596 input:input.into().into_ptr_or_null(),
597 cropRect:crop_rect.into().native(),
598 )
599 })
600}
601
602/// Create a filter that calculates the diffuse illumination from a point light source, using
603/// alpha channel of the input as the height profile of the surface (to approximate normal
604/// vectors).
605/// * `location` - The location of the point light.
606/// * `light_color` - The color of the diffuse light source.
607/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
608/// * `kd` - Diffuse reflectance coefficient.
609/// * `input` - The input filter that defines surface normals (as alpha), or uses the
610/// source bitmap when `None`.
611/// * `crop_rect` - Optional rectangle that crops the input and output.
612pub fn point_lit_diffuse(
613 location: impl Into<Point3>,
614 light_color: impl Into<Color>,
615 surface_scale: scalar,
616 kd: scalar,
617 input: impl Into<Option<ImageFilter>>,
618 crop_rect: impl Into<CropRect>,
619) -> Option<ImageFilter> {
620 ImageFilter::from_ptr(unsafe {
621 sb::C_SkImageFilters_PointLitDiffuse(
622 direction:location.into().native(),
623 lightColor:light_color.into().into_native(),
624 surfaceScale:surface_scale,
625 kd,
626 input:input.into().into_ptr_or_null(),
627 cropRect:crop_rect.into().native(),
628 )
629 })
630}
631
632/// Create a filter that calculates the diffuse illumination from a spot light source, using
633/// alpha channel of the input as the height profile of the surface (to approximate normal
634/// vectors). The spot light is restricted to be within 'cutoff_angle' of the vector between
635/// the location and target.
636/// * `location` - The location of the spot light.
637/// * `target` - The location that the spot light is point towards
638/// * `falloff_exponent` - Exponential falloff parameter for illumination outside of `cutoff_angle`
639/// * `cutoff_angle` - Maximum angle from lighting direction that receives full light
640/// * `light_color` - The color of the diffuse light source.
641/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
642/// * `kd` - Diffuse reflectance coefficient.
643/// * `input` - The input filter that defines surface normals (as alpha), or uses the
644/// source bitmap when null.
645/// * `crop_rect` - Optional rectangle that crops the input and output.
646#[allow(clippy::too_many_arguments)]
647pub fn spot_lit_diffuse(
648 location: impl Into<Point3>,
649 target: impl Into<Point3>,
650 falloff_exponent: scalar,
651 cutoff_angle: scalar,
652 light_color: impl Into<Color>,
653 surface_scale: scalar,
654 kd: scalar,
655 input: impl Into<Option<ImageFilter>>,
656 crop_rect: impl Into<CropRect>,
657) -> Option<ImageFilter> {
658 ImageFilter::from_ptr(unsafe {
659 sb::C_SkImageFilters_SpotLitDiffuse(
660 location:location.into().native(),
661 target:target.into().native(),
662 specularExponent:falloff_exponent,
663 cutoffAngle:cutoff_angle,
664 lightColor:light_color.into().into_native(),
665 surfaceScale:surface_scale,
666 kd,
667 input:input.into().into_ptr_or_null(),
668 cropRect:crop_rect.into().native(),
669 )
670 })
671}
672
673/// Create a filter that calculates the specular illumination from a distant light source,
674/// interpreting the alpha channel of the input as the height profile of the surface (to
675/// approximate normal vectors).
676/// * `direction` - The direction to the distance light.
677/// * `light_color` - The color of the specular light source.
678/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
679/// * `ks` - Specular reflectance coefficient.
680/// * `shininess` - The specular exponent determining how shiny the surface is.
681/// * `input` - The input filter that defines surface normals (as alpha), or uses the
682/// source bitmap when `None`.
683/// * `crop_rect` - Optional rectangle that crops the input and output.
684pub fn distant_lit_specular(
685 direction: impl Into<Point3>,
686 light_color: impl Into<Color>,
687 surface_scale: scalar,
688 ks: scalar,
689 shininess: scalar,
690 input: impl Into<Option<ImageFilter>>,
691 crop_rect: impl Into<CropRect>,
692) -> Option<ImageFilter> {
693 ImageFilter::from_ptr(unsafe {
694 sb::C_ImageFilters_DistantLitSpecular(
695 direction:direction.into().native(),
696 lightColor:light_color.into().into_native(),
697 surfaceScale:surface_scale,
698 ks,
699 shininess,
700 input:input.into().into_ptr_or_null(),
701 cropRect:crop_rect.into().native(),
702 )
703 })
704}
705
706/// Create a filter that calculates the specular illumination from a point light source, using
707/// alpha channel of the input as the height profile of the surface (to approximate normal
708/// vectors).
709/// * `location` - The location of the point light.
710/// * `light_color` - The color of the specular light source.
711/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
712/// * `ks` - Specular reflectance coefficient.
713/// * `shininess` - The specular exponent determining how shiny the surface is.
714/// * `input` - The input filter that defines surface normals (as alpha), or uses the
715/// source bitmap when `None`.
716/// * `crop_rect` - Optional rectangle that crops the input and output.
717pub fn point_lit_specular(
718 location: impl Into<Point3>,
719 light_color: impl Into<Color>,
720 surface_scale: scalar,
721 ks: scalar,
722 shininess: scalar,
723 input: impl Into<Option<ImageFilter>>,
724 crop_rect: impl Into<CropRect>,
725) -> Option<ImageFilter> {
726 ImageFilter::from_ptr(unsafe {
727 sb::C_SkImageFilters_PointLitSpecular(
728 location:location.into().native(),
729 lightColor:light_color.into().into_native(),
730 surfaceScale:surface_scale,
731 ks,
732 shininess,
733 input:input.into().into_ptr_or_null(),
734 cropRect:crop_rect.into().native(),
735 )
736 })
737}
738
739/// Create a filter that calculates the specular illumination from a spot light source, using
740/// alpha channel of the input as the height profile of the surface (to approximate normal
741/// vectors). The spot light is restricted to be within 'cutoff_angle' of the vector between
742/// the location and target.
743/// * `location` - The location of the spot light.
744/// * `target` - The location that the spot light is point towards
745/// * `falloff_exponent` - Exponential falloff parameter for illumination outside of `cutoff_angle`
746/// * `cutoff_angle` - Maximum angle from lighting direction that receives full light
747/// * `light_color` - The color of the specular light source.
748/// * `surface_scale` - Scale factor to transform from alpha values to physical height.
749/// * `ks` - Specular reflectance coefficient.
750/// * `shininess` - The specular exponent determining how shiny the surface is.
751/// * `input` - The input filter that defines surface normals (as alpha), or uses the
752/// source bitmap when null.
753/// * `crop_rect` - Optional rectangle that crops the input and output.
754#[allow(clippy::too_many_arguments)]
755pub fn spot_lit_specular(
756 location: impl Into<Point3>,
757 target: impl Into<Point3>,
758 falloff_exponent: scalar,
759 cutoff_angle: scalar,
760 light_color: impl Into<Color>,
761 surface_scale: scalar,
762 ks: scalar,
763 shininess: scalar,
764 input: impl Into<Option<ImageFilter>>,
765 crop_rect: impl Into<CropRect>,
766) -> Option<ImageFilter> {
767 ImageFilter::from_ptr(unsafe {
768 sb::C_SkImageFilters_SpotLitSpecular(
769 location:location.into().native(),
770 target:target.into().native(),
771 specularExponent:falloff_exponent,
772 cutoffAngle:cutoff_angle,
773 lightColor:light_color.into().into_native(),
774 surfaceScale:surface_scale,
775 ks,
776 shininess,
777 input:input.into().into_ptr_or_null(),
778 cropRect:crop_rect.into().native(),
779 )
780 })
781}
782
783impl ImageFilter {
784 /// [`arithmetic()`]
785 pub fn arithmetic<'a>(
786 inputs: impl Into<ArithmeticFPInputs>,
787 background: impl Into<Option<Self>>,
788 foreground: impl Into<Option<Self>>,
789 crop_rect: impl Into<Option<&'a IRect>>,
790 ) -> Option<Self> {
791 let inputs = inputs.into();
792 arithmetic(
793 inputs.k[0],
794 inputs.k[1],
795 inputs.k[2],
796 inputs.k[3],
797 inputs.enforce_pm_color,
798 background,
799 foreground,
800 crop_rect.into().map(|r| r.into()),
801 )
802 }
803
804 /// [`blur()`]
805 pub fn blur<'a>(
806 self,
807 crop_rect: impl Into<Option<&'a IRect>>,
808 sigma: (scalar, scalar),
809 tile_mode: impl Into<Option<crate::TileMode>>,
810 ) -> Option<Self> {
811 blur(sigma, tile_mode, self, crop_rect.into().map(|r| r.into()))
812 }
813
814 /// [`color_filter()`]
815 pub fn color_filter<'a>(
816 self,
817 crop_rect: impl Into<Option<&'a IRect>>,
818 cf: impl Into<ColorFilter>,
819 ) -> Option<Self> {
820 color_filter(cf, self, crop_rect.into().map(|r| r.into()))
821 }
822
823 /// [`compose()`]
824 pub fn compose(outer: impl Into<ImageFilter>, inner: impl Into<ImageFilter>) -> Option<Self> {
825 compose(outer, inner)
826 }
827
828 /// [`crop()`]
829 pub fn crop(
830 rect: impl AsRef<Rect>,
831 tile_mode: impl Into<Option<TileMode>>,
832 input: impl Into<Option<ImageFilter>>,
833 ) -> Option<ImageFilter> {
834 crop(rect, tile_mode, input)
835 }
836
837 /// [`displacement_map()`]
838 pub fn displacement_map_effect<'a>(
839 channel_selectors: (ColorChannel, ColorChannel),
840 scale: scalar,
841 displacement: impl Into<Option<ImageFilter>>,
842 color: impl Into<Option<ImageFilter>>,
843 crop_rect: impl Into<Option<&'a IRect>>,
844 ) -> Option<Self> {
845 displacement_map(
846 channel_selectors,
847 scale,
848 displacement,
849 color,
850 crop_rect.into().map(|r| r.into()),
851 )
852 }
853
854 /// [`distant_lit_diffuse()`]
855 pub fn distant_lit_diffuse_lighting<'a>(
856 self,
857 crop_rect: impl Into<Option<&'a IRect>>,
858 direction: impl Into<Point3>,
859 light_color: impl Into<Color>,
860 surface_scale: scalar,
861 kd: scalar,
862 ) -> Option<Self> {
863 distant_lit_diffuse(
864 direction,
865 light_color,
866 surface_scale,
867 kd,
868 self,
869 crop_rect.into().map(|r| r.into()),
870 )
871 }
872
873 /// [`point_lit_diffuse()`]
874 pub fn point_lit_diffuse_lighting<'a>(
875 self,
876 crop_rect: impl Into<Option<&'a IRect>>,
877 location: impl Into<Point3>,
878 light_color: impl Into<Color>,
879 surface_scale: scalar,
880 kd: scalar,
881 ) -> Option<Self> {
882 point_lit_diffuse(
883 location,
884 light_color,
885 surface_scale,
886 kd,
887 self,
888 crop_rect.into().map(|r| r.into()),
889 )
890 }
891
892 /// [`spot_lit_diffuse()`]
893 #[allow(clippy::too_many_arguments)]
894 pub fn spot_lit_diffuse_lighting<'a>(
895 self,
896 crop_rect: impl Into<Option<&'a IRect>>,
897 location: impl Into<Point3>,
898 target: impl Into<Point3>,
899 specular_exponent: scalar,
900 cutoff_angle: scalar,
901 light_color: impl Into<Color>,
902 surface_scale: scalar,
903 kd: scalar,
904 ) -> Option<Self> {
905 spot_lit_diffuse(
906 location,
907 target,
908 specular_exponent,
909 cutoff_angle,
910 light_color,
911 surface_scale,
912 kd,
913 self,
914 crop_rect.into().map(|r| r.into()),
915 )
916 }
917
918 /// [`distant_lit_specular()`]
919 pub fn distant_lit_specular_lighting<'a>(
920 self,
921 crop_rect: impl Into<Option<&'a IRect>>,
922 direction: impl Into<Point3>,
923 light_color: impl Into<Color>,
924 surface_scale: scalar,
925 ks: scalar,
926 shininess: scalar,
927 ) -> Option<Self> {
928 distant_lit_specular(
929 direction,
930 light_color,
931 surface_scale,
932 ks,
933 shininess,
934 self,
935 crop_rect.into().map(|r| r.into()),
936 )
937 }
938
939 /// [`point_lit_specular()`]
940 pub fn point_lit_specular_lighting<'a>(
941 self,
942 crop_rect: impl Into<Option<&'a IRect>>,
943 location: impl Into<Point3>,
944 light_color: impl Into<Color>,
945 surface_scale: scalar,
946 ks: scalar,
947 shininess: scalar,
948 ) -> Option<Self> {
949 point_lit_specular(
950 location,
951 light_color,
952 surface_scale,
953 ks,
954 shininess,
955 self,
956 crop_rect.into().map(|r| r.into()),
957 )
958 }
959
960 /// [`spot_lit_specular()`]
961 #[allow(clippy::too_many_arguments)]
962 pub fn spot_lit_specular_lighting<'a>(
963 self,
964 crop_rect: impl Into<Option<&'a IRect>>,
965 location: impl Into<Point3>,
966 target: impl Into<Point3>,
967 specular_exponent: scalar,
968 cutoff_angle: scalar,
969 light_color: impl Into<Color>,
970 surface_scale: scalar,
971 ks: scalar,
972 shininess: scalar,
973 ) -> Option<Self> {
974 spot_lit_specular(
975 location,
976 target,
977 specular_exponent,
978 cutoff_angle,
979 light_color,
980 surface_scale,
981 ks,
982 shininess,
983 self,
984 crop_rect.into().map(|r| r.into()),
985 )
986 }
987
988 /// [`magnifier()`]
989 pub fn magnifier<'a>(
990 self,
991 lens_bounds: impl AsRef<Rect>,
992 zoom_amount: scalar,
993 inset: scalar,
994 sampling_options: SamplingOptions,
995 crop_rect: impl Into<Option<&'a IRect>>,
996 ) -> Option<Self> {
997 magnifier(
998 lens_bounds,
999 zoom_amount,
1000 inset,
1001 sampling_options,
1002 self,
1003 crop_rect.into().map(|r| r.into()),
1004 )
1005 }
1006
1007 /// [`matrix_convolution()`]
1008 #[allow(clippy::too_many_arguments)]
1009 pub fn matrix_convolution<'a>(
1010 self,
1011 crop_rect: impl Into<Option<&'a IRect>>,
1012 kernel_size: impl Into<ISize>,
1013 kernel: &[scalar],
1014 gain: scalar,
1015 bias: scalar,
1016 kernel_offset: impl Into<IPoint>,
1017 tile_mode: crate::TileMode,
1018 convolve_alpha: bool,
1019 ) -> Option<Self> {
1020 matrix_convolution(
1021 kernel_size,
1022 kernel,
1023 gain,
1024 bias,
1025 kernel_offset,
1026 tile_mode,
1027 convolve_alpha,
1028 self,
1029 crop_rect.into().map(|r| r.into()),
1030 )
1031 }
1032
1033 /// [`merge()`]
1034 pub fn merge<'a>(
1035 filters: impl IntoIterator<Item = Option<Self>>,
1036 crop_rect: impl Into<Option<&'a IRect>>,
1037 ) -> Option<Self> {
1038 merge(filters, crop_rect.into().map(|r| r.into()))
1039 }
1040
1041 /// [`dilate()`]
1042 pub fn dilate<'a>(
1043 self,
1044 crop_rect: impl Into<Option<&'a IRect>>,
1045 radii: (scalar, scalar),
1046 ) -> Option<Self> {
1047 dilate(radii, self, crop_rect.into().map(|r| r.into()))
1048 }
1049
1050 /// [`erode()`]
1051 pub fn erode<'a>(
1052 self,
1053 crop_rect: impl Into<Option<&'a IRect>>,
1054 radii: (scalar, scalar),
1055 ) -> Option<Self> {
1056 erode(radii, self, crop_rect.into().map(|r| r.into()))
1057 }
1058
1059 /// [`offset()`]
1060 pub fn offset<'a>(
1061 self,
1062 crop_rect: impl Into<Option<&'a IRect>>,
1063 delta: impl Into<Vector>,
1064 ) -> Option<Self> {
1065 offset(delta, self, crop_rect.into().map(|r| r.into()))
1066 }
1067
1068 /// [`self::picture()`]
1069 pub fn from_picture<'a>(
1070 picture: impl Into<Picture>,
1071 crop_rect: impl Into<Option<&'a Rect>>,
1072 ) -> Option<Self> {
1073 self::picture(picture, crop_rect)
1074 }
1075
1076 /// [`tile()`]
1077 pub fn tile(self, src: impl AsRef<Rect>, dst: impl AsRef<Rect>) -> Option<Self> {
1078 tile(src, dst, self)
1079 }
1080}
1081
1082#[derive(Copy, Clone, PartialEq, Debug)]
1083pub struct ArithmeticFPInputs {
1084 pub k: [f32; 4],
1085 pub enforce_pm_color: bool,
1086}
1087
1088impl From<([f32; 4], bool)> for ArithmeticFPInputs {
1089 fn from((k: [f32; 4], enforce_pm_color: bool): ([f32; 4], bool)) -> Self {
1090 ArithmeticFPInputs {
1091 k,
1092 enforce_pm_color,
1093 }
1094 }
1095}
1096
1097impl ArithmeticFPInputs {
1098 pub fn new(k0: f32, k1: f32, k2: f32, k3: f32, enforce_pm_color: bool) -> Self {
1099 Self {
1100 k: [k0, k1, k2, k3],
1101 enforce_pm_color,
1102 }
1103 }
1104}
1105
1106impl Picture {
1107 pub fn as_image_filter<'a>(
1108 &self,
1109 crop_rect: impl Into<Option<&'a Rect>>,
1110 ) -> Option<ImageFilter> {
1111 self.clone().into_image_filter(crop_rect)
1112 }
1113
1114 pub fn into_image_filter<'a>(
1115 self,
1116 crop_rect: impl Into<Option<&'a Rect>>,
1117 ) -> Option<ImageFilter> {
1118 picture(self, target_rect:crop_rect)
1119 }
1120}
1121
1122#[cfg(test)]
1123mod tests {
1124 use super::CropRect;
1125 use crate::{IRect, Rect};
1126
1127 fn cr(crop_rect: impl Into<CropRect>) -> CropRect {
1128 crop_rect.into()
1129 }
1130
1131 #[test]
1132 fn test_crop_conversion_options() {
1133 assert_eq!(cr(None), CropRect::NO_CROP_RECT);
1134 assert_eq!(cr(CropRect::NO_CROP_RECT), CropRect::NO_CROP_RECT);
1135 #[allow(clippy::needless_borrow)]
1136 let cr_ref = cr(CropRect::NO_CROP_RECT);
1137 assert_eq!(cr_ref, CropRect::NO_CROP_RECT);
1138 let irect = IRect {
1139 left: 1,
1140 top: 2,
1141 right: 3,
1142 bottom: 4,
1143 };
1144 assert_eq!(cr(irect), CropRect(Some(Rect::from(irect))));
1145 #[allow(clippy::needless_borrow)]
1146 let cr_by_ref = cr(irect);
1147 assert_eq!(cr_by_ref, CropRect(Some(Rect::from(irect))));
1148 let rect = Rect {
1149 left: 1.0,
1150 top: 2.0,
1151 right: 3.0,
1152 bottom: 4.0,
1153 };
1154 assert_eq!(cr(rect), CropRect(Some(rect)));
1155 #[allow(clippy::needless_borrow)]
1156 let cr_by_ref = cr(rect);
1157 assert_eq!(cr_by_ref, CropRect(Some(rect)));
1158 }
1159}
1160