llm_browser_testkit/
vision.rs1use base64::engine::general_purpose::STANDARD;
16use base64::Engine as _;
17use headless_chrome::Tab;
18
19pub const DEFAULT_MAX_DIMENSION: u32 = 1400;
21const JPEG_QUALITY: u8 = 85;
23
24pub fn capture_screenshot_data_url(tab: &Tab, max_dimension: u32) -> Result<String, String> {
35 let png = tab
36 .capture_screenshot(
37 headless_chrome::protocol::cdp::Page::CaptureScreenshotFormatOption::Png,
38 None,
39 None,
40 true,
41 )
42 .map_err(|e| format!("screenshot capture failed: {e}"))?;
43
44 let img =
45 image::load_from_memory(&png).map_err(|e| format!("screenshot decode failed: {e}"))?;
46
47 let (width, height) = (img.width(), img.height());
48 let longest = width.max(height);
49 let resized = if max_dimension > 0 && longest > max_dimension {
50 let scale = f64::from(max_dimension) / f64::from(longest);
51 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
52 let new_width = (f64::from(width) * scale).round().max(1.0) as u32;
53 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
54 let new_height = (f64::from(height) * scale).round().max(1.0) as u32;
55 img.resize(new_width, new_height, image::imageops::FilterType::Lanczos3)
56 } else {
57 img
58 };
59
60 let mut jpeg = Vec::new();
61 {
62 let mut encoder =
63 image::codecs::jpeg::JpegEncoder::new_with_quality(&mut jpeg, JPEG_QUALITY);
64 encoder
65 .encode_image(&resized)
66 .map_err(|e| format!("screenshot encode failed: {e}"))?;
67 }
68
69 Ok(format!("data:image/jpeg;base64,{}", STANDARD.encode(jpeg)))
70}
71
72#[cfg(test)]
73mod tests {
74 use super::DEFAULT_MAX_DIMENSION;
75
76 #[test]
77 fn default_max_dimension_sane() {
78 const {
81 assert!(DEFAULT_MAX_DIMENSION >= 1280 && DEFAULT_MAX_DIMENSION <= 1600);
82 }
83 }
84}