Skip to main content

libeffectengine/effects/
white_noise.rs

1use rand::Rng;
2use std::io::Cursor;
3#[cfg(not(target_arch = "wasm32"))]
4use std::process::exit;
5use wasm_bindgen::prelude::*;
6
7use image::{DynamicImage, ImageBuffer, ImageFormat, Rgba};
8
9#[cfg(not(target_arch = "wasm32"))]
10use crate::util::subcommand_help_requested;
11use crate::util::{get_paths, read_image};
12
13#[wasm_bindgen(start)]
14pub fn main_js() {
15    // This ensures that any panic in Rust provides a useful error message in the browser console
16    console_error_panic_hook::set_once();
17}
18
19/// Applies white noise to the given image.
20#[wasm_bindgen(js_name = whiteNoise)]
21pub fn effect() -> Vec<u8> {
22    #[cfg(not(target_arch = "wasm32"))]
23    {
24        if subcommand_help_requested() {
25            print_help();
26            exit(0);
27        }
28    }
29
30    let paths = get_paths();
31    let image_data = read_image(paths.input_path);
32
33    let opacity: i32 = std::env::args()
34        .nth(4)
35        .or_else(|| Some(String::from("32")))
36        .unwrap()
37        .parse()
38        .unwrap_or_else(|_| 32);
39
40    let opacity_factor = opacity as f32 / 255.0;
41
42    let img =
43        image::load_from_memory(&image_data.data).expect("Failed to decode image from memory");
44    let image = img.to_rgba8();
45    let mut new_image = ImageBuffer::new(image.width(), image.height());
46
47    for x in 0..image.width() {
48        for y in 0..image.height() {
49            let pixel = image.get_pixel(x, y);
50            let noise_value = rand::rng().random_range(0..255);
51
52            let r = (pixel.0[0] as f32
53                + (1.0 - opacity_factor)
54                + noise_value as f32 * opacity_factor) as u8;
55            let g = (pixel.0[1] as f32
56                + (1.0 - opacity_factor)
57                + noise_value as f32 * opacity_factor) as u8;
58            let b = (pixel.0[2] as f32
59                + (1.0 - opacity_factor)
60                + noise_value as f32 * opacity_factor) as u8;
61            let a = (pixel.0[3] as f32
62                + (1.0 - opacity_factor)
63                + noise_value as f32 * opacity_factor) as u8;
64
65            new_image.put_pixel(x, y, Rgba([r, g, b, a]));
66        }
67    }
68
69    let mut cursor = Cursor::new(Vec::new());
70
71    if image_data.format == ImageFormat::Jpeg {
72        let rgb_image = DynamicImage::ImageRgba8(new_image).into_rgb8();
73        rgb_image
74            .write_to(&mut cursor, image_data.format)
75            .expect("Failed to encode JPEG");
76    } else {
77        new_image
78            .write_to(&mut cursor, image_data.format)
79            .expect("Failed to encode image");
80    }
81
82    return cursor.into_inner();
83}
84
85/// Prints the help text for this effect.
86#[cfg(not(target_arch = "wasm32"))]
87fn print_help() {
88    println!(
89        r#"
90White Noise Effect
91Overlays an image with white noise at a given opacity.
92
93USAGE:
94  effectengine-cli white-noise <INPUT_PATH> <OUTPUT_PATH> [OPACITY]
95
96ARGUMENTS:
97  <INPUT_PATH>     The path to an input image that should be processed.
98  <OUTPUT_PATH>    The path where the resulting image should be saved.
99                   Needs to include the filename.
100  <OPACITY>        The opacity of the overlaid noise. A number between 0 and
101                   255. (Default: 32)
102  "#
103    );
104}