1use image::Rgba;
2use strum::IntoEnumIterator;
3use strum_macros::{EnumIter, EnumString, IntoStaticStr};
4
5fn pixel_max(Rgba { data, .. }: &Rgba<u8>) -> u8 {
6 data[..3].iter().max().cloned().unwrap_or_default()
7}
8
9fn pixel_min(Rgba { data, .. }: &Rgba<u8>) -> u8 {
10 data[..3].iter().min().cloned().unwrap_or_default()
11}
12
13fn pixel_chroma(pixel: &Rgba<u8>) -> u8 {
14 pixel_max(pixel) - pixel_min(pixel)
15}
16
17fn pixel_hue(pixel: &Rgba<u8>) -> u8 {
18 let c = pixel_chroma(pixel);
19
20 if c == 0 {
21 return 0;
22 }
23
24 let Rgba { data, .. } = pixel;
25
26 match data[..3].iter().enumerate().max_by_key(|&(_, e)| e) {
27 Some((0, _)) => (data[1] as i16 - data[2] as i16).abs() as u8 / c * 43,
28 Some((1, _)) => (data[2] as i16 - data[0] as i16).abs() as u8 / c * 43 + 85,
29 Some((2, _)) => (data[0] as i16 - data[1] as i16).abs() as u8 / c * 43 + 171,
30 _ => 0,
31 }
32}
33
34fn pixel_saturation(pixel: &Rgba<u8>) -> u8 {
35 match pixel_max(pixel) {
36 0 => 0,
37 v => pixel_chroma(pixel) / v,
38 }
39}
40
41fn pixel_brightness(Rgba { data, .. }: &Rgba<u8>) -> u8 {
42 data[0] / 3 + data[1] / 3 + data[2] / 3 + (data[0] % 3 + data[1] % 3 + data[2] % 3) / 3
43}
44
45fn pixel_luma(Rgba { data, .. }: &Rgba<u8>) -> u8 {
47 ((data[0] as u16 * 2 + data[1] as u16 + data[2] as u16 * 4) >> 3) as u8
48}
49
50#[allow(non_camel_case_types)]
52#[derive(EnumIter, EnumString, IntoStaticStr)]
53#[strum(serialize_all = "snake_case")]
54pub enum Heuristic {
55 Luma,
56 Brightness,
57 Max,
58 Min,
59 Chroma,
60 Hue,
61 Saturation,
62 Value,
63 Red,
64 Blue,
65 Green,
66 #[doc(hidden)]
67 __Nonexhaustive,
68}
69
70impl Heuristic {
71 pub(crate) fn variants() -> Vec<&'static str> {
72 Self::iter()
73 .filter_map(|v| {
74 if let Heuristic::__Nonexhaustive = v {
75 None
76 } else {
77 Some(v)
78 }
79 })
80 .map(Into::into)
81 .collect()
82 }
83
84 pub fn func(&self) -> Box<Fn(&Rgba<u8>) -> u8> {
86 match self {
87 Heuristic::Red => Box::new(|Rgba { data, .. }| data[0]),
88 Heuristic::Green => Box::new(|Rgba { data, .. }| data[1]),
89 Heuristic::Blue => Box::new(|Rgba { data, .. }| data[2]),
90 Heuristic::Max => Box::new(pixel_max),
91 Heuristic::Min => Box::new(pixel_min),
92 Heuristic::Chroma => Box::new(pixel_chroma),
93 Heuristic::Hue => Box::new(pixel_hue),
94 Heuristic::Saturation => Box::new(pixel_saturation),
95 Heuristic::Value => Box::new(pixel_max),
96 Heuristic::Brightness => Box::new(pixel_brightness),
97 Heuristic::Luma => Box::new(pixel_luma),
98 Heuristic::__Nonexhaustive => unreachable!(),
99 }
100 }
101}