winit/
icon.rs

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