1use std::{fmt, mem::size_of};
6
7use byteorder::{BigEndian, ReadBytesExt};
8use icu_properties::sets;
9use zng_color::{ColorScheme, Rgba, rgba};
10use zng_var::impl_from_and_into_var;
11use zng_view_api::font::GlyphIndex;
12
13pub(super) fn maybe_emoji(c: char) -> bool {
14 sets::emoji().contains(c)
15}
16
17pub(super) fn definitely_emoji(c: char) -> bool {
18 sets::emoji_presentation().contains(c) || is_modifier(c)
19}
20
21pub(super) fn is_modifier(c: char) -> bool {
22 sets::emoji_modifier().contains(c)
23}
24
25pub(super) fn is_component(c: char) -> bool {
26 sets::emoji_component().contains(c)
27}
28
29const CPAL: u32 = u32::from_be_bytes(*b"CPAL");
40
41const COLR: u32 = u32::from_be_bytes(*b"COLR");
43
44#[derive(Clone, Debug)]
50pub struct ColorPalettes {
51 num_palettes: u16,
52 num_palette_entries: u16,
53 colors: Box<[Rgba]>,
54 types: Box<[ColorPaletteType]>,
55}
56impl Default for ColorPalettes {
57 fn default() -> Self {
59 Self::empty()
60 }
61}
62impl ColorPalettes {
63 pub fn empty() -> Self {
65 Self {
66 num_palettes: 0,
67 num_palette_entries: 0,
68 colors: Box::new([]),
69 types: Box::new([]),
70 }
71 }
72
73 pub fn load(font: &ttf_parser::RawFace) -> std::io::Result<Self> {
75 let table = match font.table(ttf_parser::Tag(CPAL)) {
78 Some(t) => t,
79 None => return Ok(Self::empty()),
80 };
81
82 let mut cursor = std::io::Cursor::new(&table);
97
98 let version = cursor.read_u16::<BigEndian>()?;
99 let num_palette_entries = cursor.read_u16::<BigEndian>()?;
100 let num_palettes = cursor.read_u16::<BigEndian>()?;
101 let _num_color_records = cursor.read_u16::<BigEndian>()?;
102 let color_records_array_offset = cursor.read_u32::<BigEndian>()? as u64;
103
104 let color_record_indices = cursor.position();
105 let mut colors = Vec::with_capacity(num_palettes as usize * num_palette_entries as usize);
106 for i in 0..num_palettes {
107 cursor.set_position(color_record_indices + i as u64 * size_of::<u16>() as u64);
108
109 let color_record_index = cursor.read_u16::<BigEndian>()? as u64;
110 let color_record_offset = color_records_array_offset + color_record_index * size_of::<(u8, u8, u8, u8)>() as u64;
111
112 cursor.set_position(color_record_offset);
113 for _ in 0..num_palette_entries {
114 let b = cursor.read_u8()?;
115 let g = cursor.read_u8()?;
116 let r = cursor.read_u8()?;
117 let a = cursor.read_u8()?;
118
119 colors.push(rgba(r, g, b, a));
120 }
121 }
122
123 let mut palette_types = vec![];
124
125 if version >= 1 {
126 cursor.set_position(color_record_indices + num_palettes as u64 * size_of::<u16>() as u64);
127
128 let palette_types_array_offset = cursor.read_u32::<BigEndian>()? as u64;
138 let _palette_labels_array_offset = cursor.read_u32::<BigEndian>()? as u64;
139 let _palette_entry_labels_array_offset = cursor.read_u32::<BigEndian>()? as u64;
140
141 if palette_types_array_offset > 0 {
142 palette_types.reserve(num_palettes as usize);
143
144 cursor.set_position(palette_types_array_offset);
145 for _ in 0..num_palettes {
146 let flags = cursor.read_u32::<BigEndian>()?;
147 let flags = ColorPaletteType::from_bits(flags).unwrap_or_else(ColorPaletteType::empty);
148 palette_types.push(flags);
149 }
150 }
151 }
152
153 Ok(Self {
154 num_palettes,
155 num_palette_entries,
156 colors: colors.into_boxed_slice(),
157 types: palette_types.into_boxed_slice(),
158 })
159 }
160
161 pub fn len(&self) -> usize {
163 self.num_palettes as usize
164 }
165
166 pub fn is_empty(&self) -> bool {
168 self.num_palettes == 0
169 }
170
171 pub fn palette(&self, p: impl Into<FontColorPalette>) -> Option<ColorPalette> {
173 let i = self.palette_i(p.into());
174 self.palette_get(i.unwrap_or(0))
175 }
176
177 pub fn palette_exact(&self, p: impl Into<FontColorPalette>) -> Option<ColorPalette> {
179 let i = self.palette_i(p.into())?;
180 self.palette_get(i)
181 }
182
183 fn palette_i(&self, p: FontColorPalette) -> Option<usize> {
184 match p {
185 FontColorPalette::Light => self
186 .types
187 .iter()
188 .position(|p| p.contains(ColorPaletteType::USABLE_WITH_LIGHT_BACKGROUND)),
189 FontColorPalette::Dark => self
190 .types
191 .iter()
192 .position(|p| p.contains(ColorPaletteType::USABLE_WITH_DARK_BACKGROUND)),
193 FontColorPalette::Index(i) => {
194 if i < self.num_palette_entries {
195 Some(i as _)
196 } else {
197 None
198 }
199 }
200 }
201 }
202
203 fn palette_get(&self, i: usize) -> Option<ColorPalette> {
204 let len = self.num_palette_entries as usize;
205 let s = len * i;
206 let e = s + len;
207
208 self.colors.get(s..e).map(|c| ColorPalette {
209 flags: self.types.get(i).copied().unwrap_or_else(ColorPaletteType::empty),
210 colors: c,
211 })
212 }
213
214 pub fn iter(&self) -> impl ExactSizeIterator<Item = ColorPalette> {
216 self.colors
217 .chunks_exact(self.num_palette_entries as _)
218 .enumerate()
219 .map(|(i, c)| ColorPalette {
220 flags: self.types.get(i).copied().unwrap_or_else(ColorPaletteType::empty),
221 colors: c,
222 })
223 }
224}
225
226bitflags! {
227 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
231 pub struct ColorPaletteType: u32 {
232 const USABLE_WITH_LIGHT_BACKGROUND = 0x0001;
234 const USABLE_WITH_DARK_BACKGROUND = 0x0002;
236 }
237}
238
239pub struct ColorPalette<'a> {
243 pub flags: ColorPaletteType,
245 pub colors: &'a [Rgba],
247}
248
249#[derive(Clone, Debug)]
255pub struct ColorGlyphs {
256 base_glyph_records: Vec<BaseGlyphRecord>,
257 layer_records: Vec<LayerRecord>,
258}
259impl ColorGlyphs {
260 pub fn empty() -> Self {
262 Self {
263 base_glyph_records: vec![],
264 layer_records: vec![],
265 }
266 }
267
268 pub fn load(font: &ttf_parser::RawFace) -> std::io::Result<Self> {
270 let table = match font.table(ttf_parser::Tag(COLR)) {
271 Some(t) => t,
272 None => return Ok(Self::empty()),
273 };
274
275 let mut cursor = std::io::Cursor::new(&table);
288
289 let _version = cursor.read_u16::<BigEndian>()?;
290 let num_base_glyph_records = cursor.read_u16::<BigEndian>()?;
291 let base_glyph_records_offset = cursor.read_u32::<BigEndian>()? as u64;
292 let layer_records_offset = cursor.read_u32::<BigEndian>()? as u64;
293 let num_layer_records = cursor.read_u16::<BigEndian>()?;
294
295 let mut base_glyph_records = Vec::with_capacity(num_base_glyph_records as _);
296
297 cursor.set_position(base_glyph_records_offset);
298 for _ in 0..num_base_glyph_records {
299 base_glyph_records.push(BaseGlyphRecord {
311 glyph_id: cursor.read_u16::<BigEndian>()?,
312 first_layer_index: cursor.read_u16::<BigEndian>()?,
313 num_layers: cursor.read_u16::<BigEndian>()?,
314 });
315 }
316
317 let mut layer_records = Vec::with_capacity(num_layer_records as _);
318 cursor.set_position(layer_records_offset);
319 for _ in 0..num_layer_records {
320 layer_records.push(LayerRecord {
329 glyph_id: cursor.read_u16::<BigEndian>()?,
330 palette_index: cursor.read_u16::<BigEndian>()?,
331 });
332 }
333
334 Ok(Self {
335 base_glyph_records,
336 layer_records,
337 })
338 }
339
340 pub fn glyph(&self, base_glyph: GlyphIndex) -> Option<ColorGlyph> {
350 match self.base_glyph_records.binary_search_by_key(&(base_glyph as u16), |e| e.glyph_id) {
351 Ok(i) => {
352 let rec = &self.base_glyph_records[i];
353
354 let s = rec.first_layer_index as usize;
355 let e = s + rec.num_layers as usize;
356 Some(ColorGlyph {
357 layers: &self.layer_records[s..e],
358 })
359 }
360 Err(_) => None,
361 }
362 }
363
364 pub fn is_empty(&self) -> bool {
366 self.base_glyph_records.is_empty()
367 }
368
369 pub fn len(&self) -> usize {
371 self.base_glyph_records.len()
372 }
373}
374
375pub struct ColorGlyph<'a> {
379 layers: &'a [LayerRecord],
380}
381impl<'a> ColorGlyph<'a> {
382 pub fn iter(&self) -> impl ExactSizeIterator<Item = (GlyphIndex, Option<usize>)> + 'a {
384 self.layers.iter().map(|l| (l.glyph_id(), l.palette_index()))
385 }
386
387 pub fn layers_len(&self) -> usize {
389 self.layers.len()
390 }
391}
392
393#[derive(Debug, Clone, Copy)]
394struct BaseGlyphRecord {
395 glyph_id: u16,
396 first_layer_index: u16,
397 num_layers: u16,
398}
399
400#[derive(Debug, Clone, Copy)]
401struct LayerRecord {
402 glyph_id: u16,
403 palette_index: u16,
404}
405impl LayerRecord {
406 fn glyph_id(&self) -> GlyphIndex {
407 self.glyph_id as _
408 }
409
410 fn palette_index(&self) -> Option<usize> {
411 if self.palette_index == 0xFFFF {
412 None
413 } else {
414 Some(self.palette_index as _)
415 }
416 }
417}
418
419#[derive(Clone, Copy, PartialEq, Eq)]
421pub enum FontColorPalette {
422 Light,
425 Dark,
428 Index(u16),
435}
436impl fmt::Debug for FontColorPalette {
437 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438 if f.alternate() {
439 write!(f, "FontColorPalette::")?;
440 }
441 match self {
442 Self::Light => write!(f, "Light"),
443 Self::Dark => write!(f, "Dark"),
444 Self::Index(arg0) => f.debug_tuple("Index").field(arg0).finish(),
445 }
446 }
447}
448impl_from_and_into_var! {
449 fn from(index: u16) -> FontColorPalette {
450 FontColorPalette::Index(index)
451 }
452
453 fn from(color_scheme: ColorScheme) -> FontColorPalette {
454 match color_scheme {
455 ColorScheme::Light => FontColorPalette::Light,
456 ColorScheme::Dark => FontColorPalette::Dark,
457 }
458 }
459}