1use std::f32::consts::PI;
2use std::io::Read;
3
4pub fn rgba_to_thumb_hash(w: usize, h: usize, rgba: &[u8]) -> Vec<u8> {
10 assert!(w <= 100 && h <= 100);
12 assert_eq!(rgba.len(), w * h * 4);
13
14 let mut avg_r = 0.0;
16 let mut avg_g = 0.0;
17 let mut avg_b = 0.0;
18 let mut avg_a = 0.0;
19 for rgba in rgba.chunks_exact(4) {
20 let alpha = rgba[3] as f32 / 255.0;
21 avg_r += alpha / 255.0 * rgba[0] as f32;
22 avg_g += alpha / 255.0 * rgba[1] as f32;
23 avg_b += alpha / 255.0 * rgba[2] as f32;
24 avg_a += alpha;
25 }
26 if avg_a > 0.0 {
27 avg_r /= avg_a;
28 avg_g /= avg_a;
29 avg_b /= avg_a;
30 }
31
32 let has_alpha = avg_a < (w * h) as f32;
33 let l_limit = if has_alpha { 5 } else { 7 }; let lx = (((l_limit * w) as f32 / w.max(h) as f32).round() as usize).max(1);
35 let ly = (((l_limit * h) as f32 / w.max(h) as f32).round() as usize).max(1);
36 let mut l = Vec::with_capacity(w * h); let mut p = Vec::with_capacity(w * h); let mut q = Vec::with_capacity(w * h); let mut a = Vec::with_capacity(w * h); for rgba in rgba.chunks_exact(4) {
43 let alpha = rgba[3] as f32 / 255.0;
44 let r = avg_r * (1.0 - alpha) + alpha / 255.0 * rgba[0] as f32;
45 let g = avg_g * (1.0 - alpha) + alpha / 255.0 * rgba[1] as f32;
46 let b = avg_b * (1.0 - alpha) + alpha / 255.0 * rgba[2] as f32;
47 l.push((r + g + b) / 3.0);
48 p.push((r + g) / 2.0 - b);
49 q.push(r - g);
50 a.push(alpha);
51 }
52
53 let encode_channel = |channel: &[f32], nx: usize, ny: usize| -> (f32, Vec<f32>, f32) {
55 let mut dc = 0.0;
56 let mut ac = Vec::with_capacity(nx * ny / 2);
57 let mut scale = 0.0;
58 let mut fx = [0.0].repeat(w);
59 for cy in 0..ny {
60 let mut cx = 0;
61 while cx * ny < nx * (ny - cy) {
62 let mut f = 0.0;
63 for x in 0..w {
64 fx[x] = (PI / w as f32 * cx as f32 * (x as f32 + 0.5)).cos();
65 }
66 for y in 0..h {
67 let fy = (PI / h as f32 * cy as f32 * (y as f32 + 0.5)).cos();
68 for x in 0..w {
69 f += channel[x + y * w] * fx[x] * fy;
70 }
71 }
72 f /= (w * h) as f32;
73 if cx > 0 || cy > 0 {
74 ac.push(f);
75 scale = f.abs().max(scale);
76 } else {
77 dc = f;
78 }
79 cx += 1;
80 }
81 }
82 if scale > 0.0 {
83 for ac in &mut ac {
84 *ac = 0.5 + 0.5 / scale * *ac;
85 }
86 }
87 (dc, ac, scale)
88 };
89 let (l_dc, l_ac, l_scale) = encode_channel(&l, lx.max(3), ly.max(3));
90 let (p_dc, p_ac, p_scale) = encode_channel(&p, 3, 3);
91 let (q_dc, q_ac, q_scale) = encode_channel(&q, 3, 3);
92 let (a_dc, a_ac, a_scale) = if has_alpha {
93 encode_channel(&a, 5, 5)
94 } else {
95 (1.0, Vec::new(), 1.0)
96 };
97
98 let is_landscape = w > h;
100 let header24 = (63.0 * l_dc).round() as u32
101 | (((31.5 + 31.5 * p_dc).round() as u32) << 6)
102 | (((31.5 + 31.5 * q_dc).round() as u32) << 12)
103 | (((31.0 * l_scale).round() as u32) << 18)
104 | if has_alpha { 1 << 23 } else { 0 };
105 let header16 = (if is_landscape { ly } else { lx }) as u16
106 | (((63.0 * p_scale).round() as u16) << 3)
107 | (((63.0 * q_scale).round() as u16) << 9)
108 | if is_landscape { 1 << 15 } else { 0 };
109 let mut hash = Vec::with_capacity(25);
110 hash.extend_from_slice(&[
111 (header24 & 255) as u8,
112 ((header24 >> 8) & 255) as u8,
113 (header24 >> 16) as u8,
114 (header16 & 255) as u8,
115 (header16 >> 8) as u8,
116 ]);
117 let mut is_odd = false;
118 if has_alpha {
119 hash.push((15.0 * a_dc).round() as u8 | (((15.0 * a_scale).round() as u8) << 4));
120 }
121
122 for ac in [l_ac, p_ac, q_ac] {
124 for f in ac {
125 let u = (15.0 * f).round() as u8;
126 if is_odd {
127 *hash.last_mut().unwrap() |= u << 4;
128 } else {
129 hash.push(u);
130 }
131 is_odd = !is_odd;
132 }
133 }
134 if has_alpha {
135 for f in a_ac {
136 let u = (15.0 * f).round() as u8;
137 if is_odd {
138 *hash.last_mut().unwrap() |= u << 4;
139 } else {
140 hash.push(u);
141 }
142 is_odd = !is_odd;
143 }
144 }
145 hash
146}
147
148fn read_byte(bytes: &mut &[u8]) -> Result<u8, ()> {
149 let mut byte = [0; 1];
150 bytes.read_exact(&mut byte).map_err(|_| ())?;
151 Ok(byte[0])
152}
153
154pub fn thumb_hash_to_rgba(mut hash: &[u8]) -> Result<(usize, usize, Vec<u8>), ()> {
160 let ratio = thumb_hash_to_approximate_aspect_ratio(hash)?;
161
162 let header24 = read_byte(&mut hash)? as u32
164 | ((read_byte(&mut hash)? as u32) << 8)
165 | ((read_byte(&mut hash)? as u32) << 16);
166 let header16 = read_byte(&mut hash)? as u16 | ((read_byte(&mut hash)? as u16) << 8);
167 let l_dc = (header24 & 63) as f32 / 63.0;
168 let p_dc = ((header24 >> 6) & 63) as f32 / 31.5 - 1.0;
169 let q_dc = ((header24 >> 12) & 63) as f32 / 31.5 - 1.0;
170 let l_scale = ((header24 >> 18) & 31) as f32 / 31.0;
171 let has_alpha = (header24 >> 23) != 0;
172 let p_scale = ((header16 >> 3) & 63) as f32 / 63.0;
173 let q_scale = ((header16 >> 9) & 63) as f32 / 63.0;
174 let is_landscape = (header16 >> 15) != 0;
175 let l_max = if has_alpha { 5 } else { 7 };
176 let lx = 3.max(if is_landscape { l_max } else { header16 & 7 }) as usize;
177 let ly = 3.max(if is_landscape { header16 & 7 } else { l_max }) as usize;
178 let (a_dc, a_scale) = if has_alpha {
179 let header8 = read_byte(&mut hash)?;
180 ((header8 & 15) as f32 / 15.0, (header8 >> 4) as f32 / 15.0)
181 } else {
182 (1.0, 1.0)
183 };
184
185 let mut prev_bits = None;
187 let mut decode_channel = |nx: usize, ny: usize, scale: f32| -> Result<Vec<f32>, ()> {
188 let mut ac = Vec::with_capacity(nx * ny);
189 for cy in 0..ny {
190 let mut cx = if cy > 0 { 0 } else { 1 };
191 while cx * ny < nx * (ny - cy) {
192 let bits = if let Some(bits) = prev_bits {
193 prev_bits = None;
194 bits
195 } else {
196 let bits = read_byte(&mut hash)?;
197 prev_bits = Some(bits >> 4);
198 bits & 15
199 };
200 ac.push((bits as f32 / 7.5 - 1.0) * scale);
201 cx += 1;
202 }
203 }
204 Ok(ac)
205 };
206 let l_ac = decode_channel(lx, ly, l_scale)?;
207 let p_ac = decode_channel(3, 3, p_scale * 1.25)?;
208 let q_ac = decode_channel(3, 3, q_scale * 1.25)?;
209 let a_ac = if has_alpha {
210 decode_channel(5, 5, a_scale)?
211 } else {
212 Vec::new()
213 };
214
215 let (w, h) = if ratio > 1.0 {
217 (32, (32.0 / ratio).round() as usize)
218 } else {
219 ((32.0 * ratio).round() as usize, 32)
220 };
221 let mut rgba = Vec::with_capacity(w * h * 4);
222 let mut fx = [0.0].repeat(7);
223 let mut fy = [0.0].repeat(7);
224 for y in 0..h {
225 for x in 0..w {
226 let mut l = l_dc;
227 let mut p = p_dc;
228 let mut q = q_dc;
229 let mut a = a_dc;
230
231 for cx in 0..lx.max(if has_alpha { 5 } else { 3 }) {
233 fx[cx] = (PI / w as f32 * (x as f32 + 0.5) * cx as f32).cos();
234 }
235 for cy in 0..ly.max(if has_alpha { 5 } else { 3 }) {
236 fy[cy] = (PI / h as f32 * (y as f32 + 0.5) * cy as f32).cos();
237 }
238
239 let mut j = 0;
241 for cy in 0..ly {
242 let mut cx = if cy > 0 { 0 } else { 1 };
243 let fy2 = fy[cy] * 2.0;
244 while cx * ly < lx * (ly - cy) {
245 l += l_ac[j] * fx[cx] * fy2;
246 j += 1;
247 cx += 1;
248 }
249 }
250
251 let mut j = 0;
253 for cy in 0..3 {
254 let mut cx = if cy > 0 { 0 } else { 1 };
255 let fy2 = fy[cy] * 2.0;
256 while cx < 3 - cy {
257 let f = fx[cx] * fy2;
258 p += p_ac[j] * f;
259 q += q_ac[j] * f;
260 j += 1;
261 cx += 1;
262 }
263 }
264
265 if has_alpha {
267 let mut j = 0;
268 for cy in 0..5 {
269 let mut cx = if cy > 0 { 0 } else { 1 };
270 let fy2 = fy[cy] * 2.0;
271 while cx < 5 - cy {
272 a += a_ac[j] * fx[cx] * fy2;
273 j += 1;
274 cx += 1;
275 }
276 }
277 }
278
279 let b = l - 2.0 / 3.0 * p;
281 let r = (3.0 * l - b + q) / 2.0;
282 let g = r - q;
283 rgba.extend_from_slice(&[
284 (r.clamp(0.0, 1.0) * 255.0) as u8,
285 (g.clamp(0.0, 1.0) * 255.0) as u8,
286 (b.clamp(0.0, 1.0) * 255.0) as u8,
287 (a.clamp(0.0, 1.0) * 255.0) as u8,
288 ]);
289 }
290 }
291 Ok((w, h, rgba))
292}
293
294pub fn thumb_hash_to_average_rgba(hash: &[u8]) -> Result<(f32, f32, f32, f32), ()> {
299 if hash.len() < 5 {
300 return Err(());
301 }
302 let header = hash[0] as u32 | ((hash[1] as u32) << 8) | ((hash[2] as u32) << 16);
303 let l = (header & 63) as f32 / 63.0;
304 let p = ((header >> 6) & 63) as f32 / 31.5 - 1.0;
305 let q = ((header >> 12) & 63) as f32 / 31.5 - 1.0;
306 let has_alpha = (header >> 23) != 0;
307 let a = if has_alpha {
308 (hash[5] & 15) as f32 / 15.0
309 } else {
310 1.0
311 };
312 let b = l - 2.0 / 3.0 * p;
313 let r = (3.0 * l - b + q) / 2.0;
314 let g = r - q;
315 Ok((r.clamp(0.0, 1.0), g.clamp(0.0, 1.0), b.clamp(0.0, 1.0), a))
316}
317
318pub fn thumb_hash_to_approximate_aspect_ratio(hash: &[u8]) -> Result<f32, ()> {
322 if hash.len() < 5 {
323 return Err(());
324 }
325 let has_alpha = (hash[2] & 0x80) != 0;
326 let l_max = if has_alpha { 5 } else { 7 };
327 let l_min = hash[3] & 7;
328 let is_landscape = (hash[4] & 0x80) != 0;
329 let lx = if is_landscape { l_max } else { l_min };
330 let ly = if is_landscape { l_min } else { l_max };
331 Ok(lx as f32 / ly as f32)
332}