1//! A [Color Palette Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/cpal) implementation.
3
4use core::num::NonZeroU16;
5
6use crate::parser::{FromData, LazyArray16, Offset, Offset32, Stream};
7use crate::RgbaColor;
8
9/// A [Color Palette Table](
10/// https://docs.microsoft.com/en-us/typography/opentype/spec/cpal).
11#[derive(Clone, Copy, Debug)]
12pub struct Table<'a> {
13 color_indices: LazyArray16<'a, u16>,
14 colors: LazyArray16<'a, BgraColor>,
15}
16
17impl<'a> Table<'a> {
18 /// Parses a table from raw data.
19 pub fn parse(data: &'a [u8]) -> Option<Self> {
20 let mut s = Stream::new(data);
21
22 let version = s.read::<u16>()?;
23 if version != 0 {
24 return None;
25 }
26
27 s.skip::<u16>(); // number of palette entries
28
29 let num_palettes = s.read::<u16>()?;
30 if num_palettes == 0 {
31 return None; // zero palettes is an error
32 }
33
34 let num_colors = s.read::<u16>()?;
35 let color_records_offset = s.read::<Offset32>()?;
36 let color_indices = s.read_array16::<u16>(num_palettes)?;
37
38 let colors = Stream::new_at(data, color_records_offset.to_usize())?
39 .read_array16::<BgraColor>(num_colors)?;
40
41 Some(Self {
42 color_indices,
43 colors,
44 })
45 }
46
47 /// Returns the number of palettes.
48 pub fn palettes(&self) -> NonZeroU16 {
49 // Already checked during parsing.
50 NonZeroU16::new(self.color_indices.len() as u16).unwrap()
51 }
52
53 /// Returns the color at the given index into the given palette.
54 pub fn get(&self, palette_index: u16, palette_entry: u16) -> Option<RgbaColor> {
55 let index = self
56 .color_indices
57 .get(palette_index)?
58 .checked_add(palette_entry)?;
59 self.colors.get(index).map(|c| c.to_rgba())
60 }
61}
62
63#[derive(Clone, Copy, PartialEq, Eq, Debug)]
64struct BgraColor {
65 blue: u8,
66 green: u8,
67 red: u8,
68 alpha: u8,
69}
70
71impl BgraColor {
72 #[inline]
73 fn to_rgba(self) -> RgbaColor {
74 RgbaColor::new(self.red, self.green, self.blue, self.alpha)
75 }
76}
77
78impl FromData for BgraColor {
79 const SIZE: usize = 4;
80
81 fn parse(data: &[u8]) -> Option<Self> {
82 let mut s: Stream<'_> = Stream::new(data);
83 Some(Self {
84 blue: s.read::<u8>()?,
85 green: s.read::<u8>()?,
86 red: s.read::<u8>()?,
87 alpha: s.read::<u8>()?,
88 })
89 }
90}
91