Skip to main content

hdim_render/
pixel.rs

1//! Pixel manipulation utilities.
2//!
3//! Provides functions for calculating average colors over image regions, essential for
4//! downscaling images to fit the terminal grid.
5
6use image::{DynamicImage, GenericImageView};
7
8/// Calculates the average RGB color for a specific rectangular area of the image.
9///
10/// This function iterates over the pixels within the specified bounds and computes
11/// the arithmetic mean of the Red, Green, and Blue channels.
12///
13/// # Arguments
14///
15/// * `image` - A reference to the input [DynamicImage].
16/// * `start_x` - The top-left X coordinate of the area.
17/// * `start_y` - The top-left Y coordinate of the area.
18/// * `width` - The width of the area to sample.
19/// * `height` - The height of the area to sample.
20///
21/// # Returns
22///
23/// An array `[r, g, b]` representing the average color. Returns `[0, 0, 0]` (black)
24/// if the area contains no pixels or is out of bounds.
25pub fn get_average_rgb(
26    image: &DynamicImage,
27    start_x: u32,
28    start_y: u32,
29    width: u32,
30    height: u32,
31) -> [u8; 3] {
32    let (image_width, image_height) = image.dimensions();
33    let mut r_total: u64 = 0;
34    let mut g_total: u64 = 0;
35    let mut b_total: u64 = 0;
36    let mut count: u64 = 0;
37
38    // Define the boundaries of the area to iterate over, clamping to image dimensions
39    let end_y = (start_y + height).min(image_height);
40    let end_x = (start_x + width).min(image_width);
41
42    for pixel_y in start_y..end_y {
43        for pixel_x in start_x..end_x {
44            let pixel = image.get_pixel(pixel_x, pixel_y);
45            r_total += pixel[0] as u64;
46            g_total += pixel[1] as u64;
47            b_total += pixel[2] as u64;
48            count += 1;
49        }
50    }
51
52    if count == 0 {
53        return [0, 0, 0];
54    }
55
56    [
57        (r_total / count) as u8,
58        (g_total / count) as u8,
59        (b_total / count) as u8,
60    ]
61}