image_blend/
dynamic_blend.rs

1use std::ops::DerefMut;
2
3use image::{ColorType, DynamicImage, ImageBuffer, Pixel};
4
5use crate::{BufferBlend, BufferGetAlpha, BufferSetAlpha, BufferStripAlpha, Error};
6
7pub trait DynamicChops {
8    /**
9    Blend `other` into `self` using the function `op`, where arg 0 is self and 1 is other.
10
11    Handles type conversion and alpha channel detection and placement automatically.
12
13    You may blend a luma image into an rgb image (in which case the luma image will be treated as a grayscale rgb image), but you cannot blend an rgba image into a luma image.
14
15    If `other` has an alpha channel, the output is weighted by this alpha channel (so if alpha for `other` for this pixel is 0.5, the blend effect will be 0.5 as strong)
16
17    # Arguments
18
19    Use `apply_to_color` and `apply_to_alpha` to control which channels are affected.
20
21    If `apply_to_alpha` is true but `self` or `other` does not have an alpha channel, this option has no effect.
22
23    `op` is a function that takes two f64 values and returns a f64 value. (e.g. `|self, other| self + other`)
24
25    Standard blend modes such as those found in photoshop are provided as functions (e.g. `pixel_add`, `pixel_mult`, etc.).
26
27    The values are normalized to the range 0.0..1.0 before blending, and then scaled back to the input type's range.
28
29    The output from `op` is automatically clamped from 0.0..1.0 before being converted back to the input type so you don't need to worry about overflow/underflow.
30
31    # Errors
32
33    `DimensionMismatch`: `self` and `other` have different dimensions
34
35    `UnsupportedBlend`: `self` is a luma image and `other` is an rgb image
36
37    # Examples
38
39    ## Example 1:
40
41    Using the `pixel_mult` function to blend two images together:
42    ```
43    use image::open;
44    use image_blend::DynamicChops;
45    use image_blend::pixelops::pixel_mult;
46
47    // Load an image
48    let mut img1_dynamic = open("test_data/1.png").unwrap();
49
50    // Load another image
51    let img2_dynamic = open("test_data/2.png").unwrap();
52
53    // Blend the images using the pixel_mult function
54    img1_dynamic.blend(&img2_dynamic, pixel_mult, true, false).unwrap();
55    img1_dynamic.save("tests_out/doctest_dynamic_blend_result.png").unwrap();
56
57    ```
58    ## Example 2:
59
60    Using a custom function to blend two images together:
61
62    ```
63    use image::open;
64    use image_blend::DynamicChops;
65
66    let closest_to_gray = |a: f64, b: f64| {
67        let a_diff = (a - 0.5).abs();
68        let b_diff = (b - 0.5).abs();
69        if a_diff < b_diff {
70            a
71        } else {
72            b
73        }
74    };
75
76    // Load an image
77    let mut img1_dynamic = open("test_data/1.png").unwrap();
78
79    // Load another image
80    let img2_dynamic = open("test_data/2.png").unwrap();
81
82    // Blend the images using our custom function
83    img1_dynamic.blend(&img2_dynamic, closest_to_gray, true, false).unwrap();
84    img1_dynamic.save("tests_out/doctest_dynamic_custom_result.png").unwrap();
85
86    ```
87    */
88    fn blend (
89        &mut self,
90        other: &Self,
91        op: fn(f64, f64) -> f64,
92        apply_to_color: bool,
93        apply_to_alpha: bool,
94    ) -> Result<(), Error>;
95    /**
96    Get the alpha channel of this image as a grayscale with the same number of channels as the input image. (i.e a 4 channel rgba image will return a 4 channel rgba grayscale image with the alpha channel set to the maximum value of the input type)
97
98    The alpha channel of the returned image is set to the maximum value of the input type.
99
100    If the image does not have an alpha channel, return None.
101
102
103    # Examples
104
105    ```
106    use image::open;
107    use image_blend::DynamicChops;
108
109    // Load an image and get its alpha channel
110    let img1_dynamic = open("test_data/1.png").unwrap();
111    let img1_alpha = img1_dynamic.get_alpha().unwrap();
112    img1_alpha.clone().save("tests_out/doctest_dynamic_getalpha_alpha.png").unwrap();
113
114    // Load another image and set its alpha channel to the first image's alpha channel, using the copied alpha channel
115    let mut img2_dynamic = open("test_data/2.png").unwrap();
116    img2_dynamic.set_alpha(&img1_alpha).unwrap();
117    img2_dynamic.save("tests_out/doctest_dynamic_getalpha_result.png").unwrap();
118
119    ```
120    */
121    fn get_alpha(
122        &self,
123    ) -> Option<Self> where Self: std::marker::Sized;
124    /**
125    Set an image's alpha channel from another images alpha channel. 
126
127    Handles type conversion and alpha channel placement automatically.
128
129    # Errors
130    `NoAlphaChannel`: `self` or `other` does not have an alpha channel
131
132    `DimensionMismatch`: `self` and `other` have different dimensions
133
134
135    # Examples
136
137    ```
138    use image::open;
139    use image_blend::DynamicChops;
140
141    // Load an image that has an alpha channel
142    let img1_dynamic = open("test_data/1.png").unwrap();
143
144    // Load another image and set its alpha channel to a copy of the first image's alpha channel.
145    let mut img2_dynamic = open("test_data/2.png").unwrap();
146    img2_dynamic.transplant_alpha(&img1_dynamic).unwrap();
147    img2_dynamic.save("tests_out/doctest_dynamic_transplantalpha_result.png").unwrap();
148    ```
149    */
150    fn transplant_alpha(
151        &mut self,
152        other: &Self
153    ) -> Result<(), Error>;
154    /**
155    Set an image's alpha channel using the grascale color of another image. 
156
157    Handles type conversion and alpha channel detection and placement automatically.
158
159    WARNING: `other` can be of any type, but only the first channel will be used to set the alpha channel. In a grayscale image this will be the luma channel, in an rgb image the red channel. Consider converting to grayscale if this matters.
160
161    # Errors
162    `NoAlphaChannel`: `self` does not have an alpha channel
163
164    `DimensionMismatch`: `self` and `other` have different dimensions
165
166
167    # Examples
168
169    ```
170    use image::open;
171    use image_blend::DynamicChops;
172
173    // Load an image and get its alpha channel
174    let img1_dynamic = open("test_data/1.png").unwrap();
175    let img1_alpha = img1_dynamic.get_alpha().unwrap();
176    img1_alpha.clone().save("tests_out/doctest_dynamic_setalpha_alpha.png").unwrap();
177
178    // Load another image and set its alpha channel to the first image's alpha channel, using the copied alpha channel
179    let mut img2_dynamic = open("test_data/2.png").unwrap();
180    img2_dynamic.set_alpha(&img1_alpha).unwrap();
181    img2_dynamic.save("tests_out/doctest_dynamic_setalpha_result.png").unwrap();
182
183    ```
184    */
185    fn set_alpha(
186        &mut self,
187        other: &Self
188    ) -> Result<(), Error> where Self: std::marker::Sized;
189
190    /**
191    Remove this images alpha channel by setting it to the maximum value for every pixel.
192
193    Does not modify the underlying type.
194
195
196    # Errors
197    `NoAlphaChannel`: `self` does not have an alpha channel
198
199
200    # Examples
201
202    ```
203    use image::open;
204    use image_blend::{DynamicChops};
205
206    // Load an image and remove its alpha channel
207    let mut img2_dynamic = open("test_data/2.png").unwrap();
208    img2_dynamic.strip_alpha().unwrap();
209    img2_dynamic.save("tests_out/doctest_dynamic_stripalpha_result.png").unwrap();
210    ```
211    */
212    fn strip_alpha(
213        &mut self
214    ) -> Result<(), Error> where Self: std::marker::Sized;
215}
216impl DynamicChops for DynamicImage {
217    fn blend (
218        &mut self,
219        other: &Self,
220        op: fn(f64, f64) -> f64,
221        apply_to_color: bool,
222        apply_to_alpha: bool,
223    ) -> Result<(), Error> {
224        match self.color() {
225            ColorType::L8 => blend_step_a(self.as_mut_luma8().unwrap(), other, op, apply_to_color, apply_to_alpha),
226            ColorType::La8 => blend_step_a(self.as_mut_luma_alpha8().unwrap(), other, op, apply_to_color, apply_to_alpha),
227            ColorType::Rgb8 => blend_step_a(self.as_mut_rgb8().unwrap(), other, op, apply_to_color, apply_to_alpha),
228            ColorType::Rgba8 => blend_step_a(self.as_mut_rgba8().unwrap(), other, op, apply_to_color, apply_to_alpha),
229            ColorType::L16 => blend_step_a(self.as_mut_luma16().unwrap(), other, op, apply_to_color, apply_to_alpha),
230            ColorType::La16 => blend_step_a(self.as_mut_luma_alpha16().unwrap(), other, op, apply_to_color, apply_to_alpha),
231            ColorType::Rgb16 => blend_step_a(self.as_mut_rgb16().unwrap(), other, op, apply_to_color, apply_to_alpha),
232            ColorType::Rgba16 => blend_step_a(self.as_mut_rgba16().unwrap(), other, op, apply_to_color, apply_to_alpha),
233            ColorType::Rgb32F => blend_step_a(self.as_mut_rgb32f().unwrap(), other, op, apply_to_color, apply_to_alpha),
234            ColorType::Rgba32F => blend_step_a(self.as_mut_rgba32f().unwrap(), other, op, apply_to_color, apply_to_alpha),
235            _ => Err(Error::UnsupportedType),
236
237        }
238    }
239    fn get_alpha(
240        &self,
241    ) -> Option<DynamicImage> {
242        let color = self.color();
243        let mut copy = self.clone();
244        match color {
245            ColorType::L8 => get_alpha_step_a(copy.as_mut_luma8().unwrap()),
246            ColorType::La8 => get_alpha_step_a(copy.as_mut_luma_alpha8().unwrap()),
247            ColorType::Rgb8 => get_alpha_step_a(copy.as_mut_rgb8().unwrap()),
248            ColorType::Rgba8 => get_alpha_step_a(copy.as_mut_rgba8().unwrap()),
249            ColorType::L16 => get_alpha_step_a(copy.as_mut_luma16().unwrap()),
250            ColorType::La16 => get_alpha_step_a(copy.as_mut_luma_alpha16().unwrap()),
251            ColorType::Rgb16 => get_alpha_step_a(copy.as_mut_rgb16().unwrap()),
252            ColorType::Rgba16 => get_alpha_step_a(copy.as_mut_rgba16().unwrap()),
253            ColorType::Rgb32F => get_alpha_step_a(copy.as_mut_rgb32f().unwrap()),
254            ColorType::Rgba32F => get_alpha_step_a(copy.as_mut_rgba32f().unwrap()),
255            _ => Err(Error::UnsupportedType),
256        }.ok()?;
257        Some(copy)
258    }
259    fn transplant_alpha(
260            &mut self,
261            other: &Self
262    ) -> Result<(), Error> {
263        match self.color() {
264            ColorType::L8 => transplant_alpha_step_a(self.as_mut_luma8().unwrap(), other),
265            ColorType::La8 => transplant_alpha_step_a(self.as_mut_luma_alpha8().unwrap(), other),
266            ColorType::Rgb8 => transplant_alpha_step_a(self.as_mut_rgb8().unwrap(), other),
267            ColorType::Rgba8 => transplant_alpha_step_a(self.as_mut_rgba8().unwrap(), other),
268            ColorType::L16 => transplant_alpha_step_a(self.as_mut_luma16().unwrap(), other),
269            ColorType::La16 => transplant_alpha_step_a(self.as_mut_luma_alpha16().unwrap(), other),
270            ColorType::Rgb16 => transplant_alpha_step_a(self.as_mut_rgb16().unwrap(), other),
271            ColorType::Rgba16 => transplant_alpha_step_a(self.as_mut_rgba16().unwrap(), other),
272            ColorType::Rgb32F => transplant_alpha_step_a(self.as_mut_rgb32f().unwrap(), other),
273            ColorType::Rgba32F => transplant_alpha_step_a(self.as_mut_rgba32f().unwrap(), other),
274            _ => Err(Error::UnsupportedType),
275        }?;
276        Ok(())
277    }
278    fn set_alpha(
279        &mut self,
280        other: &Self
281    ) -> Result<(), Error> {
282        match self.color() {
283            ColorType::L8 => set_alpha_step_a(self.as_mut_luma8().unwrap(), other),
284            ColorType::La8 => set_alpha_step_a(self.as_mut_luma_alpha8().unwrap(), other),
285            ColorType::Rgb8 => set_alpha_step_a(self.as_mut_rgb8().unwrap(), other),
286            ColorType::Rgba8 => set_alpha_step_a(self.as_mut_rgba8().unwrap(), other),
287            ColorType::L16 => set_alpha_step_a(self.as_mut_luma16().unwrap(), other),
288            ColorType::La16 => set_alpha_step_a(self.as_mut_luma_alpha16().unwrap(), other),
289            ColorType::Rgb16 => set_alpha_step_a(self.as_mut_rgb16().unwrap(), other),
290            ColorType::Rgba16 => set_alpha_step_a(self.as_mut_rgba16().unwrap(), other),
291            ColorType::Rgb32F => set_alpha_step_a(self.as_mut_rgb32f().unwrap(), other),
292            ColorType::Rgba32F => set_alpha_step_a(self.as_mut_rgba32f().unwrap(), other),
293            _ => Err(Error::UnsupportedType),
294        }?;
295        Ok(())
296    }
297    fn strip_alpha(
298            &mut self
299        ) -> Result<(), Error> where Self: std::marker::Sized {
300        match self.color() {
301            ColorType::L8 => self.as_mut_luma8().unwrap().strip_alpha(),
302            ColorType::La8 => self.as_mut_luma_alpha8().unwrap().strip_alpha(),
303            ColorType::Rgb8 => self.as_mut_rgb8().unwrap().strip_alpha(),
304            ColorType::Rgba8 => self.as_mut_rgba8().unwrap().strip_alpha(),
305            ColorType::L16 => self.as_mut_luma16().unwrap().strip_alpha(),
306            ColorType::La16 => self.as_mut_luma_alpha16().unwrap().strip_alpha(),
307            ColorType::Rgb16 => self.as_mut_rgb16().unwrap().strip_alpha(),
308            ColorType::Rgba16 => self.as_mut_rgba16().unwrap().strip_alpha(),
309            ColorType::Rgb32F => self.as_mut_rgb32f().unwrap().strip_alpha(),
310            ColorType::Rgba32F => self.as_mut_rgba32f().unwrap().strip_alpha(),
311            _ => Err(Error::UnsupportedType),
312        }?;
313        Ok(())
314    }
315}
316fn blend_step_a<Pmut, ContainerMut>(subject: &mut ImageBuffer<Pmut, ContainerMut>, other: &DynamicImage, op: fn(f64, f64) -> f64, apply_to_color: bool, apply_to_alpha: bool) -> Result<(), Error>
317where 
318    Pmut: Pixel,
319    ContainerMut: DerefMut<Target = [Pmut::Subpixel]>
320    + DerefMut<Target = [Pmut::Subpixel]>
321    + AsMut<[<Pmut as Pixel>::Subpixel]>
322{
323    match other.color() {
324        ColorType::L8 => subject.blend(other.as_luma8().unwrap(), op, apply_to_color, apply_to_alpha),
325        ColorType::La8 => subject.blend(other.as_luma_alpha8().unwrap(), op, apply_to_color, apply_to_alpha),
326        ColorType::Rgb8 => subject.blend(other.as_rgb8().unwrap(), op, apply_to_color, apply_to_alpha),
327        ColorType::Rgba8 => subject.blend(other.as_rgba8().unwrap(), op, apply_to_color, apply_to_alpha),
328        ColorType::L16 => subject.blend(other.as_luma16().unwrap(), op, apply_to_color, apply_to_alpha),
329        ColorType::La16 => subject.blend(other.as_luma_alpha16().unwrap(), op, apply_to_color, apply_to_alpha),
330        ColorType::Rgb16 => subject.blend(other.as_rgb16().unwrap(), op, apply_to_color, apply_to_alpha),
331        ColorType::Rgba16 => subject.blend(other.as_rgba16().unwrap(), op, apply_to_color, apply_to_alpha),
332        ColorType::Rgb32F => subject.blend(other.as_rgb32f().unwrap(), op, apply_to_color, apply_to_alpha),
333        ColorType::Rgba32F => subject.blend(other.as_rgba32f().unwrap(), op, apply_to_color, apply_to_alpha),
334        _ => Err(Error::UnsupportedType),
335    }
336}
337fn get_alpha_step_a<P, Container>(subject: &mut ImageBuffer<P, Container>) -> Result<(), Error>
338where 
339    P: Pixel,
340    Container: DerefMut<Target = [P::Subpixel]> + AsRef<[<P as image::Pixel>::Subpixel]> + Clone,
341{
342    let alpha = subject.get_alpha().ok_or(Error::NoAlphaChannel)?;
343    *subject = alpha;
344    Ok(())
345}
346fn set_alpha_step_a<Pmut, ContainerMut>(subject: &mut ImageBuffer<Pmut, ContainerMut>, other: &DynamicImage) -> Result<(), Error>
347where 
348    Pmut: Pixel,
349    ContainerMut: DerefMut<Target = [Pmut::Subpixel]>
350    + DerefMut<Target = [Pmut::Subpixel]>
351    + AsMut<[<Pmut as Pixel>::Subpixel]>
352{
353    match other.color() {
354        ColorType::L8 => subject.set_alpha(other.as_luma8().unwrap()),
355        ColorType::La8 => subject.set_alpha(other.as_luma_alpha8().unwrap()),
356        ColorType::Rgb8 => subject.set_alpha(other.as_rgb8().unwrap()),
357        ColorType::Rgba8 => subject.set_alpha(other.as_rgba8().unwrap()),
358        ColorType::L16 => subject.set_alpha(other.as_luma16().unwrap()),
359        ColorType::La16 => subject.set_alpha(other.as_luma_alpha16().unwrap()),
360        ColorType::Rgb16 => subject.set_alpha(other.as_rgb16().unwrap()),
361        ColorType::Rgba16 => subject.set_alpha(other.as_rgba16().unwrap()),
362        ColorType::Rgb32F => subject.set_alpha(other.as_rgb32f().unwrap()),
363        ColorType::Rgba32F => subject.set_alpha(other.as_rgba32f().unwrap()),
364        _ => Err(Error::UnsupportedType),
365    }
366}
367fn transplant_alpha_step_a<Pmut, ContainerMut>(subject: &mut ImageBuffer<Pmut, ContainerMut>, other: &DynamicImage) -> Result<(), Error>
368where 
369    Pmut: Pixel,
370    ContainerMut: DerefMut<Target = [Pmut::Subpixel]>
371    + DerefMut<Target = [Pmut::Subpixel]>
372    + AsMut<[<Pmut as Pixel>::Subpixel]>,
373{
374    match other.color() {
375        ColorType::L8 => subject.transplant_alpha(other.as_luma8().unwrap()),
376        ColorType::La8 => subject.transplant_alpha(other.as_luma_alpha8().unwrap()),
377        ColorType::Rgb8 => subject.transplant_alpha(other.as_rgb8().unwrap()),
378        ColorType::Rgba8 => subject.transplant_alpha(other.as_rgba8().unwrap()),
379        ColorType::L16 => subject.transplant_alpha(other.as_luma16().unwrap()),
380        ColorType::La16 => subject.transplant_alpha(other.as_luma_alpha16().unwrap()),
381        ColorType::Rgb16 => subject.transplant_alpha(other.as_rgb16().unwrap()),
382        ColorType::Rgba16 => subject.transplant_alpha(other.as_rgba16().unwrap()),
383        ColorType::Rgb32F => subject.transplant_alpha(other.as_rgb32f().unwrap()),
384        ColorType::Rgba32F => subject.transplant_alpha(other.as_rgba32f().unwrap()),
385        _ => Err(Error::UnsupportedType),
386    }
387}