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    fn source(&self) -> Option<&(dyn Error + 'static)> {
54        Some(self)
55    }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub(crate) struct RgbaIcon {
60    pub(crate) rgba: Vec<u8>,
61    pub(crate) width: u32,
62    pub(crate) height: u32,
63}
64
65/// For platforms which don't have window icons (e.g. web)
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub(crate) struct NoIcon;
68
69#[allow(dead_code)] // These are not used on every platform
70mod constructors {
71    use super::*;
72
73    impl RgbaIcon {
74        pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
75            if rgba.len() % PIXEL_SIZE != 0 {
76                return Err(BadIcon::ByteCountNotDivisibleBy4 {
77                    byte_count: rgba.len(),
78                });
79            }
80            let pixel_count = rgba.len() / PIXEL_SIZE;
81            if pixel_count != (width * height) as usize {
82                Err(BadIcon::DimensionsVsPixelCount {
83                    width,
84                    height,
85                    width_x_height: (width * height) as usize,
86                    pixel_count,
87                })
88            } else {
89                Ok(RgbaIcon {
90                    rgba,
91                    width,
92                    height,
93                })
94            }
95        }
96    }
97
98    impl NoIcon {
99        pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
100            // Create the rgba icon anyway to validate the input
101            let _ = RgbaIcon::from_rgba(rgba, width, height)?;
102            Ok(NoIcon)
103        }
104    }
105}
106
107/// An icon used for the window titlebar, taskbar, etc.
108#[derive(Clone)]
109pub struct Icon {
110    pub(crate) inner: PlatformIcon,
111}
112
113impl fmt::Debug for Icon {
114    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
115        fmt::Debug::fmt(&self.inner, formatter)
116    }
117}
118
119impl Icon {
120    /// Creates an icon from 32bpp RGBA data.
121    ///
122    /// The length of `rgba` must be divisible by 4, and `width * height` must equal
123    /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
124    pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
125        Ok(Icon {
126            inner: PlatformIcon::from_rgba(rgba, width, height)?,
127        })
128    }
129}