1use std::{
2 iter::{zip, Zip},
3 ops::{Deref, DerefMut},
4 vec,
5};
6
7use image::{GenericImageView, ImageBuffer, Pixel};
8use num_traits::{Bounded, NumCast};
9
10use crate::{
11 enums::{ColorString, ColorStructure},
12 error::Error,
13};
14
15pub(crate) fn dims_match<T: GenericImageView, U: GenericImageView>(a: &mut T, b: &U) -> Result<(), Error> {
16 if (a.dimensions()) != b.dimensions() {
17 return Err(Error::DimensionMismatch);
18 }
19 Ok(())
20}
21pub trait BufferBlend<P, Container>
22where
23 P: Pixel,
24 Container: Deref<Target = [P::Subpixel]> + AsRef<[P::Subpixel]>,
25{
26 fn blend(
112 &mut self,
113 other: &ImageBuffer<P, Container>,
114 op: fn(f64, f64) -> f64,
115 apply_to_color: bool,
116 apply_to_alpha: bool,
117 ) -> Result<(), Error>;
118}
119impl<P, Pmut, Container, ContainerMut> BufferBlend<P, Container> for ImageBuffer<Pmut, ContainerMut>
120where
121 Pmut: Pixel,
122 P: Pixel,
123 Container: Deref<Target = [P::Subpixel]> + AsRef<[<P as Pixel>::Subpixel]>,
124 ContainerMut: DerefMut<Target = [Pmut::Subpixel]>
125 + DerefMut<Target = [Pmut::Subpixel]>
126 + AsMut<[<Pmut as Pixel>::Subpixel]>,
127{
128 fn blend(
129 &mut self,
130 other: &ImageBuffer<P, Container>,
131 op: fn(f64, f64) -> f64,
132 apply_to_color: bool,
133 apply_to_alpha: bool,
134 ) -> Result<(), Error> {
135 dims_match(self, other)?;
136 let structure_a: ColorStructure = self.sample_layout().try_into()?;
137 let structure_b: ColorStructure = other.sample_layout().try_into()?;
138
139 let (color_channels, alpha_channels) = get_channels(&structure_a, &structure_b)?;
140
141 let a_max = type_max::<Pmut>();
142 let b_max = type_max::<P>();
143
144 if apply_to_color {
145 zip(self.pixels_mut(), other.pixels()).for_each(|(px_a, px_b)| {
146 let channel_a = px_a.channels_mut();
147 let channel_b = px_b.channels();
148 let alpha_weight = match structure_b.alpha_channel() {
149 Some(alpha_channel) => {
150 <f64 as NumCast>::from(channel_b[alpha_channel]).unwrap() / b_max
151 }
152 None => 1.,
153 };
154 if alpha_weight == 0. {
155 return;
156 };
157 color_channels.clone().for_each(|(ch_a, ch_b)| {
158 let a_f64: f64 = <f64 as NumCast>::from(channel_a[ch_a]).unwrap() / a_max;
159 let b_f64: f64 = <f64 as NumCast>::from(channel_b[ch_b]).unwrap() / b_max;
160 let new_64_unweighted: f64 = NumCast::from(op(a_f64, b_f64)).unwrap();
161 let new_64 = new_64_unweighted * alpha_weight + a_f64 * (1. - alpha_weight);
162 let new_val = NumCast::from(new_64.clamp(0., 1.0) * a_max).unwrap();
163 channel_a[ch_a] = new_val;
164 });
165 });
166 };
167 if apply_to_alpha {
168 if let Some((alpha_a, alpha_b)) = alpha_channels {
169 zip(self.pixels_mut(), other.pixels()).for_each(|(px_a, px_b)| {
170 let channel_a = px_a.channels_mut();
171 let channel_b = px_b.channels();
172
173 let a_f64: f64 = <f64 as NumCast>::from(channel_a[alpha_a]).unwrap() / a_max;
174 let b_f64: f64 = <f64 as NumCast>::from(channel_b[alpha_b]).unwrap() / b_max;
175 let new_64: f64 = NumCast::from(op(a_f64, b_f64)).unwrap();
176 let new_val = NumCast::from(new_64.clamp(0., 1.0) * a_max).unwrap();
177 channel_a[alpha_a] = new_val;
178 });
179 }
180 }
181
182 Ok(())
183 }
184}
185
186pub(crate) fn type_max<P>() -> f64 where P: Pixel {
187 let max: f64 = NumCast::from(<P as Pixel>::Subpixel::max_value()).unwrap();
188 let f32_max: f64 = NumCast::from(<f32 as Bounded>::max_value()).unwrap();
189 if max - f32_max == 0. {
191 return 1.
192 }
193 max
194}
195
196type ChannelIter = (
197 Zip<vec::IntoIter<usize>, vec::IntoIter<usize>>,
198 Option<(usize, usize)>,
199);
200fn get_channels(
201 structure_a: &ColorStructure,
202 structure_b: &ColorStructure,
203) -> Result<ChannelIter, Error> {
204 let color_channels = match (structure_a.rgb(), structure_b.rgb()) {
205 (true, true) => zip(vec![0usize, 1, 2], vec![0usize, 1, 2]),
206 (true, false) => zip(vec![0, 1, 2], vec![0, 0, 0]),
207 (false, false) => zip(vec![0], vec![0]),
208 (false, true) => Err(Error::UnsupportedBlend(
209 structure_a.color_str(),
210 structure_b.color_str(),
211 ))?,
212 };
213 let alpha_channels = match (structure_a.alpha(), structure_b.alpha()) {
214 (true, true) => Some((
215 structure_a.alpha_channel().unwrap(),
216 structure_b.alpha_channel().unwrap(),
217 )),
218 _ => None,
219 };
220 Ok((color_channels, alpha_channels))
221}