1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum PixelFormat {
6 Rgba8,
8
9 Bgra8,
11}
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum AlphaType {
16 Premultiplied,
18
19 Straight,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub struct PixelLayout {
26 pub size: [u32; 2],
28
29 pub row_bytes: u32,
31
32 pub format: PixelFormat,
34
35 pub alpha: AlphaType,
37}
38
39impl PixelLayout {
40 pub fn packed(size: [u32; 2], format: PixelFormat, alpha: AlphaType) -> Self {
42 Self {
43 size,
44 row_bytes: size[0].saturating_mul(4),
45 format,
46 alpha,
47 }
48 }
49
50 pub fn byte_len(self) -> Result<usize, ImageError> {
52 let width = self.size[0]
53 .checked_mul(4)
54 .ok_or(ImageError::InvalidLayout)?;
55 if self.size.contains(&0) || self.row_bytes < width {
56 return Err(ImageError::InvalidLayout);
57 }
58 usize::try_from(u64::from(self.row_bytes) * u64::from(self.size[1]))
59 .map_err(|_| ImageError::InvalidLayout)
60 }
61}
62
63pub struct PixelBuffer {
68 layout: PixelLayout,
69 pixels: Vec<u8>,
70}
71
72impl PixelBuffer {
73 pub fn new(layout: PixelLayout, pixels: Vec<u8>) -> Result<Self, ImageError> {
75 if pixels.len() != layout.byte_len()? {
76 return Err(ImageError::InvalidLayout);
77 }
78 Ok(Self { layout, pixels })
79 }
80
81 pub fn layout(&self) -> PixelLayout {
83 self.layout
84 }
85
86 pub fn bytes(&self) -> &[u8] {
88 &self.pixels
89 }
90
91 pub fn into_bytes(self) -> Vec<u8> {
93 self.pixels
94 }
95
96 pub(crate) fn into_premultiplied_rgba(self) -> Vec<u8> {
98 let Self { layout, mut pixels } = self;
99 let row = layout.size[0] as usize * 4;
100 for y in 0..layout.size[1] as usize {
101 let from = y * layout.row_bytes as usize;
102 pixels.copy_within(from..from + row, y * row);
103 normalize_row(&mut pixels[y * row..(y + 1) * row], layout);
104 }
105 pixels.truncate(row * layout.size[1] as usize);
106 pixels
107 }
108}
109
110fn normalize_row(row: &mut [u8], layout: PixelLayout) {
111 for pixel in row.as_chunks_mut::<4>().0 {
112 if layout.format == PixelFormat::Bgra8 {
113 pixel.swap(0, 2);
114 }
115 if layout.alpha == AlphaType::Straight {
116 premultiply(pixel);
117 }
118 }
119}
120
121fn premultiply(pixel: &mut [u8]) {
122 let alpha = u16::from(pixel[3]);
123 for channel in &mut pixel[..3] {
124 *channel = ((u16::from(*channel) * alpha + 127) / 255) as u8;
125 }
126}
127
128#[derive(Clone, Debug, PartialEq, Eq)]
130pub enum ImageError {
131 InvalidLayout,
133
134 TooLarge,
136
137 IncompatibleTexture,
139
140 WrongDevice,
142
143 UnsupportedBackend,
145}
146
147impl std::fmt::Display for ImageError {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 write!(f, "{self:?}")
150 }
151}
152
153impl std::error::Error for ImageError {}