Skip to main content

teksilo_core/window/
icon.rs

1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Window icon.
5//!
6//! A raw RGBA8 bitmap plus dimensions. The app-level window manager
7//! converts this into the winit `Icon` type at window-creation time.
8
9/// Window icon as raw RGBA8 bytes (4 bytes per pixel, row-major,
10/// top-left origin).
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct WindowIcon {
13    pub rgba: Vec<u8>,
14    pub width: u32,
15    pub height: u32,
16}
17
18impl WindowIcon {
19    /// Construct an icon from a row-major RGBA8 buffer.
20    ///
21    /// The buffer must contain exactly `width * height * 4` bytes. The
22    /// app-level manager validates this when converting to the
23    /// platform icon and logs + drops the icon on mismatch — the
24    /// window still opens, just without a custom icon.
25    pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Self {
26        Self {
27            rgba,
28            width,
29            height,
30        }
31    }
32
33    /// Expected buffer size in bytes for `width × height` RGBA8.
34    pub fn expected_len(&self) -> usize {
35        (self.width as usize) * (self.height as usize) * 4
36    }
37
38    /// `true` when `rgba.len()` matches `width × height × 4`.
39    pub fn is_valid(&self) -> bool {
40        self.rgba.len() == self.expected_len()
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn valid_icon_passes_check() {
50        let icon = WindowIcon::from_rgba(vec![0; 16 * 16 * 4], 16, 16);
51        assert!(icon.is_valid());
52    }
53
54    #[test]
55    fn mismatched_len_is_invalid() {
56        let icon = WindowIcon::from_rgba(vec![0; 100], 16, 16);
57        assert!(!icon.is_valid());
58    }
59}