1use std::f32::consts::PI;
2
3use super::{alternating_current, base83, direct_current};
4use super::Error;
5use super::util::extract_hash_dimensions;
6use super::util::linear_to_srgb;
7
8pub fn decode(blurhash: &str, width: u32, height: u32, punch: f32) -> Result<Vec<u8>, Error> {
13 let (num_x, num_y) =
14 extract_hash_dimensions(
15 blurhash,
16 )?;
17
18 let quantised_maximum_value =
19 base83::decode(
20 &blurhash[1..2],
21 )?;
22
23 let maximum_value =
24 (quantised_maximum_value + 1) as f32 / 166.;
25
26 let mut colors =
27 vec![[0.; 3]; num_x * num_y];
28
29 for i in 0..colors.len() {
30 if i == 0 {
31 let value =
32 base83::decode(
33 &blurhash[2..6],
34 )?;
35
36 colors[i as usize] =
37 direct_current::decode(
38 value as u32,
39 );
40
41 continue;
42 }
43
44 let value =
45 base83::decode(
46 &blurhash[4 + i * 2..6 + i * 2]
47 )?;
48
49 colors[i as usize] =
50 alternating_current::decode(
51 value as u32,
52 maximum_value * punch,
53 );
54 }
55
56 let bytes_per_row = width * 4;
57 let mut pixels = vec![0; (bytes_per_row * height) as usize];
58
59 for y in 0..height {
60 for x in 0..width {
61 let mut pixel = [0.; 3];
62
63 for j in 0..num_y {
64 for i in 0..num_x {
65 let basis =
66 f32::cos((PI * x as f32 * i as f32) / width as f32)
67 * f32::cos((PI * y as f32 * j as f32) / height as f32);
68
69 let color =
70 &colors[i + j * num_x as usize];
71
72 pixel[0] += color[0] * basis;
73 pixel[1] += color[1] * basis;
74 pixel[2] += color[2] * basis;
75 }
76 }
77
78 let int_r = linear_to_srgb(pixel[0]);
79 let int_g = linear_to_srgb(pixel[1]);
80 let int_b = linear_to_srgb(pixel[2]);
81
82 pixels[(4 * x + y * bytes_per_row) as usize] = int_r as u8;
83 pixels[(4 * x + 1 + y * bytes_per_row) as usize] = int_g as u8;
84 pixels[(4 * x + 2 + y * bytes_per_row) as usize] = int_b as u8;
85 pixels[(4 * x + 3 + y * bytes_per_row) as usize] = 255u8;
86 }
87 }
88 Ok(pixels)
89}