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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Bayer reader that mirrors pixels on the border.
//!
//! If the raw data is given by the unprimed values shown below, this
//! reader will produce the following row, where the primed values
//! have the same value as the unprimed values.
//!
//! ```text
//!   r2' g1' r1' g0' | r0 g0 r1 g1 r2 g2 ... rl gl rm gm rn gn | rn' gm' rm' gl'
//! ```

use std::io::Read;

use ::BayerResult;
use bayer::*;

/// Tuple structs (x1, x2, x3) designating the different sub-regions
/// of the output lines.
///
/// ```text
///    0 .. x1 => left border
///   x1 .. x2 => raw data
///   x2 .. x3 => right border
/// ```
pub struct BorderMirror8(usize, usize, usize);
pub struct BorderMirror16BE(usize, usize, usize);
pub struct BorderMirror16LE(usize, usize, usize);

macro_rules! fill_row {
    ($dst:ident, $x1:expr, $x2:expr, $x3:expr) => {{
        let mut i;
        let mut j;

        // Left border.
        i = $x1;
        j = $x1 + 1;
        while i > 0 {
            $dst[i - 1] = $dst[j];
            i = i - 1;
            j = j + 1;
        }

        // Right border.
        i = $x2;
        j = $x2 - 2;
        while i < $x3 {
            $dst[i] = $dst[j];
            i = i + 1;
            j = j - 1;
        }
    }}
}

impl BorderMirror8 {
    pub fn new(width: usize, padding: usize) -> Self {
        let x1 = padding;
        let x2 = x1.checked_add(width).expect("overflow");
        let x3 = x2.checked_add(padding).expect("overflow");
        assert!(width > padding);

        BorderMirror8(x1, x2, x3)
    }
}

impl BayerRead8 for BorderMirror8 {
    fn read_line(&self, r: &mut Read, dst: &mut [u8])
            -> BayerResult<()> {
        let BorderMirror8(x1, x2, x3) = *self;
        read_exact_u8(r, &mut dst[x1..x2])?;
        fill_row!(dst, x1, x2, x3);
        Ok(())
    }
}

impl BorderMirror16BE {
    pub fn new(width: usize, padding: usize) -> Self {
        let x1 = padding;
        let x2 = x1.checked_add(width).expect("overflow");
        let x3 = x2.checked_add(padding).expect("overflow");
        assert!(width > padding);

        BorderMirror16BE(x1, x2, x3)
    }
}

impl BayerRead16 for BorderMirror16BE {
    fn read_line(&self, r: &mut Read, dst: &mut [u16])
            -> BayerResult<()> {
        let BorderMirror16BE(x1, x2, x3) = *self;
        read_exact_u16be(r, &mut dst[x1..x2])?;
        fill_row!(dst, x1, x2, x3);
        Ok(())
    }
}

impl BorderMirror16LE {
    pub fn new(width: usize, padding: usize) -> Self {
        let x1 = padding;
        let x2 = x1.checked_add(width).expect("overflow");
        let x3 = x2.checked_add(padding).expect("overflow");
        assert!(width > padding);

        BorderMirror16LE(x1, x2, x3)
    }
}

impl BayerRead16 for BorderMirror16LE {
    fn read_line(&self, r: &mut Read, dst: &mut [u16])
            -> BayerResult<()> {
        let BorderMirror16LE(x1, x2, x3) = *self;
        read_exact_u16le(r, &mut dst[x1..x2])?;
        fill_row!(dst, x1, x2, x3);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use std::io::Cursor;
    use bayer::BayerRead8;
    use super::BorderMirror8;

    #[test]
    fn test_mirror_even() {
        let src = [
            1,2, 3,4, 5,6 ];

        let expected = [
            5,4, 3,2,
            /*-----*/ 1,2, 3,4, 5,6,
            /*--------------------*/ 5,4, 3,2 ];

        let rdr = BorderMirror8::new(6, 4);
        let mut buf = [0u8; 4 + 6 + 4];

        let res = rdr.read_line(&mut Cursor::new(&src[..]), &mut buf);
        assert!(res.is_ok());
        assert_eq!(&buf[..], &expected[..]);
    }

    #[test]
    fn test_mirror_odd() {
        let src = [
            1,2, 3,4, 5, ];

        let expected = [
            4, 3,2,
            /*---*/ 1,2, 3,4, 5,
            /*---------------*/ 4, 3,2 ];

        let rdr = BorderMirror8::new(5, 3);
        let mut buf = [0u8; 3 + 5 + 3];

        let res = rdr.read_line(&mut Cursor::new(&src[..]), &mut buf);
        assert!(res.is_ok());
        assert_eq!(&buf[..], &expected[..]);
    }
}