gardesk/garlock / c6c3d0f

Browse files

Add background processing with gaussian blur

- Convert screenshot to blurred background
- Configurable blur radius and brightness
- Fallback solid color support
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
c6c3d0fb03358ededb47b10bb984ae5f85f7df99
Parents
f745691
Tree
667eb5f

1 changed file

StatusFile+-
A garlock/src/background.rs 107 0
garlock/src/background.rsadded
@@ -0,0 +1,107 @@
1
+//! Background processing for garlock
2
+//!
3
+//! Applies blur and brightness adjustments to captured screenshots.
4
+
5
+use anyhow::{Context, Result};
6
+use image::{imageops, RgbaImage};
7
+
8
+use crate::screenshot::{rgba_to_bgra, Screenshot};
9
+
10
+/// Processed background ready for display
11
+pub struct Background {
12
+    /// Pixel data in BGRA format for X11
13
+    pub data: Vec<u8>,
14
+    /// Image width
15
+    pub width: u32,
16
+    /// Image height
17
+    pub height: u32,
18
+}
19
+
20
+impl Background {
21
+    /// Create a blurred background from a screenshot
22
+    pub fn from_screenshot(
23
+        screenshot: Screenshot,
24
+        blur_radius: f32,
25
+        brightness: f32,
26
+    ) -> Result<Self> {
27
+        tracing::debug!(
28
+            blur_radius,
29
+            brightness,
30
+            "Processing screenshot into background"
31
+        );
32
+
33
+        // Convert BGRA to RGBA for image crate
34
+        let rgba_data = screenshot.to_rgba();
35
+
36
+        // Create image from raw data
37
+        let img = RgbaImage::from_raw(screenshot.width, screenshot.height, rgba_data)
38
+            .context("Failed to create image from screenshot data")?;
39
+
40
+        // Apply gaussian blur
41
+        tracing::debug!("Applying blur (radius={})", blur_radius);
42
+        let blurred = imageops::blur(&img, blur_radius);
43
+
44
+        // Apply brightness adjustment
45
+        tracing::debug!("Applying brightness adjustment (factor={})", brightness);
46
+        let adjusted = adjust_brightness(&blurred, brightness);
47
+
48
+        // Convert back to BGRA for X11
49
+        let mut bgra_data = adjusted.into_raw();
50
+        rgba_to_bgra(&mut bgra_data);
51
+
52
+        Ok(Self {
53
+            data: bgra_data,
54
+            width: screenshot.width,
55
+            height: screenshot.height,
56
+        })
57
+    }
58
+
59
+    /// Create a solid color fallback background
60
+    pub fn solid_color(width: u32, height: u32, hex_color: &str) -> Self {
61
+        let (r, g, b) = parse_hex_color(hex_color).unwrap_or((26, 26, 46)); // #1a1a2e default
62
+
63
+        let pixel_count = (width * height) as usize;
64
+        let mut data = Vec::with_capacity(pixel_count * 4);
65
+
66
+        for _ in 0..pixel_count {
67
+            // BGRA format
68
+            data.push(b);
69
+            data.push(g);
70
+            data.push(r);
71
+            data.push(255); // Alpha
72
+        }
73
+
74
+        tracing::debug!(width, height, r, g, b, "Created solid color background");
75
+
76
+        Self { data, width, height }
77
+    }
78
+}
79
+
80
+/// Adjust image brightness by a factor
81
+/// - 0.0-1.0: darken
82
+/// - 1.0: no change
83
+/// - >1.0: brighten
84
+fn adjust_brightness(img: &RgbaImage, factor: f32) -> RgbaImage {
85
+    let mut result = img.clone();
86
+    for pixel in result.pixels_mut() {
87
+        pixel[0] = (pixel[0] as f32 * factor).min(255.0) as u8;
88
+        pixel[1] = (pixel[1] as f32 * factor).min(255.0) as u8;
89
+        pixel[2] = (pixel[2] as f32 * factor).min(255.0) as u8;
90
+        // Alpha unchanged
91
+    }
92
+    result
93
+}
94
+
95
+/// Parse a hex color string like "#1a1a2e" or "1a1a2e"
96
+fn parse_hex_color(hex: &str) -> Option<(u8, u8, u8)> {
97
+    let hex = hex.trim_start_matches('#');
98
+    if hex.len() != 6 {
99
+        return None;
100
+    }
101
+
102
+    let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
103
+    let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
104
+    let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
105
+
106
+    Some((r, g, b))
107
+}