ass_renderer/debug/analyzer/
report.rs1use crate::Frame;
2
3#[cfg(feature = "nostd")]
4use alloc::vec::Vec;
5
6use super::TextArea;
7
8#[derive(Debug, Clone)]
10pub struct AnalysisReport {
11 pub width: u32,
13 pub height: u32,
15 pub total_pixels: usize,
17 pub non_transparent_pixels: usize,
19 pub transparency_percentage: f32,
21 pub pixel_histogram: PixelHistogram,
23 pub regions: Vec<Region>,
25 pub text_areas: Vec<TextArea>,
27 pub dominant_color: [u8; 4],
29 pub contrast_ratio: f32,
31}
32
33impl AnalysisReport {
34 pub(super) fn new(width: u32, height: u32) -> Self {
35 Self {
36 width,
37 height,
38 total_pixels: (width * height) as usize,
39 non_transparent_pixels: 0,
40 transparency_percentage: 0.0,
41 pixel_histogram: PixelHistogram::default(),
42 regions: Vec::new(),
43 text_areas: Vec::new(),
44 dominant_color: [0, 0, 0, 0],
45 contrast_ratio: 0.0,
46 }
47 }
48
49 pub(super) fn calculate_statistics(&mut self, frame: &Frame) {
50 self.non_transparent_pixels = self.pixel_histogram.non_transparent_count;
51 self.transparency_percentage = ((self.total_pixels - self.non_transparent_pixels) as f32
52 / self.total_pixels as f32)
53 * 100.0;
54
55 self.dominant_color = self.calculate_dominant_color();
57
58 self.contrast_ratio = self.calculate_contrast_ratio(frame);
60 }
61
62 fn calculate_dominant_color(&self) -> [u8; 4] {
63 let hist = &self.pixel_histogram;
64
65 let mut dominant = [0u8; 4];
67
68 for (i, channel) in [&hist.red, &hist.green, &hist.blue, &hist.alpha]
69 .iter()
70 .enumerate()
71 {
72 let mut max_count = 0;
73 let mut max_value = 0;
74
75 for (value, &count) in channel.iter().enumerate() {
76 if count > max_count {
77 max_count = count;
78 max_value = value;
79 }
80 }
81
82 dominant[i] = max_value as u8;
83 }
84
85 dominant
86 }
87
88 fn calculate_contrast_ratio(&self, _frame: &Frame) -> f32 {
89 if self.pixel_histogram.white_pixels > 0 && self.pixel_histogram.black_pixels > 0 {
92 let white_ratio =
93 self.pixel_histogram.white_pixels as f32 / self.non_transparent_pixels as f32;
94 let black_ratio =
95 self.pixel_histogram.black_pixels as f32 / self.non_transparent_pixels as f32;
96
97 (white_ratio * black_ratio * 100.0).min(1.0)
99 } else {
100 0.0
101 }
102 }
103
104 pub fn print_summary(&self) {
105 println!("\n╔════════════════════════════════════════╗");
106 println!("║ Frame Analysis Report ║");
107 println!("╚════════════════════════════════════════╝");
108
109 println!("\n📊 Basic Statistics:");
110 println!(
111 " • Resolution: {width}x{height}",
112 width = self.width,
113 height = self.height
114 );
115 println!(
116 " • Total Pixels: {total_pixels}",
117 total_pixels = self.total_pixels
118 );
119 println!(
120 " • Non-transparent: {} ({:.1}%)",
121 self.non_transparent_pixels,
122 (self.non_transparent_pixels as f32 / self.total_pixels as f32) * 100.0
123 );
124 println!(
125 " • Transparency: {transparency:.1}%",
126 transparency = self.transparency_percentage
127 );
128
129 println!("\n🎨 Color Distribution:");
130 println!(
131 " • White pixels: {white_pixels}",
132 white_pixels = self.pixel_histogram.white_pixels
133 );
134 println!(
135 " • Black pixels: {black_pixels}",
136 black_pixels = self.pixel_histogram.black_pixels
137 );
138 println!(
139 " • Gray pixels: {gray_pixels}",
140 gray_pixels = self.pixel_histogram.gray_pixels
141 );
142 println!(
143 " • Colored pixels: {colored_pixels}",
144 colored_pixels = self.pixel_histogram.colored_pixels
145 );
146 println!(
147 " • Dominant color: RGBA({r}, {g}, {b}, {a})",
148 r = self.dominant_color[0],
149 g = self.dominant_color[1],
150 b = self.dominant_color[2],
151 a = self.dominant_color[3]
152 );
153
154 println!("\n🔍 Region Detection:");
155 println!(
156 " • Detected regions: {regions_count}",
157 regions_count = self.regions.len()
158 );
159 for (i, region) in self.regions.iter().enumerate() {
160 println!(
161 " {}. Box: ({}, {}) to ({}, {}) | Pixels: {} | Color: RGBA({}, {}, {}, {})",
162 i + 1,
163 region.min_x,
164 region.min_y,
165 region.max_x,
166 region.max_y,
167 region.pixel_count,
168 region.avg_color[0],
169 region.avg_color[1],
170 region.avg_color[2],
171 region.avg_color[3]
172 );
173 }
174
175 println!("\n📝 Text Detection:");
176 if self.text_areas.is_empty() {
177 println!(" • No text areas detected");
178 } else {
179 println!(
180 " • Detected text areas: {text_areas_count}",
181 text_areas_count = self.text_areas.len()
182 );
183 for (i, area) in self.text_areas.iter().enumerate() {
184 println!(
185 " {}. Position: ({}, {}) | Size: {}x{} | Font: ~{}px | Confidence: {:.1}%",
186 i + 1,
187 area.x,
188 area.y,
189 area.width,
190 area.height,
191 area.estimated_font_size,
192 area.confidence * 100.0
193 );
194 }
195 }
196
197 println!("\n⚡ Performance Indicators:");
198 println!(
199 " • Contrast ratio: {contrast_ratio:.2}",
200 contrast_ratio = self.contrast_ratio
201 );
202 println!(
203 " • Memory usage: {memory_kb} KB",
204 memory_kb = (self.total_pixels * 4) / 1024
205 );
206
207 println!("\n═══════════════════════════════════════");
208 }
209
210 pub fn to_json(&self) -> String {
211 format!(
212 r#"{{
213 "resolution": {{ "width": {}, "height": {} }},
214 "pixels": {{
215 "total": {},
216 "non_transparent": {},
217 "transparency_percentage": {:.2}
218 }},
219 "color_distribution": {{
220 "white": {},
221 "black": {},
222 "gray": {},
223 "colored": {}
224 }},
225 "regions": {},
226 "text_areas": {},
227 "dominant_color": [{}, {}, {}, {}],
228 "contrast_ratio": {:.2}
229}}"#,
230 self.width,
231 self.height,
232 self.total_pixels,
233 self.non_transparent_pixels,
234 self.transparency_percentage,
235 self.pixel_histogram.white_pixels,
236 self.pixel_histogram.black_pixels,
237 self.pixel_histogram.gray_pixels,
238 self.pixel_histogram.colored_pixels,
239 self.regions.len(),
240 self.text_areas.len(),
241 self.dominant_color[0],
242 self.dominant_color[1],
243 self.dominant_color[2],
244 self.dominant_color[3],
245 self.contrast_ratio
246 )
247 }
248}
249
250#[derive(Debug, Clone)]
251pub struct PixelHistogram {
252 pub red: [usize; 256],
253 pub green: [usize; 256],
254 pub blue: [usize; 256],
255 pub alpha: [usize; 256],
256 pub non_transparent_count: usize,
257 pub white_pixels: usize,
258 pub black_pixels: usize,
259 pub gray_pixels: usize,
260 pub colored_pixels: usize,
261}
262
263impl Default for PixelHistogram {
264 fn default() -> Self {
265 Self {
266 red: [0; 256],
267 green: [0; 256],
268 blue: [0; 256],
269 alpha: [0; 256],
270 non_transparent_count: 0,
271 white_pixels: 0,
272 black_pixels: 0,
273 gray_pixels: 0,
274 colored_pixels: 0,
275 }
276 }
277}
278
279#[derive(Debug, Clone)]
280pub struct Region {
281 pub min_x: u32,
282 pub min_y: u32,
283 pub max_x: u32,
284 pub max_y: u32,
285 pub pixel_count: usize,
286 pub avg_color: [u8; 4],
287}