floem_winit/
icon.rs

1use crate::platform_impl::PlatformIcon;
2use std::{error::Error, fmt, io, mem};
3
4#[repr(C)]
5#[derive(Debug)]
6pub(crate) struct Pixel {
7    pub(crate) r: u8,
8    pub(crate) g: u8,
9    pub(crate) b: u8,
10    pub(crate) a: u8,
11}
12
13pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
14
15#[derive(Debug)]
16/// An error produced when using [`Icon::from_rgba`] with invalid arguments.
17pub enum BadIcon {
18    /// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
19    /// safely interpreted as 32bpp RGBA pixels.
20    ByteCountNotDivisibleBy4 { byte_count: usize },
21    /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
22    /// At least one of your arguments is incorrect.
23    DimensionsVsPixelCount {
24        width: u32,
25        height: u32,
26        width_x_height: usize,
27        pixel_count: usize,
28    },
29    /// Produced when underlying OS functionality failed to create the icon
30    OsError(io::Error),
31}
32
33impl fmt::Display for BadIcon {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
37                "The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
38            ),
39            BadIcon::DimensionsVsPixelCount {
40                width,
41                height,
42                width_x_height,
43                pixel_count,
44            } => write!(f,
45                "The specified dimensions ({width:?}x{height:?}) don't match the number of pixels supplied by the `rgba` argument ({pixel_count:?}). For those dimensions, the expected pixel count is {width_x_height:?}.",
46            ),
47            BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {e:?}"),
48        }
49    }
50}
51
52impl Error for BadIcon {}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub(crate) struct RgbaIcon {
56    pub(crate) rgba: Vec<u8>,
57    pub(crate) width: u32,
58    pub(crate) height: u32,
59}
60
61/// For platforms which don't have window icons (e.g. web)
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub(crate) struct NoIcon;
64
65#[allow(dead_code)] // These are not used on every platform
66mod constructors {
67    use super::*;
68
69    impl RgbaIcon {
70        pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
71            if rgba.len() % PIXEL_SIZE != 0 {
72                return Err(BadIcon::ByteCountNotDivisibleBy4 {
73                    byte_count: rgba.len(),
74                });
75            }
76            let pixel_count = rgba.len() / PIXEL_SIZE;
77            if pixel_count != (width * height) as usize {
78                Err(BadIcon::DimensionsVsPixelCount {
79                    width,
80                    height,
81                    width_x_height: (width * height) as usize,
82                    pixel_count,
83                })
84            } else {
85                Ok(RgbaIcon {
86                    rgba,
87                    width,
88                    height,
89                })
90            }
91        }
92    }
93
94    impl NoIcon {
95        pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
96            // Create the rgba icon anyway to validate the input
97            let _ = RgbaIcon::from_rgba(rgba, width, height)?;
98            Ok(NoIcon)
99        }
100    }
101}
102
103/// An icon used for the window titlebar, taskbar, etc.
104#[derive(Clone)]
105pub struct Icon {
106    pub(crate) inner: PlatformIcon,
107}
108
109impl fmt::Debug for Icon {
110    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
111        fmt::Debug::fmt(&self.inner, formatter)
112    }
113}
114
115impl Icon {
116    /// Creates an icon from 32bpp RGBA data.
117    ///
118    /// The length of `rgba` must be divisible by 4, and `width * height` must equal
119    /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
120    pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
121        Ok(Icon {
122            inner: PlatformIcon::from_rgba(rgba, width, height)?,
123        })
124    }
125}