winit_core/
icon.rs

1use std::error::Error;
2use std::ops::Deref;
3use std::sync::Arc;
4use std::{fmt, io, mem};
5
6use crate::as_any::AsAny;
7
8pub(crate) const PIXEL_SIZE: usize = mem::size_of::<u32>();
9
10/// An icon used for the window titlebar, taskbar, etc.
11#[derive(Debug, Clone)]
12pub struct Icon(pub Arc<dyn IconProvider>);
13
14// TODO remove that once split.
15pub trait IconProvider: AsAny + fmt::Debug + Send + Sync {}
16
17impl Deref for Icon {
18    type Target = dyn IconProvider;
19
20    fn deref(&self) -> &Self::Target {
21        self.0.as_ref()
22    }
23}
24
25impl_dyn_casting!(IconProvider);
26
27#[derive(Debug)]
28/// An error produced when using [`RgbaIcon::new`] with invalid arguments.
29pub enum BadIcon {
30    /// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
31    /// safely interpreted as 32bpp RGBA pixels.
32    ByteCountNotDivisibleBy4 { byte_count: usize },
33    /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
34    /// At least one of your arguments is incorrect.
35    DimensionsVsPixelCount { width: u32, height: u32, width_x_height: usize, pixel_count: usize },
36    /// Produced when underlying OS functionality failed to create the icon
37    OsError(io::Error),
38}
39
40impl fmt::Display for BadIcon {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(
44                f,
45                "The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making \
46                 it impossible to interpret as 32bpp RGBA pixels.",
47            ),
48            BadIcon::DimensionsVsPixelCount { width, height, width_x_height, pixel_count } => {
49                write!(
50                    f,
51                    "The specified dimensions ({width:?}x{height:?}) don't match the number of \
52                     pixels supplied by the `rgba` argument ({pixel_count:?}). For those \
53                     dimensions, the expected pixel count is {width_x_height:?}.",
54                )
55            },
56            BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {e:?}"),
57        }
58    }
59}
60
61impl Error for BadIcon {}
62
63#[derive(Debug, Clone, PartialEq, Eq, Hash)]
64pub struct RgbaIcon {
65    pub(crate) width: u32,
66    pub(crate) height: u32,
67    pub(crate) rgba: Vec<u8>,
68}
69
70impl RgbaIcon {
71    pub fn new(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
72        if rgba.len() % PIXEL_SIZE != 0 {
73            return Err(BadIcon::ByteCountNotDivisibleBy4 { byte_count: rgba.len() });
74        }
75        let pixel_count = rgba.len() / PIXEL_SIZE;
76        if pixel_count != (width * height) as usize {
77            Err(BadIcon::DimensionsVsPixelCount {
78                width,
79                height,
80                width_x_height: (width * height) as usize,
81                pixel_count,
82            })
83        } else {
84            Ok(RgbaIcon { rgba, width, height })
85        }
86    }
87
88    pub fn width(&self) -> u32 {
89        self.width
90    }
91
92    pub fn height(&self) -> u32 {
93        self.height
94    }
95
96    pub fn buffer(&self) -> &[u8] {
97        self.rgba.as_slice()
98    }
99}
100
101impl IconProvider for RgbaIcon {}
102
103impl From<RgbaIcon> for Icon {
104    fn from(value: RgbaIcon) -> Self {
105        Self(Arc::new(value))
106    }
107}