1use image::{RgbImage, Rgb};
2
3fn median_cut_sort_bucket(pixel_bucket: &mut [Rgb<u8>]) {
5 let mut r_minmax = (u8::MAX, 0);
6 let mut g_minmax = (u8::MAX, 0);
7 let mut b_minmax = (u8::MAX, 0);
8
9 for pixel in pixel_bucket.iter() {
10 let r_val = pixel.0[0];
11 let g_val = pixel.0[1];
12 let b_val = pixel.0[2];
13
14 r_minmax.0 = r_minmax.0.min(r_val);
15 r_minmax.1 = r_minmax.1.max(r_val);
16
17 g_minmax.0 = g_minmax.0.min(g_val);
18 g_minmax.1 = g_minmax.1.max(g_val);
19
20 b_minmax.0 = b_minmax.0.min(b_val);
21 b_minmax.1 = b_minmax.1.max(b_val);
22 }
23
24
25 let ranges = [
26 r_minmax.1 - r_minmax.0,
27 g_minmax.1 - g_minmax.0,
28 b_minmax.1 - b_minmax.0
29 ];
30
31 let largest_range_channel = ranges.iter()
32 .enumerate()
33 .max_by_key(|x| x.1)
34 .expect("Guaranteed to be a max")
35 .0;
36
37 pixel_bucket.sort_by_key(|x| x.0[largest_range_channel]);
38}
39
40fn average_pixels(pixels: &[Rgb<u8>]) -> Rgb<u8> {
41 let pixels_size = pixels.len();
42
43 let mut total_pixel_values = (0, 0, 0);
44 for pixel in pixels {
45 total_pixel_values.0 += pixel.0[0] as usize;
46 total_pixel_values.1 += pixel.0[1] as usize;
47 total_pixel_values.2 += pixel.0[2] as usize;
48 }
49
50 let pixel_values_average = [
51 (total_pixel_values.0/pixels_size) as u8,
52 (total_pixel_values.1/pixels_size) as u8,
53 (total_pixel_values.2/pixels_size) as u8
54 ];
55
56 Rgb::from(pixel_values_average)
57}
58
59
60fn bucket_from_image_mut(image: &mut [u8], bucket_size: usize, bucket_index: usize) -> &mut [Rgb<u8>] {
61 let slice_start = bucket_size*bucket_index*3;
62 let slice_end = bucket_size*(bucket_index+1)*3;
63 let bucket = image.get_mut(slice_start..slice_end).expect("Guaranteed to be in bounds");
64
65 unsafe { std::slice::from_raw_parts_mut(bucket.as_mut_ptr().cast(), bucket.len()/3) }
71}
72
73pub fn median_cut_palette(rgb_image: &mut RgbImage, palette_n: u8) -> Vec<Rgb<u8>> {
77 let pixel_count = rgb_image.len() / 3;
78
79 let iterations = (palette_n as f32).log2().ceil() as u32 + 1;
80 let mut bucket_count = 1;
81 let mut bucket_size = pixel_count;
82 for i in 0..iterations {
83 bucket_count = 2_u32.pow(i) as usize;
84 bucket_size = pixel_count / bucket_count;
85
86 for bucket_index in 0..bucket_count {
87 let bucket_pixels = bucket_from_image_mut(rgb_image, bucket_size, bucket_index);
88 median_cut_sort_bucket(bucket_pixels);
89 }
90 }
91
92 debug_assert!(bucket_count >= palette_n as usize);
93 let mut colors = Vec::with_capacity(palette_n as usize);
94 for bucket_index in 0..palette_n as usize {
95 let bucket_pixels = bucket_from_image_mut(rgb_image, bucket_size, bucket_index);
96 colors.push(average_pixels(&bucket_pixels));
97 }
98
99 colors
100}
101
102fn calculate_saturation(pixel: Rgb<u8>) -> u8 {
103 let max = *pixel.0.iter().max().expect("Impossible as color array is not empty");
104 let min = *pixel.0.iter().min().expect("Impossible as color array is not empty");
105 (255.0 * (1.0 - min as f32/max as f32)) as u8
106}
107
108fn change_brightness(pixel: Rgb<u8>, target_brightness: u8) -> Rgb<u8> {
109 let max_brightness = *pixel.0.iter().max().expect("Impossible as color array is not empty");
110 let multiplier = target_brightness as f32 / max_brightness as f32;
111
112 let new_values = [
113 (pixel.0[0] as f32 * multiplier).min(255.0) as u8,
114 (pixel.0[1] as f32 * multiplier).min(255.0) as u8,
115 (pixel.0[2] as f32 * multiplier).min(255.0) as u8
116 ];
117
118 Rgb::from(new_values)
119}
120
121pub fn get_theme_color(palette: &[Rgb<u8>], brightness: Option<u8>) -> Option<Rgb<u8>> {
124 let theme = *palette.iter().max_by_key(|&&x| calculate_saturation(x))?;
125
126 Some(match brightness {
127 Some(b) => change_brightness(theme, b),
128 None => theme
129 })
130}