spectrusty_core/video/
pixel.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8//! Building blocks for rendering pixel surfaces.
9use core::slice::IterMut;
10
11/// A trait for providing a way for placing pixels into byte buffers.
12pub trait PixelBuffer<'a> {
13    /// Specifies the type used for pixels.
14    type Pixel: Copy;
15    /// Should return a new instance of `PixelBuffer` implementation from the mutable slice of bytes representing
16    /// a single line of pixels of the target buffer.
17    fn from_line(line_buffer: &'a mut [u8]) -> Self;
18    /// Puts the next `pixel` into the line buffer and increases an internal cursor position by a single pixel.
19    ///
20    /// If the internal buffer boundaries would be overflown, this method must not panic, but instead, it
21    /// should just return without writing anything to the underlying buffer.
22    fn put_pixel(&mut self, pixel: Self::Pixel);
23    /// Puts `count` number of `pixel` copies into the line buffer and increases an internal cursor position
24    /// accordingly.
25    ///
26    /// If the internal buffer boundaries would be overflown, this method must not panic, but instead, it
27    /// should just return without writing anything to the underlying buffer.
28    #[inline]
29    fn put_pixels(&mut self, pixel: Self::Pixel, count: usize) {
30        for _ in 0..count {
31            self.put_pixel(pixel);
32        }
33    }
34    /// Returns the stride of a single pixel in bytes.
35    #[inline]
36    fn pixel_stride() -> usize {
37        core::mem::size_of::<Self::Pixel>()
38    }
39}
40
41/// A trait used for obtaining pixel colors.
42pub trait Palette {
43    /// Specifies the type used for pixels.
44    type Pixel: Copy;
45    /// Should return one of ZX Spectrum pixel colors:
46    /// ```text
47    /// index color   index color
48    ///   0 - black     8 - bright black (same as black)
49    ///   1 - blue      9 - bright blue
50    ///   2 - red      10 - bright red
51    ///   3 - magenta  11 - bright magenta
52    ///   4 - green    12 - bright green
53    ///   5 - cyan     13 - bright cyan
54    ///   6 - yellow   14 - bright yellow
55    ///   7 - white    15 - bright white
56    /// ```
57    #[inline]
58    fn get_pixel(index: u8) -> Self::Pixel {
59        Self::get_pixel_grb8(index_to_grb(index))
60    }
61    /// Should return a grayscale pixel with the intensity of one of the ZX Spectrum colors: [0, 15].
62    ///
63    /// See [Palette::get_pixel] for color values.
64    fn get_pixel_gray(index: u8) -> Self::Pixel;
65    /// Should return one of [ULAplus](https://sinclair.wiki.zxnet.co.uk/wiki/ULAplus#GRB_palette_entries) colors.
66    fn get_pixel_grb8(g3r3b2: u8) -> Self::Pixel;
67    /// Should return a grayscale pixel (0 - black, 255 - full intensity white).
68    fn get_pixel_gray8(value: u8) -> Self::Pixel;
69}
70
71/// A [PixelBuffer] tool for placing pixels into byte buffers using 3 `u8` element arrays of color channels
72/// (3 bytes per pixel).
73pub struct PixelBufA24<'a> {
74    iter: IterMut<'a, [u8;3]>
75}
76
77/// A [PixelBuffer] tool for placing pixels into byte buffers using 4 `u8` element arrays of color channels
78/// (4 bytes per pixel).
79pub struct PixelBufA32<'a> {
80    iter: IterMut<'a, [u8;4]>
81}
82
83/// A [PixelBuffer] tool for placing pixels into byte buffers using `u32` packed color channels.
84pub struct PixelBufP32<'a> {
85    iter: IterMut<'a, u32>
86}
87
88/// A [PixelBuffer] tool for placing pixels into byte buffers using `u16` packed color channels.
89pub struct PixelBufP16<'a> {
90    iter: IterMut<'a, u16>
91}
92
93/// A [PixelBuffer] tool for placing pixels into byte buffers using `u8` packed color channels.
94pub struct PixelBufP8<'a> {
95    iter: IterMut<'a, u8>
96}
97
98/// A color ZX Spectrum [Palette] implementation to be used with [PixelBufA24].
99pub struct SpectrumPalRGB24;
100
101/// A color ZX Spectrum [Palette] implementation to be used with [PixelBufA32].
102pub struct SpectrumPalRGBA32;
103
104/// A color ZX Spectrum [Palette] implementation to be used with [PixelBufA32].
105pub struct SpectrumPalARGB32;
106
107/// A color ZX Spectrum [Palette] implementation to be used with [PixelBufP32].
108pub struct SpectrumPalA8R8G8B8;
109
110/// A color ZX Spectrum [Palette] implementation to be used with [PixelBufP32].
111pub struct SpectrumPalR8G8B8A8;
112
113/// A color ZX Spectrum [Palette] implementation to be used with [PixelBufP16].
114pub struct SpectrumPalR5G6B5;
115
116/// A color ZX Spectrum [Palette] implementation to be used with [PixelBufP8].
117pub struct SpectrumPalR3G3B2;
118
119/// A grayscale ZX Spectrum [Palette] implementation to be used with [PixelBufA24].
120pub struct GrayscalePalRGB24;
121
122/// A grayscale ZX Spectrum [Palette] implementation to be used with [PixelBufA32].
123pub struct GrayscalePalRGBA32;
124
125/// A grayscale ZX Spectrum [Palette] implementation to be used with [PixelBufA32].
126pub struct GrayscalePalARGB32;
127
128/// A grayscale ZX Spectrum [Palette] implementation to be used with [PixelBufP32].
129pub struct GrayscalePalA8R8G8B8;
130
131/// A grayscale ZX Spectrum [Palette] implementation to be used with [PixelBufP32].
132pub struct GrayscalePalR8G8B8A8;
133
134/// A grayscale ZX Spectrum [Palette] implementation to be used with [PixelBufP16].
135pub struct GrayscalePalR5G6B5;
136
137/// A grayscale ZX Spectrum [Palette] implementation to be used with [PixelBufP8].
138pub struct GrayscalePalR3G3B2;
139
140#[allow(clippy::unusual_byte_groupings)]
141#[inline]
142fn index_to_grb(index: u8) -> u8 {
143    #[allow(clippy::inconsistent_digit_grouping)]
144    match index & 15 {
145         0 => 0b000_000_00,
146         1 => 0b000_000_10,
147         2 => 0b000_101_00,
148         3 => 0b000_101_10,
149         4 => 0b101_000_00,
150         5 => 0b101_000_10,
151         6 => 0b101_101_00,
152         7 => 0b101_101_10,
153         8 => 0b000_000_00,
154         9 => 0b000_000_11,
155        10 => 0b000_111_00,
156        11 => 0b000_111_11,
157        12 => 0b111_000_00,
158        13 => 0b111_000_11,
159        14 => 0b111_111_00,
160        15 => 0b111_111_11,
161        _ => unsafe { core::hint::unreachable_unchecked() }
162    }
163}
164
165macro_rules! impl_pixel_buffer {
166    ($pixel_buf:ty, $pixel:ty) => {
167        impl<'a> PixelBuffer<'a> for $pixel_buf {
168            type Pixel = $pixel;
169
170            fn from_line(line_buffer: &'a mut [u8]) -> Self {
171                let (_, pixels, _) = unsafe { line_buffer.align_to_mut::<Self::Pixel>() };
172                let iter = pixels.iter_mut();
173                Self { iter }
174            }
175
176            #[inline]
177            fn put_pixel(&mut self, pixel: Self::Pixel) {
178                if let Some(dest) = self.iter.next() {
179                    *dest = pixel;
180                }
181            }
182
183            #[inline]
184            fn put_pixels(&mut self, pixel: Self::Pixel, count: usize) {
185                for dest in self.iter.by_ref().take(count) {
186                    *dest = pixel;
187                }
188            }
189        }
190    };
191}
192
193impl_pixel_buffer!(PixelBufA24<'a>, [u8;3]);
194impl_pixel_buffer!(PixelBufA32<'a>, [u8;4]);
195impl_pixel_buffer!(PixelBufP32<'a>, u32);
196impl_pixel_buffer!(PixelBufP16<'a>, u16);
197impl_pixel_buffer!(PixelBufP8<'a>,  u8);
198
199macro_rules! impl_palette {
200    ($palette:ty, $pixel:ty) => {
201        impl Palette for $palette {
202            type Pixel = $pixel;
203
204            #[inline(always)]
205            fn get_pixel_grb8(g3r3b2: u8) -> Self::Pixel {
206                Self::pixel_grb(g3r3b2)
207            }
208            #[inline(always)]
209            fn get_pixel_gray(index: u8) -> Self::Pixel {
210                Self::pixel_gray_grb(index_to_grb(index))
211            }
212            #[inline(always)]
213            fn get_pixel_gray8(value: u8) -> Self::Pixel {
214                Self::pixel_gray_intensity(value)
215            }
216        }        
217    };
218}
219
220macro_rules! impl_pixel_grb_const {
221    ($palette:ty, $pixel:ty, $grayscale:ident) => {
222        impl $palette {
223            #[inline(always)]
224            fn pixel_grb(g3r3b2: u8) -> $pixel {
225                Self::COLORS_GRB[g3r3b2 as usize]
226            }
227            #[inline(always)]
228            fn pixel_gray_grb(i: u8) -> $pixel {
229                Self::pixel_gray_intensity($grayscale[i as usize])
230            }
231        }
232    };
233}
234
235macro_rules! impl_pixel_grb_gray {
236    ($palette:ty, $pixel:ty, $grayscale:ident) => {
237        impl $palette {
238            #[inline(always)]
239            fn pixel_grb(g3r3b2: u8) -> $pixel {
240                Self::pixel_gray_grb(g3r3b2)
241            }
242            #[inline(always)]
243            fn pixel_gray_grb(i: u8) -> $pixel {
244                Self::pixel_gray_intensity($grayscale[i as usize])
245            }
246        }
247    };
248}
249
250const ALPHA_MAX: u8 = u8::max_value();
251
252const fn grb_2r(grb: u8) -> u8 {
253    ((grb >> 3) & 3) | (grb & 0b11100) | ((grb & 0b11100) << 3)
254}
255
256const fn grb_2g(grb: u8) -> u8 {
257    ((grb >> 6) & 3) | ((grb >> 3) & 0b11100) | (grb & 0b11100000)
258}
259
260const fn grb_2b(grb: u8) -> u8 {
261    (grb & 3) | ((grb << 3) & 0b11000) | (((grb << 2) | (grb << 1)) & 0b100) |
262    ((grb << 6) & 0b11000000) | (((grb << 5) | (grb << 4)) & 0b100000)
263}
264
265#[inline(always)]
266const fn pack_8888(a: u8, b: u8, c: u8, d: u8) -> u32 {
267    ((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32)
268}
269
270#[inline(always)]
271const fn pack_565(a: u8, b: u8, c: u8) -> u16 {
272    (((a as u16) >> 3) << 11) | (((b as u16) >> 2) << 5) | ((c as u16) >> 3)
273}
274
275#[inline(always)]
276const fn pack_332(a: u8, b: u8, c: u8) -> u8 {
277    ((a >> 5) << 5) | ((b >> 5) << 2) | (c >> 6)
278}
279
280const fn grayscale(r: u8, g: u8, b: u8) -> u8 {
281    ((13933 * r as u32 + 46871 * g as u32 + 4732 * b as u32) >> 16) as u8
282}
283
284macro_rules! impl_palette_plus_formats {
285    ([$($grb:expr),*]) => {
286        const GRAYSCALE: [u8; 256] = [$(grayscale(grb_2r($grb), grb_2g($grb), grb_2b($grb))),*];
287
288        impl SpectrumPalRGB24 {
289            const COLORS_GRB: [[u8;3]; 256] = [
290                $([grb_2r($grb), grb_2g($grb), grb_2b($grb)]),*
291            ];
292            #[inline(always)]
293            fn pixel_gray_intensity(v: u8) -> [u8;3] { [v, v, v] }
294        }
295        impl_pixel_grb_const!(SpectrumPalRGB24, [u8;3], GRAYSCALE);
296
297        impl SpectrumPalRGBA32 {
298            const COLORS_GRB: [[u8;4]; 256] = [
299                $([grb_2r($grb), grb_2g($grb), grb_2b($grb), ALPHA_MAX]),*
300            ];
301            #[inline(always)]
302            fn pixel_gray_intensity(v: u8) -> [u8;4] { [v, v, v, ALPHA_MAX] }
303        }
304        impl_pixel_grb_const!(SpectrumPalRGBA32, [u8;4], GRAYSCALE);
305
306        impl SpectrumPalARGB32 {
307            const COLORS_GRB: [[u8;4]; 256] = [
308                $([ALPHA_MAX, grb_2r($grb), grb_2g($grb), grb_2b($grb)]),*
309            ];
310            #[inline(always)]
311            fn pixel_gray_intensity(v: u8) -> [u8;4] { [ALPHA_MAX, v, v, v] }
312        }
313        impl_pixel_grb_const!(SpectrumPalARGB32, [u8;4], GRAYSCALE);
314
315        impl SpectrumPalA8R8G8B8 {
316            const COLORS_GRB: [u32; 256] = [
317                $(pack_8888(ALPHA_MAX, grb_2r($grb), grb_2g($grb), grb_2b($grb))),*
318            ];
319            #[inline(always)]
320            fn pixel_gray_intensity(v: u8) -> u32 { pack_8888(ALPHA_MAX, v, v, v) }
321        }
322        impl_pixel_grb_const!(SpectrumPalA8R8G8B8, u32, GRAYSCALE);
323
324        impl SpectrumPalR8G8B8A8 {
325            const COLORS_GRB: [u32; 256] = [
326                $(pack_8888(grb_2r($grb), grb_2g($grb), grb_2b($grb), ALPHA_MAX)),*
327            ];
328            #[inline(always)]
329            fn pixel_gray_intensity(v: u8) -> u32 { pack_8888(v, v, v, ALPHA_MAX) }
330        }
331        impl_pixel_grb_const!(SpectrumPalR8G8B8A8, u32, GRAYSCALE);
332
333        impl SpectrumPalR5G6B5 {
334            const COLORS_GRB: [u16; 256] = [
335                $(pack_565(grb_2r($grb), grb_2g($grb), grb_2b($grb))),*
336            ];
337            #[inline(always)]
338            fn pixel_gray_intensity(v: u8) -> u16 { pack_565(v, v, v) }
339        }
340        impl_pixel_grb_const!(SpectrumPalR5G6B5, u16, GRAYSCALE);
341
342        impl SpectrumPalR3G3B2 {
343            const COLORS_GRB: [u8; 256] = [
344                $(pack_332(grb_2r($grb), grb_2g($grb), grb_2b($grb))),*
345            ];
346            #[inline(always)]
347            fn pixel_gray_intensity(v: u8) -> u8 { pack_332(v, v, v) }
348        }
349        impl_pixel_grb_const!(SpectrumPalR3G3B2, u8, GRAYSCALE);
350
351        impl GrayscalePalRGB24 {
352            #[inline(always)]
353            fn pixel_gray_intensity(v: u8) -> [u8;3] { [v, v, v] }
354        }
355        impl_pixel_grb_gray!(GrayscalePalRGB24, [u8;3], GRAYSCALE);
356
357        impl GrayscalePalRGBA32 {
358            #[inline(always)]
359            fn pixel_gray_intensity(v: u8) -> [u8;4] { [v, v, v, ALPHA_MAX] }
360        }
361        impl_pixel_grb_gray!(GrayscalePalRGBA32, [u8;4], GRAYSCALE);
362
363        impl GrayscalePalARGB32 {
364            #[inline(always)]
365            fn pixel_gray_intensity(v: u8) -> [u8;4] { [ALPHA_MAX, v, v, v] }
366        }
367        impl_pixel_grb_gray!(GrayscalePalARGB32, [u8;4], GRAYSCALE);
368
369        impl GrayscalePalA8R8G8B8 {
370            #[inline(always)]
371            fn pixel_gray_intensity(v: u8) -> u32 { pack_8888(ALPHA_MAX, v, v, v) }
372        }
373        impl_pixel_grb_gray!(GrayscalePalA8R8G8B8, u32, GRAYSCALE);
374
375        impl GrayscalePalR8G8B8A8 {
376            #[inline(always)]
377            fn pixel_gray_intensity(v: u8) -> u32 { pack_8888(v, v, v, ALPHA_MAX) }
378        }
379        impl_pixel_grb_gray!(GrayscalePalR8G8B8A8, u32, GRAYSCALE);
380
381        impl GrayscalePalR5G6B5 {
382            #[inline(always)]
383            fn pixel_gray_intensity(v: u8) -> u16 { pack_565(v, v, v) }
384        }
385        impl_pixel_grb_gray!(GrayscalePalR5G6B5, u16, GRAYSCALE);
386
387        impl GrayscalePalR3G3B2 {
388            #[inline(always)]
389            fn pixel_gray_intensity(v: u8) -> u8 { pack_332(v, v, v) }
390        }
391        impl_pixel_grb_gray!(GrayscalePalR3G3B2, u8, GRAYSCALE);
392    };
393}
394
395// let colors = [
396//     (  0,  0,  0),
397//     ( 21, 21,201),
398//     (202, 33, 33),
399//     (203, 38,203),
400//     ( 44,203, 44),
401//     ( 47,204,204),
402//     (205,205, 53),
403//     (205,205,205),
404//     (  0,  0,  0),
405//     ( 27, 27,251),
406//     (252, 41, 41),
407//     (252, 47,252),
408//     ( 55,253, 55),
409//     ( 59,254,254),
410//     (255,255, 65),
411//     (255,255,255)
412// ];
413
414// constant eval iterators where are you...
415impl_palette_plus_formats!([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]);
416impl_palette!(SpectrumPalRGB24,     [u8;3]);
417impl_palette!(SpectrumPalRGBA32,    [u8;4]);
418impl_palette!(SpectrumPalARGB32,    [u8;4]);
419impl_palette!(SpectrumPalA8R8G8B8,  u32);
420impl_palette!(SpectrumPalR8G8B8A8,  u32);
421impl_palette!(SpectrumPalR5G6B5,    u16);
422impl_palette!(SpectrumPalR3G3B2,    u8);
423impl_palette!(GrayscalePalRGB24,    [u8;3]);
424impl_palette!(GrayscalePalRGBA32,   [u8;4]);
425impl_palette!(GrayscalePalARGB32,   [u8;4]);
426impl_palette!(GrayscalePalA8R8G8B8, u32);
427impl_palette!(GrayscalePalR8G8B8A8, u32);
428impl_palette!(GrayscalePalR5G6B5,   u16);
429impl_palette!(GrayscalePalR3G3B2,   u8);
430
431#[cfg(test)]
432mod tests {
433    use super::*;
434
435    #[test]
436    fn pixel_palette_works() {
437        for i in (0..=255).step_by(16) {
438            assert_eq!(SpectrumPalRGB24::get_pixel(i + 0),  [         0,          0,          0]);
439            assert_eq!(SpectrumPalRGB24::get_pixel(i + 1),  [         0,          0, 0b10110110]);
440            assert_eq!(SpectrumPalRGB24::get_pixel(i + 2),  [0b10110110,          0,          0]);
441            assert_eq!(SpectrumPalRGB24::get_pixel(i + 3),  [0b10110110,          0, 0b10110110]);
442            assert_eq!(SpectrumPalRGB24::get_pixel(i + 4),  [         0, 0b10110110,          0]);
443            assert_eq!(SpectrumPalRGB24::get_pixel(i + 5),  [         0, 0b10110110, 0b10110110]);
444            assert_eq!(SpectrumPalRGB24::get_pixel(i + 6),  [0b10110110, 0b10110110,          0]);
445            assert_eq!(SpectrumPalRGB24::get_pixel(i + 7),  [0b10110110, 0b10110110, 0b10110110]);
446            assert_eq!(SpectrumPalRGB24::get_pixel(i + 8),  [         0,          0,          0]);
447            assert_eq!(SpectrumPalRGB24::get_pixel(i + 9),  [         0,          0, 0b11111111]);
448            assert_eq!(SpectrumPalRGB24::get_pixel(i + 10), [0b11111111,          0,          0]);
449            assert_eq!(SpectrumPalRGB24::get_pixel(i + 11), [0b11111111,          0, 0b11111111]);
450            assert_eq!(SpectrumPalRGB24::get_pixel(i + 12), [         0, 0b11111111,          0]);
451            assert_eq!(SpectrumPalRGB24::get_pixel(i + 13), [         0, 0b11111111, 0b11111111]);
452            assert_eq!(SpectrumPalRGB24::get_pixel(i + 14), [0b11111111, 0b11111111,          0]);
453            assert_eq!(SpectrumPalRGB24::get_pixel(i + 15), [0b11111111, 0b11111111, 0b11111111]);
454
455            assert_eq!(GrayscalePalRGB24::get_pixel(i + 0),  [  0,  0,  0]);
456            assert_eq!(GrayscalePalRGB24::get_pixel(i + 1),  [ 13, 13, 13]);
457            assert_eq!(GrayscalePalRGB24::get_pixel(i + 2),  [ 38, 38, 38]);
458            assert_eq!(GrayscalePalRGB24::get_pixel(i + 3),  [ 51, 51, 51]);
459            assert_eq!(GrayscalePalRGB24::get_pixel(i + 4),  [130,130,130]);
460            assert_eq!(GrayscalePalRGB24::get_pixel(i + 5),  [143,143,143]);
461            assert_eq!(GrayscalePalRGB24::get_pixel(i + 6),  [168,168,168]);
462            assert_eq!(GrayscalePalRGB24::get_pixel(i + 7),  [182,182,182]);
463            assert_eq!(GrayscalePalRGB24::get_pixel(i + 8),  [  0,  0,  0]);
464            assert_eq!(GrayscalePalRGB24::get_pixel(i + 9),  [ 18, 18, 18]);
465            assert_eq!(GrayscalePalRGB24::get_pixel(i + 10), [ 54, 54, 54]);
466            assert_eq!(GrayscalePalRGB24::get_pixel(i + 11), [ 72, 72, 72]);
467            assert_eq!(GrayscalePalRGB24::get_pixel(i + 12), [182,182,182]);
468            assert_eq!(GrayscalePalRGB24::get_pixel(i + 13), [200,200,200]);
469            assert_eq!(GrayscalePalRGB24::get_pixel(i + 14), [236,236,236]);
470            assert_eq!(GrayscalePalRGB24::get_pixel(i + 15), [255,255,255]);
471        }
472    }
473
474    #[test]
475    fn pixel_palette_grb_works() {
476        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(0), [0, 0, 0]);
477        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(0b000_001_00), [0b00100100, 0, 0]);
478        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(0b001_010_00), [0b01001001, 0b00100100, 0]);
479        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(1), [0, 0, 0b01101101]);
480        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(0b000_101_01), [0b10110110, 0, 0b01101101]);
481        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(0b110_111_01), [0b11111111, 0b11011011, 0b01101101]);
482        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(2), [0, 0, 0b10110110]);
483        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(0b011_011_10), [0b01101101, 0b01101101, 0b10110110]);
484        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(3), [0, 0, 0b11111111]);
485        assert_eq!(SpectrumPalRGB24::get_pixel_grb8(255), [255, 255, 255]);
486        assert_eq!(SpectrumPalRGBA32::get_pixel_grb8(0), [0, 0, 0, 255]);
487        assert_eq!(SpectrumPalRGBA32::get_pixel_grb8(0b111_011_01), [0b01101101, 0b11111111, 0b01101101, 255]);
488        assert_eq!(SpectrumPalRGBA32::get_pixel_grb8(255), [255, 255, 255, 255]);
489        assert_eq!(SpectrumPalARGB32::get_pixel_grb8(0), [255, 0, 0, 0]);
490        assert_eq!(SpectrumPalARGB32::get_pixel_grb8(0b111_011_01), [255, 0b01101101, 0b11111111, 0b01101101]);
491        assert_eq!(SpectrumPalARGB32::get_pixel_grb8(255), [255, 255, 255, 255]);
492        assert_eq!(SpectrumPalR8G8B8A8::get_pixel_grb8(0), 255);
493        assert_eq!(SpectrumPalR8G8B8A8::get_pixel_grb8(0b111_011_01), 0b01101101_11111111_01101101_11111111);
494        assert_eq!(SpectrumPalR8G8B8A8::get_pixel_grb8(255), 0xffff_ffff);
495        assert_eq!(SpectrumPalA8R8G8B8::get_pixel_grb8(0), 0xff00_0000);
496        assert_eq!(SpectrumPalA8R8G8B8::get_pixel_grb8(0b111_011_01), 0b11111111_01101101_11111111_01101101);
497        assert_eq!(SpectrumPalA8R8G8B8::get_pixel_grb8(255), 0xffff_ffff);
498        assert_eq!(SpectrumPalR5G6B5::get_pixel_grb8(0), 0);
499        assert_eq!(SpectrumPalR5G6B5::get_pixel_grb8(0b111_011_01), 0b01101_111111_01101);
500        assert_eq!(SpectrumPalR5G6B5::get_pixel_grb8(255), 0xffff);
501        assert_eq!(SpectrumPalR3G3B2::get_pixel_grb8(0), 0);
502        assert_eq!(SpectrumPalR3G3B2::get_pixel_grb8(0b111_011_01), 0b011_111_01);
503        assert_eq!(SpectrumPalR3G3B2::get_pixel_grb8(255), 0xff);
504    }
505
506    #[test]
507    fn pixel_palette_gray_works() {
508        let grayscale_u16 = |v| (((v as u16) >> 3) << 11)|(((v as u16) >> 2) << 5)|((v as u16) >> 3);
509        let grayscale_u8 = |v| ((v >> 5) << 5)|((v >> 5) << 2)|(v >> 6);
510        for i in 0..=255u8 {
511            let v = GRAYSCALE[i as usize];
512            assert_eq!(SpectrumPalRGB24::get_pixel_gray8(i), [i, i, i]);
513            assert_eq!(GrayscalePalRGB24::get_pixel_gray8(i), [i, i, i]);
514            assert_eq!(GrayscalePalRGB24::get_pixel_grb8(i), [v, v, v]);
515            assert_eq!(SpectrumPalRGBA32::get_pixel_gray8(i), [i, i, i, 255]);
516            assert_eq!(GrayscalePalRGBA32::get_pixel_gray8(i), [i, i, i, 255]);
517            assert_eq!(GrayscalePalRGBA32::get_pixel_grb8(i), [v, v, v, 255]);
518            assert_eq!(SpectrumPalARGB32::get_pixel_gray8(i), [255, i, i, i]);
519            assert_eq!(GrayscalePalARGB32::get_pixel_gray8(i), [255, i, i, i]);
520            assert_eq!(GrayscalePalARGB32::get_pixel_grb8(i), [255, v, v, v]);
521            assert_eq!(SpectrumPalA8R8G8B8::get_pixel_gray8(i), u32::from_be_bytes([255, i, i, i]));
522            assert_eq!(GrayscalePalA8R8G8B8::get_pixel_gray8(i), u32::from_be_bytes([255, i, i, i]));
523            assert_eq!(GrayscalePalA8R8G8B8::get_pixel_grb8(i), u32::from_be_bytes([255, v, v, v]));
524            assert_eq!(SpectrumPalR8G8B8A8::get_pixel_gray8(i), u32::from_be_bytes([i, i, i, 255]));
525            assert_eq!(GrayscalePalR8G8B8A8::get_pixel_gray8(i), u32::from_be_bytes([i, i, i, 255]));
526            assert_eq!(GrayscalePalR8G8B8A8::get_pixel_grb8(i), u32::from_be_bytes([v, v, v, 255]));
527            assert_eq!(SpectrumPalR5G6B5::get_pixel_gray8(i), grayscale_u16(i));
528            assert_eq!(GrayscalePalR5G6B5::get_pixel_gray8(i), grayscale_u16(i));
529            assert_eq!(GrayscalePalR5G6B5::get_pixel_grb8(i), grayscale_u16(v));
530            assert_eq!(SpectrumPalR3G3B2::get_pixel_gray8(i), grayscale_u8(i));
531            assert_eq!(GrayscalePalR3G3B2::get_pixel_gray8(i), grayscale_u8(i));
532            assert_eq!(GrayscalePalR3G3B2::get_pixel_grb8(i), grayscale_u8(v));
533        }
534    }
535}