1use super::{
4 super::Outlines,
5 cow_slice::CowSlice,
6 definition::{Definition, DefinitionMap, DefinitionState},
7 engine::Engine,
8 error::HintError,
9 graphics::RetainedGraphicsState,
10 program::{Program, ProgramState},
11 value_stack::ValueStack,
12 zone::Zone,
13 HintOutline, PointFlags, Target,
14};
15use alloc::vec::Vec;
16use raw::{
17 types::{F26Dot6, F2Dot14, Fixed, Point},
18 TableProvider,
19};
20
21#[derive(Clone, Default)]
22pub struct HintInstance {
23 functions: Vec<Definition>,
24 instructions: Vec<Definition>,
25 cvt: Vec<i32>,
26 storage: Vec<i32>,
27 graphics: RetainedGraphicsState,
28 twilight_scaled: Vec<Point<F26Dot6>>,
29 twilight_original_scaled: Vec<Point<F26Dot6>>,
30 twilight_flags: Vec<PointFlags>,
31 axis_count: u16,
32 max_stack: usize,
33}
34
35impl HintInstance {
36 pub fn reconfigure(
37 &mut self,
38 outlines: &Outlines,
39 scale: i32,
40 ppem: i32,
41 target: Target,
42 coords: &[F2Dot14],
43 ) -> Result<(), HintError> {
44 self.setup(outlines, scale, coords);
45 let twilight_contours = [self.twilight_scaled.len() as u16];
46 let twilight = Zone::new(
47 &[],
48 &mut self.twilight_original_scaled,
49 &mut self.twilight_scaled,
50 &mut self.twilight_flags,
51 &twilight_contours,
52 );
53 let glyph = Zone::default();
54 let mut stack_buf = vec![0; self.max_stack];
55 let value_stack = ValueStack::new(&mut stack_buf, false);
56 let graphics = RetainedGraphicsState::new(scale, ppem, target);
57 let mut engine = Engine::new(
58 outlines,
59 ProgramState::new(outlines.fpgm, outlines.prep, &[], Program::Font),
60 graphics,
61 DefinitionState::new(
62 DefinitionMap::Mut(&mut self.functions),
63 DefinitionMap::Mut(&mut self.instructions),
64 ),
65 CowSlice::new_mut(&mut self.cvt),
66 CowSlice::new_mut(&mut self.storage),
67 value_stack,
68 twilight,
69 glyph,
70 self.axis_count,
71 coords,
72 false,
73 );
74 engine.run_program(Program::Font, false)?;
76 engine.run_program(Program::ControlValue, false)?;
78 self.graphics = *engine.retained_graphics_state();
80 Ok(())
81 }
82
83 pub fn is_enabled(&self) -> bool {
87 self.graphics.instruct_control & 1 == 0
89 }
90
91 pub fn backward_compatibility(&self) -> bool {
94 if self.graphics.target.preserve_linear_metrics() {
96 true
97 } else if self.graphics.target.is_smooth() {
98 (self.graphics.instruct_control & 0x4) == 0
99 } else {
100 false
101 }
102 }
103
104 pub fn hint(
105 &self,
106 outlines: &Outlines,
107 outline: &mut HintOutline,
108 is_pedantic: bool,
109 ) -> Result<(), HintError> {
110 let twilight_count = outline.twilight_scaled.len();
112 let twilight_contours = [twilight_count as u16];
113 outline
114 .twilight_original_scaled
115 .copy_from_slice(&self.twilight_original_scaled);
116 outline
117 .twilight_scaled
118 .copy_from_slice(&self.twilight_scaled);
119 outline.twilight_flags.copy_from_slice(&self.twilight_flags);
120 let twilight = Zone::new(
121 &[],
122 outline.twilight_original_scaled,
123 outline.twilight_scaled,
124 outline.twilight_flags,
125 &twilight_contours,
126 );
127 let glyph = Zone::new(
129 outline.unscaled,
130 outline.original_scaled,
131 outline.scaled,
132 outline.flags,
133 outline.contours,
134 );
135 let value_stack = ValueStack::new(outline.stack, is_pedantic);
136 let cvt = CowSlice::new(&self.cvt, outline.cvt).unwrap();
137 let storage = CowSlice::new(&self.storage, outline.storage).unwrap();
138 let mut engine = Engine::new(
139 outlines,
140 ProgramState::new(
141 outlines.fpgm,
142 outlines.prep,
143 outline.bytecode,
144 Program::Glyph,
145 ),
146 self.graphics,
147 DefinitionState::new(
148 DefinitionMap::Ref(&self.functions),
149 DefinitionMap::Ref(&self.instructions),
150 ),
151 cvt,
152 storage,
153 value_stack,
154 twilight,
155 glyph,
156 self.axis_count,
157 outline.coords,
158 outline.is_composite,
159 );
160 engine
161 .run_program(Program::Glyph, is_pedantic)
162 .map_err(|mut e| {
163 e.glyph_id = Some(outline.glyph_id);
164 e
165 })?;
166 if !engine.backward_compatibility() {
169 for (i, p) in (outline.scaled[outline.scaled.len() - 4..])
170 .iter()
171 .enumerate()
172 {
173 outline.phantom[i] = *p;
174 }
175 }
176 Ok(())
177 }
178
179 fn setup(&mut self, outlines: &Outlines, scale: i32, coords: &[F2Dot14]) {
181 let axis_count = outlines
182 .gvar
183 .as_ref()
184 .map(|gvar| gvar.axis_count())
185 .unwrap_or_default();
186 self.functions.clear();
187 self.functions
188 .resize(outlines.max_function_defs as usize, Definition::default());
189 self.instructions.resize(
190 outlines.max_instruction_defs as usize,
191 Definition::default(),
192 );
193 self.cvt.clear();
194 let cvt = outlines.font.cvt().unwrap_or_default();
195 if let Ok(cvar) = outlines.font.cvar() {
196 self.cvt.resize(cvt.len(), 0);
198 let _ = cvar.deltas(axis_count, coords, &mut self.cvt);
199 for (value, base_value) in self.cvt.iter_mut().zip(cvt.iter()) {
201 let delta = Fixed::from_bits(*value).to_f26dot6().to_bits();
204 let base_value = base_value.get() as i32 * 64;
205 *value = base_value + delta;
206 }
207 } else {
208 self.cvt
211 .extend(cvt.iter().map(|value| (value.get() as i32) * 64));
212 }
213 let scale = Fixed::from_bits(scale >> 6);
217 for value in &mut self.cvt {
218 *value = (Fixed::from_bits(*value) * scale).to_bits();
219 }
220 self.storage.clear();
221 self.storage.resize(outlines.max_storage as usize, 0);
222 let max_twilight_points = outlines.max_twilight_points as usize;
223 self.twilight_scaled.clear();
224 self.twilight_scaled
225 .resize(max_twilight_points, Default::default());
226 self.twilight_original_scaled.clear();
227 self.twilight_original_scaled
228 .resize(max_twilight_points, Default::default());
229 self.twilight_flags.clear();
230 self.twilight_flags
231 .resize(max_twilight_points, Default::default());
232 self.axis_count = axis_count;
233 self.max_stack = outlines.max_stack_elements as usize;
234 self.graphics = RetainedGraphicsState::default();
235 }
236}
237
238#[cfg(test)]
239impl HintInstance {
240 pub fn simulate_prep_flag_suppress_hinting(&mut self) {
245 self.graphics.instruct_control |= 1;
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::{super::super::Outlines, HintInstance};
252 use read_fonts::{types::F2Dot14, FontRef};
253
254 #[test]
255 fn scaled_cvar_cvt() {
256 let font = FontRef::new(font_test_data::CVAR).unwrap();
257 let outlines = Outlines::new(&font).unwrap();
258 let mut instance = HintInstance::default();
259 let coords = [0.5, -0.5].map(F2Dot14::from_f32);
260 let ppem = 16;
261 let scale = 67109;
263 instance
264 .reconfigure(&outlines, scale, ppem, Default::default(), &coords)
265 .unwrap();
266 let expected = [
267 778, 10, 731, 0, 731, 10, 549, 10, 0, 0, 0, -10, 0, -10, -256, -10, 0, 0, 0, 0, 0, 0,
268 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
269 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 137, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
270 0, 0, 0, 0, 0, 0, 60, 0, 81, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
271 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
272 ];
273 assert_eq!(&instance.cvt, &expected);
274 }
275}