1use crate::{
2 bool_mask::LazySelect,
3 cast::{self, ArrayCast},
4 num::{Abs, Arithmetics, Clamp, MinMax, One, PartialCmp, Real, Sqrt, Zero},
5 stimulus::{Stimulus, StimulusColor},
6 Alpha,
7};
8
9use super::{blend_alpha, PreAlpha, Premultiply};
10
11pub trait Blend {
20 #[must_use]
23 fn multiply(self, other: Self) -> Self;
24
25 #[must_use]
27 fn screen(self, other: Self) -> Self;
28
29 #[must_use]
32 fn overlay(self, other: Self) -> Self;
33
34 #[must_use]
36 fn darken(self, other: Self) -> Self;
37
38 #[must_use]
40 fn lighten(self, other: Self) -> Self;
41
42 #[must_use]
45 fn dodge(self, other: Self) -> Self;
46
47 #[must_use]
50 fn burn(self, other: Self) -> Self;
51
52 #[must_use]
56 fn hard_light(self, other: Self) -> Self;
57
58 #[must_use]
62 fn soft_light(self, other: Self) -> Self;
63
64 #[must_use]
67 fn difference(self, other: Self) -> Self;
68
69 #[must_use]
73 fn exclusion(self, other: Self) -> Self;
74}
75
76impl<C, T, const N: usize> Blend for PreAlpha<C>
77where
78 C: Premultiply<Scalar = T> + StimulusColor + ArrayCast<Array = [T; N]> + Clone,
79 T: Real + Zero + One + MinMax + Clamp + Sqrt + Abs + Arithmetics + PartialCmp + Clone,
80 T::Mask: LazySelect<T>,
81{
82 #[inline]
83 fn multiply(self, other: Self) -> Self {
84 blend_separable(self.into(), other.into(), multiply_blend)
85 }
86
87 #[inline]
88 fn screen(self, other: Self) -> Self {
89 blend_separable(self.into(), other.into(), screen_blend)
90 }
91
92 #[inline]
93 fn overlay(self, other: Self) -> Self {
94 blend_separable(self.into(), other.into(), overlay_blend)
95 }
96
97 #[inline]
98 fn darken(self, other: Self) -> Self {
99 blend_separable(self.into(), other.into(), darken_blend)
100 }
101
102 #[inline]
103 fn lighten(self, other: Self) -> Self {
104 blend_separable(self.into(), other.into(), lighten_blend)
105 }
106
107 #[inline]
108 fn dodge(self, other: Self) -> Self {
109 blend_separable(self.into(), other.into(), dodge_blend)
110 }
111
112 #[inline]
113 fn burn(self, other: Self) -> Self {
114 blend_separable(self.into(), other.into(), burn_blend)
115 }
116
117 #[inline]
118 fn hard_light(self, other: Self) -> Self {
119 blend_separable(self.into(), other.into(), hard_light_blend)
120 }
121
122 #[inline]
123 fn soft_light(self, other: Self) -> Self {
124 blend_separable(self.into(), other.into(), soft_light_blend)
125 }
126
127 #[inline]
128 fn difference(self, other: Self) -> Self {
129 blend_separable(self.into(), other.into(), difference_blend)
130 }
131
132 #[inline]
133 fn exclusion(self, other: Self) -> Self {
134 blend_separable(self.into(), other.into(), exclusion_blend)
135 }
136}
137
138impl<C, T, const N: usize> Blend for C
139where
140 C: Premultiply<Scalar = T> + StimulusColor + ArrayCast<Array = [T; N]> + Clone,
141 T: Real + Zero + One + MinMax + Clamp + Sqrt + Abs + Arithmetics + PartialCmp + Clone,
142 T::Mask: LazySelect<T>,
143{
144 fn multiply(self, other: Self) -> Self {
145 let src = BlendInput::new_opaque(self);
146 let dst = BlendInput::new_opaque(other);
147 blend_separable(src, dst, multiply_blend)
148 .unpremultiply()
149 .color
150 }
151
152 fn screen(self, other: Self) -> Self {
153 let src = BlendInput::new_opaque(self);
154 let dst = BlendInput::new_opaque(other);
155 blend_separable(src, dst, screen_blend)
156 .unpremultiply()
157 .color
158 }
159
160 fn overlay(self, other: Self) -> Self {
161 let src = BlendInput::new_opaque(self);
162 let dst = BlendInput::new_opaque(other);
163 blend_separable(src, dst, overlay_blend)
164 .unpremultiply()
165 .color
166 }
167
168 fn darken(self, other: Self) -> Self {
169 let src = BlendInput::new_opaque(self);
170 let dst = BlendInput::new_opaque(other);
171 blend_separable(src, dst, darken_blend)
172 .unpremultiply()
173 .color
174 }
175
176 fn lighten(self, other: Self) -> Self {
177 let src = BlendInput::new_opaque(self);
178 let dst = BlendInput::new_opaque(other);
179 blend_separable(src, dst, lighten_blend)
180 .unpremultiply()
181 .color
182 }
183
184 fn dodge(self, other: Self) -> Self {
185 let src = BlendInput::new_opaque(self);
186 let dst = BlendInput::new_opaque(other);
187 blend_separable(src, dst, dodge_blend).unpremultiply().color
188 }
189
190 fn burn(self, other: Self) -> Self {
191 let src = BlendInput::new_opaque(self);
192 let dst = BlendInput::new_opaque(other);
193 blend_separable(src, dst, burn_blend).unpremultiply().color
194 }
195
196 fn hard_light(self, other: Self) -> Self {
197 let src = BlendInput::new_opaque(self);
198 let dst = BlendInput::new_opaque(other);
199 blend_separable(src, dst, hard_light_blend)
200 .unpremultiply()
201 .color
202 }
203
204 fn soft_light(self, other: Self) -> Self {
205 let src = BlendInput::new_opaque(self);
206 let dst = BlendInput::new_opaque(other);
207 blend_separable(src, dst, soft_light_blend)
208 .unpremultiply()
209 .color
210 }
211
212 fn difference(self, other: Self) -> Self {
213 let src = BlendInput::new_opaque(self);
214 let dst = BlendInput::new_opaque(other);
215 blend_separable(src, dst, difference_blend)
216 .unpremultiply()
217 .color
218 }
219
220 fn exclusion(self, other: Self) -> Self {
221 let src = BlendInput::new_opaque(self);
222 let dst = BlendInput::new_opaque(other);
223 blend_separable(src, dst, exclusion_blend)
224 .unpremultiply()
225 .color
226 }
227}
228
229impl<C, T, const N: usize> Blend for Alpha<C, T>
230where
231 C: Premultiply<Scalar = T> + StimulusColor + ArrayCast<Array = [T; N]> + Clone,
232 T: Real + Zero + One + MinMax + Clamp + Sqrt + Abs + Arithmetics + PartialCmp + Clone,
233 T::Mask: LazySelect<T>,
234{
235 #[inline]
236 fn multiply(self, other: Self) -> Self {
237 blend_separable(self.into(), other.into(), multiply_blend).unpremultiply()
238 }
239
240 #[inline]
241 fn screen(self, other: Self) -> Self {
242 blend_separable(self.into(), other.into(), screen_blend).unpremultiply()
243 }
244
245 #[inline]
246 fn overlay(self, other: Self) -> Self {
247 blend_separable(self.into(), other.into(), overlay_blend).unpremultiply()
248 }
249
250 #[inline]
251 fn darken(self, other: Self) -> Self {
252 blend_separable(self.into(), other.into(), darken_blend).unpremultiply()
253 }
254
255 #[inline]
256 fn lighten(self, other: Self) -> Self {
257 blend_separable(self.into(), other.into(), lighten_blend).unpremultiply()
258 }
259
260 #[inline]
261 fn dodge(self, other: Self) -> Self {
262 blend_separable(self.into(), other.into(), dodge_blend).unpremultiply()
263 }
264
265 #[inline]
266 fn burn(self, other: Self) -> Self {
267 blend_separable(self.into(), other.into(), burn_blend).unpremultiply()
268 }
269
270 #[inline]
271 fn hard_light(self, other: Self) -> Self {
272 blend_separable(self.into(), other.into(), hard_light_blend).unpremultiply()
273 }
274
275 #[inline]
276 fn soft_light(self, other: Self) -> Self {
277 blend_separable(self.into(), other.into(), soft_light_blend).unpremultiply()
278 }
279
280 #[inline]
281 fn difference(self, other: Self) -> Self {
282 blend_separable(self.into(), other.into(), difference_blend).unpremultiply()
283 }
284
285 #[inline]
286 fn exclusion(self, other: Self) -> Self {
287 blend_separable(self.into(), other.into(), exclusion_blend).unpremultiply()
288 }
289}
290
291struct BlendInput<C: Premultiply> {
292 color: C,
293 color_pre: C,
294 alpha: C::Scalar,
295}
296
297impl<C> BlendInput<C>
298where
299 C: Premultiply + Clone,
300{
301 fn new_opaque(color: C) -> Self {
302 BlendInput {
303 color_pre: color.clone(),
304 color,
305 alpha: C::Scalar::max_intensity(),
306 }
307 }
308}
309
310impl<C> From<Alpha<C, C::Scalar>> for BlendInput<C>
311where
312 C: Premultiply + Clone,
313{
314 fn from(color: Alpha<C, C::Scalar>) -> Self {
315 let color_pre: PreAlpha<C> = color.color.clone().premultiply(color.alpha);
316 BlendInput {
317 color: color.color,
318 color_pre: color_pre.color,
319 alpha: color_pre.alpha,
320 }
321 }
322}
323
324impl<C> From<PreAlpha<C>> for BlendInput<C>
325where
326 C: Premultiply + Clone,
327{
328 fn from(color: PreAlpha<C>) -> Self {
329 let color_pre = color.color.clone();
330 let (color, alpha) = C::unpremultiply(color);
331 BlendInput {
332 color,
333 color_pre,
334 alpha,
335 }
336 }
337}
338
339#[inline]
340fn multiply_blend<T>(src: T, dst: T) -> T
341where
342 T: Arithmetics,
343{
344 src * dst
345}
346
347#[inline]
348fn screen_blend<T>(src: T, dst: T) -> T
349where
350 T: Arithmetics + Clone,
351{
352 src.clone() + &dst - src * dst
353}
354
355#[inline]
356fn overlay_blend<T>(src: T, dst: T) -> T
357where
358 T: One + Arithmetics + PartialCmp + Clone,
359 T::Mask: LazySelect<T>,
360{
361 hard_light_blend(dst, src)
362}
363
364#[inline]
365fn darken_blend<T>(src: T, dst: T) -> T
366where
367 T: MinMax,
368{
369 src.min(dst)
370}
371
372#[inline]
373fn lighten_blend<T>(src: T, dst: T) -> T
374where
375 T: MinMax,
376{
377 src.max(dst)
378}
379
380#[inline]
381fn dodge_blend<T>(src: T, dst: T) -> T
382where
383 T: One + Zero + MinMax + Arithmetics + PartialCmp,
384 T::Mask: LazySelect<T>,
385{
386 lazy_select! {
389 if dst.lt_eq(&T::zero()) => T::zero(),
390 if src.gt_eq(&T::one()) => T::one(),
391 else => T::one().min(dst / (T::one() - src)),
392 }
393}
394
395#[inline]
396fn burn_blend<T>(src: T, dst: T) -> T
397where
398 T: One + Zero + MinMax + Arithmetics + PartialCmp,
399 T::Mask: LazySelect<T>,
400{
401 lazy_select! {
404 if dst.gt_eq(&T::one()) => T::one(),
405 if src.lt_eq(&T::zero()) => T::zero(),
406 else => T::one() - T::one().min((T::one() - dst) / src),
407 }
408}
409
410#[inline]
411fn hard_light_blend<T>(src: T, dst: T) -> T
412where
413 T: One + Arithmetics + PartialCmp + Clone,
414 T::Mask: LazySelect<T>,
415{
416 let two_src = src.clone() + src;
417
418 lazy_select! {
419 if two_src.lt_eq(&T::one()) => multiply_blend(two_src.clone(), dst.clone()),
420 else => screen_blend(two_src.clone() - T::one(), dst.clone()),
421 }
422}
423
424#[inline]
425fn soft_light_blend<T>(src: T, dst: T) -> T
426where
427 T: Real + One + Arithmetics + Sqrt + PartialCmp + Clone,
428 T::Mask: LazySelect<T>,
429{
430 let four = T::from_f64(4.0);
431 let twelve = T::from_f64(12.0);
432
433 let four_dst = dst.clone() * &four;
434 let two_src = src.clone() + &src;
435
436 let d_dst = lazy_select! {
437 if four_dst.lt_eq(&T::one()) => {
438 let sixteen_dst = four_dst * &four;
439 ((sixteen_dst - twelve) * &dst + four) * &dst
440 },
441 else => dst.clone().sqrt(),
442 };
443
444 lazy_select! {
445 if two_src.lt_eq(&T::one()) => {
446 dst.clone() - (T::one() - &two_src) * &dst * (T::one() - &dst)
447 },
448 else => dst.clone() + (two_src.clone() - T::one()) * (d_dst - &dst),
449 }
450}
451
452#[inline]
453fn difference_blend<T>(src: T, dst: T) -> T
454where
455 T: Arithmetics + Abs,
456{
457 (dst - src).abs()
458}
459
460#[inline]
461fn exclusion_blend<T>(src: T, dst: T) -> T
462where
463 T: Arithmetics + Clone,
464{
465 dst.clone() + &src - (dst.clone() + dst) * src
466}
467
468#[inline]
469fn blend_separable<C, T, F, const N: usize>(
470 src: BlendInput<C>,
471 mut dst: BlendInput<C>,
472 mut blend: F,
473) -> PreAlpha<C>
474where
475 C: ArrayCast<Array = [T; N]> + Premultiply<Scalar = T>,
476 T: One + Zero + Arithmetics + Clamp + Clone,
477 F: FnMut(T, T) -> T,
478{
479 let src_alpha = src.alpha.clone();
480 let zipped_input = zip_input(src, dst.color, &mut dst.color_pre, dst.alpha.clone());
481
482 for (src, src_pre, src_alpha, dst, dst_pre, dst_alpha) in zipped_input {
483 *dst_pre = src_pre * (T::one() - &dst_alpha)
484 + blend(src, dst) * &src_alpha * dst_alpha
485 + (T::one() - src_alpha) * &*dst_pre;
486 }
487
488 PreAlpha {
489 color: dst.color_pre,
490 alpha: blend_alpha(src_alpha, dst.alpha),
491 }
492}
493
494fn zip_input<'a, C, T, const N: usize>(
495 src: BlendInput<C>,
496 dst: C,
497 dst_pre: &'a mut C,
498 dst_alpha: T,
499) -> impl Iterator<Item = (T, T, T, T, &'a mut T, T)>
500where
501 C: ArrayCast<Array = [T; N]> + Premultiply<Scalar = T>,
502 T: 'a + Clone,
503{
504 let src_alpha = src.alpha;
505 IntoIterator::into_iter(cast::into_array(src.color))
506 .zip(cast::into_array(src.color_pre))
507 .zip(cast::into_array(dst))
508 .zip(cast::into_array_mut(dst_pre))
509 .map(move |(((src_color, src_pre), dst_color), dst_pre)| {
510 (
511 src_color,
512 src_pre,
513 src_alpha.clone(),
514 dst_color,
515 dst_pre,
516 dst_alpha.clone(),
517 )
518 })
519}