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 for delta in deltas.iter_mut() {
64 *delta = Default::default();
65 }
66 if gvar.glyph_variation_data(glyph_id).is_err() {
67 return Ok(());
69 };
70 let SimpleGlyph {
71 points,
72 flags,
73 contours,
74 } = glyph;
75 compute_deltas_for_glyph(gvar, glyph_id, coords, deltas, |scalar, tuple, deltas| {
76 for ((flag, point), iup_point) in flags.iter_mut().zip(points).zip(&mut iup_buffer[..]) {
80 *iup_point = point.map(D::from);
81 flag.clear_marker(PointMarker::HAS_DELTA);
82 }
83 tuple.accumulate_sparse_deltas(iup_buffer, flags, scalar)?;
84 interpolate_deltas(points, flags, contours, &mut iup_buffer[..])
85 .ok_or(ReadError::OutOfBounds)?;
86 for ((delta, point), iup_point) in deltas.iter_mut().zip(points).zip(iup_buffer.iter()) {
87 *delta += *iup_point - point.map(D::from);
88 }
89 Ok(())
90 })?;
91 Ok(())
92}
93
94fn compute_deltas_for_glyph<C, D>(
96 gvar: &Gvar,
97 glyph_id: GlyphId,
98 coords: &[F2Dot14],
99 deltas: &mut [Point<D>],
100 mut apply_tuple_missing_deltas_fn: impl FnMut(
101 Fixed,
102 TupleVariation<GlyphDelta>,
103 &mut [Point<D>],
104 ) -> Result<(), ReadError>,
105) -> Result<(), ReadError>
106where
107 C: PointCoord,
108 D: PointCoord,
109 D: From<C>,
110{
111 for delta in deltas.iter_mut() {
112 *delta = Default::default();
113 }
114 let Ok(Some(var_data)) = gvar.glyph_variation_data(glyph_id) else {
115 return Ok(());
117 };
118 for (tuple, scalar) in var_data.active_tuples_at(coords) {
119 if tuple.has_deltas_for_all_points() {
122 tuple.accumulate_dense_deltas(deltas, scalar)?;
123 } else {
124 apply_tuple_missing_deltas_fn(scalar, tuple, deltas)?;
127 }
128 }
129 Ok(())
130}
131
132fn interpolate_deltas<C, D>(
138 points: &[Point<C>],
139 flags: &[PointFlags],
140 contours: &[u16],
141 out_points: &mut [Point<D>],
142) -> Option<()>
143where
144 C: PointCoord,
145 D: PointCoord,
146 D: From<C>,
147{
148 let mut jiggler = Jiggler { points, out_points };
149 let mut point_ix = 0usize;
150 for &end_point_ix in contours {
151 let end_point_ix = end_point_ix as usize;
152 let first_point_ix = point_ix;
153 while point_ix <= end_point_ix && !flags.get(point_ix)?.has_marker(PointMarker::HAS_DELTA) {
155 point_ix += 1;
156 }
157 if point_ix > end_point_ix {
160 continue;
161 }
162 let first_delta_ix = point_ix;
163 let mut cur_delta_ix = point_ix;
164 point_ix += 1;
165 while point_ix <= end_point_ix {
167 if flags.get(point_ix)?.has_marker(PointMarker::HAS_DELTA) {
168 jiggler.interpolate(
170 cur_delta_ix + 1..=point_ix - 1,
171 RefPoints(cur_delta_ix, point_ix),
172 )?;
173 cur_delta_ix = point_ix;
174 }
175 point_ix += 1;
176 }
177 if cur_delta_ix == first_delta_ix {
179 jiggler.shift(first_point_ix..=end_point_ix, cur_delta_ix)?;
180 } else {
181 jiggler.interpolate(
184 cur_delta_ix + 1..=end_point_ix,
185 RefPoints(cur_delta_ix, first_delta_ix),
186 )?;
187 if first_delta_ix > 0 {
188 jiggler.interpolate(
189 first_point_ix..=first_delta_ix - 1,
190 RefPoints(cur_delta_ix, first_delta_ix),
191 )?;
192 }
193 }
194 }
195 Some(())
196}
197
198struct RefPoints(usize, usize);
199
200struct Jiggler<'a, C, D>
201where
202 C: PointCoord,
203 D: PointCoord,
204 D: From<C>,
205{
206 points: &'a [Point<C>],
207 out_points: &'a mut [Point<D>],
208}
209
210impl<C, D> Jiggler<'_, C, D>
211where
212 C: PointCoord,
213 D: PointCoord,
214 D: From<C>,
215{
216 fn shift(&mut self, range: RangeInclusive<usize>, ref_ix: usize) -> Option<()> {
221 let ref_in = self.points.get(ref_ix)?.map(D::from);
222 let ref_out = self.out_points.get(ref_ix)?;
223 let delta = *ref_out - ref_in;
224 if delta.x == D::zeroed() && delta.y == D::zeroed() {
225 return Some(());
226 }
227 for out_point in self.out_points.get_mut(*range.start()..ref_ix)? {
230 *out_point += delta;
231 }
232 for out_point in self.out_points.get_mut(ref_ix + 1..=*range.end())? {
233 *out_point += delta;
234 }
235 Some(())
236 }
237
238 fn interpolate(&mut self, range: RangeInclusive<usize>, ref_points: RefPoints) -> Option<()> {
245 if range.is_empty() {
246 return Some(());
247 }
248 macro_rules! interp_coord {
251 ($coord:ident) => {
252 let RefPoints(mut ref1_ix, mut ref2_ix) = ref_points;
253 if self.points.get(ref1_ix)?.$coord > self.points.get(ref2_ix)?.$coord {
254 core::mem::swap(&mut ref1_ix, &mut ref2_ix);
255 }
256 let in1 = D::from(self.points.get(ref1_ix)?.$coord);
257 let in2 = D::from(self.points.get(ref2_ix)?.$coord);
258 let out1 = self.out_points.get(ref1_ix)?.$coord;
259 let out2 = self.out_points.get(ref2_ix)?.$coord;
260 if in1 != in2 || out1 == out2 {
263 let scale = if in1 != in2 {
264 (out2 - out1) / (in2 - in1)
265 } else {
266 D::zeroed()
267 };
268 let d1 = out1 - in1;
269 let d2 = out2 - in2;
270 for (point, out_point) in self
271 .points
272 .get(range.clone())?
273 .iter()
274 .zip(self.out_points.get_mut(range.clone())?)
275 {
276 let mut out = D::from(point.$coord);
277 if out <= in1 {
278 out += d1;
279 } else if out >= in2 {
280 out += d2;
281 } else {
282 out = out1 + (out - in1) * scale;
283 }
284 out_point.$coord = out;
285 }
286 }
287 };
288 }
289 interp_coord!(x);
290 interp_coord!(y);
291 Some(())
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use super::*;
298
299 fn make_points(tuples: &[(i32, i32)]) -> Vec<Point<i32>> {
300 tuples.iter().map(|&(x, y)| Point::new(x, y)).collect()
301 }
302
303 fn make_working_points_and_flags(
304 points: &[Point<i32>],
305 deltas: &[Point<i32>],
306 ) -> (Vec<Point<Fixed>>, Vec<PointFlags>) {
307 let working_points = points
308 .iter()
309 .zip(deltas)
310 .map(|(point, delta)| point.map(Fixed::from_i32) + delta.map(Fixed::from_i32))
311 .collect();
312 let flags = deltas
313 .iter()
314 .map(|delta| {
315 let mut flags = PointFlags::default();
316 if delta.x != 0 || delta.y != 0 {
317 flags.set_marker(PointMarker::HAS_DELTA);
318 }
319 flags
320 })
321 .collect();
322 (working_points, flags)
323 }
324
325 #[test]
326 fn shift() {
327 let points = make_points(&[(245, 630), (260, 700), (305, 680)]);
328 let deltas = make_points(&[(20, -10), (0, 0), (0, 0)]);
330 let (mut working_points, flags) = make_working_points_and_flags(&points, &deltas);
331 interpolate_deltas(&points, &flags, &[2], &mut working_points).unwrap();
332 let expected = &[
333 Point::new(265, 620).map(Fixed::from_i32),
334 Point::new(280, 690).map(Fixed::from_i32),
335 Point::new(325, 670).map(Fixed::from_i32),
336 ];
337 assert_eq!(&working_points, expected);
338 }
339
340 #[test]
341 fn interpolate() {
342 let points = make_points(&[(245, 630), (260, 700), (305, 680)]);
346 let deltas = make_points(&[(28, -62), (0, 0), (-42, -57)]);
347 let (mut working_points, flags) = make_working_points_and_flags(&points, &deltas);
348 interpolate_deltas(&points, &flags, &[2], &mut working_points).unwrap();
349 assert_eq!(
350 working_points[1],
351 Point::new(
352 Fixed::from_f64(260.0 + 10.4999237060547),
353 Fixed::from_f64(700.0 - 57.0)
354 )
355 );
356 }
357}