1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use misc::*;

#[derive(Copy, Clone)]
pub struct DirectWaveformRenderer {
    pub sample_rate: f64,
    pub config: WaveformConfig,
}

impl DirectWaveformRenderer {
    /// Generates an image as a `Vec<u8>` directly from the given samples.
    ///
    /// # Arguments
    ///
    /// * `samples` - The `Sample`s that will be used to render the image.
    /// * `shape` - The `(width, height)` of the resulting image.
    pub fn render_vec<T: Sample>(&self, samples: &[T], shape: (usize, usize)) -> Option<Vec<u8>> {
        let (w, h) = shape;
        if w == 0 || h == 0 {
            return None;
        }
        let mut img = vec![0u8; w * h * 4];
        let nb_samples = samples.len();
        let samples_per_pixel = nb_samples / w;

        // Unlike BinnedWaveformRenderer, the minmax values corresponding to
        // each horizontal pixel is calculated beforehand, for the same reasons
        // discussed later in these comments.
        let mut minmax = MinMaxPairSequence {
            data: Vec::with_capacity(w),
        };
        for x in 0..w {
            let mut min = samples[x * samples_per_pixel + 0];
            let mut max = samples[x * samples_per_pixel + 0];
            if samples_per_pixel > 1 {
                for i in 1..samples_per_pixel {
                    let idx = x * samples_per_pixel + i;
                    if idx >= nb_samples {
                        break;
                    }
                    let s = samples[idx];
                    if s > max {
                        max = s;
                    }
                    if s < min {
                        min = s;
                    }
                }
            }
            minmax.data.push(MinMaxPair { min: min, max: max });
        }

        // Unlike BinnedWaveformRenderer, the `match` is outside the `for`s
        // because it's faster this way.
        // I've also tried it in BinnedWaveformRenderer but it didn't make a
        // significant improvement in speed, so it's left that way.
        match (self.config.get_background(), self.config.get_foreground()) {
            (Color::Scalar(ba), Color::Scalar(fa)) => for y in 0..h {
                let y_translated = ((h - y) as f64) / (h as f64) * (self.config.amp_max - self.config.amp_min) + self.config.amp_min;
                for x in 0..w {
                    if y_translated < minmax.data[x].min.into() || y_translated > minmax.data[x].max.into() {
                        img[1 * (y * w + x) + 0] = ba;
                    } else {
                        img[1 * (y * w + x) + 0] = fa;
                    }
                }
            },

            (
                Color::Vector4(br, bg, bb, ba),
                Color::Vector4(fr, fg, fb, fa),
            ) => for y in 0..h {
                let y_translated = ((h - y) as f64) / (h as f64) * (self.config.amp_max - self.config.amp_min) + self.config.amp_min;
                for x in 0..w {
                    if y_translated < minmax.data[x].min.into() || y_translated > minmax.data[x].max.into() {
                        img[4 * (y * w + x) + 0] = br;
                        img[4 * (y * w + x) + 1] = bg;
                        img[4 * (y * w + x) + 2] = bb;
                        img[4 * (y * w + x) + 3] = ba;
                    } else {
                        img[4 * (y * w + x) + 0] = fr;
                        img[4 * (y * w + x) + 1] = fg;
                        img[4 * (y * w + x) + 2] = fb;
                        img[4 * (y * w + x) + 3] = fa;
                    }
                }
            },
            
            _ => {
                panic!("Color formats of background and foreground are inconsistent!");
            }
        }
        Some(img)
    }
}