1 | use std::ptr; |
2 | |
3 | use skia_bindings::{self as sb, SkImageFilter, SkRect}; |
4 | |
5 | use 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)] |
16 | pub struct CropRect(Option<Rect>); |
17 | |
18 | impl 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 | |
33 | impl Default for CropRect { |
34 | fn default() -> Self { |
35 | CropRect::NO_CROP_RECT |
36 | } |
37 | } |
38 | |
39 | impl From<Option<CropRect>> for CropRect { |
40 | fn from(opt: Option<CropRect>) -> Self { |
41 | opt.unwrap_or(Self::NO_CROP_RECT) |
42 | } |
43 | } |
44 | |
45 | impl From<&CropRect> for CropRect { |
46 | fn from(cr: &CropRect) -> Self { |
47 | *cr |
48 | } |
49 | } |
50 | |
51 | impl From<IRect> for CropRect { |
52 | fn from(r: IRect) -> Self { |
53 | Self(Some(Rect::from(r))) |
54 | } |
55 | } |
56 | |
57 | impl From<&IRect> for CropRect { |
58 | fn from(r: &IRect) -> Self { |
59 | Self::from(*r) |
60 | } |
61 | } |
62 | |
63 | impl From<Rect> for CropRect { |
64 | fn from(r: Rect) -> Self { |
65 | Self(Some(r)) |
66 | } |
67 | } |
68 | |
69 | impl 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)] |
84 | pub 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. |
113 | pub 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. |
136 | pub 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 | |
153 | pub 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. |
171 | pub 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` |
187 | pub 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. |
212 | pub 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. |
239 | pub 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. |
270 | pub 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. |
293 | pub 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. |
303 | pub 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. |
339 | pub 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)] |
375 | pub 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. |
412 | pub 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. |
431 | pub 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. |
450 | pub 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. |
471 | pub 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 | |
486 | pub use skia_bindings::SkImageFilters_Dither as Dither; |
487 | variant_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 |
498 | pub fn shader(shader: impl Into<Shader>, crop_rect: impl Into<CropRect>) -> Option<ImageFilter> { |
499 | shader_with_dither(shader, Dither::No, crop_rect) |
500 | } |
501 | |
502 | pub 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. |
516 | pub 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. |
536 | pub 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. |
557 | pub 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. |
582 | pub 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. |
612 | pub 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)] |
647 | pub 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. |
684 | pub 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. |
717 | pub 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)] |
755 | pub 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 | |
783 | impl 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)] |
1083 | pub struct ArithmeticFPInputs { |
1084 | pub k: [f32; 4], |
1085 | pub enforce_pm_color: bool, |
1086 | } |
1087 | |
1088 | impl 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 | |
1097 | impl 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 | |
1106 | impl 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)] |
1123 | mod 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 | |