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#[derive(Debug, Clone)]
12pub struct Icon(pub Arc<dyn IconProvider>);
13
14pub 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)]
28pub enum BadIcon {
30 ByteCountNotDivisibleBy4 { byte_count: usize },
33 DimensionsVsPixelCount { width: u32, height: u32, width_x_height: usize, pixel_count: usize },
36 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}