1#[cfg(feature = "blend_dissolve")]
2use crate::rng::FastUnsecurePrng;
3use crate::{blend_mode::BlendMode, AsRgba};
4use image::{DynamicImage, GenericImage, GenericImageView};
5
6macro_rules! dynamic_map {
7 ($dynimage: expr, $image:pat_param, $action: expr) => {{
8 match $dynimage {
9 DynamicImage::ImageLuma8($image) => $action,
10 DynamicImage::ImageLuma16($image) => $action,
11 DynamicImage::ImageLumaA8($image) => $action,
12 DynamicImage::ImageLumaA16($image) => $action,
13 DynamicImage::ImageRgb8($image) => $action,
14 DynamicImage::ImageRgb16($image) => $action,
15 DynamicImage::ImageRgb32F($image) => $action,
16 DynamicImage::ImageRgba8($image) => $action,
17 DynamicImage::ImageRgba16($image) => $action,
18 DynamicImage::ImageRgba32F($image) => $action,
19 _ => unimplemented!(),
20 }
21 }};
22}
23
24
25pub fn overlay<B, F>(bottom: &mut B, top: &F, x: i64, y: i64, blend_mode: BlendMode)
46where
47 B: GenericImage::<Pixel: AsRgba>,
48 F: GenericImageView::<Pixel: AsRgba>,
49{
50 let (
51 origin_bottom_x,
52 origin_bottom_y,
53 origin_top_x,
54 origin_top_y,
55 range_width,
56 range_height
57 ) = overlay_bounds_ext(bottom.dimensions(), top.dimensions(), x, y);
58
59 macro_rules! overlay {
60 (($bg: pat, $fg: pat) => $blend: expr) => {{
61 for y in 0..range_height {
62 for x in 0..range_width {
63 let (bg_x, bg_y) = (origin_bottom_x + x, origin_bottom_y + y);
64 let (fg_x, fg_y) = (origin_top_x + x, origin_top_y + y);
65
66 #[cfg(debug_assertions)] {
67 let (mut _bg, _fg) = (
68 bottom.get_pixel(bg_x, bg_y),
69 top.get_pixel(fg_x, fg_y),
70 );
71
72 let ($bg, $fg) = (&mut _bg, &_fg);
73 $blend;
74
75 bottom.put_pixel(bg_x, bg_y, _bg);
76 }
77
78 #[cfg(not(debug_assertions))] unsafe {
79 let (mut _bg, _fg) = (
80 bottom.unsafe_get_pixel(bg_x, bg_y),
81 top.unsafe_get_pixel(fg_x, fg_y),
82 );
83
84 let ($bg, $fg) = (&mut _bg, &_fg);
85 $blend;
86
87 bottom.unsafe_put_pixel(bg_x, bg_y, _bg);
88 }
89 }
90 }
91 }};
92 }
93
94
95 use crate::blend::*;
96
97 match blend_mode {
98 #[cfg(feature = "blend_dissolve")]
99 BlendMode::Dissolve => {
100 let ref mut rng = FastUnsecurePrng::new(top.width(), top.height());
102
103 overlay!((bg, fg) => blend_dissolve(bg, fg, rng));
104 }
105 BlendMode::Normal => overlay!((bg, fg) => blend_normal(bg, fg)),
106 BlendMode::Darken => overlay!((bg, fg) => blend_darken(bg, fg)),
107 BlendMode::Multiply => overlay!((bg, fg) => blend_multiply(bg, fg)),
108 BlendMode::ColorBurn => overlay!((bg, fg) => blend_color_burn(bg, fg)),
109 BlendMode::LinearBurn => overlay!((bg, fg) => blend_linear_burn(bg, fg)),
110 BlendMode::Lighten => overlay!((bg, fg) => blend_lighten(bg, fg)),
111 BlendMode::Screen => overlay!((bg, fg) => blend_screen(bg, fg)),
112 BlendMode::ColorDodge => overlay!((bg, fg) => blend_color_dodge(bg, fg)),
113 BlendMode::LinearDodge => overlay!((bg, fg) => blend_linear_dodge(bg, fg)),
114 BlendMode::Overlay => overlay!((bg, fg) => blend_overlay(bg, fg)),
115 BlendMode::SoftLight => overlay!((bg, fg) => blend_soft_light(bg, fg)),
116 BlendMode::HardLight => overlay!((bg, fg) => blend_hard_light(bg, fg)),
117 BlendMode::VividLight => overlay!((bg, fg) => blend_vivid_light(bg, fg)),
118 BlendMode::LinearLight => overlay!((bg, fg) => blend_linear_light(bg, fg)),
119 BlendMode::PinLight => overlay!((bg, fg) => blend_pin_light(bg, fg)),
120 BlendMode::HardMix => overlay!((bg, fg) => blend_hard_mix(bg, fg)),
121 BlendMode::Difference => overlay!((bg, fg) => blend_difference(bg, fg)),
122 BlendMode::Exclusion => overlay!((bg, fg) => blend_exclusion(bg, fg)),
123 BlendMode::Subtract => overlay!((bg, fg) => blend_subtract(bg, fg)),
124 BlendMode::Divide => overlay!((bg, fg) => blend_divide(bg, fg)),
125 BlendMode::DarkerColor => overlay!((bg, fg) => blend_darker_color(bg, fg)),
126 BlendMode::LighterColor => overlay!((bg, fg) => blend_lighter_color(bg, fg)),
127 BlendMode::Hue => overlay!((bg, fg) => blend_hue(bg, fg)),
128 BlendMode::Saturation => overlay!((bg, fg) => blend_saturation(bg, fg)),
129 BlendMode::Color => overlay!((bg, fg) => blend_color(bg, fg)),
130 BlendMode::Luminosity => overlay!((bg, fg) => blend_luminosity(bg, fg)),
131 }
132}
133
134pub fn overlay_dyn_img(
138 bottom: &mut DynamicImage,
139 top: &DynamicImage,
140 x: i64,
141 y: i64,
142 blend_mode: BlendMode
143) {
144
145 dynamic_map!(bottom, bottom, {
146 dynamic_map!(top, top, {
147 overlay(bottom, top, x, y, blend_mode);
148 })
149 })
150}
151
152pub fn overlay_dyn_img_to_img<B>(
156 bottom: &mut B,
157 top: &DynamicImage,
158 x: i64,
159 y: i64,
160 blend_mode: BlendMode
161)
162where
163 B: GenericImage::<Pixel: AsRgba>
164{
165
166 dynamic_map!(top, top, {
167 overlay(bottom, top, x, y, blend_mode);
168 })
169}
170
171pub fn overlay_img_to_dyn_img<F>(
175 bottom: &mut DynamicImage,
176 top: &F,
177 x: i64,
178 y: i64,
179 blend_mode: BlendMode
180)
181where
182 F: GenericImageView::<Pixel: AsRgba>,
183{
184
185 dynamic_map!(bottom, bottom, {
186 overlay(bottom, top, x, y, blend_mode);
187 })
188}
189
190
191fn overlay_bounds_ext(
214 (bottom_width, bottom_height): (u32, u32),
215 (top_width, top_height): (u32, u32),
216 x: i64,
217 y: i64,
218) -> (u32, u32, u32, u32, u32, u32) {
219 if x > i64::from(bottom_width)
221 || y > i64::from(bottom_height)
222 || x.saturating_add(i64::from(top_width)) <= 0
223 || y.saturating_add(i64::from(top_height)) <= 0
224 {
225 return (0, 0, 0, 0, 0, 0);
226 }
227
228 let max_x = x.saturating_add(i64::from(top_width));
230 let max_y = y.saturating_add(i64::from(top_height));
231
232 let max_inbounds_x = max_x.clamp(0, i64::from(bottom_width)) as u32;
236 let max_inbounds_y = max_y.clamp(0, i64::from(bottom_height)) as u32;
237 let origin_bottom_x = x.clamp(0, i64::from(bottom_width)) as u32;
238 let origin_bottom_y = y.clamp(0, i64::from(bottom_height)) as u32;
239
240 let x_range = max_inbounds_x - origin_bottom_x;
245 let y_range = max_inbounds_y - origin_bottom_y;
246
247 let origin_top_x = x.saturating_mul(-1).clamp(0, i64::from(top_width)) as u32;
249 let origin_top_y = y.saturating_mul(-1).clamp(0, i64::from(top_height)) as u32;
250
251 (
252 origin_bottom_x,
253 origin_bottom_y,
254 origin_top_x,
255 origin_top_y,
256 x_range,
257 y_range,
258 )
259}