diff --git a/Cargo.lock b/Cargo.lock index c711a27b35307563d7aec2a890cc1206c2d529e3..bee549ef84e25a6488d1be3859d7af598b8f06e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1052,6 +1052,7 @@ dependencies = [ "imageproc", "log", "musicbrainz_rs", + "rayon", "read_color", "reqwest 0.10.4", "serde", diff --git a/Cargo.toml b/Cargo.toml index 69e0f47050de9c760b0e6c8f9c00b4273b9c7a8a..3a667d6155a4c182fbac0b0d56c8d44525a19b6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ image = "0.23.2" # Creating parsing and manipulating images. imageproc = "0.20.0" # Bunch of of filters and algos to process images. read_color = "1.0.0" # What's that? Idunno. musicbrainz_rs = "0.2.0" - +rayon = "1.1" # Parallelizing processing. [dependencies.fern] # Simple, efficient logging. version = "0.5" diff --git a/lib/img_processors/mod.rs b/lib/img_processors/mod.rs index adf94250722c93c2b0b0d829a265a9e11ccef942..0cedda44fd780b8dd8b114649cb0cffc27db86b5 100644 --- a/lib/img_processors/mod.rs +++ b/lib/img_processors/mod.rs @@ -1,10 +1,13 @@ use image::imageops::overlay; -use image::{DynamicImage, GenericImageView}; +use image::{DynamicImage, GenericImageView, RgbaImage}; use crate::config::image_processors::ProcessorParams; use crate::config::Config; use crate::display::get_max_resolution; -use crate::result::{MBGError, MBGResult}; +use crate::result::MBGResult; +use rayon::prelude::*; +use std::collections::HashMap; +use std::collections::hash_map::RandomState; pub mod blur; pub mod border; @@ -63,40 +66,74 @@ impl Default for Processors { } } +pub fn process_layer( + screen_height: i32, + screen_width: i32, + image: DynamicImage, + processors: Vec<ProcessorParams>, +) -> MBGResult<DynamicImage> { + let mut image = image; + for processor_params in processors { + let processor = mapper(processor_params, screen_height, screen_width); + image = processor.process(&image)?; + debug!("Processor applied") + } + Ok(image) +} + pub fn process_image(img_bytes: Vec<u8>) -> MBGResult<DynamicImage> { let conf = Config::get_props(); let (screen_height, screen_width) = get_max_resolution()?; debug!("Maximum resolution: {}x{}", screen_width, screen_height); let image = image::load_from_memory(img_bytes.as_slice())?; - let mut layers = vec![image.clone()]; - for params in conf.processors.into_iter() { - let processor = mapper(params, screen_height, screen_width); - if let Some(layer_img) = layers.get(processor.get_layer()) { - layers[processor.get_layer()] = processor.process(layer_img)?; - } else { - layers.push(processor.process(&image.clone())?); - } - debug!("Filter applied"); - } - if layers.is_empty() { - return Err(MBGError::ImageError(String::from( - "No processors was found.", - ))); - } - if conf.blender.is_empty() { - Ok(layers[0].clone()) - } else { - blend_layers(layers, conf.blender) - } + let layers_conf = + conf.processors + .iter() + .fold(HashMap::<usize, Vec<ProcessorParams>>::new(), |mut acc, x| { + let new_processors = if let Some(current) = acc.get(&x.layer) { + let mut mutable = current.clone(); + mutable.push(x.clone()); + mutable + } else { + vec![x.clone()] + }; + acc.insert(x.layer, new_processors); + acc + }); + let processed_layers = layers_conf + .par_iter() + .map(|(layer, processors)| { + let img = process_layer( + screen_height, + screen_width, + image.clone(), + processors.to_vec(), + ); + (layer, img) + }) + .collect::<HashMap<_, _, RandomState>>(); + blend_layers(screen_height, screen_width, processed_layers, conf.blender) } -pub fn blend_layers(layers: Vec<DynamicImage>, blender: Vec<u8>) -> MBGResult<DynamicImage> { - let mut res = layers[0].clone(); +pub fn blend_layers<S: std::hash::BuildHasher>( + height: i32, + width: i32, + layers: HashMap<&usize, MBGResult<DynamicImage>, S>, + blender: Vec<u8>, +) -> MBGResult<DynamicImage> { + let mut res = DynamicImage::ImageRgba8(RgbaImage::new(width as u32, height as u32)); for index in blender.into_iter() { - if let Some(layer) = layers.get(usize::from(index)) { - let pos_x = (res.width() / 2) - (layer.width() / 2); - let pos_y = (res.height() / 2) - (layer.height() / 2); - overlay(&mut res, layer, pos_x, pos_y); + if let Some(layer) = layers.get(&usize::from(index)) { + match layer { + Ok(image) => { + let pos_x = (res.width() / 2) - (image.width() / 2); + let pos_y = (res.height() / 2) - (image.height() / 2); + overlay(&mut res, image, pos_x, pos_y); + } + Err(error) => { + error!("{}", error); + } + } } } Ok(res)