1pub const NES_PALETTE: [[u8; 3]; 64] = [
14 [0x6A, 0x6D, 0x6A],
15 [0x00, 0x13, 0x80],
16 [0x1E, 0x00, 0x8A],
17 [0x39, 0x00, 0x7A],
18 [0x55, 0x00, 0x56],
19 [0x5A, 0x00, 0x18],
20 [0x4F, 0x10, 0x00],
21 [0x3D, 0x1C, 0x00],
22 [0x25, 0x3A, 0x00],
23 [0x00, 0x4E, 0x00],
24 [0x00, 0x46, 0x00],
25 [0x00, 0x4A, 0x18],
26 [0x00, 0x40, 0x5A],
27 [0x00, 0x00, 0x00],
28 [0x00, 0x00, 0x00],
29 [0x00, 0x00, 0x00],
30 [0xB9, 0xBC, 0xB9],
31 [0x18, 0x4B, 0xCF],
32 [0x4B, 0x24, 0xE8],
33 [0x7C, 0x12, 0xE0],
34 [0xAB, 0x13, 0xB5],
35 [0xB7, 0x21, 0x64],
36 [0xAB, 0x37, 0x18],
37 [0x8B, 0x5A, 0x00],
38 [0x5C, 0x7A, 0x00],
39 [0x20, 0x90, 0x00],
40 [0x00, 0x8F, 0x00],
41 [0x00, 0x8C, 0x42],
42 [0x00, 0x7D, 0x8A],
43 [0x00, 0x00, 0x00],
44 [0x00, 0x00, 0x00],
45 [0x00, 0x00, 0x00],
46 [0xFF, 0xFF, 0xFF],
47 [0x64, 0xA0, 0xFF],
48 [0x84, 0x79, 0xFF],
49 [0xAC, 0x68, 0xFF],
50 [0xDA, 0x60, 0xFF],
51 [0xE2, 0x6B, 0xC5],
52 [0xDC, 0x83, 0x4C],
53 [0xC3, 0x9A, 0x18],
54 [0x9C, 0xB0, 0x00],
55 [0x60, 0xC0, 0x00],
56 [0x30, 0xC8, 0x3C],
57 [0x28, 0xC5, 0x8C],
58 [0x3C, 0xB7, 0xC9],
59 [0x4C, 0x4C, 0x4C],
60 [0x00, 0x00, 0x00],
61 [0x00, 0x00, 0x00],
62 [0xFF, 0xFF, 0xFF],
63 [0xC8, 0xDD, 0xFF],
64 [0xD5, 0xCC, 0xFF],
65 [0xE5, 0xC7, 0xFF],
66 [0xF5, 0xC5, 0xFF],
67 [0xFA, 0xC9, 0xE6],
68 [0xF8, 0xD2, 0xBD],
69 [0xEF, 0xDA, 0x99],
70 [0xE1, 0xE1, 0x88],
71 [0xC8, 0xE7, 0x88],
72 [0xB0, 0xEA, 0x9C],
73 [0xA4, 0xEB, 0xBD],
74 [0xAA, 0xE5, 0xE2],
75 [0xB0, 0xB0, 0xB0],
76 [0x00, 0x00, 0x00],
77 [0x00, 0x00, 0x00],
78];
79
80#[must_use]
82pub const fn nes_color_to_rgba(idx: u8) -> [u8; 4] {
83 let rgb = NES_PALETTE[(idx & 0x3F) as usize];
84 [rgb[0], rgb[1], rgb[2], 0xFF]
85}
86
87#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Default)]
100pub enum PpuPalette {
101 #[default]
103 Composite2C02,
104 Rgb2C03,
107 Rgb2C04_0001,
109 Rgb2C04_0002,
111 Rgb2C04_0003,
113 Rgb2C04_0004,
115 Rgb2C05,
118}
119
120impl PpuPalette {
121 const fn rgb_table(self) -> Option<&'static [[u8; 3]; 64]> {
124 match self {
125 Self::Composite2C02 => None,
126 Self::Rgb2C03 | Self::Rgb2C05 => Some(&RGB_2C03),
128 Self::Rgb2C04_0001 => Some(&RGB_2C04_0001),
129 Self::Rgb2C04_0002 => Some(&RGB_2C04_0002),
130 Self::Rgb2C04_0003 => Some(&RGB_2C04_0003),
131 Self::Rgb2C04_0004 => Some(&RGB_2C04_0004),
132 }
133 }
134
135 #[must_use]
137 pub const fn is_composite(self) -> bool {
138 matches!(self, Self::Composite2C02)
139 }
140}
141
142#[must_use]
156pub const fn palette_color_to_rgba(
157 palette: PpuPalette,
158 idx: u8,
159 emph_red: bool,
160 emph_green: bool,
161 emph_blue: bool,
162) -> [u8; 4] {
163 match palette.rgb_table() {
164 None => {
165 let rgba = nes_color_to_rgba(idx);
167 apply_emphasis(rgba, emph_red, emph_green, emph_blue)
168 }
169 Some(table) => {
170 let rgb = table[(idx & 0x3F) as usize];
171 let mut out = [rgb[0], rgb[1], rgb[2], 0xFF];
172 if emph_red {
174 out[0] = 0xFF;
175 }
176 if emph_green {
177 out[1] = 0xFF;
178 }
179 if emph_blue {
180 out[2] = 0xFF;
181 }
182 out
183 }
184 }
185}
186
187#[must_use]
195pub const fn build_rgba_lut(palette: PpuPalette) -> [[u8; 4]; 512] {
196 let mut lut = [[0u8; 4]; 512];
197 let mut e = 0usize;
198 while e < 8 {
199 let mut c = 0usize;
200 while c < 64 {
201 #[allow(clippy::cast_possible_truncation)] let idx = c as u8;
203 lut[(e << 6) | c] =
204 palette_color_to_rgba(palette, idx, e & 1 != 0, e & 2 != 0, e & 4 != 0);
205 c += 1;
206 }
207 e += 1;
208 }
209 lut
210}
211
212#[must_use]
224pub const fn build_rgba_lut_from_base(base: &[[u8; 3]; 64]) -> [[u8; 4]; 512] {
225 let mut lut = [[0u8; 4]; 512];
226 let mut e = 0usize;
227 while e < 8 {
228 let mut c = 0usize;
229 while c < 64 {
230 let rgb = base[c];
231 lut[(e << 6) | c] = apply_emphasis(
232 [rgb[0], rgb[1], rgb[2], 0xFF],
233 e & 1 != 0,
234 e & 2 != 0,
235 e & 4 != 0,
236 );
237 c += 1;
238 }
239 e += 1;
240 }
241 lut
242}
243
244#[must_use]
250pub const fn apply_emphasis(
251 mut rgba: [u8; 4],
252 emph_red: bool,
253 emph_green: bool,
254 emph_blue: bool,
255) -> [u8; 4] {
256 if emph_red {
257 rgba[1] = ((rgba[1] as u16 * 13) >> 4) as u8;
258 rgba[2] = ((rgba[2] as u16 * 13) >> 4) as u8;
259 }
260 if emph_green {
261 rgba[0] = ((rgba[0] as u16 * 13) >> 4) as u8;
262 rgba[2] = ((rgba[2] as u16 * 13) >> 4) as u8;
263 }
264 if emph_blue {
265 rgba[0] = ((rgba[0] as u16 * 13) >> 4) as u8;
266 rgba[1] = ((rgba[1] as u16 * 13) >> 4) as u8;
267 }
268 rgba
269}
270
271const RGB_2C03: [[u8; 3]; 64] = [
275 [0x6D, 0x6D, 0x6D], [0x00, 0x24, 0x91], [0x00, 0x00, 0xDA], [0x6D, 0x48, 0xDA], [0x91, 0x00, 0x6D], [0xB6, 0x00, 0x6D], [0xB6, 0x24, 0x00], [0x91, 0x48, 0x00], [0x6D, 0x48, 0x00], [0x24, 0x48, 0x00], [0x00, 0x6D, 0x24], [0x00, 0x91, 0x00], [0x00, 0x48, 0x48], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0xB6, 0xB6, 0xB6], [0x00, 0x6D, 0xDA], [0x00, 0x48, 0xFF], [0x91, 0x00, 0xFF], [0xB6, 0x00, 0xFF], [0xFF, 0x00, 0x91], [0xFF, 0x00, 0x00], [0xDA, 0x6D, 0x00], [0x91, 0x6D, 0x00], [0x24, 0x91, 0x00], [0x00, 0x91, 0x00], [0x00, 0xB6, 0x6D], [0x00, 0x91, 0x91], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0xFF, 0xFF, 0xFF], [0x6D, 0xB6, 0xFF], [0x91, 0x91, 0xFF], [0xDA, 0x6D, 0xFF], [0xFF, 0x00, 0xFF], [0xFF, 0x6D, 0xFF], [0xFF, 0x91, 0x00], [0xFF, 0xB6, 0x00], [0xDA, 0xDA, 0x00], [0x6D, 0xDA, 0x00], [0x00, 0xFF, 0x00], [0x48, 0xFF, 0xDA], [0x00, 0xFF, 0xFF], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0xFF, 0xFF, 0xFF], [0xB6, 0xDA, 0xFF], [0xDA, 0xB6, 0xFF], [0xFF, 0xB6, 0xFF], [0xFF, 0x91, 0xFF], [0xFF, 0xB6, 0xB6], [0xFF, 0xDA, 0x91], [0xFF, 0xFF, 0x48], [0xFF, 0xFF, 0x6D], [0xB6, 0xFF, 0x48], [0x91, 0xFF, 0x6D], [0x48, 0xFF, 0xDA], [0x91, 0xDA, 0xFF], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], ];
340
341const RGB_2C04_0001: [[u8; 3]; 64] = [
343 [0xFF, 0xB6, 0xB6], [0xDA, 0x6D, 0xFF], [0xFF, 0x00, 0x00], [0x91, 0x91, 0xFF], [0x00, 0x91, 0x91], [0x24, 0x48, 0x00], [0x48, 0x48, 0x48], [0xFF, 0x00, 0x91], [0xFF, 0xFF, 0xFF], [0x6D, 0x6D, 0x6D], [0xFF, 0xB6, 0x00], [0xB6, 0x00, 0x6D], [0x91, 0x00, 0x6D], [0xDA, 0xDA, 0x00], [0x6D, 0x48, 0x00], [0xFF, 0xFF, 0xFF], [0x6D, 0xB6, 0xFF], [0xDA, 0xB6, 0x6D], [0x6D, 0x24, 0x00], [0x6D, 0xDA, 0x00], [0x91, 0xDA, 0xFF], [0xDA, 0xB6, 0xFF], [0xFF, 0xDA, 0x91], [0x00, 0x48, 0xFF], [0xFF, 0xDA, 0x00], [0x48, 0xFF, 0xDA], [0x00, 0x00, 0x00], [0x48, 0x00, 0x00], [0xDA, 0xDA, 0xDA], [0x91, 0x91, 0x91], [0xFF, 0x00, 0xFF], [0x00, 0x24, 0x91], [0x00, 0x00, 0x6D], [0xB6, 0xDA, 0xFF], [0xFF, 0xB6, 0xFF], [0x00, 0xFF, 0x00], [0x00, 0xFF, 0xFF], [0x00, 0x48, 0x48], [0x00, 0xB6, 0x6D], [0xB6, 0x00, 0xFF], [0x00, 0x00, 0x00], [0x91, 0x48, 0x00], [0xFF, 0x91, 0xFF], [0xB6, 0x24, 0x00], [0x91, 0x00, 0xFF], [0x00, 0x00, 0xDA], [0xFF, 0x91, 0x00], [0x00, 0x00, 0x00], [0x00, 0x00, 0x00], [0x24, 0x91, 0x00], [0xB6, 0xB6, 0xB6], [0x00, 0x6D, 0x24], [0xB6, 0xFF, 0x48], [0x6D, 0x48, 0xDA], [0xFF, 0xFF, 0x00], [0xDA, 0x6D, 0x00], [0x00, 0x48, 0x00], [0x00, 0x6D, 0xDA], [0x00, 0x91, 0x00], [0x24, 0x24, 0x24], [0xFF, 0xFF, 0x6D], [0xFF, 0x6D, 0xFF], [0x91, 0x6D, 0x00], [0x91, 0xFF, 0x6D], ];
408
409const RGB_2C04_0002: [[u8; 3]; 64] = [
411 [0x00, 0x00, 0x00], [0xFF, 0xB6, 0x00], [0x91, 0x6D, 0x00], [0xB6, 0xFF, 0x48], [0x91, 0xFF, 0x6D], [0xFF, 0x6D, 0xFF], [0x00, 0x91, 0x91], [0xB6, 0xDA, 0xFF], [0xFF, 0x00, 0x00], [0x91, 0x00, 0xFF], [0xFF, 0xFF, 0x6D], [0xFF, 0x91, 0xFF], [0xFF, 0xFF, 0xFF], [0xDA, 0x6D, 0xFF], [0x91, 0xDA, 0xFF], [0x00, 0x91, 0x00], [0x00, 0x48, 0x00], [0x6D, 0xB6, 0xFF], [0xB6, 0x24, 0x00], [0xDA, 0xDA, 0xDA], [0x00, 0xB6, 0x6D], [0x6D, 0xDA, 0x00], [0x48, 0x00, 0x00], [0x91, 0x91, 0xFF], [0x48, 0x48, 0x48], [0xFF, 0x00, 0xFF], [0x00, 0x00, 0x6D], [0x48, 0xFF, 0xDA], [0xDA, 0xB6, 0xFF], [0x6D, 0x48, 0x00], [0x00, 0x00, 0x00], [0x6D, 0x48, 0xDA], [0x91, 0x00, 0x6D], [0xFF, 0xDA, 0x91], [0xFF, 0x91, 0x00], [0xFF, 0xB6, 0xFF], [0x00, 0x6D, 0xDA], [0x6D, 0x24, 0x00], [0xB6, 0xB6, 0xB6], [0x00, 0x00, 0xDA], [0xB6, 0x00, 0xFF], [0xFF, 0xDA, 0x00], [0x6D, 0x6D, 0x6D], [0x24, 0x48, 0x00], [0x00, 0x48, 0xFF], [0x00, 0x00, 0x00], [0xDA, 0xDA, 0x00], [0xFF, 0xFF, 0xFF], [0xDA, 0xB6, 0x6D], [0x24, 0x24, 0x24], [0x00, 0xFF, 0x00], [0xDA, 0x6D, 0x00], [0x00, 0x48, 0x48], [0x00, 0x24, 0x91], [0xFF, 0x00, 0x91], [0x24, 0x91, 0x00], [0x00, 0x00, 0x00], [0x00, 0xFF, 0xFF], [0x91, 0x48, 0x00], [0xFF, 0xFF, 0x00], [0xFF, 0xB6, 0xB6], [0xB6, 0x00, 0x6D], [0x00, 0x6D, 0x24], [0x91, 0x91, 0x91], ];
476
477const RGB_2C04_0003: [[u8; 3]; 64] = [
479 [0xB6, 0x00, 0xFF], [0xFF, 0x6D, 0xFF], [0x91, 0xFF, 0x6D], [0xB6, 0xB6, 0xB6], [0x00, 0x91, 0x00], [0xFF, 0xFF, 0xFF], [0xB6, 0xDA, 0xFF], [0x24, 0x48, 0x00], [0x00, 0x24, 0x91], [0x00, 0x00, 0x00], [0xFF, 0xDA, 0x91], [0x6D, 0x48, 0x00], [0xFF, 0x00, 0x91], [0xDA, 0xDA, 0xDA], [0xDA, 0xB6, 0x6D], [0x91, 0xDA, 0xFF], [0x91, 0x91, 0xFF], [0x00, 0x91, 0x91], [0xB6, 0x00, 0x6D], [0x00, 0x48, 0xFF], [0x24, 0x91, 0x00], [0x91, 0x6D, 0x00], [0xDA, 0x6D, 0x00], [0x00, 0xB6, 0x6D], [0x6D, 0x6D, 0x6D], [0x6D, 0x48, 0xDA], [0x00, 0x00, 0x00], [0x00, 0x00, 0xDA], [0xFF, 0x00, 0x00], [0xB6, 0x24, 0x00], [0xFF, 0x91, 0xFF], [0xFF, 0xB6, 0xB6], [0xDA, 0x6D, 0xFF], [0x00, 0x48, 0x00], [0x00, 0x00, 0x6D], [0xFF, 0xFF, 0x00], [0x24, 0x24, 0x24], [0xFF, 0xB6, 0x00], [0xFF, 0x91, 0x00], [0xFF, 0xFF, 0xFF], [0x6D, 0xDA, 0x00], [0x91, 0x00, 0x6D], [0x6D, 0xB6, 0xFF], [0xFF, 0x00, 0xFF], [0x00, 0x6D, 0xDA], [0x91, 0x91, 0x91], [0x00, 0x00, 0x00], [0x6D, 0x24, 0x00], [0x00, 0xFF, 0xFF], [0x48, 0x00, 0x00], [0xB6, 0xFF, 0x48], [0xFF, 0xB6, 0xFF], [0x91, 0x48, 0x00], [0x00, 0xFF, 0x00], [0xDA, 0xDA, 0x00], [0x48, 0x48, 0x48], [0x00, 0x6D, 0x24], [0x00, 0x00, 0x00], [0xDA, 0xB6, 0xFF], [0xFF, 0xFF, 0x6D], [0x91, 0x00, 0xFF], [0x48, 0xFF, 0xDA], [0xFF, 0xDA, 0x00], [0x00, 0x48, 0x48], ];
544
545const RGB_2C04_0004: [[u8; 3]; 64] = [
547 [0x91, 0x6D, 0x00], [0x6D, 0x48, 0xDA], [0x00, 0x91, 0x91], [0xDA, 0xDA, 0x00], [0x00, 0x00, 0x00], [0xFF, 0xB6, 0xB6], [0x00, 0x24, 0x91], [0xDA, 0x6D, 0x00], [0xB6, 0xB6, 0xB6], [0x6D, 0x24, 0x00], [0x00, 0xFF, 0x00], [0x00, 0x00, 0x6D], [0xFF, 0xDA, 0x91], [0xFF, 0xFF, 0x00], [0x00, 0x91, 0x00], [0xB6, 0xFF, 0x48], [0xFF, 0x6D, 0xFF], [0x48, 0x00, 0x00], [0x00, 0x48, 0xFF], [0xFF, 0x91, 0xFF], [0x00, 0x00, 0x00], [0x48, 0x48, 0x48], [0xB6, 0x24, 0x00], [0xFF, 0x91, 0x00], [0xDA, 0xB6, 0x6D], [0x00, 0xB6, 0x6D], [0x91, 0x91, 0xFF], [0x24, 0x91, 0x00], [0x91, 0x00, 0x6D], [0x00, 0x00, 0x00], [0x91, 0xFF, 0x6D], [0x6D, 0xB6, 0xFF], [0xB6, 0x00, 0x6D], [0x00, 0x6D, 0x24], [0x91, 0x48, 0x00], [0x00, 0x00, 0xDA], [0x91, 0x00, 0xFF], [0xB6, 0x00, 0xFF], [0x6D, 0x6D, 0x6D], [0xFF, 0x00, 0x91], [0x00, 0x48, 0x48], [0xDA, 0xDA, 0xDA], [0x00, 0x6D, 0xDA], [0x00, 0x48, 0x00], [0x24, 0x24, 0x24], [0xFF, 0xFF, 0x6D], [0x91, 0x91, 0x91], [0xFF, 0x00, 0xFF], [0xFF, 0xB6, 0xFF], [0xFF, 0xFF, 0xFF], [0x6D, 0x48, 0x00], [0xFF, 0x00, 0x00], [0xFF, 0xDA, 0x00], [0x48, 0xFF, 0xDA], [0xFF, 0xFF, 0xFF], [0x91, 0xDA, 0xFF], [0x00, 0x00, 0x00], [0xFF, 0xB6, 0x00], [0xDA, 0x6D, 0xFF], [0xB6, 0xDA, 0xFF], [0x6D, 0xDA, 0x00], [0xDA, 0xB6, 0xFF], [0x00, 0xFF, 0xFF], [0x24, 0x48, 0x00], ];
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616
617 #[test]
618 fn custom_base_lut_matches_builtin_composite() {
619 let from_base = build_rgba_lut_from_base(&NES_PALETTE);
624 let builtin = build_rgba_lut(PpuPalette::Composite2C02);
625 assert_eq!(from_base, builtin);
626 }
627
628 #[test]
629 fn nes_color_indices_round_trip() {
630 let [r, g, b, a] = nes_color_to_rgba(0x0F);
632 assert_eq!(a, 0xFF);
633 assert_eq!((r, g, b), (0, 0, 0));
634 }
635
636 #[test]
637 fn nes_color_index_30_is_white() {
638 let [r, g, b, _] = nes_color_to_rgba(0x30);
639 assert_eq!((r, g, b), (0xFF, 0xFF, 0xFF));
640 }
641
642 #[test]
643 fn emphasis_dims_other_channels() {
644 let base = nes_color_to_rgba(0x30);
645 let red = apply_emphasis(base, true, false, false);
646 assert_eq!(red[0], base[0]);
647 assert!(red[1] <= base[1]);
648 assert!(red[2] <= base[2]);
649 }
650
651 #[test]
652 fn composite_palette_is_byte_identical_to_legacy_path() {
653 for idx in 0u8..64 {
656 for emask in 0u8..8 {
657 let (r, g, b) = (emask & 1 != 0, emask & 2 != 0, emask & 4 != 0);
658 let legacy = apply_emphasis(nes_color_to_rgba(idx), r, g, b);
659 let routed = palette_color_to_rgba(PpuPalette::Composite2C02, idx, r, g, b);
660 assert_eq!(legacy, routed, "idx={idx} emask={emask}");
661 }
662 }
663 }
664
665 #[test]
666 fn rgb_2c03_documented_entries() {
667 let none = (false, false, false);
671 assert_eq!(
672 palette_color_to_rgba(PpuPalette::Rgb2C03, 0x00, none.0, none.1, none.2),
673 [0x6D, 0x6D, 0x6D, 0xFF]
674 );
675 assert_eq!(
676 palette_color_to_rgba(PpuPalette::Rgb2C03, 0x01, none.0, none.1, none.2),
677 [0x00, 0x24, 0x91, 0xFF]
678 );
679 assert_eq!(
680 palette_color_to_rgba(PpuPalette::Rgb2C03, 0x0F, none.0, none.1, none.2),
681 [0x00, 0x00, 0x00, 0xFF]
682 );
683 assert_eq!(
684 palette_color_to_rgba(PpuPalette::Rgb2C03, 0x20, none.0, none.1, none.2),
685 [0xFF, 0xFF, 0xFF, 0xFF]
686 );
687 assert_eq!(
688 palette_color_to_rgba(PpuPalette::Rgb2C03, 0x16, none.0, none.1, none.2),
689 [0xFF, 0x00, 0x00, 0xFF]
690 );
691 }
692
693 #[test]
694 fn rgb_2c05_shares_2c03_table() {
695 for idx in 0u8..64 {
696 assert_eq!(
697 palette_color_to_rgba(PpuPalette::Rgb2C05, idx, false, false, false),
698 palette_color_to_rgba(PpuPalette::Rgb2C03, idx, false, false, false),
699 "idx={idx}"
700 );
701 }
702 }
703
704 #[test]
705 fn rgb_2c04_first_entries_match_wiki() {
706 let none = (false, false, false);
707 assert_eq!(
709 palette_color_to_rgba(PpuPalette::Rgb2C04_0001, 0x00, none.0, none.1, none.2),
710 [0xFF, 0xB6, 0xB6, 0xFF] );
712 assert_eq!(
713 palette_color_to_rgba(PpuPalette::Rgb2C04_0002, 0x00, none.0, none.1, none.2),
714 [0x00, 0x00, 0x00, 0xFF] );
716 assert_eq!(
717 palette_color_to_rgba(PpuPalette::Rgb2C04_0003, 0x00, none.0, none.1, none.2),
718 [0xB6, 0x00, 0xFF, 0xFF] );
720 assert_eq!(
721 palette_color_to_rgba(PpuPalette::Rgb2C04_0004, 0x00, none.0, none.1, none.2),
722 [0x91, 0x6D, 0x00, 0xFF] );
724 }
725
726 #[test]
727 fn rgb_emphasis_forces_channel_full_brightness() {
728 let base = palette_color_to_rgba(PpuPalette::Rgb2C03, 0x00, false, false, false);
731 assert_eq!(base, [0x6D, 0x6D, 0x6D, 0xFF]);
732 let red = palette_color_to_rgba(PpuPalette::Rgb2C03, 0x00, true, false, false);
733 assert_eq!(red, [0xFF, 0x6D, 0x6D, 0xFF]);
734 let green = palette_color_to_rgba(PpuPalette::Rgb2C03, 0x00, false, true, false);
735 assert_eq!(green, [0x6D, 0xFF, 0x6D, 0xFF]);
736 let blue = palette_color_to_rgba(PpuPalette::Rgb2C03, 0x00, false, false, true);
737 assert_eq!(blue, [0x6D, 0x6D, 0xFF, 0xFF]);
738 let all = palette_color_to_rgba(PpuPalette::Rgb2C03, 0x00, true, true, true);
739 assert_eq!(all, [0xFF, 0xFF, 0xFF, 0xFF]);
740 }
741
742 #[test]
743 fn composite_default_and_is_composite() {
744 assert_eq!(PpuPalette::default(), PpuPalette::Composite2C02);
745 assert!(PpuPalette::Composite2C02.is_composite());
746 assert!(!PpuPalette::Rgb2C03.is_composite());
747 assert!(!PpuPalette::Rgb2C05.is_composite());
748 }
749
750 #[test]
754 fn rgba_lut_matches_direct_computation_exhaustively() {
755 for palette in [
756 PpuPalette::Composite2C02,
757 PpuPalette::Rgb2C03,
758 PpuPalette::Rgb2C04_0001,
759 PpuPalette::Rgb2C04_0002,
760 PpuPalette::Rgb2C04_0003,
761 PpuPalette::Rgb2C04_0004,
762 PpuPalette::Rgb2C05,
763 ] {
764 let lut = build_rgba_lut(palette);
765 for e in 0..8usize {
766 for c in 0..64usize {
767 #[allow(clippy::cast_possible_truncation)]
768 let direct =
769 palette_color_to_rgba(palette, c as u8, e & 1 != 0, e & 2 != 0, e & 4 != 0);
770 assert_eq!(lut[(e << 6) | c], direct, "{palette:?} e={e} c={c}");
771 }
772 }
773 }
774 }
775}