devela/media/image/sixel/output/
pixel_format.rs
1#![allow(clippy::identity_op, reason = "symmetry")]
15
16use crate::{ConstDefault, SixelError, SixelResult};
17
18#[repr(u8)]
23#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
24pub enum PixelFormat {
25 RGB555 = 1,
27 RGB565 = 2,
29 #[default]
31 RGB888 = 3, BGR555 = 4,
34 BGR565 = 5,
36 BGR888 = 6,
38 ARGB8888 = 0x10,
40 RGBA8888 = 0x11,
42 ABGR8888 = 0x12,
44 BGRA8888 = 0x13,
46 G1 = (1 << 6),
48 G2 = (1 << 6) | 0x01,
50 G4 = (1 << 6) | 0x02,
52 G8 = (1 << 6) | 0x03,
54 AG88 = (1 << 6) | 0x13,
56 GA88 = (1 << 6) | 0x23,
58 PAL1 = (1 << 7),
60 PAL2 = (1 << 7) | 0x01,
62 PAL4 = (1 << 7) | 0x02,
64 PAL8 = (1 << 7) | 0x03,
66}
67#[rustfmt::skip]
68impl ConstDefault for PixelFormat { const DEFAULT: Self = Self::RGB888; }
69
70impl PixelFormat {
71 #[rustfmt::skip]
73 pub const fn bits_per_pixel(self) -> usize {
74 match self {
75 PixelFormat::RGB555
76 | PixelFormat::BGR555 => 15,
77 PixelFormat::RGB565
78 | PixelFormat::BGR565
79 | PixelFormat::AG88
80 | PixelFormat::GA88 => 16,
81 PixelFormat::RGB888
82 | PixelFormat::BGR888
83 | PixelFormat::G8
84 | PixelFormat::PAL8 => 24,
85 PixelFormat::ARGB8888
86 | PixelFormat::RGBA8888
87 | PixelFormat::ABGR8888
88 | PixelFormat::BGRA8888 => 32,
89 PixelFormat::G1 | PixelFormat::PAL1 => 1,
90 PixelFormat::G2 | PixelFormat::PAL2 => 2,
91 PixelFormat::G4 | PixelFormat::PAL4 => 4,
92 }
93 }
94
95 pub const fn bytes_per_pixel(self) -> usize {
97 match self {
98 PixelFormat::ARGB8888
99 | PixelFormat::RGBA8888
100 | PixelFormat::ABGR8888
101 | PixelFormat::BGRA8888 => 4,
102 PixelFormat::RGB888 | PixelFormat::BGR888 => 3,
103 PixelFormat::RGB555
104 | PixelFormat::RGB565
105 | PixelFormat::BGR555
106 | PixelFormat::BGR565
107 | PixelFormat::AG88
108 | PixelFormat::GA88 => 2,
109 PixelFormat::G1
110 | PixelFormat::G2
111 | PixelFormat::G4
112 | PixelFormat::G8
113 | PixelFormat::PAL1
114 | PixelFormat::PAL2
115 | PixelFormat::PAL4
116 | PixelFormat::PAL8 => 1,
117 }
118 }
119
120 pub const fn required_bytes(self, width: i32, height: i32) -> usize {
123 let total_bits = width as usize * height as usize * self.bits_per_pixel();
124 crate::Mem::bytes_from_bits(total_bits)
125 }
126
127 pub(crate) fn normalize(
129 self,
130 dst: &mut [u8],
131 src: &[u8],
132 width: i32,
133 height: i32,
134 ) -> SixelResult<PixelFormat> {
135 match self {
136 PixelFormat::G8 => {
137 expand_rgb(dst, src, width, height, self, 1);
138 Ok(PixelFormat::RGB888)
139 }
140
141 PixelFormat::RGB565
142 | PixelFormat::RGB555
143 | PixelFormat::BGR565
144 | PixelFormat::BGR555
145 | PixelFormat::GA88
146 | PixelFormat::AG88 => {
147 expand_rgb(dst, src, width, height, self, 2);
148 Ok(PixelFormat::RGB888)
149 }
150
151 PixelFormat::RGB888 | PixelFormat::BGR888 => {
152 expand_rgb(dst, src, width, height, self, 3);
153 Ok(PixelFormat::RGB888)
154 }
155
156 PixelFormat::RGBA8888
157 | PixelFormat::ARGB8888
158 | PixelFormat::BGRA8888
159 | PixelFormat::ABGR8888 => {
160 expand_rgb(dst, src, width, height, self, 4);
161 Ok(PixelFormat::RGB888)
162 }
163
164 PixelFormat::PAL1 | PixelFormat::PAL2 | PixelFormat::PAL4 => {
165 expand_palette(dst, src, width, height, self)?;
166 Ok(PixelFormat::PAL8)
167 }
168
169 PixelFormat::G1 | PixelFormat::G2 | PixelFormat::G4 => {
170 expand_palette(dst, src, width, height, self)?;
171 Ok(PixelFormat::G8)
172 }
173 PixelFormat::PAL8 => {
174 dst[..((width * height) as usize)]
175 .copy_from_slice(&src[..((width * height) as usize)]);
176 Ok(self)
177 }
178 }
179 }
180}
181
182#[allow(clippy::identity_op, reason = "symmetry")]
184fn get_rgb(data: &[u8], pixelformat: PixelFormat, depth: usize) -> (u8, u8, u8) {
185 let mut count = 0;
186 let mut pixels: u32 = 0;
187 while count < depth {
188 pixels = data[count] as u32 | (pixels << 8);
189 count += 1;
190 }
191 let (r, g, b) = match pixelformat {
201 PixelFormat::RGB555 => {
202 (((pixels >> 10) & 0x1f) << 3, ((pixels >> 5) & 0x1f) << 3, ((pixels >> 0) & 0x1f) << 3)
203 }
204 PixelFormat::RGB565 => {
205 (((pixels >> 11) & 0x1f) << 3, ((pixels >> 5) & 0x3f) << 2, ((pixels >> 0) & 0x1f) << 3)
206 }
207 PixelFormat::RGB888 => ((pixels >> 16) & 0xff, (pixels >> 8) & 0xff, (pixels >> 0) & 0xff),
208 PixelFormat::BGR555 => {
209 (((pixels >> 0) & 0x1f) << 3, ((pixels >> 5) & 0x1f) << 3, ((pixels >> 10) & 0x1f) << 3)
210 }
211 PixelFormat::BGR565 => {
212 (((pixels >> 0) & 0x1f) << 3, ((pixels >> 5) & 0x3f) << 2, ((pixels >> 11) & 0x1f) << 3)
213 }
214 PixelFormat::BGR888 => ((pixels >> 0) & 0xff, (pixels >> 8) & 0xff, (pixels >> 16) & 0xff),
215 PixelFormat::ARGB8888 => {
216 ((pixels >> 16) & 0xff, (pixels >> 8) & 0xff, (pixels >> 0) & 0xff)
217 }
218 PixelFormat::RGBA8888 => {
219 ((pixels >> 24) & 0xff, (pixels >> 16) & 0xff, (pixels >> 8) & 0xff)
220 }
221 PixelFormat::ABGR8888 => {
222 ((pixels >> 0) & 0xff, (pixels >> 8) & 0xff, (pixels >> 16) & 0xff)
223 }
224 PixelFormat::BGRA8888 => {
225 ((pixels >> 8) & 0xff, (pixels >> 16) & 0xff, (pixels >> 24) & 0xff)
226 }
227 PixelFormat::G8 | PixelFormat::AG88 => (pixels & 0xff, pixels & 0xff, pixels & 0xff),
228 PixelFormat::GA88 => ((pixels >> 8) & 0xff, (pixels >> 8) & 0xff, (pixels >> 8) & 0xff),
229 _ => (0, 0, 0),
230 };
231 (r as u8, g as u8, b as u8)
232}
233
234fn expand_rgb(
236 dst: &mut [u8],
237 src: &[u8],
238 width: i32,
239 height: i32,
240 pixelformat: PixelFormat,
241 depth: usize,
242) {
243 for y in 0..height {
244 for x in 0..width {
245 let src_offset = depth * (y * width + x) as usize;
246 let dst_offset: usize = 3 * (y * width + x) as usize;
247 let (r, g, b) = get_rgb(&src[src_offset..], pixelformat, depth);
248
249 dst[dst_offset + 0] = r;
250 dst[dst_offset + 1] = g;
251 dst[dst_offset + 2] = b;
252 }
253 }
254}
255
256fn expand_palette(
258 dst: &mut [u8],
259 src: &[u8],
260 width: i32,
261 height: i32,
262 pixelformat: PixelFormat,
263) -> SixelResult<()> {
264 let bpp = match pixelformat {
265 PixelFormat::PAL1 | PixelFormat::G1 => 1,
266
267 PixelFormat::PAL2 | PixelFormat::G2 => 2,
268
269 PixelFormat::PAL4 | PixelFormat::G4 => 4,
270
271 PixelFormat::PAL8 | PixelFormat::G8 => {
272 dst[..((width * height) as usize)].copy_from_slice(&src[..((width * height) as usize)]);
273 return Ok(());
274 }
275
276 _ => return Err(SixelError::BadArgument),
278 };
279 let mut dst_offset = 0;
280 let mut src_offset = 0;
281
282 let max_x = width * bpp / 8;
283 for _y in 0..height {
284 for _x in 0..max_x {
285 for i in 0..8 / bpp {
286 let shift = ((8 / bpp) - 1 - i) * (bpp & (1 << (bpp - 1)));
287 dst[dst_offset] = ((src[src_offset] as i32) >> shift) as u8;
288 dst_offset += 1;
289 }
290 src_offset += 1;
291 }
292
293 let x = width - max_x * 8 / bpp;
294 if x > 0 {
295 for i in 0..x {
296 dst[dst_offset] = src[src_offset] >> ((8 - (i + 1) * bpp) & ((1 << bpp) - 1));
297 dst_offset += 1;
298 }
299 src_offset += 1;
300 }
301 }
302 Ok(())
303}