pinot/
cpal.rs

1//! Color palette table.
2
3use super::name::NameId;
4use super::parse_prelude::*;
5
6/// Tag for the `CPAL` table.
7pub const CPAL: Tag = Tag::new(b"CPAL");
8
9/// Color palette table.
10///
11/// <https://docs.microsoft.com/en-us/typography/opentype/spec/cpal>
12#[derive(Copy, Clone)]
13pub struct Cpal<'a> {
14    data: Buffer<'a>,
15    version: u16,
16    len: u16,
17    offset: u32,
18}
19
20impl<'a> Cpal<'a> {
21    /// Creates a new color palette table from a byte slice containing the
22    /// table data.
23    pub fn new(data: &'a [u8]) -> Self {
24        let data = Buffer::new(data);
25        let version = data.read_or_default(0);
26        let len = data.read_or_default(4);
27        let offset = data.read_or_default(8);
28        Self {
29            data,
30            version,
31            len,
32            offset,
33        }
34    }
35
36    /// Returns the version of the table.
37    pub fn version(&self) -> u16 {
38        self.version
39    }
40
41    /// Returns the number of palettes in the table.
42    pub fn len(&self) -> u16 {
43        self.len
44    }
45
46    /// Returns true if the table is empty.
47    pub fn is_empty(&self) -> bool {
48        self.len == 0
49    }
50
51    /// Returns the palette at the specified index.
52    pub fn get(&self, index: u16) -> Option<Palette<'a>> {
53        if index >= self.len {
54            return None;
55        }
56        let d = &self.data;
57        let name_id = (|| {
58            if self.version == 0 {
59                return None;
60            }
61            let base = 16 + self.len as usize * 2;
62            let labels_offset = d.read_u32(base)? as usize;
63            if labels_offset == 0 {
64                return None;
65            }
66            d.read_u16(labels_offset + index as usize * 2)
67        })();
68        let flags = (|| {
69            if self.version == 0 {
70                return None;
71            }
72            let base = 12 + self.len as usize * 2;
73            let types_offset = d.read_u32(base)? as usize;
74            if types_offset == 0 {
75                return None;
76            }
77            d.read_u32(types_offset + index as usize * 4)
78        })()
79        .unwrap_or(0);
80        let theme = match flags & 0b11 {
81            0b01 => Theme::Light,
82            0b10 => Theme::Dark,
83            _ => Theme::Any,
84        };
85        let len = d.read::<u16>(2)? as usize;
86        let first = d.read_u32(12 + index as usize * 2)? as usize;
87        let offset = self.offset as usize + first;
88        let colors = d.read_slice(offset, len)?;
89        Some(Palette {
90            index,
91            name_id,
92            theme,
93            colors,
94        })
95    }
96
97    /// Returns an iterator over the palettes in the table.
98    pub fn palettes(&self) -> impl Iterator<Item = Palette<'a>> + 'a + Clone {
99        let copy = *self;
100        (0..self.len).filter_map(move |i| copy.get(i))
101    }
102}
103
104/// Collection of colors.
105#[derive(Copy, Clone)]
106pub struct Palette<'a> {
107    /// Index of the palette.
108    pub index: u16,
109    /// Identifier for the name of the palette.
110    pub name_id: Option<NameId>,
111    /// Theme of the palette.
112    pub theme: Theme,
113    /// Color values.
114    pub colors: Slice<'a, Color>,
115}
116
117/// Theme of a palette with respect to background color.
118#[derive(Copy, Clone, PartialEq, Eq, Debug)]
119pub enum Theme {
120    /// Usable with both light and dark backgrounds.
121    Any,
122    /// Usable with light backgrounds.
123    Light,
124    /// Usable with dark backgrounds.
125    Dark,
126}
127
128/// RGBA color value.
129#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
130pub struct Color {
131    /// Red component.
132    pub r: u8,
133    /// Green component.
134    pub g: u8,
135    /// Blue component.
136    pub b: u8,
137    /// Alpha component.
138    pub a: u8,
139}
140
141impl ReadData for Color {
142    unsafe fn read_data_unchecked(buf: &[u8], offset: usize) -> Self {
143        Self {
144            r: *buf.get_unchecked(offset + 2),
145            g: *buf.get_unchecked(offset + 1),
146            b: *buf.get_unchecked(offset),
147            a: *buf.get_unchecked(offset + 3),
148        }
149    }
150}