1use rayon::iter::IndexedParallelIterator;
2use rayon::iter::IntoParallelIterator;
3use rayon::iter::ParallelIterator;
4
5use super::{alternating_current, base83, direct_current};
6use super::Error;
7use super::util::multiply_basis_function;
8
9pub fn encode(
11 components_x: u32,
12 components_y: u32,
13 width: u32,
14 height: u32,
15 rgba_image: &[u8],
16) -> Result<String, Error> {
17 if !(1..=9).contains(&components_x)
18 || !(1..=9).contains(&components_y)
19 {
20 return Err(Error::ComponentsOutOfRange);
21 }
22
23 let factors: Vec<[f32; 3]> = (0..components_y)
24 .into_par_iter()
25 .map(
26 |y| {
27 (0..components_x)
28 .into_par_iter()
29 .map(
30 |x| {
31 multiply_basis_function(
32 x,
33 y,
34 width,
35 height,
36 rgba_image,
37 )
38 }
39 )
40 .collect::<Vec<[f32; 3]>>()
41 }
42 )
43 .flatten()
44 .collect();
45
46 let dc = factors[0];
47 let ac = &factors[1..];
48
49 let mut blurhash = String::new();
50
51 let size_flag =
52 (components_x - 1)
53 + (components_y - 1)
54 * 9;
55
56 blurhash
57 .push_str(
58 &base83::encode(
59 size_flag,
60 1,
61 ),
62 );
63
64 let maximum_value: f32;
65
66 if !ac.is_empty() {
67 let mut actualmaximum_value = 0.0;
68
69 for i in 0..components_y * components_x - 1 {
70 actualmaximum_value = f32::max(f32::abs(ac[i as usize][0]), actualmaximum_value);
71 actualmaximum_value = f32::max(f32::abs(ac[i as usize][1]), actualmaximum_value);
72 actualmaximum_value = f32::max(f32::abs(ac[i as usize][2]), actualmaximum_value);
73 }
74
75 let quantised_maximum_value =
76 f32::max(
77 0.,
78 f32::min(
79 82.,
80 f32::floor(
81 actualmaximum_value
82 * 166.
83 - 0.5,
84 ),
85 ),
86 ) as u32;
87
88 maximum_value = (quantised_maximum_value + 1) as f32 / 166.;
89
90 blurhash.push_str(&base83::encode(quantised_maximum_value, 1));
91 } else {
92 maximum_value = 1.;
93
94 blurhash.push_str(&base83::encode(0, 1));
95 }
96
97 blurhash.push_str(&base83::encode(direct_current::encode(dc), 4));
98
99 for i in 0..components_y * components_x - 1 {
100 blurhash.push_str(&base83::encode(
101 alternating_current::encode(ac[i as usize], maximum_value),
102 2,
103 ));
104 }
105
106 Ok(blurhash)
107}