1use super::super::{
18 metrics::{fixed_div, fixed_mul, Scale},
19 outline::{Outline, Point},
20 recorder::HintsRecorder,
21 style::ScriptGroup,
22 topo::{Axis, Dimension},
23 ScaleFlags,
24};
25use core::cmp::Ordering;
26use raw::tables::glyf::PointMarker;
27
28pub(crate) fn align_edge_points(
32 outline: &mut Outline,
33 axis: &Axis,
34 group: ScriptGroup,
35 scale: &Scale,
36) -> Option<()> {
37 let edges = axis.edges.as_slice();
38 let segments = axis.segments.as_slice();
39 let points = outline.points.as_mut_slice();
40 let snap = group == ScriptGroup::Default
43 || (axis.dim == Dimension::Horizontal && scale.flags.contains(ScaleFlags::HORIZONTAL_SNAP))
44 || (axis.dim == Dimension::Vertical && scale.flags.contains(ScaleFlags::VERTICAL_SNAP));
45 for segment in segments {
46 let Some(edge) = segment.edge(edges) else {
47 continue;
48 };
49 let delta = edge.pos - edge.opos;
50 let mut point_ix = segment.first();
51 let last_ix = segment.last();
52 loop {
53 let point = points.get_mut(point_ix)?;
54 if axis.dim == Dimension::Horizontal {
55 if snap {
56 point.x = edge.pos;
57 } else {
58 point.x += delta;
59 }
60 point.flags.set_marker(PointMarker::TOUCHED_X);
61 } else {
62 if snap {
63 point.y = edge.pos;
64 } else {
65 point.y += delta;
66 }
67 point.flags.set_marker(PointMarker::TOUCHED_Y);
68 }
69 if point_ix == last_ix {
70 break;
71 }
72 point_ix = point.next();
73 }
74 }
75 Some(())
76}
77
78pub(crate) fn align_strong_points(
82 outline: &mut Outline,
83 axis: &mut Axis,
84 mut recorder: Option<&mut HintsRecorder>,
85) -> Option<()> {
86 if axis.edges.is_empty() {
87 return Some(());
88 }
89 let dim = axis.dim;
90 let touch_flag = if dim == Dimension::Horizontal {
91 PointMarker::TOUCHED_X
92 } else {
93 PointMarker::TOUCHED_Y
94 };
95 let points = outline.points.as_mut_slice();
96 'points: for (point_ix, point) in points.iter_mut().enumerate() {
97 if point
100 .flags
101 .has_marker(touch_flag | PointMarker::WEAK_INTERPOLATION)
102 {
103 continue;
104 }
105 let (u, ou) = if dim == Dimension::Vertical {
106 (point.fy, point.oy)
107 } else {
108 (point.fx, point.ox)
109 };
110 let edges = axis.edges.as_mut_slice();
111 let edge = edges.first()?;
113 let delta = edge.fpos as i32 - u;
114 if delta >= 0 {
115 if let Some(recorder) = recorder.as_mut() {
116 recorder.record_ip_before(dim, point_ix);
117 }
118 store_point(point, dim, edge.pos - (edge.opos - ou));
119 continue;
120 }
121 let edge = edges.last()?;
123 let delta = u - edge.fpos as i32;
124 if delta >= 0 {
125 if let Some(recorder) = recorder.as_mut() {
126 recorder.record_ip_after(dim, point_ix);
127 }
128 store_point(point, dim, edge.pos + (ou - edge.opos));
129 continue;
130 }
131 let min_ix = if edges.len() <= 8 {
138 if let Some((min_ix, edge)) = edges
139 .iter()
140 .enumerate()
141 .find(|(_ix, edge)| edge.fpos as i32 >= u)
142 {
143 if edge.fpos as i32 == u {
144 if let Some(recorder) = recorder.as_mut() {
145 recorder.record_ip_on(dim, point_ix, min_ix);
146 }
147 store_point(point, dim, edge.pos);
148 continue 'points;
149 }
150 min_ix
151 } else {
152 0
153 }
154 } else {
155 let mut min_ix = 0;
156 let mut max_ix = edges.len();
157 while min_ix < max_ix {
158 let mid_ix = (min_ix + max_ix) >> 1;
159 let edge = &edges[mid_ix];
160 let fpos = edge.fpos as i32;
161 match u.cmp(&fpos) {
162 Ordering::Less => max_ix = mid_ix,
163 Ordering::Greater => min_ix = mid_ix + 1,
164 Ordering::Equal => {
165 if let Some(recorder) = recorder.as_mut() {
167 recorder.record_ip_on(dim, point_ix, mid_ix);
168 }
169 store_point(point, dim, edge.pos);
170 continue 'points;
171 }
172 }
173 }
174 min_ix
175 };
176 if let Some(before_ix) = min_ix.checked_sub(1) {
178 let edge_before = edges.get(before_ix)?;
179 let before_pos = edge_before.pos;
180 let before_fpos = edge_before.fpos as i32;
181 let scale = if edge_before.scale == 0 {
182 let edge_after = edges.get(min_ix)?;
183 let scale = fixed_div(
184 edge_after.pos - edge_before.pos,
185 edge_after.fpos as i32 - before_fpos,
186 );
187 edges[before_ix].scale = scale;
188 scale
189 } else {
190 edge_before.scale
191 };
192 if let Some(recorder) = recorder.as_mut() {
193 recorder.record_ip_between(dim, point_ix, before_ix, min_ix);
194 }
195 store_point(point, dim, before_pos + fixed_mul(u - before_fpos, scale));
196 }
197 }
198 Some(())
199}
200
201pub(crate) fn align_weak_points(outline: &mut Outline, dim: Dimension) -> Option<()> {
205 let touch_marker = if dim == Dimension::Horizontal {
206 for point in &mut outline.points {
207 point.u = point.x;
208 point.v = point.ox;
209 }
210 PointMarker::TOUCHED_X
211 } else {
212 for point in &mut outline.points {
213 point.u = point.y;
214 point.v = point.oy;
215 }
216 PointMarker::TOUCHED_Y
217 };
218 for contour in &outline.contours {
219 let points = outline.points.get_mut(contour.range())?;
220 let Some(first_touched_ix) = points
222 .iter()
223 .position(|point| point.flags.has_marker(touch_marker))
224 else {
225 continue;
226 };
227 let last_ix = points.len() - 1;
228 let mut point_ix = first_touched_ix;
229 let mut last_touched_ix;
230 'outer: loop {
231 while point_ix < last_ix && points.get(point_ix + 1)?.flags.has_marker(touch_marker) {
233 point_ix += 1;
234 }
235 last_touched_ix = point_ix;
236 point_ix += 1;
238 loop {
239 if point_ix > last_ix {
240 break 'outer;
241 }
242 if points[point_ix].flags.has_marker(touch_marker) {
243 break;
244 }
245 point_ix += 1;
246 }
247 iup_interpolate(
248 points,
249 last_touched_ix + 1,
250 point_ix - 1,
251 last_touched_ix,
252 point_ix,
253 );
254 }
255 if last_touched_ix == first_touched_ix {
256 iup_shift(points, 0, last_ix, first_touched_ix);
258 } else {
259 if last_touched_ix < last_ix {
261 iup_interpolate(
262 points,
263 last_touched_ix + 1,
264 last_ix,
265 last_touched_ix,
266 first_touched_ix,
267 );
268 }
269 if first_touched_ix > 0 {
270 iup_interpolate(
271 points,
272 0,
273 first_touched_ix - 1,
274 last_touched_ix,
275 first_touched_ix,
276 );
277 }
278 }
279 }
280 if dim == Dimension::Horizontal {
282 for point in &mut outline.points {
283 point.x = point.u;
284 }
285 } else {
286 for point in &mut outline.points {
287 point.y = point.u;
288 }
289 }
290 Some(())
291}
292
293#[inline(always)]
294fn store_point(point: &mut Point, dim: Dimension, u: i32) {
295 if dim == Dimension::Horizontal {
296 point.x = u;
297 point.flags.set_marker(PointMarker::TOUCHED_X);
298 } else {
299 point.y = u;
300 point.flags.set_marker(PointMarker::TOUCHED_Y);
301 }
302}
303
304fn iup_shift(points: &mut [Point], p1_ix: usize, p2_ix: usize, ref_ix: usize) -> Option<()> {
313 let ref_point = points.get(ref_ix)?;
314 let delta = ref_point.u - ref_point.v;
315 if delta == 0 {
316 return Some(());
317 }
318 for point in points.get_mut(p1_ix..ref_ix)? {
319 point.u = point.v + delta;
320 }
321 for point in points.get_mut(ref_ix + 1..=p2_ix)? {
322 point.u = point.v + delta;
323 }
324 Some(())
325}
326
327fn iup_interpolate(
336 points: &mut [Point],
337 p1_ix: usize,
338 p2_ix: usize,
339 ref1_ix: usize,
340 ref2_ix: usize,
341) -> Option<()> {
342 if p1_ix > p2_ix {
343 return Some(());
344 }
345 let mut ref_point1 = points.get(ref1_ix)?;
346 let mut ref_point2 = points.get(ref2_ix)?;
347 if ref_point1.v > ref_point2.v {
348 core::mem::swap(&mut ref_point1, &mut ref_point2);
349 }
350 let (u1, v1) = (ref_point1.u, ref_point1.v);
351 let (u2, v2) = (ref_point2.u, ref_point2.v);
352 let d1 = u1 - v1;
353 let d2 = u2 - v2;
354 if u1 == u2 || v1 == v2 {
355 for point in points.get_mut(p1_ix..=p2_ix)? {
356 point.u = if point.v <= v1 {
357 point.v + d1
358 } else if point.v >= v2 {
359 point.v + d2
360 } else {
361 u1
362 };
363 }
364 } else {
365 let scale = fixed_div(u2 - u1, v2 - v1);
366 for point in points.get_mut(p1_ix..=p2_ix)? {
367 point.u = if point.v <= v1 {
368 point.v + d1
369 } else if point.v >= v2 {
370 point.v + d2
371 } else {
372 u1 + fixed_mul(point.v - v1, scale)
373 };
374 }
375 }
376 Some(())
377}
378
379#[cfg(test)]
380mod tests {
381 use super::{
382 super::super::{
383 metrics::{compute_unscaled_style_metrics, Scale},
384 recorder::{EdgeAction, HintAction, HintsRecorder, PointAction},
385 shape::{Shaper, ShaperMode},
386 style,
387 },
388 super::{EdgeMetrics, HintedMetrics},
389 *,
390 };
391 use crate::{attribute::Style, MetadataProvider};
392 use raw::{
393 types::{F2Dot14, GlyphId},
394 FontRef, TableProvider,
395 };
396
397 #[test]
398 fn hinted_coords_and_metrics_default() {
399 let font = FontRef::new(font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS).unwrap();
400 let (outline, metrics) = hint_outline(
401 &font,
402 16.0,
403 Default::default(),
404 GlyphId::new(9),
405 &style::STYLE_CLASSES[style::StyleClass::HEBR],
406 );
407 #[rustfmt::skip]
410 let expected_coords = [
411 (133, -256),
412 (133, 282),
413 (133, 343),
414 (146, 431),
415 (158, 463),
416 (158, 463),
417 (57, 463),
418 (30, 463),
419 (0, 495),
420 (0, 534),
421 (0, 548),
422 (2, 570),
423 (11, 604),
424 (17, 633),
425 (50, 633),
426 (50, 629),
427 (50, 604),
428 (77, 576),
429 (101, 576),
430 (163, 576),
431 (180, 576),
432 (192, 562),
433 (192, 542),
434 (192, 475),
435 (190, 457),
436 (187, 423),
437 (187, 366),
438 (187, 315),
439 (187, -220),
440 (178, -231),
441 (159, -248),
442 (146, -256),
443 ];
444 let coords = outline
445 .points
446 .iter()
447 .map(|point| (point.x, point.y))
448 .collect::<Vec<_>>();
449 assert_eq!(coords, expected_coords);
450 let expected_metrics = HintedMetrics {
451 x_scale: 67109,
452 edge_metrics: Some(EdgeMetrics {
453 left_opos: 15,
454 left_pos: 0,
455 right_opos: 210,
456 right_pos: 192,
457 }),
458 };
459 assert_eq!(metrics, expected_metrics);
460 }
461
462 #[test]
463 fn recorder_collects_edge_and_point_actions() {
464 let font = FontRef::new(font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS).unwrap();
465 let shaper = Shaper::new(&font, ShaperMode::Nominal);
466 let glyph = font.outline_glyphs().get(GlyphId::new(9)).unwrap();
467 let mut outline = Outline::default();
468 outline.fill(&glyph, &[], Default::default()).unwrap();
469 let metrics = compute_unscaled_style_metrics(
470 &shaper,
471 &[],
472 &style::STYLE_CLASSES[style::StyleClass::HEBR],
473 Default::default(),
474 );
475 let scale = Scale::new(
476 16.0,
477 font.head().unwrap().units_per_em() as i32,
478 Style::Normal,
479 Default::default(),
480 metrics.style_class().script.group,
481 );
482 let mut recorder = HintsRecorder::default();
483
484 super::super::hint_outline_with_recorder(
485 &mut outline,
486 &metrics,
487 &scale,
488 None,
489 &mut recorder,
490 );
491
492 assert!(recorder
493 .actions
494 .iter()
495 .any(|record| matches!(record, HintAction::Edge(edge) if edge.blue.is_some())));
496 assert!(recorder.actions.iter().any(|record| {
497 matches!(
498 record,
499 HintAction::Edge(edge)
500 if matches!(edge.action, EdgeAction::Blue | EdgeAction::BlueAnchor | EdgeAction::Anchor | EdgeAction::Stem | EdgeAction::Adjust)
501 )
502 }));
503 assert!(recorder.actions.iter().any(|record| {
504 matches!(
505 record,
506 HintAction::Point(point)
507 if matches!(point.action, PointAction::IpBefore | PointAction::IpAfter | PointAction::IpOn | PointAction::IpBetween)
508 )
509 }));
510 }
511
512 #[test]
513 fn hinted_coords_and_metrics_cjk() {
514 let font = FontRef::new(font_test_data::NOTOSERIFTC_AUTOHINT_METRICS).unwrap();
515 let (outline, metrics) = hint_outline(
516 &font,
517 16.0,
518 Default::default(),
519 GlyphId::new(9),
520 &style::STYLE_CLASSES[style::StyleClass::HANI],
521 );
522 let expected_coords = [
525 (279, 768),
526 (568, 768),
527 (618, 829),
528 (618, 829),
529 (634, 812),
530 (657, 788),
531 (685, 758),
532 (695, 746),
533 (692, 720),
534 (667, 720),
535 (288, 720),
536 (704, 704),
537 (786, 694),
538 (785, 685),
539 (777, 672),
540 (767, 670),
541 (767, 163),
542 (767, 159),
543 (750, 148),
544 (728, 142),
545 (716, 142),
546 (704, 142),
547 (402, 767),
548 (473, 767),
549 (473, 740),
550 (450, 598),
551 (338, 357),
552 (236, 258),
553 (220, 270),
554 (274, 340),
555 (345, 499),
556 (390, 675),
557 (344, 440),
558 (398, 425),
559 (464, 384),
560 (496, 343),
561 (501, 307),
562 (486, 284),
563 (458, 281),
564 (441, 291),
565 (434, 314),
566 (398, 366),
567 (354, 416),
568 (334, 433),
569 (832, 841),
570 (934, 830),
571 (932, 819),
572 (914, 804),
573 (896, 802),
574 (896, 30),
575 (896, 5),
576 (885, -35),
577 (848, -60),
578 (809, -65),
579 (807, -51),
580 (794, -27),
581 (781, -19),
582 (767, -11),
583 (715, 0),
584 (673, 5),
585 (673, 21),
586 (673, 21),
587 (707, 18),
588 (756, 15),
589 (799, 13),
590 (807, 13),
591 (821, 13),
592 (832, 23),
593 (832, 35),
594 (407, 624),
595 (594, 624),
596 (594, 546),
597 (396, 546),
598 (569, 576),
599 (558, 576),
600 (599, 614),
601 (677, 559),
602 (671, 552),
603 (654, 547),
604 (636, 545),
605 (622, 458),
606 (572, 288),
607 (488, 130),
608 (357, -5),
609 (259, -60),
610 (246, -45),
611 (327, 9),
612 (440, 150),
613 (516, 311),
614 (558, 486),
615 (128, 542),
616 (158, 581),
617 (226, 576),
618 (223, 562),
619 (207, 543),
620 (193, 539),
621 (193, -44),
622 (193, -46),
623 (175, -56),
624 (152, -64),
625 (141, -64),
626 (128, -64),
627 (195, 850),
628 (300, 820),
629 (295, 799),
630 (259, 799),
631 (234, 712),
632 (163, 543),
633 (80, 395),
634 (33, 338),
635 (19, 347),
636 (54, 410),
637 (120, 575),
638 (176, 759),
639 ];
640 let coords = outline
641 .points
642 .iter()
643 .map(|point| (point.x, point.y))
644 .collect::<Vec<_>>();
645 assert_eq!(coords, expected_coords);
646 let expected_metrics = HintedMetrics {
647 x_scale: 67109,
648 edge_metrics: Some(EdgeMetrics {
649 left_opos: 141,
650 left_pos: 128,
651 right_opos: 933,
652 right_pos: 896,
653 }),
654 };
655 assert_eq!(metrics, expected_metrics);
656 }
657
658 #[test]
661 fn missing_edge_metrics() {
662 let font = FontRef::new(font_test_data::CUBIC_GLYF).unwrap();
663 let (_outline, metrics) = hint_outline(
664 &font,
665 16.0,
666 Default::default(),
667 GlyphId::new(1),
668 &style::STYLE_CLASSES[style::StyleClass::LATN],
669 );
670 let expected_metrics = HintedMetrics {
671 x_scale: 65536,
672 edge_metrics: None,
673 };
674 assert_eq!(metrics, expected_metrics);
675 }
676
677 #[test]
681 fn skia_ahem_test_case() {
682 let font = FontRef::new(font_test_data::AHEM).unwrap();
683 let outline = hint_outline(
684 &font,
685 24.0,
686 Default::default(),
687 GlyphId::new(5),
690 &style::STYLE_CLASSES[style::StyleClass::LATN],
691 )
692 .0;
693 let expected_coords = [(0, 1216), (1536, 1216), (1536, -320), (0, -320)];
694 let expected_float_coords = [(0.0, 19.0), (24.0, 19.0), (24.0, -5.0), (0.0, -5.0)];
697 let coords = outline
698 .points
699 .iter()
700 .map(|point| (point.x, point.y))
701 .collect::<Vec<_>>();
702 let float_coords = coords
703 .iter()
704 .map(|(x, y)| (*x as f32 / 64.0, *y as f32 / 64.0))
705 .collect::<Vec<_>>();
706 assert_eq!(coords, expected_coords);
707 assert_eq!(float_coords, expected_float_coords);
708 }
709
710 fn hint_outline(
711 font: &FontRef,
712 size: f32,
713 coords: &[F2Dot14],
714 gid: GlyphId,
715 style: &style::StyleClass,
716 ) -> (Outline, HintedMetrics) {
717 let shaper = Shaper::new(font, ShaperMode::Nominal);
718 let glyphs = font.outline_glyphs();
719 let glyph = glyphs.get(gid).unwrap();
720 let mut outline = Outline::default();
721 outline.fill(&glyph, coords, Default::default()).unwrap();
722 let metrics = compute_unscaled_style_metrics(&shaper, coords, style, Default::default());
723 let scale = Scale::new(
724 size,
725 font.head().unwrap().units_per_em() as i32,
726 Style::Normal,
727 Default::default(),
728 metrics.style_class().script.group,
729 );
730 let hinted_metrics = super::super::hint_outline(&mut outline, &metrics, &scale, None);
731 (outline, hinted_metrics)
732 }
733}