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}