1use image::{Rgb, RgbImage};
2use std::path::Path;
3use crate::numeric::{i32_to_u32, u32_to_i32};
4
5#[derive(Debug, Clone, Copy)]
7pub struct LayoutRect {
8 pub x: i32,
9 pub y: i32,
10 pub width: u32,
11 pub height: u32,
12}
13
14#[derive(Debug)]
16pub enum LayoutElement {
17 Image {
18 rect: LayoutRect,
19 path: String,
20 },
21 RowLabel {
22 rect: LayoutRect,
23 text: String,
24 },
25 ColumnLabel {
26 rect: LayoutRect,
27 text: String,
28 },
29 Padding {
30 rect: LayoutRect,
31 description: String,
32 },
33}
34
35#[derive(Debug)]
37pub struct Layout {
38 pub elements: Vec<LayoutElement>,
39 pub total_width: u32,
40 pub total_height: u32,
41}
42
43impl Layout {
44 #[must_use] pub fn new(width: u32, height: u32) -> Self {
46 Self {
47 elements: Vec::new(),
48 total_width: width,
49 total_height: height,
50 }
51 }
52
53 pub fn add_element(&mut self, element: LayoutElement) {
55 self.elements.push(element);
56 }
57
58 #[must_use] pub fn render_debug(&self) -> RgbImage {
60 let mut canvas = RgbImage::new(self.total_width, self.total_height);
61
62 for pixel in canvas.pixels_mut() {
64 *pixel = Rgb([255, 255, 255]);
65 }
66
67 let image_color = Rgb([200, 200, 255]); let row_label_color = Rgb([255, 200, 200]); let col_label_color = Rgb([200, 255, 200]); let padding_color = Rgb([240, 240, 240]); for element in &self.elements {
75 let color = match element {
76 LayoutElement::Image { path, .. } => {
77 (image_color, format!("Image: {}", Path::new(path).file_name().unwrap_or_default().to_string_lossy()))
78 }
79 LayoutElement::RowLabel { text, .. } => {
80 (row_label_color, format!("Row: {text}"))
81 }
82 LayoutElement::ColumnLabel { text, .. } => {
83 (col_label_color, format!("Col: {text}"))
84 }
85 LayoutElement::Padding { description, .. } => {
86 (padding_color, format!("Pad: {description}"))
87 }
88 }.0;
89
90 let rect = match element {
91 LayoutElement::Image { rect, .. } |
92 LayoutElement::RowLabel { rect, .. } |
93 LayoutElement::ColumnLabel { rect, .. } |
94 LayoutElement::Padding { rect, .. } => rect,
95 };
96
97 let y_end = rect.y.saturating_add(u32_to_i32(rect.height));
99 let x_end = rect.x.saturating_add(u32_to_i32(rect.width));
100 for y in rect.y.max(0)..y_end {
101 for x in rect.x.max(0)..x_end {
102 if x >= 0 && y >= 0 &&
103 x < u32_to_i32(canvas.width()) &&
104 y < u32_to_i32(canvas.height()) {
105 canvas.put_pixel(i32_to_u32(x), i32_to_u32(y), color);
106 }
107 }
108 }
109
110 let border_color = Rgb([100, 100, 100]);
112 let x_end = rect.x.saturating_add(u32_to_i32(rect.width));
113 for x in rect.x.max(0)..x_end {
114 if x >= 0 && x < u32_to_i32(canvas.width()) {
115 if rect.y >= 0 && rect.y < u32_to_i32(canvas.height()) {
116 canvas.put_pixel(i32_to_u32(x), i32_to_u32(rect.y), border_color);
117 }
118 let bottom_y = rect.y.saturating_add(u32_to_i32(rect.height) - 1);
119 if bottom_y >= 0 && bottom_y < u32_to_i32(canvas.height()) {
120 canvas.put_pixel(i32_to_u32(x), i32_to_u32(bottom_y), border_color);
121 }
122 }
123 }
124
125 let y_end = rect.y.saturating_add(u32_to_i32(rect.height));
126 for y in rect.y.max(0)..y_end {
127 if y >= 0 && y < u32_to_i32(canvas.height()) {
128 if rect.x >= 0 && rect.x < u32_to_i32(canvas.width()) {
129 canvas.put_pixel(i32_to_u32(rect.x), i32_to_u32(y), border_color);
130 }
131 let right_x = rect.x.saturating_add(u32_to_i32(rect.width) - 1);
132 if right_x >= 0 && right_x < u32_to_i32(canvas.width()) {
133 canvas.put_pixel(i32_to_u32(right_x), i32_to_u32(y), border_color);
134 }
135 }
136 }
137 }
138
139 canvas
140 }
141}