1use crate::{blend_mode::BlendMode, rng::FastUnsecurePrng, AsRgba};
2use image::{DynamicImage, GenericImage, GenericImageView};
3
4
5pub fn overlay<B, F>(bottom: &mut B, top: &F, x: i64, y: i64, blend_mode: BlendMode)
26where
27 B: GenericImage::<Pixel: AsRgba>,
28 F: GenericImageView::<Pixel: AsRgba>,
29{
30 let (
31 origin_bottom_x,
32 origin_bottom_y,
33 origin_top_x,
34 origin_top_y,
35 range_width,
36 range_height
37 ) = overlay_bounds_ext(bottom.dimensions(), top.dimensions(), x, y);
38
39 macro_rules! overlay {
40 (($bg: pat, $fg: pat) => $blend: expr) => {{
41 for y in 0..range_height {
42 for x in 0..range_width {
43 let (bg_x, bg_y) = (origin_bottom_x + x, origin_bottom_y + y);
44 let (fg_x, fg_y) = (origin_top_x + x, origin_top_y + y);
45
46 #[cfg(debug_assertions)] {
47 let (mut _bg, _fg) = (
48 bottom.get_pixel(bg_x, bg_y),
49 top.get_pixel(fg_x, fg_y),
50 );
51
52 let ($bg, $fg) = (&mut _bg, &_fg);
53 $blend;
54
55 bottom.put_pixel(bg_x, bg_y, _bg);
56 }
57
58 #[cfg(not(debug_assertions))] unsafe {
59 let (mut _bg, _fg) = (
60 bottom.unsafe_get_pixel(bg_x, bg_y),
61 top.unsafe_get_pixel(fg_x, fg_y),
62 );
63
64 let ($bg, $fg) = (&mut _bg, &_fg);
65 $blend;
66
67 bottom.unsafe_put_pixel(bg_x, bg_y, _bg);
68 }
69 }
70 }
71 }};
72 }
73
74
75 use crate::blend::*;
76
77 match blend_mode {
78 BlendMode::Dissolve => {
79 let ref mut rng = FastUnsecurePrng::new(top.width(), top.height());
81
82 overlay!((bg, fg) => blend_dissolve(bg, fg, rng));
83 }
84 BlendMode::Normal => overlay!((bg, fg) => blend_normal(bg, fg)),
85 BlendMode::Darken => overlay!((bg, fg) => blend_darken(bg, fg)),
86 BlendMode::Multiply => overlay!((bg, fg) => blend_multiply(bg, fg)),
87 BlendMode::ColorBurn => overlay!((bg, fg) => blend_color_burn(bg, fg)),
88 BlendMode::LinearBurn => overlay!((bg, fg) => blend_linear_burn(bg, fg)),
89 BlendMode::Lighten => overlay!((bg, fg) => blend_lighten(bg, fg)),
90 BlendMode::Screen => overlay!((bg, fg) => blend_screen(bg, fg)),
91 BlendMode::ColorDodge => overlay!((bg, fg) => blend_color_dodge(bg, fg)),
92 BlendMode::LinearDodge => overlay!((bg, fg) => blend_linear_dodge(bg, fg)),
93 BlendMode::Overlay => overlay!((bg, fg) => blend_overlay(bg, fg)),
94 BlendMode::SoftLight => overlay!((bg, fg) => blend_soft_light(bg, fg)),
95 BlendMode::HardLight => overlay!((bg, fg) => blend_hard_light(bg, fg)),
96 BlendMode::VividLight => overlay!((bg, fg) => blend_vivid_light(bg, fg)),
97 BlendMode::LinearLight => overlay!((bg, fg) => blend_linear_light(bg, fg)),
98 BlendMode::PinLight => overlay!((bg, fg) => blend_pin_light(bg, fg)),
99 BlendMode::HardMix => overlay!((bg, fg) => blend_hard_mix(bg, fg)),
100 BlendMode::Difference => overlay!((bg, fg) => blend_difference(bg, fg)),
101 BlendMode::Exclusion => overlay!((bg, fg) => blend_exclusion(bg, fg)),
102 BlendMode::Subtract => overlay!((bg, fg) => blend_subtract(bg, fg)),
103 BlendMode::Divide => overlay!((bg, fg) => blend_divide(bg, fg)),
104 BlendMode::DarkerColor => overlay!((bg, fg) => blend_darker_color(bg, fg)),
105 BlendMode::LighterColor => overlay!((bg, fg) => blend_lighter_color(bg, fg)),
106 BlendMode::Hue => overlay!((bg, fg) => blend_hue(bg, fg)),
107 BlendMode::Saturation => overlay!((bg, fg) => blend_saturation(bg, fg)),
108 BlendMode::Color => overlay!((bg, fg) => blend_color(bg, fg)),
109 BlendMode::Luminosity => overlay!((bg, fg) => blend_luminosity(bg, fg)),
110 }
111}
112
113pub fn overlay_dyn_img(bottom: &mut DynamicImage, top: &DynamicImage, x: i64, y: i64, blend_mode: BlendMode) {
117 macro_rules! dynamic_map {
118 ($dynimage: expr, $image:pat_param, $action: expr) => {{
119 match $dynimage {
120 DynamicImage::ImageLuma8($image) => $action,
121 DynamicImage::ImageLuma16($image) => $action,
122 DynamicImage::ImageLumaA8($image) => $action,
123 DynamicImage::ImageLumaA16($image) => $action,
124 DynamicImage::ImageRgb8($image) => $action,
125 DynamicImage::ImageRgb16($image) => $action,
126 DynamicImage::ImageRgb32F($image) => $action,
127 DynamicImage::ImageRgba8($image) => $action,
128 DynamicImage::ImageRgba16($image) => $action,
129 DynamicImage::ImageRgba32F($image) => $action,
130 _ => unimplemented!(),
131 }
132 }};
133 }
134
135 dynamic_map!(bottom, bottom, {
136 dynamic_map!(top, top, {
137 overlay(bottom, top, x, y, blend_mode);
138 })
139 })
140}
141
142
143fn overlay_bounds_ext(
166 (bottom_width, bottom_height): (u32, u32),
167 (top_width, top_height): (u32, u32),
168 x: i64,
169 y: i64,
170) -> (u32, u32, u32, u32, u32, u32) {
171 if x > i64::from(bottom_width)
173 || y > i64::from(bottom_height)
174 || x.saturating_add(i64::from(top_width)) <= 0
175 || y.saturating_add(i64::from(top_height)) <= 0
176 {
177 return (0, 0, 0, 0, 0, 0);
178 }
179
180 let max_x = x.saturating_add(i64::from(top_width));
182 let max_y = y.saturating_add(i64::from(top_height));
183
184 let max_inbounds_x = max_x.clamp(0, i64::from(bottom_width)) as u32;
188 let max_inbounds_y = max_y.clamp(0, i64::from(bottom_height)) as u32;
189 let origin_bottom_x = x.clamp(0, i64::from(bottom_width)) as u32;
190 let origin_bottom_y = y.clamp(0, i64::from(bottom_height)) as u32;
191
192 let x_range = max_inbounds_x - origin_bottom_x;
197 let y_range = max_inbounds_y - origin_bottom_y;
198
199 let origin_top_x = x.saturating_mul(-1).clamp(0, i64::from(top_width)) as u32;
201 let origin_top_y = y.saturating_mul(-1).clamp(0, i64::from(top_height)) as u32;
202
203 (
204 origin_bottom_x,
205 origin_bottom_y,
206 origin_top_x,
207 origin_top_y,
208 x_range,
209 y_range,
210 )
211}