⎇ 4528e08e0469d6cd68eb015768e9c4482508afa9
crates/modules/pineal/heatmap/src/encoder.rs
Historia
Quién tocó qué
crudo
feat(pineal): cierra stub heatmap — matrix + viridis + encoder + paint
1//! Encoder: `HeatmapMatrix` → buffer ARGB para subir como textura.
2
3use crate::matrix::HeatmapMatrix;
4use crate::palette::Ramp;
5
6/// Normaliza cada celda a `[0,1]` por min/max, la mapea por la rampa y
7/// empaqueta el resultado como `u32` ARGB (0xAARRGGBB), fila por fila.
8///
9/// El backend GPUI sube este buffer como una textura y la rendea con un
10/// solo `drawImageRect`, en vez de N draw calls.
11pub fn encode_argb(matrix: &HeatmapMatrix, ramp: Ramp) -> Vec<u32> {
12 let (min, max) = matrix.min_max();
13 let span = max - min;
14 let mut out = Vec::with_capacity(matrix.width() * matrix.height());
15 for &v in matrix.data() {
16 let t = if span > 0.0 { (v - min) / span } else { 0.0 };
17 let c = ramp.sample(t);
18 out.push(pack_argb(c.a, c.r, c.g, c.b));
19 }
20 out
21}
22
23/// Empaqueta 4 canales `f32` `[0,1]` en `0xAARRGGBB`.
24fn pack_argb(a: f32, r: f32, g: f32, b: f32) -> u32 {
25 let q = |v: f32| (v.clamp(0.0, 1.0) * 255.0).round() as u32;
26 (q(a) << 24) | (q(r) << 16) | (q(g) << 8) | q(b)
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32
33 #[test]
34 fn encodes_one_pixel_per_cell() {
35 let m = HeatmapMatrix::from_data(vec![0.0, 1.0, 2.0, 3.0], 2, 2).unwrap();
36 let buf = encode_argb(&m, Ramp::Grayscale);
37 assert_eq!(buf.len(), 4);
38 }
39
40 #[test]
41 fn normalizes_min_to_ramp_start() {
42 // Grayscale: min → negro (0x000000), max → blanco (0xffffff).
43 let m = HeatmapMatrix::from_data(vec![10.0, 20.0], 2, 1).unwrap();
44 let buf = encode_argb(&m, Ramp::Grayscale);
45 assert_eq!(buf[0] & 0x00ff_ffff, 0x0000_0000); // min
46 assert_eq!(buf[1] & 0x00ff_ffff, 0x00ff_ffff); // max
47 assert_eq!(buf[0] >> 24, 0xff); // alpha opaco
48 }
49
50 #[test]
51 fn flat_matrix_does_not_divide_by_zero() {
52 let m = HeatmapMatrix::from_data(vec![5.0; 4], 2, 2).unwrap();
53 let buf = encode_argb(&m, Ramp::Viridis);
54 assert_eq!(buf.len(), 4);
55 assert!(buf.iter().all(|&p| p == buf[0]));
56 }
57}