1use core::ops::RangeInclusive;
2
3use raw::tables::glyf::PointCoord;
4use read_fonts::{
5 tables::glyf::{PointFlags, PointMarker},
6 tables::gvar::{GlyphDelta, Gvar},
7 tables::variations::TupleVariation,
8 types::{F2Dot14, Fixed, GlyphId, Point},
9 ReadError,
10};
11
12use super::PHANTOM_POINT_COUNT;
13
14pub(super) fn composite_glyph<D: PointCoord>(
19 gvar: &Gvar,
20 glyph_id: GlyphId,
21 coords: &[F2Dot14],
22 deltas: &mut [Point<D>],
23) -> Result<(), ReadError> {
24 compute_deltas_for_glyph(gvar, glyph_id, coords, deltas, |scalar, tuple, deltas| {
25 for tuple_delta in tuple.deltas() {
26 let ix = tuple_delta.position as usize;
27 if let Some(delta) = deltas.get_mut(ix) {
28 *delta += tuple_delta.apply_scalar(scalar);
29 }
30 }
31 Ok(())
32 })?;
33 Ok(())
34}
35
36pub(super) struct SimpleGlyph<'a, C: PointCoord> {
37 pub points: &'a [Point<C>],
38 pub flags: &'a mut [PointFlags],
39 pub contours: &'a [u16],
40}
41
42pub(super) fn simple_glyph<C, D>(
48 gvar: &Gvar,
49 glyph_id: GlyphId,
50 coords: &[F2Dot14],
51 glyph: SimpleGlyph<C>,
52 iup_buffer: &mut [Point<D>],
53 deltas: &mut [Point<D>],
54) -> Result<(), ReadError>
55where
56 C: PointCoord,
57 D: PointCoord,
58 D: From<C>,
59{
60 if iup_buffer.len() < glyph.points.len() || glyph.points.len() < PHANTOM_POINT_COUNT {
61 return Err(ReadError::InvalidArrayLen);
62 }
63 if gvar.glyph_variation_data(glyph_id).is_err() {
64 return Ok(());
66 };
67 let SimpleGlyph {
68 points,
69 flags,
70 contours,
71 } = glyph;
72 compute_deltas_for_glyph(gvar, glyph_id, coords, deltas, |scalar, tuple, deltas| {
73 for ((flag, point), iup_point) in flags.iter_mut().zip(points).zip(&mut iup_buffer[..]) {
77 *iup_point = point.map(D::from);
78 flag.clear_marker(PointMarker::HAS_DELTA);
79 }
80 tuple.accumulate_sparse_deltas(iup_buffer, flags, scalar)?;
81 interpolate_deltas(points, flags, contours, &mut iup_buffer[..])
82 .ok_or(ReadError::OutOfBounds)?;
83 for ((delta, point), iup_point) in deltas.iter_mut().zip(points).zip(iup_buffer.iter()) {
84 *delta += *iup_point - point.map(D::from);
85 }
86 Ok(())
87 })?;
88 Ok(())
89}
90
91fn compute_deltas_for_glyph<C, D>(
93 gvar: &Gvar,
94 glyph_id: GlyphId,
95 coords: &[F2Dot14],
96 deltas: &mut [Point<D>],
97 mut apply_tuple_missing_deltas_fn: impl FnMut(
98 Fixed,
99 TupleVariation<GlyphDelta>,
100 &mut [Point<D>],
101 ) -> Result<(), ReadError>,
102) -> Result<(), ReadError>
103where
104 C: PointCoord,
105 D: PointCoord,
106 D: From<C>,
107{
108 for delta in deltas.iter_mut() {
109 *delta = Default::default();
110 }
111 let Ok(Some(var_data)) = gvar.glyph_variation_data(glyph_id) else {
112 return Ok(());
114 };
115 for (tuple, scalar) in var_data.active_tuples_at(coords) {
116 if tuple.has_deltas_for_all_points() {
119 tuple.accumulate_dense_deltas(deltas, scalar)?;
120 } else {
121 apply_tuple_missing_deltas_fn(scalar, tuple, deltas)?;
124 }
125 }
126 Ok(())
127}
128
129fn interpolate_deltas<C, D>(
135 points: &[Point<C>],
136 flags: &[PointFlags],
137 contours: &[u16],
138 out_points: &mut [Point<D>],
139) -> Option<()>
140where
141 C: PointCoord,
142 D: PointCoord,
143 D: From<C>,
144{
145 let mut jiggler = Jiggler { points, out_points };
146 let mut point_ix = 0usize;
147 for &end_point_ix in contours {
148 let end_point_ix = end_point_ix as usize;
149 let first_point_ix = point_ix;
150 while point_ix <= end_point_ix && !flags.get(point_ix)?.has_marker(PointMarker::HAS_DELTA) {
152 point_ix += 1;
153 }
154 if point_ix > end_point_ix {
157 continue;
158 }
159 let first_delta_ix = point_ix;
160 let mut cur_delta_ix = point_ix;
161 point_ix += 1;
162 while point_ix <= end_point_ix {
164 if flags.get(point_ix)?.has_marker(PointMarker::HAS_DELTA) {
165 jiggler.interpolate(
167 cur_delta_ix + 1..=point_ix - 1,
168 RefPoints(cur_delta_ix, point_ix),
169 )?;
170 cur_delta_ix = point_ix;
171 }
172 point_ix += 1;
173 }
174 if cur_delta_ix == first_delta_ix {
176 jiggler.shift(first_point_ix..=end_point_ix, cur_delta_ix)?;
177 } else {
178 jiggler.interpolate(
181 cur_delta_ix + 1..=end_point_ix,
182 RefPoints(cur_delta_ix, first_delta_ix),
183 )?;
184 if first_delta_ix > 0 {
185 jiggler.interpolate(
186 first_point_ix..=first_delta_ix - 1,
187 RefPoints(cur_delta_ix, first_delta_ix),
188 )?;
189 }
190 }
191 }
192 Some(())
193}
194
195struct RefPoints(usize, usize);
196
197struct Jiggler<'a, C, D>
198where
199 C: PointCoord,
200 D: PointCoord,
201 D: From<C>,
202{
203 points: &'a [Point<C>],
204 out_points: &'a mut [Point<D>],
205}
206
207impl<C, D> Jiggler<'_, C, D>
208where
209 C: PointCoord,
210 D: PointCoord,
211 D: From<C>,
212{
213 fn shift(&mut self, range: RangeInclusive<usize>, ref_ix: usize) -> Option<()> {
218 let ref_in = self.points.get(ref_ix)?.map(D::from);
219 let ref_out = self.out_points.get(ref_ix)?;
220 let delta = *ref_out - ref_in;
221 if delta.x == D::zeroed() && delta.y == D::zeroed() {
222 return Some(());
223 }
224 for out_point in self.out_points.get_mut(*range.start()..ref_ix)? {
227 *out_point += delta;
228 }
229 for out_point in self.out_points.get_mut(ref_ix + 1..=*range.end())? {
230 *out_point += delta;
231 }
232 Some(())
233 }
234
235 fn interpolate(&mut self, range: RangeInclusive<usize>, ref_points: RefPoints) -> Option<()> {
242 if range.is_empty() {
243 return Some(());
244 }
245 macro_rules! interp_coord {
248 ($coord:ident) => {
249 let RefPoints(mut ref1_ix, mut ref2_ix) = ref_points;
250 if self.points.get(ref1_ix)?.$coord > self.points.get(ref2_ix)?.$coord {
251 core::mem::swap(&mut ref1_ix, &mut ref2_ix);
252 }
253 let in1 = D::from(self.points.get(ref1_ix)?.$coord);
254 let in2 = D::from(self.points.get(ref2_ix)?.$coord);
255 let out1 = self.out_points.get(ref1_ix)?.$coord;
256 let out2 = self.out_points.get(ref2_ix)?.$coord;
257 if in1 != in2 || out1 == out2 {
260 let scale = if in1 != in2 {
261 (out2 - out1) / (in2 - in1)
262 } else {
263 D::zeroed()
264 };
265 let d1 = out1 - in1;
266 let d2 = out2 - in2;
267 for (point, out_point) in self
268 .points
269 .get(range.clone())?
270 .iter()
271 .zip(self.out_points.get_mut(range.clone())?)
272 {
273 let mut out = D::from(point.$coord);
274 if out <= in1 {
275 out += d1;
276 } else if out >= in2 {
277 out += d2;
278 } else {
279 out = out1 + (out - in1) * scale;
280 }
281 out_point.$coord = out;
282 }
283 }
284 };
285 }
286 interp_coord!(x);
287 interp_coord!(y);
288 Some(())
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295
296 fn make_points(tuples: &[(i32, i32)]) -> Vec<Point<i32>> {
297 tuples.iter().map(|&(x, y)| Point::new(x, y)).collect()
298 }
299
300 fn make_working_points_and_flags(
301 points: &[Point<i32>],
302 deltas: &[Point<i32>],
303 ) -> (Vec<Point<Fixed>>, Vec<PointFlags>) {
304 let working_points = points
305 .iter()
306 .zip(deltas)
307 .map(|(point, delta)| point.map(Fixed::from_i32) + delta.map(Fixed::from_i32))
308 .collect();
309 let flags = deltas
310 .iter()
311 .map(|delta| {
312 let mut flags = PointFlags::default();
313 if delta.x != 0 || delta.y != 0 {
314 flags.set_marker(PointMarker::HAS_DELTA);
315 }
316 flags
317 })
318 .collect();
319 (working_points, flags)
320 }
321
322 #[test]
323 fn shift() {
324 let points = make_points(&[(245, 630), (260, 700), (305, 680)]);
325 let deltas = make_points(&[(20, -10), (0, 0), (0, 0)]);
327 let (mut working_points, flags) = make_working_points_and_flags(&points, &deltas);
328 interpolate_deltas(&points, &flags, &[2], &mut working_points).unwrap();
329 let expected = &[
330 Point::new(265, 620).map(Fixed::from_i32),
331 Point::new(280, 690).map(Fixed::from_i32),
332 Point::new(325, 670).map(Fixed::from_i32),
333 ];
334 assert_eq!(&working_points, expected);
335 }
336
337 #[test]
338 fn interpolate() {
339 let points = make_points(&[(245, 630), (260, 700), (305, 680)]);
343 let deltas = make_points(&[(28, -62), (0, 0), (-42, -57)]);
344 let (mut working_points, flags) = make_working_points_and_flags(&points, &deltas);
345 interpolate_deltas(&points, &flags, &[2], &mut working_points).unwrap();
346 assert_eq!(
347 working_points[1],
348 Point::new(
349 Fixed::from_f64(260.0 + 10.4999237060547),
350 Fixed::from_f64(700.0 - 57.0)
351 )
352 );
353 }
354}