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: &usize } => 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: &u32,
41 height: &u32,
42 width_x_height: &usize,
43 pixel_count: &usize,
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: &Error) => 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, f: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}
126