blurhash_fast/
decode.rs

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
8/// Decodes the given blurhash to an image of the specified size.
9///
10/// The punch parameter can be used to de- or increase the contrast of the
11/// resulting image.
12pub 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}