swash/
palette.rs

1//! Collections of colors for layered outlines.
2
3use super::internal::*;
4use super::{
5    string::{LocalizedString, StringId},
6    FontRef,
7};
8
9const CPAL: RawTag = raw_tag(b"CPAL");
10
11/// Iterator over a collection of color palettes.
12#[derive(Copy, Clone)]
13pub struct ColorPalettes<'a> {
14    font: FontRef<'a>,
15    data: Bytes<'a>,
16    len: usize,
17    pos: usize,
18}
19
20impl<'a> ColorPalettes<'a> {
21    pub(crate) fn new(font: FontRef<'a>, data: &'a [u8]) -> Self {
22        let data = Bytes::new(data);
23        let len = data.read_or_default::<u16>(4) as usize;
24        Self {
25            font,
26            data,
27            len,
28            pos: 0,
29        }
30    }
31
32    pub(crate) fn from_font(font: &FontRef<'a>) -> Self {
33        let data = font.table_data(CPAL).unwrap_or(&[]);
34        Self::new(*font, data)
35    }
36
37    // Unused when render feature is disabled.
38    #[allow(dead_code)]
39    pub(crate) fn from_font_and_offset(font: &FontRef<'a>, offset: u32) -> Self {
40        let data = font.data.get(offset as usize..).unwrap_or(&[]);
41        Self::new(*font, data)
42    }
43
44    fn get(&self, index: usize) -> Option<ColorPalette<'a>> {
45        if index >= self.len {
46            return None;
47        }
48        let b = &self.data;
49        let version = b.read::<u16>(0)?;
50        let num_entries = b.read::<u16>(2)?;
51        let offset = b.read::<u32>(8)? as usize;
52        let first = b.read::<u16>(12 + index * 2)? as usize;
53        let offset = offset + first * 4;
54        Some(ColorPalette {
55            font: self.font,
56            data: *b,
57            version,
58            index,
59            num_entries,
60            offset,
61        })
62    }
63}
64
65impl_iter!(ColorPalettes, ColorPalette);
66
67/// Collection of colors.
68#[derive(Copy, Clone)]
69pub struct ColorPalette<'a> {
70    font: FontRef<'a>,
71    data: Bytes<'a>,
72    version: u16,
73    index: usize,
74    num_entries: u16,
75    offset: usize,
76}
77
78impl<'a> ColorPalette<'a> {
79    /// Returns the index of the palette.
80    pub fn index(&self) -> u16 {
81        self.index as u16
82    }
83
84    /// Returns the name identifier for the palette, if available.
85    pub fn name_id(&self) -> Option<StringId> {
86        if self.version == 0 {
87            return None;
88        }
89        let d = &self.data;
90        let num_palettes = d.read::<u16>(4)? as usize;
91        let base = 16 + num_palettes * 2;
92        let labels_offset = d.read::<u32>(base)? as usize;
93        if labels_offset == 0 {
94            return None;
95        }
96        Some(StringId::Other(
97            d.read::<u16>(labels_offset + self.index * 2)?,
98        ))
99    }
100
101    /// Returns the name for the palette, optionally for a particular
102    /// language.
103    pub fn name(&self, language: Option<&str>) -> Option<LocalizedString<'a>> {
104        self.name_id()
105            .and_then(|id| self.font.localized_strings().find_by_id(id, language))
106    }
107
108    /// Returns the theme usability of the palette, if available.
109    pub fn usability(&self) -> Option<Usability> {
110        let flags = self.flags()?;
111        Some(match flags & 0b11 {
112            0b01 => Usability::Light,
113            0b10 => Usability::Dark,
114            0b11 => Usability::Both,
115            _ => return None,
116        })
117    }
118
119    /// Returns the number of color entries in the palette.
120    pub fn len(&self) -> u16 {
121        self.num_entries
122    }
123
124    /// Returns whether this palette is empty.
125    pub fn is_empty(&self) -> bool {
126        self.num_entries == 0
127    }
128
129    /// Returns the color for the specified entry in RGBA order.
130    pub fn get(&self, index: u16) -> [u8; 4] {
131        if index >= self.num_entries {
132            return [0; 4];
133        }
134        let offset = self.offset + index as usize * 4;
135        let d = &self.data;
136        let b = d.read_or_default::<u8>(offset);
137        let g = d.read_or_default::<u8>(offset + 1);
138        let r = d.read_or_default::<u8>(offset + 2);
139        let a = d.read_or_default::<u8>(offset + 3);
140        [r, g, b, a]
141    }
142
143    fn flags(&self) -> Option<u32> {
144        if self.version == 0 {
145            return None;
146        }
147        let d = &self.data;
148        let num_palettes = d.read::<u16>(4)? as usize;
149        let base = 12 + num_palettes * 2;
150        let types_offset = d.read::<u32>(base)? as usize;
151        if types_offset == 0 {
152            return None;
153        }
154        d.read::<u32>(types_offset + self.index * 4)
155    }
156}
157
158/// Theme of a palette with respect to background color.
159#[derive(Copy, Clone, PartialEq, Eq, Debug)]
160pub enum Usability {
161    /// Usable with light backgrounds.
162    Light,
163    /// Usable with dark backgrounds.
164    Dark,
165    /// Usable with both light and dark backgrounds.
166    Both,
167}