1use std::error::Error;
2
3#[derive(Debug, Clone, Copy, PartialEq)]
5pub enum BayerPattern {
6 RGGB,
8 BGGR,
10 GRBG,
12 GBRG,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct SuperpixelOffsets {
20 pub red: (u32, u32),
22 pub green: [(u32, u32); 2],
24 pub blue: (u32, u32),
26}
27
28impl BayerPattern {
29 pub fn superpixel_offsets(&self) -> SuperpixelOffsets {
35 match self {
36 BayerPattern::RGGB => SuperpixelOffsets {
37 red: (0, 0),
38 green: [(1, 0), (0, 1)],
39 blue: (1, 1),
40 },
41 BayerPattern::BGGR => SuperpixelOffsets {
42 red: (1, 1),
43 green: [(1, 0), (0, 1)],
44 blue: (0, 0),
45 },
46 BayerPattern::GRBG => SuperpixelOffsets {
47 red: (1, 0),
48 green: [(0, 0), (1, 1)],
49 blue: (0, 1),
50 },
51 BayerPattern::GBRG => SuperpixelOffsets {
52 red: (0, 1),
53 green: [(0, 0), (1, 1)],
54 blue: (1, 0),
55 },
56 }
57 }
58}
59
60impl From<BayerPattern> for String {
61 fn from(pattern: BayerPattern) -> Self {
62 match pattern {
63 BayerPattern::RGGB => "RGGB".to_string(),
64 BayerPattern::BGGR => "BGGR".to_string(),
65 BayerPattern::GRBG => "GRBG".to_string(),
66 BayerPattern::GBRG => "GBRG".to_string(),
67 }
68 }
69}
70
71impl TryFrom<String> for BayerPattern {
72 type Error = Box<dyn Error + Send + Sync>;
73
74 fn try_from(value: String) -> Result<Self, Self::Error> {
75 match value.to_lowercase().as_str() {
76 "rggb" => Ok(BayerPattern::RGGB),
77 "bggr" => Ok(BayerPattern::BGGR),
78 "grbg" => Ok(BayerPattern::GRBG),
79 "gbrg" => Ok(BayerPattern::GBRG),
80 _ => Err(From::from(format!("Invalid BAYERPAT value: {}", value))),
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::BayerPattern;
88
89 fn tile(pattern: BayerPattern) -> String {
91 let offsets = pattern.superpixel_offsets();
92 let mut tile = [' '; 4];
93
94 let index = |(x, y): (u32, u32)| (y * 2 + x) as usize;
95 tile[index(offsets.red)] = 'R';
96 tile[index(offsets.green[0])] = 'G';
97 tile[index(offsets.green[1])] = 'G';
98 tile[index(offsets.blue)] = 'B';
99
100 tile.iter().collect()
101 }
102
103 #[test]
104 fn every_pattern_lays_out_the_tile_its_name_describes() {
105 assert_eq!(tile(BayerPattern::RGGB), "RGGB");
106 assert_eq!(tile(BayerPattern::BGGR), "BGGR");
107 assert_eq!(tile(BayerPattern::GRBG), "GRBG");
108 assert_eq!(tile(BayerPattern::GBRG), "GBRG");
109 }
110
111 #[test]
112 fn every_position_in_the_tile_is_used_exactly_once() {
113 for pattern in [
114 BayerPattern::RGGB,
115 BayerPattern::BGGR,
116 BayerPattern::GRBG,
117 BayerPattern::GBRG,
118 ] {
119 let offsets = pattern.superpixel_offsets();
120 let mut positions = vec![
121 offsets.red,
122 offsets.green[0],
123 offsets.green[1],
124 offsets.blue,
125 ];
126 positions.sort();
127
128 assert_eq!(
129 positions,
130 vec![(0, 0), (0, 1), (1, 0), (1, 1)],
131 "{:?} does not cover its tile",
132 pattern
133 );
134 }
135 }
136}