Skip to content

Commit 9e5c8a8

Browse files
committed
Bloom min/max threshold
1 parent 50fa90d commit 9e5c8a8

File tree

3 files changed

+125
-60
lines changed

3 files changed

+125
-60
lines changed

src/bitwise.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -143,45 +143,59 @@ pub enum BitshiftDirection {
143143
RIGHT,
144144
}
145145

146-
pub fn bitshift(img: DynamicImage, direction: BitshiftDirection, bits: u8, raw: bool) -> RgbaImage {
146+
pub fn bitshift(
147+
img: DynamicImage,
148+
direction: BitshiftDirection,
149+
lhs: Option<Vec<String>>,
150+
bits: u8,
151+
raw: bool,
152+
) -> RgbaImage {
147153
let (width, height) = img.dimensions();
148154

149155
let mut output: RgbaImage = ImageBuffer::new(width, height);
150156

151157
output.enumerate_pixels_mut().for_each(|(x, y, pixel)| {
152158
let in_pixel = img.get_pixel(x, y);
153159

160+
let lhs = match lhs {
161+
Some(ref lhs) => (
162+
get_channel_by_name_rgba_u8(&lhs[0], &in_pixel),
163+
get_channel_by_name_rgba_u8(&lhs[1], &in_pixel),
164+
get_channel_by_name_rgba_u8(&lhs[2], &in_pixel),
165+
),
166+
None => (in_pixel[0], in_pixel[1], in_pixel[2]),
167+
};
154168
let (r, g, b, a) = match direction {
155169
BitshiftDirection::LEFT => {
156170
if raw {
157171
(
158-
((in_pixel[0] as u16) << bits) as u8,
159-
((in_pixel[1] as u16) << bits) as u8,
160-
((in_pixel[2] as u16) << bits) as u8,
172+
((lhs.0 as u16) << bits) as u8,
173+
((lhs.1 as u16) << bits) as u8,
174+
((lhs.2 as u16) << bits) as u8,
161175
in_pixel[3],
162176
)
163177
} else {
164178
(
165-
((in_pixel[0] as u16) << bits).min(255) as u8,
166-
((in_pixel[1] as u16) << bits).min(255) as u8,
167-
((in_pixel[2] as u16) << bits).min(255) as u8,
179+
((lhs.0 as u16) << bits).min(255) as u8,
180+
((lhs.1 as u16) << bits).min(255) as u8,
181+
((lhs.2 as u16) << bits).min(255) as u8,
168182
in_pixel[3],
169183
)
170184
}
171185
}
172186
BitshiftDirection::RIGHT => {
173187
if raw {
174188
(
175-
(in_pixel[0] >> bits),
176-
(in_pixel[1] >> bits),
177-
(in_pixel[2] >> bits),
189+
(lhs.0 >> bits),
190+
(lhs.1 >> bits),
191+
(lhs.2 >> bits),
178192
in_pixel[3],
179193
)
180194
} else {
181195
(
182-
(in_pixel[0].wrapping_shr(bits.into())),
183-
(in_pixel[1].wrapping_shr(bits.into())),
184-
(in_pixel[2].wrapping_shr(bits.into())),
196+
(lhs.0.wrapping_shr(bits.into())),
197+
(lhs.1.wrapping_shr(bits.into())),
198+
(lhs.2.wrapping_shr(bits.into())),
185199
in_pixel[3],
186200
)
187201
}

src/functions.rs

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use clap::builder::styling::RgbColor;
2-
use image::{DynamicImage, GenericImageView, ImageBuffer, Rgba, RgbaImage};
2+
use image::{imageops::blur, DynamicImage, GenericImageView, ImageBuffer, Rgba, RgbaImage};
33

44
use crate::utils::{get_channel_by_name_rgb_color, get_channel_by_name_rgba_u8};
55

@@ -50,46 +50,61 @@ pub fn average(
5050

5151
pub fn bloom(
5252
img: DynamicImage,
53-
lhs: Option<Vec<String>>,
54-
rhs: Option<Vec<String>>,
55-
color: RgbColor,
5653
intensity: f32,
54+
blur_radius: f32,
55+
min_threshold: u8,
56+
max_threshold: Option<u8>,
5757
) -> RgbaImage {
5858
let (width, height) = img.dimensions();
5959

60-
let mut output: RgbaImage = ImageBuffer::new(width, height);
61-
62-
let rhs = match rhs {
63-
Some(rhs) => (
64-
get_channel_by_name_rgb_color(&rhs[0], &color),
65-
get_channel_by_name_rgb_color(&rhs[1], &color),
66-
get_channel_by_name_rgb_color(&rhs[2], &color),
67-
),
68-
None => (color.r(), color.g(), color.b()),
69-
};
60+
let rgba_img = img.to_rgba8();
61+
62+
let mut light_mask: RgbaImage = ImageBuffer::new(width, height);
63+
64+
for (x, y, pixel) in rgba_img.enumerate_pixels() {
65+
let r = pixel[0];
66+
let g = pixel[1];
67+
let b = pixel[2];
68+
69+
// = 0.2126 R + 0.7152 G + 0.0722 B
70+
let luminance = 0.2126 * (r as f32) + 0.7152 * (g as f32) + 0.0722 * (b as f32);
71+
72+
match max_threshold {
73+
Some(threshold) => {
74+
if luminance > min_threshold as f32 && luminance < threshold as f32 {
75+
light_mask.put_pixel(x, y, Rgba([r, g, b, pixel[3]]));
76+
} else {
77+
light_mask.put_pixel(x, y, Rgba([0, 0, 0, 0]));
78+
}
79+
}
80+
None => {
81+
if luminance > min_threshold as f32 {
82+
light_mask.put_pixel(x, y, Rgba([r, g, b, pixel[3]]));
83+
} else {
84+
light_mask.put_pixel(x, y, Rgba([0, 0, 0, 0]));
85+
}
86+
}
87+
}
88+
}
89+
90+
let blurred_light = blur(&light_mask, blur_radius);
7091

71-
output.enumerate_pixels_mut().for_each(|(x, y, pixel)| {
72-
let in_pixel = img.get_pixel(x, y);
92+
let mut output: RgbaImage = ImageBuffer::new(width, height);
7393

74-
let lhs = match lhs {
75-
Some(ref lhs) => (
76-
get_channel_by_name_rgba_u8(&lhs[0], &in_pixel),
77-
get_channel_by_name_rgba_u8(&lhs[1], &in_pixel),
78-
get_channel_by_name_rgba_u8(&lhs[2], &in_pixel),
79-
),
80-
None => (in_pixel[0], in_pixel[1], in_pixel[2]),
81-
};
94+
for (x, y, pixel) in rgba_img.enumerate_pixels() {
95+
let blurred_pixel = blurred_light.get_pixel(x, y);
8296

97+
// Blend the blurred light with the original image
8398
let (r, g, b) = (
84-
((lhs.0 as f32) + (lhs.0 as f32 * rhs.0 as f32 / 255.0) * intensity).min(255.0) as u8,
85-
((lhs.1 as f32) + (lhs.1 as f32 * rhs.1 as f32 / 255.0) * intensity).min(255.0) as u8,
86-
((lhs.2 as f32) + (lhs.2 as f32 * rhs.2 as f32 / 255.0) * intensity).min(255.0) as u8,
99+
((pixel[0] as f32) + (blurred_pixel[0] as f32 * intensity)).min(255.0) as u8,
100+
((pixel[1] as f32) + (blurred_pixel[1] as f32 * intensity)).min(255.0) as u8,
101+
((pixel[2] as f32) + (blurred_pixel[2] as f32 * intensity)).min(255.0) as u8,
87102
);
88103

89-
let a = in_pixel[3];
104+
let a = pixel[3];
90105

91-
*pixel = Rgba([r, g, b, a]);
92-
});
106+
output.put_pixel(x, y, Rgba([r, g, b, a]));
107+
}
93108

94109
output
95110
}

src/main.rs

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,51 @@ mod utils;
1616
#[derive(Subcommand)]
1717
#[allow(non_camel_case_types)]
1818
enum SubCommands {
19-
OR { color: String },
20-
AND { color: String },
21-
XOR { color: String },
22-
LEFT { bits: String, raw: Option<String> },
23-
RIGHT { bits: String, raw: Option<String> },
24-
ADD { color: String },
25-
SUB { color: String, raw: Option<String> },
26-
MULT { color: String },
27-
DIV { color: String },
28-
AVG { color: String },
29-
SCREEN { color: String },
30-
OVERLAY { color: String },
31-
BLOOM { color: String, intensity: f32 },
19+
OR {
20+
color: String,
21+
},
22+
AND {
23+
color: String,
24+
},
25+
XOR {
26+
color: String,
27+
},
28+
LEFT {
29+
bits: String,
30+
raw: Option<String>,
31+
},
32+
RIGHT {
33+
bits: String,
34+
raw: Option<String>,
35+
},
36+
ADD {
37+
color: String,
38+
},
39+
SUB {
40+
color: String,
41+
raw: Option<String>,
42+
},
43+
MULT {
44+
color: String,
45+
},
46+
DIV {
47+
color: String,
48+
},
49+
AVG {
50+
color: String,
51+
},
52+
SCREEN {
53+
color: String,
54+
},
55+
OVERLAY {
56+
color: String,
57+
},
58+
BLOOM {
59+
intensity: f32,
60+
radius: f32,
61+
min_threshold: u8,
62+
max_threshold: Option<u8>,
63+
},
3264
}
3365

3466
#[derive(Parser)]
@@ -139,6 +171,7 @@ fn main() {
139171
bitshift(
140172
img,
141173
BitshiftDirection::LEFT,
174+
lhs,
142175
bit_shift
143176
.parse::<u8>()
144177
.expect("Could not parse bits arg to u8"),
@@ -157,6 +190,7 @@ fn main() {
157190
bitshift(
158191
img,
159192
BitshiftDirection::RIGHT,
193+
lhs,
160194
bit_shift
161195
.parse::<u8>()
162196
.expect("Could not parse bits arg to u8"),
@@ -175,10 +209,12 @@ fn main() {
175209
let rgb = hex_to_rgb(&color).expect("Could not convert color to rgb");
176210
overlay(img, lhs, rhs, RgbColor(rgb.0, rgb.1, rgb.2))
177211
}
178-
SubCommands::BLOOM { color, intensity } => {
179-
let rgb = hex_to_rgb(&color).expect("Could not convert color to rgb");
180-
bloom(img, lhs, rhs, RgbColor(rgb.0, rgb.1, rgb.2), intensity)
181-
}
212+
SubCommands::BLOOM {
213+
intensity,
214+
radius,
215+
min_threshold,
216+
max_threshold,
217+
} => bloom(img, intensity, radius, min_threshold, max_threshold),
182218
};
183219

184220
// Epic fast buffer writing

0 commit comments

Comments
 (0)