1use crate::benchmark::Benchmark;
2use crate::blending::{
3 blend_images, get_blending_algorithm, is_algorithm_multiplied, multiply_image, BlendAlgorithm,
4};
5use crate::errors::PConvertError;
6use crate::parallelism::{ResultMessage, ThreadPool};
7use crate::utils::{read_png_from_file, write_png_parallel, write_png_to_file};
8use image::codecs::png::{CompressionType, FilterType};
9use image::Rgba;
10use std::fmt::{Display, Formatter};
11use std::{fmt, sync::mpsc::Receiver};
12
13const THREAD_POOL_SIZE: usize = 5;
14
15#[derive(Clone)]
16pub enum Background {
17 Alpha,
18 White,
19 Blue,
20 Texture,
21}
22
23impl Display for Background {
24 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
25 match self {
26 Background::Alpha => write!(f, "alpha"),
27 Background::White => write!(f, "white"),
28 Background::Blue => write!(f, "blue"),
29 Background::Texture => write!(f, "texture"),
30 }
31 }
32}
33
34pub fn compose(
39 dir: &str,
40 algorithm: BlendAlgorithm,
41 background: &Background,
42 compression: CompressionType,
43 filter: FilterType,
44 benchmark: &mut Benchmark,
45) -> Result<String, PConvertError> {
46 let demultiply = is_algorithm_multiplied(&algorithm);
47
48 let algorithm_fn = get_blending_algorithm(&algorithm);
49
50 let background_file = format!("background_{}.png", background);
53 let png_file_names = vec![
54 "sole.png",
55 "back.png",
56 "front.png",
57 "shoelace.png",
58 &background_file,
59 ];
60
61 let png_paths = png_file_names
62 .iter()
63 .map(|name| format!("{}{}", dir, name))
64 .collect::<Vec<String>>();
65
66 let top = benchmark.execute(Benchmark::add_read_png_time, || {
67 read_png_from_file(format!("{}sole.png", dir), demultiply)
68 })?;
69
70 let mut bot =
71 png_paths[..png_file_names.len() - 1]
72 .iter()
73 .fold(top, |mut composition, path| {
74 let layer = benchmark
75 .execute(Benchmark::add_read_png_time, || {
76 read_png_from_file(path.clone(), demultiply)
77 })
78 .unwrap();
79
80 benchmark.execute(Benchmark::add_blend_time, || {
81 blend_images(&mut composition, &layer, &algorithm_fn, &None)
82 });
83
84 composition
85 });
86
87 if demultiply {
88 benchmark.execute(Benchmark::add_blend_time, || multiply_image(&mut bot));
89 }
90
91 let mut composition = benchmark.execute(Benchmark::add_read_png_time, || {
92 read_png_from_file(format!("{}background_{}.png", dir, background), false)
93 })?;
94
95 benchmark.execute(Benchmark::add_blend_time, || {
96 blend_images(&mut composition, &bot, &algorithm_fn, &None)
97 });
98
99 let file_name = format!(
101 "result_{}_{}_{:#?}_{:#?}.png",
102 algorithm, background, compression, filter
103 );
104 let file_out = format!("{}{}", dir, file_name);
105 benchmark.execute(Benchmark::add_write_png_time, || {
106 write_png_to_file(file_out, &composition, compression, filter)
107 })?;
108
109 Ok(file_name)
110}
111
112pub fn compose_parallel(
116 dir: &str,
117 algorithm: BlendAlgorithm,
118 background: &Background,
119 compression: CompressionType,
120 filter: FilterType,
121 benchmark: &mut Benchmark,
122) -> Result<String, PConvertError> {
123 let demultiply = is_algorithm_multiplied(&algorithm);
124 let algorithm_fn = get_blending_algorithm(&algorithm);
125
126 let mut thread_pool = ThreadPool::new(THREAD_POOL_SIZE)?;
127 thread_pool.start();
128
129 let background_file = format!("background_{}.png", background);
132 let png_file_names = vec![
133 "sole.png",
134 "back.png",
135 "front.png",
136 "shoelace.png",
137 &background_file,
138 ];
139
140 let result_channels = png_file_names
141 .iter()
142 .map(|name| format!("{}{}", dir, name))
143 .map(|path| {
144 thread_pool
145 .execute(move || ResultMessage::ImageResult(read_png_from_file(path, demultiply)))
146 })
147 .collect::<Vec<Receiver<ResultMessage>>>();
148
149 let mut bot = benchmark.execute(Benchmark::add_read_png_time, || {
152 if let Ok(ResultMessage::ImageResult(result)) = result_channels[0].recv() {
153 result
154 } else {
155 panic!("failure reading 'sole.png'")
156 }
157 })?;
158
159 for i in 1..=3 {
160 let top = benchmark.execute(Benchmark::add_read_png_time, || {
161 if let Ok(ResultMessage::ImageResult(result)) = result_channels[i].recv() {
162 result
163 } else {
164 panic!("failure reading '{}'", png_file_names[i])
165 }
166 })?;
167 benchmark.execute(Benchmark::add_blend_time, || {
168 blend_images(&mut bot, &top, &algorithm_fn, &None)
169 });
170 }
171
172 if demultiply {
173 multiply_image(&mut bot);
174 }
175
176 let mut composition = benchmark.execute(Benchmark::add_read_png_time, || {
177 if let Ok(ResultMessage::ImageResult(result)) = result_channels[4].recv() {
178 result
179 } else {
180 panic!("failure reading '{}'", background_file)
181 }
182 })?;
183 benchmark.execute(Benchmark::add_blend_time, || {
184 blend_images(&mut composition, &bot, &algorithm_fn, &None)
185 });
186
187 let file_name = format!(
190 "result_{}_{}_{:#?}_{:#?}.png",
191 algorithm, background, compression, filter
192 );
193 let file_out = format!("{}{}", dir, file_name);
194 benchmark.execute(Benchmark::add_write_png_time, || {
195 write_png_parallel(file_out, &composition, compression, filter)
196 })?;
197
198 Ok(file_name)
199}
200
201pub fn apply_blue_filter(pixel: &mut Rgba<u8>) {
203 pixel[0] = 0;
205 pixel[1] = pixel[2];
206}