mirror of
https://github.com/harivansh-afk/deskctl.git
synced 2026-04-18 03:00:39 +00:00
Phase 3: screenshot annotation with bounding boxes and @wN labels
- Add imageproc and ab_glyph dependencies - Annotation module drawing colored bounding boxes per window - White @wN labels on dark background at each window's top-left - 8-color palette cycling for distinct window identification - Back-to-front iteration so topmost labels are not occluded - Embedded DejaVu Sans Mono font via include_bytes - Wire --annotate flag into snapshot pipeline
This commit is contained in:
parent
79e6e0e25c
commit
0072a260b8
6 changed files with 1903 additions and 11 deletions
72
src/backend/annotate.rs
Normal file
72
src/backend/annotate.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use ab_glyph::{FontArc, PxScale};
|
||||
use image::{Rgba, RgbaImage};
|
||||
use imageproc::drawing::{draw_filled_rect_mut, draw_hollow_rect_mut, draw_text_mut, text_size};
|
||||
use imageproc::rect::Rect;
|
||||
|
||||
use crate::core::types::WindowInfo;
|
||||
|
||||
// Embedded font - DejaVu Sans Mono for guaranteed availability
|
||||
const FONT_BYTES: &[u8] = include_bytes!("../../assets/DejaVuSansMono.ttf");
|
||||
|
||||
const COLORS: &[Rgba<u8>] = &[
|
||||
Rgba([0, 255, 0, 255]), // green
|
||||
Rgba([255, 100, 0, 255]), // orange
|
||||
Rgba([0, 150, 255, 255]), // blue
|
||||
Rgba([255, 0, 255, 255]), // magenta
|
||||
Rgba([255, 255, 0, 255]), // yellow
|
||||
Rgba([0, 255, 255, 255]), // cyan
|
||||
Rgba([255, 50, 50, 255]), // red
|
||||
Rgba([150, 100, 255, 255]), // purple
|
||||
];
|
||||
|
||||
const LABEL_BG: Rgba<u8> = Rgba([0, 0, 0, 200]);
|
||||
const LABEL_FG: Rgba<u8> = Rgba([255, 255, 255, 255]);
|
||||
|
||||
pub fn annotate_screenshot(image: &mut RgbaImage, windows: &[WindowInfo]) {
|
||||
let font = match FontArc::try_from_slice(FONT_BYTES) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return, // Silently skip annotation if font fails to load
|
||||
};
|
||||
let scale = PxScale { x: 18.0, y: 18.0 };
|
||||
|
||||
// Iterate back-to-front so topmost window labels are drawn last (not occluded)
|
||||
for (i, win) in windows.iter().enumerate().rev() {
|
||||
if win.minimized {
|
||||
continue; // Don't annotate hidden windows
|
||||
}
|
||||
|
||||
let color = COLORS[i % COLORS.len()];
|
||||
let x = win.x.max(0);
|
||||
let y = win.y.max(0);
|
||||
let w = win.width;
|
||||
let h = win.height;
|
||||
|
||||
// Draw bounding box (2px thick by drawing at offset 0 and 1)
|
||||
if w > 0 && h > 0 {
|
||||
draw_hollow_rect_mut(image, Rect::at(x, y).of_size(w, h), color);
|
||||
if w > 2 && h > 2 {
|
||||
draw_hollow_rect_mut(
|
||||
image,
|
||||
Rect::at(x + 1, y + 1).of_size(w - 2, h - 2),
|
||||
color,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw label
|
||||
let label = format!("@{}", win.ref_id);
|
||||
let (tw, th) = text_size(scale, &font, &label);
|
||||
let label_x = x;
|
||||
let label_y = (y - th as i32 - 4).max(0);
|
||||
|
||||
// Label background
|
||||
draw_filled_rect_mut(
|
||||
image,
|
||||
Rect::at(label_x, label_y).of_size(tw + 6, th + 4),
|
||||
LABEL_BG,
|
||||
);
|
||||
|
||||
// Label text
|
||||
draw_text_mut(image, LABEL_FG, label_x + 3, label_y + 2, scale, &font, &label);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue