use crate::helpers;
use crate::iter::ImageIterator;
use crate::{PhotonImage, Rgb};
use image::Pixel;
use image::Rgba;
use image::{GenericImage, GenericImageView};
use imageproc::drawing::draw_filled_rect_mut;
use imageproc::rect::Rect;
use perlin2d::PerlinNoise2D;
use std::collections::HashMap;
use std::f64;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn offset(photon_image: &mut PhotonImage, channel_index: usize, offset: u32) {
if channel_index > 2 {
panic!("Invalid channel index passed. Channel1 must be equal to 0, 1, or 2.");
}
let mut img = helpers::dyn_image_from_raw(&photon_image);
let (width, height) = img.dimensions();
for x in 0..width - 10 {
for y in 0..height - 10 {
let px = img.get_pixel(x, y);
if x + offset < width - 1 && y + offset < height - 1 {
let offset_px = img.get_pixel(x + offset, y + offset);
let offset_px_channels = offset_px.channels();
let px_channels = px.channels();
let px = match channel_index {
0 => image::Rgba([
offset_px_channels[0],
px_channels[1],
px_channels[2],
255,
]),
1 => image::Rgba([
px_channels[0],
offset_px_channels[1],
px_channels[2],
255,
]),
2 => image::Rgba([
px_channels[0],
px_channels[1],
offset_px_channels[2],
255,
]),
_ => image::Rgba([
px_channels[0],
px_channels[1],
offset_px_channels[2],
255,
]),
};
img.put_pixel(x, y, px);
}
}
}
let raw_pixels = img.to_bytes();
photon_image.raw_pixels = raw_pixels;
}
#[wasm_bindgen]
pub fn offset_red(img: &mut PhotonImage, offset_amt: u32) {
offset(img, 0, offset_amt)
}
#[wasm_bindgen]
pub fn offset_green(img: &mut PhotonImage, offset_amt: u32) {
offset(img, 1, offset_amt)
}
#[wasm_bindgen]
pub fn offset_blue(img: &mut PhotonImage, offset_amt: u32) {
offset(img, 2, offset_amt)
}
#[wasm_bindgen]
pub fn multiple_offsets(
mut photon_image: &mut PhotonImage,
offset: u32,
channel_index: usize,
channel_index2: usize,
) {
if channel_index > 2 {
panic!("Invalid channel index passed. Channel1 must be equal to 0, 1, or 2.");
}
if channel_index2 > 2 {
panic!("Invalid channel index passed. Channel2 must be equal to 0, 1, or 2.");
}
let mut img = helpers::dyn_image_from_raw(&photon_image);
let (width, height) = img.dimensions();
for (x, y) in ImageIterator::new(width, height) {
let mut px = img.get_pixel(x, y);
if x + offset < width - 1 && y + offset < height - 1 {
let offset_px = img.get_pixel(x + offset, y);
px[channel_index] = offset_px[channel_index];
}
if x as i32 - offset as i32 > 0 && y as i32 - offset as i32 > 0 {
let offset_px2 = img.get_pixel(x - offset, y);
px[channel_index2] = offset_px2[channel_index2];
}
img.put_pixel(x, y, px);
}
let raw_pixels = img.to_bytes();
photon_image.raw_pixels = raw_pixels;
}
pub fn halftone(mut photon_image: &mut PhotonImage) {
let mut img = helpers::dyn_image_from_raw(&photon_image);
let (width, height) = img.dimensions();
for x in (0..width - 4).step_by(2_usize) {
for y in (0..height - 4).step_by(2_usize) {
let mut px1 = img.get_pixel(x, y);
let mut px2 = img.get_pixel(x, y + 1);
let mut px3 = img.get_pixel(x + 1, y);
let mut px4 = img.get_pixel(x + 1, y + 1);
let gray1 = (px1[0] as f64 * 0.299)
+ (px1[1] as f64 * 0.587)
+ (px1[2] as f64 * 0.114);
let gray2 = (px2[0] as f64 * 0.299)
+ (px2[1] as f64 * 0.587)
+ (px2[2] as f64 * 0.114);
let gray3 = (px3[0] as f64 * 0.299)
+ (px3[1] as f64 * 0.587)
+ (px3[2] as f64 * 0.114);
let gray4 = (px4[0] as f64 * 0.299)
+ (px4[1] as f64 * 0.587)
+ (px4[2] as f64 * 0.114);
let sat = (gray1 + gray2 + gray3 + gray4) / 4.0;
if sat > 200.0 {
px1[0] = 255;
px1[1] = 255;
px1[2] = 255;
px2[0] = 255;
px2[1] = 255;
px2[2] = 255;
px3[0] = 255;
px3[1] = 255;
px3[2] = 255;
px4[0] = 255;
px4[1] = 255;
px4[2] = 255;
} else if sat > 159.0 {
px1[0] = 255;
px1[1] = 255;
px1[2] = 255;
px2[0] = 0;
px2[1] = 0;
px2[2] = 0;
px3[0] = 255;
px3[1] = 255;
px3[2] = 255;
px4[0] = 255;
px4[1] = 255;
px4[2] = 255;
} else if sat > 95.0 {
px1[0] = 255;
px1[1] = 255;
px1[2] = 255;
px2[0] = 0;
px2[1] = 0;
px2[2] = 0;
px3[0] = 0;
px3[1] = 0;
px3[2] = 0;
px4[0] = 255;
px4[1] = 255;
px4[2] = 255;
} else if sat > 32.0 {
px1[0] = 0;
px1[1] = 0;
px1[2] = 0;
px2[0] = 255;
px2[1] = 255;
px2[2] = 255;
px3[0] = 0;
px3[1] = 0;
px3[2] = 0;
px4[0] = 0;
px4[1] = 0;
px4[2] = 0;
} else {
px1[0] = 0;
px1[1] = 0;
px1[2] = 0;
px2[0] = 0;
px2[1] = 0;
px2[2] = 0;
px3[0] = 0;
px3[1] = 0;
px3[2] = 0;
px4[0] = 0;
px4[1] = 0;
px4[2] = 0;
}
img.put_pixel(x, y, px1);
}
}
let raw_pixels = img.to_bytes();
photon_image.raw_pixels = raw_pixels;
}
#[wasm_bindgen]
pub fn primary(img: &mut PhotonImage) {
let end = img.raw_pixels.len() - 4;
for i in (0..end).step_by(4) {
let mut r_val = img.raw_pixels[0];
let mut g_val = img.raw_pixels[1];
let mut b_val = img.raw_pixels[2];
if r_val > 128 {
r_val = 255;
} else {
r_val = 0;
}
if g_val > 128 {
g_val = 255;
} else {
g_val = 0;
}
if b_val > 128 {
g_val = 255;
} else {
b_val = 0;
}
img.raw_pixels[i] = r_val;
img.raw_pixels[i + 1] = g_val;
img.raw_pixels[i + 2] = b_val;
}
}
#[wasm_bindgen]
pub fn colorize(mut photon_image: &mut PhotonImage) {
let mut img = helpers::dyn_image_from_raw(&photon_image);
let threshold = 220;
for (x, y) in ImageIterator::with_dimension(&img.dimensions()) {
let mut px = img.get_pixel(x, y);
let channels = px.channels();
let px_as_rgb = Rgb {
r: channels[0],
g: channels[1],
b: channels[2],
};
let baseline_color = Rgb {
r: 0,
g: 255,
b: 255,
};
let square_distance = crate::helpers::square_distance(baseline_color, px_as_rgb);
let mut r = channels[0] as f32;
let mut g = channels[1] as f32;
let mut b = channels[2] as f32;
if square_distance < i32::pow(threshold, 2) {
r *= 0.5;
g *= 1.25;
b *= 0.5;
}
px = image::Rgba([r as u8, g as u8, b as u8, 255]);
img.put_pixel(x, y, px);
}
let raw_pixels = img.to_bytes();
photon_image.raw_pixels = raw_pixels;
}
#[wasm_bindgen]
pub fn solarize(photon_image: &mut PhotonImage) {
let end = photon_image.get_raw_pixels().len() - 4;
for i in (0..end).step_by(4) {
let r_val = photon_image.raw_pixels[i];
if 200 - r_val as i32 > 0 {
photon_image.raw_pixels[i] = 200 - r_val;
}
}
}
#[wasm_bindgen]
pub fn solarize_retimg(photon_image: &PhotonImage) -> PhotonImage {
let mut img = helpers::dyn_image_from_raw(&photon_image);
for (x, y) in ImageIterator::with_dimension(&img.dimensions()) {
let mut px = img.get_pixel(x, y);
let channels = px.channels();
if 200_i32 - channels[0] as i32 > 0 {
let new_r_val = 200 - channels[0];
px = image::Rgba([new_r_val, channels[1], channels[2], channels[3]]);
}
img.put_pixel(x, y, px);
}
PhotonImage {
raw_pixels: img.to_bytes(),
width: img.width(),
height: img.height(),
}
}
#[wasm_bindgen]
pub fn inc_brightness(photon_image: &mut PhotonImage, brightness: u8) {
let end = photon_image.get_raw_pixels().len() - 4;
for i in (0..end).step_by(4) {
let r_val = photon_image.raw_pixels[i];
let g_val = photon_image.raw_pixels[i + 1];
let b_val = photon_image.raw_pixels[i + 2];
if r_val <= 255 - brightness {
photon_image.raw_pixels[i] += brightness;
} else {
photon_image.raw_pixels[i] = 255;
}
if g_val <= 255 - brightness {
photon_image.raw_pixels[i + 1] += brightness;
} else {
photon_image.raw_pixels[1] = 255
}
if b_val <= 255 - brightness {
photon_image.raw_pixels[i + 2] += brightness;
} else {
photon_image.raw_pixels[i + 2] = 255
}
}
}
#[wasm_bindgen]
pub fn adjust_contrast(mut photon_image: &mut PhotonImage, contrast: f32) {
let mut img = helpers::dyn_image_from_raw(&photon_image);
let clamped_contrast = num::clamp(contrast, -255.0, 255.0);
let factor =
(259.0 * (clamped_contrast + 255.0)) / (255.0 * (259.0 - clamped_contrast));
let mut lookup_table: Vec<u8> = vec![0; 256];
let offset = -128.0 * factor + 128.0;
for (i, table) in lookup_table.iter_mut().enumerate().take(256_usize) {
let new_val = i as f32 * factor + offset;
*table = num::clamp(new_val, 0.0, 255.0) as u8;
}
for (x, y) in ImageIterator::with_dimension(&img.dimensions()) {
let mut px = img.get_pixel(x, y);
let channels = px.channels();
px = image::Rgba([
lookup_table[channels[0] as usize],
lookup_table[channels[1] as usize],
lookup_table[channels[2] as usize],
255,
]);
img.put_pixel(x, y, px);
}
photon_image.raw_pixels = img.to_bytes();
}
#[wasm_bindgen]
pub fn tint(
mut photon_image: &mut PhotonImage,
r_offset: u32,
g_offset: u32,
b_offset: u32,
) {
let mut img = helpers::dyn_image_from_raw(&photon_image);
for (x, y) in ImageIterator::with_dimension(&img.dimensions()) {
let mut px = img.get_pixel(x, y);
let channels = px.channels();
let (r_val, g_val, b_val) =
(channels[0] as u32, channels[1] as u32, channels[2] as u32);
let new_r_val = if r_val as u32 + r_offset < 255 {
r_val as u8 + r_offset as u8
} else {
255
};
let new_g_val = if g_val as u32 + g_offset < 255 {
g_val as u8 + g_offset as u8
} else {
255
};
let new_b_val = if b_val as u32 + b_offset < 255 {
b_val as u8 + b_offset as u8
} else {
255
};
px = image::Rgba([new_r_val, new_g_val, new_b_val, 255]);
img.put_pixel(x, y, px);
}
let raw_pixels = img.to_bytes();
photon_image.raw_pixels = raw_pixels;
}
fn draw_horizontal_strips(
mut photon_image: &mut PhotonImage,
num_strips: u8,
color: Rgb,
) {
let mut img = helpers::dyn_image_from_raw(&photon_image);
let (width, height) = img.dimensions();
let total_strips = (num_strips * 2) - 1;
let height_strip = height / total_strips as u32;
let mut y_pos: u32 = 0;
for i in 1..num_strips {
draw_filled_rect_mut(
&mut img,
Rect::at(0, (y_pos + height_strip) as i32).of_size(width, height_strip),
Rgba([color.r, color.g, color.b, 255u8]),
);
y_pos = i as u32 * (height_strip * 2);
}
let raw_pixels = img.to_bytes();
photon_image.raw_pixels = raw_pixels;
}
#[wasm_bindgen]
pub fn horizontal_strips(mut photon_image: &mut PhotonImage, num_strips: u8) {
let color = Rgb {
r: 255,
g: 255,
b: 255,
};
draw_horizontal_strips(&mut photon_image, num_strips, color)
}
#[wasm_bindgen]
pub fn color_horizontal_strips(
mut photon_image: &mut PhotonImage,
num_strips: u8,
color: Rgb,
) {
draw_horizontal_strips(&mut photon_image, num_strips, color)
}
fn draw_vertical_strips(mut photon_image: &mut PhotonImage, num_strips: u8, color: Rgb) {
let mut img = helpers::dyn_image_from_raw(&photon_image);
let (width, height) = img.dimensions();
let total_strips = (num_strips * 2) - 1;
let width_strip = width / total_strips as u32;
let mut x_pos: u32 = 0;
for i in 1..num_strips {
draw_filled_rect_mut(
&mut img,
Rect::at((x_pos + width_strip) as i32, 0).of_size(width_strip, height),
Rgba([color.r, color.g, color.b, 255u8]),
);
x_pos = i as u32 * (width_strip * 2);
}
let raw_pixels = img.to_bytes();
photon_image.raw_pixels = raw_pixels;
}
#[wasm_bindgen]
pub fn vertical_strips(mut photon_image: &mut PhotonImage, num_strips: u8) {
let color = Rgb {
r: 255,
g: 255,
b: 255,
};
draw_vertical_strips(&mut photon_image, num_strips, color)
}
#[wasm_bindgen]
pub fn color_vertical_strips(
mut photon_image: &mut PhotonImage,
num_strips: u8,
color: Rgb,
) {
draw_vertical_strips(&mut photon_image, num_strips, color)
}
struct Intensity {
val: i32,
r: i32,
g: i32,
b: i32,
}
#[wasm_bindgen]
pub fn oil(mut photon_image: &mut PhotonImage, radius: i32, intensity: f64) {
let img = helpers::dyn_image_from_raw(&photon_image);
let (width, height) = img.dimensions();
let mut target = image::DynamicImage::new_rgba8(width, height);
let mut pixel_intensity_count: HashMap<usize, Intensity>;
let mut intensity_lut = vec![vec![0; width as usize]; height as usize];
for y in 0..height {
for x in 0..width {
let single_pix = img.get_pixel(x, y);
let current_val = single_pix.channels();
let avg = (current_val[0] as i32
+ current_val[1] as i32
+ current_val[2] as i32) as f64
/ 3.0;
let val = (avg * intensity) / 255.0;
intensity_lut[y as usize][x as usize] = val.round() as usize;
}
}
for y in 0..height {
for x in 0..width {
pixel_intensity_count = HashMap::new();
for yy in -radius..=radius {
let yyy = (y as i32) + yy;
for xx in -radius..=radius {
let xxx = (x as i32) + xx;
if yyy > 0
&& yyy < (height as i32)
&& xxx > 0
&& xxx < (width as i32)
{
let idx_x = xxx as usize;
let idx_y = yyy as usize;
let intensity_val = intensity_lut[idx_y][idx_x];
let single_pix = img.get_pixel(idx_x as u32, idx_y as u32);
let pix = single_pix.channels();
match pixel_intensity_count.get_mut(&(intensity_val as usize)) {
Some(val) => {
val.val += 1;
val.r += pix[0] as i32;
val.g += pix[1] as i32;
val.b += pix[2] as i32;
}
None => {
pixel_intensity_count.insert(
intensity_val as usize,
Intensity {
val: 1,
r: pix[0] as i32,
g: pix[1] as i32,
b: pix[2] as i32,
},
);
}
}
}
}
}
let mut map_vec: Vec<_> = pixel_intensity_count.iter().collect();
map_vec.sort_by(|a, b| (b.1.val - a.1.val).cmp(&0));
let cur_max = map_vec[0].1;
target.put_pixel(
x,
y,
Rgba::<u8>([
(cur_max.r / cur_max.val) as u8,
(cur_max.g / cur_max.val) as u8,
(cur_max.b / cur_max.val) as u8,
255,
]),
)
}
}
let raw_pixels = target.to_bytes();
photon_image.raw_pixels = raw_pixels;
}
#[wasm_bindgen]
pub fn frosted_glass(photon_image: &mut PhotonImage) {
let img_orig_buf = photon_image.get_raw_pixels();
let width = photon_image.get_width();
let height = photon_image.get_height();
let end = img_orig_buf.len();
let mut img_buf = Vec::<u8>::new();
Vec::resize(&mut img_buf, end, 0_u8);
let perlin = PerlinNoise2D::new(2, 10.0, 10.0, 10.0, 1.0, (100.0, 100.0), 0.5, 101);
for pixel in (0..end).step_by(4) {
let x = (pixel / 4) / width as usize;
let y = (pixel / 4) % width as usize;
let res = [
perlin.get_noise(x as f64, y as f64) - 0.5,
(perlin.get_noise(100.0 + x as f64, y as f64) - 0.5) * 4.0,
];
let x_new = f64::clamp(f64::floor(x as f64 + res[0]), 0.0, height as f64 - 1.0);
let x_new = x_new as usize;
let y_new = f64::clamp(f64::floor(y as f64 + res[1]), 0.0, width as f64 - 1.0);
let y_new = y_new as usize;
let pixel_new = (x_new * width as usize + y_new) * 4;
if pixel_new > end as usize {
continue;
}
img_buf[pixel as usize] = img_orig_buf[pixel_new as usize];
img_buf[pixel as usize + 1] = img_orig_buf[pixel_new as usize + 1];
img_buf[pixel as usize + 2] = img_orig_buf[pixel_new as usize + 2];
img_buf[pixel as usize + 3] = img_orig_buf[pixel_new as usize + 3];
}
photon_image.raw_pixels = img_buf;
}