1use super::internal::*;
4use super::{
5 string::{LocalizedString, StringId},
6 FontRef,
7};
8
9const CPAL: RawTag = raw_tag(b"CPAL");
10
11#[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 #[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#[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 pub fn index(&self) -> u16 {
81 self.index as u16
82 }
83
84 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 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 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 pub fn len(&self) -> u16 {
121 self.num_entries
122 }
123
124 pub fn is_empty(&self) -> bool {
126 self.num_entries == 0
127 }
128
129 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#[derive(Copy, Clone, PartialEq, Eq, Debug)]
160pub enum Usability {
161 Light,
163 Dark,
165 Both,
167}