read_fonts/tables/postscript/
blend.rs1use font_types::{BigEndian, F2Dot14, Fixed};
4
5use super::Error;
6use crate::tables::variations::{ItemVariationData, ItemVariationStore};
7
8const MAX_PRECOMPUTED_SCALARS: usize = 16;
15
16pub struct BlendState<'a> {
29 store: ItemVariationStore<'a>,
30 coords: &'a [F2Dot14],
31 store_index: u16,
32 data: Option<ItemVariationData<'a>>,
34 region_indices: &'a [BigEndian<u16>],
35 scalars: [Fixed; MAX_PRECOMPUTED_SCALARS],
36}
37
38impl<'a> BlendState<'a> {
39 pub fn new(
40 store: ItemVariationStore<'a>,
41 coords: &'a [F2Dot14],
42 store_index: u16,
43 ) -> Result<Self, Error> {
44 let mut state = Self {
45 store,
46 coords,
47 store_index,
48 data: None,
49 region_indices: &[],
50 scalars: Default::default(),
51 };
52 state.update_precomputed_scalars()?;
53 Ok(state)
54 }
55
56 pub fn set_store_index(&mut self, store_index: u16) -> Result<(), Error> {
61 if self.store_index != store_index {
62 self.store_index = store_index;
63 self.update_precomputed_scalars()?;
64 }
65 Ok(())
66 }
67
68 pub fn region_count(&self) -> Result<usize, Error> {
71 Ok(self.region_indices.len())
72 }
73
74 pub fn scalars(&self) -> Result<impl Iterator<Item = Result<Fixed, Error>> + '_, Error> {
77 let total_count = self.region_indices.len();
78 let cached = &self.scalars[..MAX_PRECOMPUTED_SCALARS.min(total_count)];
79 let remaining_regions = if total_count > MAX_PRECOMPUTED_SCALARS {
80 &self.region_indices[MAX_PRECOMPUTED_SCALARS..]
81 } else {
82 &[]
83 };
84 Ok(cached.iter().copied().map(Ok).chain(
85 remaining_regions
86 .iter()
87 .map(|region_ix| self.region_scalar(region_ix.get())),
88 ))
89 }
90
91 fn update_precomputed_scalars(&mut self) -> Result<(), Error> {
92 self.data = None;
93 self.region_indices = &[];
94 let store = &self.store;
95 let variation_data = store.item_variation_data();
96 let data = variation_data
97 .get(self.store_index as usize)
98 .ok_or(Error::InvalidVariationStoreIndex(self.store_index))??;
99 let region_indices = data.region_indexes();
100 let regions = self.store.variation_region_list()?.variation_regions();
101 for (region_ix, scalar) in region_indices
103 .iter()
104 .take(MAX_PRECOMPUTED_SCALARS)
105 .zip(&mut self.scalars)
106 {
107 let region = regions.get(region_ix.get() as usize)?;
110 *scalar = region.compute_scalar(self.coords);
111 }
112 self.data = Some(data);
113 self.region_indices = region_indices;
114 Ok(())
115 }
116
117 fn region_scalar(&self, index: u16) -> Result<Fixed, Error> {
118 Ok(self
119 .store
120 .variation_region_list()?
121 .variation_regions()
122 .get(index as usize)
123 .map_err(Error::Read)?
124 .compute_scalar(self.coords))
125 }
126}
127
128#[cfg(test)]
129mod test {
130 use super::*;
131 use crate::{FontData, FontRead};
132
133 #[test]
134 fn example_blends() {
135 example_test(&[-1.0], &[0.0, 1.0]);
137 example_test(&[-0.25], &[0.5, 0.0]);
138 example_test(&[-0.5], &[1.0, 0.0]);
139 example_test(&[-0.75], &[0.5, 0.5]);
140 example_test(&[0.0], &[0.0, 0.0]);
141 example_test(&[0.5], &[0.0, 0.0]);
142 example_test(&[1.0], &[0.0, 0.0]);
143 }
144
145 fn example_test(coords: &[f32], expected: &[f64]) {
146 let scalars = example_scalars_for_coords(coords);
147 let expected: Vec<_> = expected.iter().copied().map(Fixed::from_f64).collect();
148 assert_eq!(scalars, expected);
149 }
150
151 fn example_scalars_for_coords(coords: &[f32]) -> Vec<Fixed> {
152 let ivs = example_ivs();
153 let coords: Vec<_> = coords
154 .iter()
155 .map(|coord| F2Dot14::from_f32(*coord))
156 .collect();
157 let blender = BlendState::new(ivs, &coords, 0).unwrap();
158 blender.scalars().unwrap().map(|res| res.unwrap()).collect()
159 }
160
161 fn example_ivs() -> ItemVariationStore<'static> {
162 let ivs_data = &font_test_data::cff2::EXAMPLE[18..];
164 ItemVariationStore::read(FontData::new(ivs_data)).unwrap()
165 }
166}