1use super::{
10 super::{
11 metrics::{fixed_div, fixed_mul, Scale, ScaledAxisMetrics, ScaledBlue, UnscaledBlue},
12 outline::Direction,
13 style::ScriptGroup,
14 },
15 Axis, Edge, Segment,
16};
17
18pub(crate) fn compute_edges(
22 axis: &mut Axis,
23 metrics: &ScaledAxisMetrics,
24 top_to_bottom_hinting: bool,
25 y_scale: i32,
26 group: ScriptGroup,
27) {
28 axis.edges.clear();
29 let scale = metrics.scale;
30 let top_to_bottom_hinting = if axis.dim == Axis::HORIZONTAL || group != ScriptGroup::Default {
34 false
35 } else {
36 top_to_bottom_hinting
37 };
38 let segment_length_threshold = if axis.dim == Axis::HORIZONTAL {
40 fixed_div(64, y_scale)
41 } else {
42 0
43 };
44 let segment_width_threshold = fixed_div(32, scale);
46 let initial_threshold = metrics.width_metrics.edge_distance_threshold;
49 const EDGE_DISTANCE_THRESHOLD_MAX: i32 = 64 / 4;
50 let edge_distance_threshold = if group == ScriptGroup::Default {
51 fixed_div(
52 fixed_mul(initial_threshold, scale).min(EDGE_DISTANCE_THRESHOLD_MAX),
53 scale,
54 )
55 } else {
56 let threshold = fixed_mul(initial_threshold, scale);
59 if threshold > EDGE_DISTANCE_THRESHOLD_MAX {
60 fixed_div(EDGE_DISTANCE_THRESHOLD_MAX, scale)
61 } else {
62 initial_threshold
63 }
64 };
65 for segment_ix in 0..axis.segments.len() {
70 let segment = &axis.segments[segment_ix];
71 if group == ScriptGroup::Default {
72 if (segment.height as i32) < segment_length_threshold
74 || (segment.delta as i32 > segment_width_threshold)
75 || segment.dir == Direction::None
76 {
77 continue;
78 }
79 if segment.serif_ix.is_some()
81 && (2 * segment.height as i32) < (3 * segment_length_threshold)
82 {
83 continue;
84 }
85 }
86 let mut best_dist = i32::MAX;
88 let mut best_edge_ix = None;
89 for edge_ix in 0..axis.edges.len() {
90 let edge = &axis.edges[edge_ix];
91 let dist = (segment.pos as i32 - edge.fpos as i32).abs();
92 if dist < edge_distance_threshold && edge.dir == segment.dir && dist < best_dist {
93 if group == ScriptGroup::Default {
94 best_edge_ix = Some(edge_ix);
95 break;
96 }
97 if let Some(link) = segment.link(&axis.segments).copied() {
100 let first_ix = edge.first_ix as usize;
103 let mut seg1 = &axis.segments[first_ix];
104 let mut dist2 = 0;
105 loop {
106 if let Some(link1) = seg1.link(&axis.segments).copied() {
107 dist2 = (link.pos as i32 - link1.pos as i32).abs();
108 if dist2 >= edge_distance_threshold {
109 break;
110 }
111 }
112 if seg1.edge_next_ix == Some(first_ix as u16) {
113 break;
114 }
115 if let Some(next) = seg1.next_in_edge(&axis.segments) {
116 seg1 = next;
117 } else {
118 break;
119 }
120 }
121 if dist2 >= edge_distance_threshold {
122 continue;
123 }
124 }
125 best_dist = dist;
126 best_edge_ix = Some(edge_ix);
127 }
128 }
129 if let Some(edge_ix) = best_edge_ix {
130 axis.append_segment_to_edge(segment_ix, edge_ix);
131 } else {
132 let opos = fixed_mul(segment.pos as i32, scale);
134 let edge = Edge {
135 fpos: segment.pos,
136 opos,
137 pos: opos,
138 dir: segment.dir,
139 first_ix: segment_ix as u16,
140 last_ix: segment_ix as u16,
141 ..Default::default()
142 };
143 axis.insert_edge(edge, top_to_bottom_hinting);
144 axis.segments[segment_ix].edge_next_ix = Some(segment_ix as u16);
145 }
146 }
147 if group == ScriptGroup::Default {
148 for segment_ix in 0..axis.segments.len() {
151 let segment = &axis.segments[segment_ix];
152 if segment.dir != Direction::None {
153 continue;
154 }
155 if let Some(edge_ix) = axis
158 .edges
159 .iter()
160 .enumerate()
161 .filter_map(|(ix, edge)| {
162 ((segment.pos as i32 - edge.fpos as i32).abs() < edge_distance_threshold)
163 .then_some(ix)
164 })
165 .next()
166 {
167 axis.append_segment_to_edge(segment_ix, edge_ix);
169 }
170 }
171 }
172 link_segments_to_edges(axis);
173 compute_edge_properties(axis);
174}
175
176fn link_segments_to_edges(axis: &mut Axis) {
179 let segments = axis.segments.as_mut_slice();
180 for edge_ix in 0..axis.edges.len() {
181 let edge = &axis.edges[edge_ix];
182 let mut ix = edge.first_ix as usize;
183 let last_ix = edge.last_ix as usize;
184 loop {
185 let segment = &mut segments[ix];
186 segment.edge_ix = Some(edge_ix as u16);
187 if ix == last_ix {
188 break;
189 }
190 ix = segment
191 .edge_next_ix
192 .map(|ix| ix as usize)
193 .unwrap_or(last_ix);
194 }
195 }
196}
197
198fn compute_edge_properties(axis: &mut Axis) {
203 let edges = axis.edges.as_mut_slice();
204 let segments = axis.segments.as_slice();
205 for edge_ix in 0..edges.len() {
206 let mut roundness = 0;
207 let mut straightness = 0;
208 let edge = edges[edge_ix];
209 let mut segment_ix = edge.first_ix as usize;
210 let last_segment_ix = edge.last_ix as usize;
211 loop {
212 let edge = edges[edge_ix];
215 let segment = &segments[segment_ix];
216 let next_segment_ix = segment.edge_next_ix;
217 if segment.flags & Segment::ROUND != 0 {
219 roundness += 1;
220 } else {
221 straightness += 1;
222 }
223 let is_serif = if let Some(serif_ix) = segment.serif_ix {
225 let serif = &segments[serif_ix as usize];
226 serif.edge_ix.is_some() && serif.edge_ix != Some(edge_ix as u16)
227 } else {
228 false
229 };
230 if is_serif
232 || (segment.link_ix.is_some()
233 && segments[segment.link_ix.unwrap() as usize]
234 .edge_ix
235 .is_some())
236 {
237 let (edge2_ix, segment2_ix) = if is_serif {
238 (edge.serif_ix, segment.serif_ix)
239 } else {
240 (edge.link_ix, segment.link_ix)
241 };
242 let edge2_ix = if let (Some(edge2_ix), Some(segment2_ix)) = (edge2_ix, segment2_ix)
243 {
244 let edge2 = &edges[edge2_ix as usize];
245 let edge_delta = (edge.fpos as i32 - edge2.fpos as i32).abs();
246 let segment2 = &segments[segment2_ix as usize];
247 let segment_delta = (segment.pos as i32 - segment2.pos as i32).abs();
248 if segment_delta < edge_delta {
249 segment2.edge_ix
250 } else {
251 Some(edge2_ix)
252 }
253 } else if let Some(segment2_ix) = segment2_ix {
254 segments[segment2_ix as usize].edge_ix
255 } else {
256 edge2_ix
257 };
258 if is_serif {
259 edges[edge_ix].serif_ix = edge2_ix;
260 edges[edge2_ix.unwrap() as usize].flags |= Edge::SERIF;
261 } else {
262 edges[edge_ix].link_ix = edge2_ix;
263 }
264 }
265 if segment_ix == last_segment_ix {
266 break;
267 }
268 segment_ix = next_segment_ix
269 .map(|ix| ix as usize)
270 .unwrap_or(last_segment_ix);
271 }
272 let edge = &mut edges[edge_ix];
273 edge.flags = Edge::NORMAL;
274 if roundness > 0 && roundness >= straightness {
275 edge.flags |= Edge::ROUND;
276 }
277 if edge.serif_ix.is_some() && edge.link_ix.is_some() {
279 edge.serif_ix = None;
280 }
281 }
282}
283
284pub(crate) fn compute_blue_edges(
290 axis: &mut Axis,
291 scale: &Scale,
292 unscaled_blues: &[UnscaledBlue],
293 blues: &[ScaledBlue],
294 group: ScriptGroup,
295) {
296 if axis.dim != Axis::VERTICAL && group == ScriptGroup::Default {
300 return;
301 }
302 let axis_scale = if axis.dim == Axis::HORIZONTAL {
303 scale.x_scale
304 } else {
305 scale.y_scale
306 };
307 let initial_best_dest = fixed_mul(scale.units_per_em / 40, axis_scale).min(64 / 2);
309 for edge in &mut axis.edges {
310 let mut best_blue = None;
311 let mut best_is_neutral = false;
312 let mut best_dist = initial_best_dest;
315 for (unscaled_blue, blue) in unscaled_blues.iter().zip(blues) {
316 if !blue.is_active {
318 continue;
319 }
320 let is_top = blue.zones.is_top_like();
321 let is_neutral = blue.zones.is_neutral();
322 let is_major_dir = edge.dir == axis.major_dir;
323 if is_top ^ is_major_dir || is_neutral {
325 let (ref_pos, matching_blue) = if group == ScriptGroup::Default {
327 (unscaled_blue.position, blue.position)
328 } else {
329 if (edge.fpos as i32 - unscaled_blue.position).abs()
333 > (edge.fpos as i32 - unscaled_blue.overshoot).abs()
334 {
335 (unscaled_blue.overshoot, blue.overshoot)
336 } else {
337 (unscaled_blue.position, blue.position)
338 }
339 };
340 let dist = fixed_mul((edge.fpos as i32 - ref_pos).abs(), axis_scale);
341 if dist < best_dist {
342 best_dist = dist;
343 best_blue = Some(matching_blue);
344 best_is_neutral = is_neutral;
345 }
346 if group == ScriptGroup::Default {
347 if edge.flags & Edge::ROUND != 0 && dist != 0 && !is_neutral {
351 let is_under_ref = (edge.fpos as i32) < unscaled_blue.position;
352 if is_top ^ is_under_ref {
353 let dist = fixed_mul(
354 (edge.fpos as i32 - unscaled_blue.overshoot).abs(),
355 axis_scale,
356 );
357 if dist < best_dist {
358 best_dist = dist;
359 best_blue = Some(blue.overshoot);
360 best_is_neutral = is_neutral;
361 }
362 }
363 }
364 }
365 }
366 }
367 if let Some(best_blue) = best_blue {
368 edge.blue_edge = Some(best_blue);
369 if best_is_neutral {
370 edge.flags |= Edge::NEUTRAL;
371 }
372 }
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use super::{
379 super::super::{
380 metrics::{self, ScaledWidth},
381 outline::Outline,
382 shape::{Shaper, ShaperMode},
383 style,
384 },
385 super::segments,
386 *,
387 };
388 use crate::{attribute::Style, MetadataProvider};
389 use raw::{types::GlyphId, FontRef, TableProvider};
390
391 #[test]
392 fn edges_default() {
393 let expected_h_edges = [
394 Edge {
395 fpos: 15,
396 opos: 15,
397 pos: 15,
398 flags: Edge::ROUND,
399 dir: Direction::Up,
400 blue_edge: None,
401 link_ix: Some(3),
402 serif_ix: None,
403 scale: 0,
404 first_ix: 1,
405 last_ix: 1,
406 },
407 Edge {
408 fpos: 123,
409 opos: 126,
410 pos: 126,
411 flags: 0,
412 dir: Direction::Up,
413 blue_edge: None,
414 link_ix: Some(2),
415 serif_ix: None,
416 scale: 0,
417 first_ix: 0,
418 last_ix: 0,
419 },
420 Edge {
421 fpos: 186,
422 opos: 190,
423 pos: 190,
424 flags: 0,
425 dir: Direction::Down,
426 blue_edge: None,
427 link_ix: Some(1),
428 serif_ix: None,
429 scale: 0,
430 first_ix: 4,
431 last_ix: 4,
432 },
433 Edge {
434 fpos: 205,
435 opos: 210,
436 pos: 210,
437 flags: Edge::ROUND,
438 dir: Direction::Down,
439 blue_edge: None,
440 link_ix: Some(0),
441 serif_ix: None,
442 scale: 0,
443 first_ix: 3,
444 last_ix: 3,
445 },
446 ];
447 let expected_v_edges = [
448 Edge {
449 fpos: -240,
450 opos: -246,
451 pos: -246,
452 flags: 0,
453 dir: Direction::Left,
454 blue_edge: Some(ScaledWidth {
455 scaled: -246,
456 fitted: -256,
457 }),
458 link_ix: None,
459 serif_ix: Some(1),
460 scale: 0,
461 first_ix: 3,
462 last_ix: 3,
463 },
464 Edge {
465 fpos: 481,
466 opos: 493,
467 pos: 493,
468 flags: 0,
469 dir: Direction::Left,
470 blue_edge: None,
471 link_ix: Some(2),
472 serif_ix: None,
473 scale: 0,
474 first_ix: 0,
475 last_ix: 0,
476 },
477 Edge {
478 fpos: 592,
479 opos: 606,
480 pos: 606,
481 flags: Edge::ROUND | Edge::SERIF,
482 dir: Direction::Right,
483 blue_edge: Some(ScaledWidth {
484 scaled: 606,
485 fitted: 576,
486 }),
487 link_ix: Some(1),
488 serif_ix: None,
489 scale: 0,
490 first_ix: 2,
491 last_ix: 2,
492 },
493 Edge {
494 fpos: 647,
495 opos: 663,
496 pos: 663,
497 flags: 0,
498 dir: Direction::Right,
499 blue_edge: None,
500 link_ix: None,
501 serif_ix: Some(2),
502 scale: 0,
503 first_ix: 1,
504 last_ix: 1,
505 },
506 ];
507 check_edges(
508 font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS,
509 GlyphId::new(9),
510 style::StyleClass::HEBR,
511 &expected_h_edges,
512 &expected_v_edges,
513 );
514 }
515
516 #[test]
517 fn edges_cjk() {
518 let expected_h_edges = [
519 Edge {
520 fpos: 138,
521 opos: 141,
522 pos: 141,
523 flags: 0,
524 dir: Direction::Up,
525 blue_edge: None,
526 link_ix: Some(1),
527 serif_ix: None,
528 scale: 0,
529 first_ix: 8,
530 last_ix: 8,
531 },
532 Edge {
533 fpos: 201,
534 opos: 206,
535 pos: 206,
536 flags: 0,
537 dir: Direction::Down,
538 blue_edge: None,
539 link_ix: Some(0),
540 serif_ix: None,
541 scale: 0,
542 first_ix: 7,
543 last_ix: 7,
544 },
545 Edge {
546 fpos: 458,
547 opos: 469,
548 pos: 469,
549 flags: 0,
550 dir: Direction::Down,
551 blue_edge: None,
552 link_ix: None,
553 serif_ix: None,
554 scale: 0,
555 first_ix: 2,
556 last_ix: 2,
557 },
558 Edge {
559 fpos: 569,
560 opos: 583,
561 pos: 583,
562 flags: 0,
563 dir: Direction::Down,
564 blue_edge: None,
565 link_ix: None,
566 serif_ix: None,
567 scale: 0,
568 first_ix: 6,
569 last_ix: 6,
570 },
571 Edge {
572 fpos: 670,
573 opos: 686,
574 pos: 686,
575 flags: 0,
576 dir: Direction::Up,
577 blue_edge: None,
578 link_ix: Some(6),
579 serif_ix: None,
580 scale: 0,
581 first_ix: 1,
582 last_ix: 1,
583 },
584 Edge {
585 fpos: 693,
586 opos: 710,
587 pos: 710,
588 flags: 0,
589 dir: Direction::Up,
590 blue_edge: None,
591 link_ix: None,
592 serif_ix: Some(7),
593 scale: 0,
594 first_ix: 4,
595 last_ix: 4,
596 },
597 Edge {
598 fpos: 731,
599 opos: 749,
600 pos: 749,
601 flags: 0,
602 dir: Direction::Down,
603 blue_edge: None,
604 link_ix: Some(4),
605 serif_ix: None,
606 scale: 0,
607 first_ix: 0,
608 last_ix: 0,
609 },
610 Edge {
611 fpos: 849,
612 opos: 869,
613 pos: 869,
614 flags: 0,
615 dir: Direction::Up,
616 blue_edge: None,
617 link_ix: Some(8),
618 serif_ix: None,
619 scale: 0,
620 first_ix: 5,
621 last_ix: 5,
622 },
623 Edge {
624 fpos: 911,
625 opos: 933,
626 pos: 933,
627 flags: 0,
628 dir: Direction::Down,
629 blue_edge: None,
630 link_ix: Some(7),
631 serif_ix: None,
632 scale: 0,
633 first_ix: 3,
634 last_ix: 3,
635 },
636 ];
637 let expected_v_edges = [
638 Edge {
639 fpos: -78,
640 opos: -80,
641 pos: -80,
642 flags: Edge::ROUND,
643 dir: Direction::Left,
644 blue_edge: Some(ScaledWidth {
645 scaled: -80,
646 fitted: -64,
647 }),
648 link_ix: None,
649 serif_ix: None,
650 scale: 0,
651 first_ix: 8,
652 last_ix: 8,
653 },
654 Edge {
655 fpos: 3,
656 opos: 3,
657 pos: 3,
658 flags: Edge::ROUND,
659 dir: Direction::Right,
660 blue_edge: None,
661 link_ix: None,
662 serif_ix: None,
663 scale: 0,
664 first_ix: 4,
665 last_ix: 4,
666 },
667 Edge {
668 fpos: 133,
669 opos: 136,
670 pos: 136,
671 flags: Edge::ROUND,
672 dir: Direction::Left,
673 blue_edge: None,
674 link_ix: None,
675 serif_ix: None,
676 scale: 0,
677 first_ix: 2,
678 last_ix: 2,
679 },
680 Edge {
681 fpos: 547,
682 opos: 560,
683 pos: 560,
684 flags: 0,
685 dir: Direction::Left,
686 blue_edge: None,
687 link_ix: None,
688 serif_ix: Some(5),
689 scale: 0,
690 first_ix: 6,
691 last_ix: 6,
692 },
693 Edge {
694 fpos: 576,
695 opos: 590,
696 pos: 590,
697 flags: 0,
698 dir: Direction::Right,
699 blue_edge: None,
700 link_ix: Some(5),
701 serif_ix: None,
702 scale: 0,
703 first_ix: 5,
704 last_ix: 5,
705 },
706 Edge {
707 fpos: 576,
708 opos: 590,
709 pos: 590,
710 flags: 0,
711 dir: Direction::Left,
712 blue_edge: None,
713 link_ix: Some(4),
714 serif_ix: None,
715 scale: 0,
716 first_ix: 7,
717 last_ix: 7,
718 },
719 Edge {
720 fpos: 729,
721 opos: 746,
722 pos: 746,
723 flags: 0,
724 dir: Direction::Left,
725 blue_edge: None,
726 link_ix: Some(7),
727 serif_ix: None,
728 scale: 0,
729 first_ix: 1,
730 last_ix: 1,
731 },
732 Edge {
733 fpos: 758,
734 opos: 776,
735 pos: 776,
736 flags: 0,
737 dir: Direction::Right,
738 blue_edge: None,
739 link_ix: Some(6),
740 serif_ix: None,
741 scale: 0,
742 first_ix: 0,
743 last_ix: 3,
744 },
745 Edge {
746 fpos: 788,
747 opos: 807,
748 pos: 807,
749 flags: Edge::ROUND,
750 dir: Direction::Left,
751 blue_edge: None,
752 link_ix: None,
753 serif_ix: None,
754 scale: 0,
755 first_ix: 9,
756 last_ix: 9,
757 },
758 ];
759 check_edges(
760 font_test_data::NOTOSERIFTC_AUTOHINT_METRICS,
761 GlyphId::new(9),
762 style::StyleClass::HANI,
763 &expected_h_edges,
764 &expected_v_edges,
765 );
766 }
767
768 fn check_edges(
769 font_data: &[u8],
770 glyph_id: GlyphId,
771 style_class: usize,
772 expected_h_edges: &[Edge],
773 expected_v_edges: &[Edge],
774 ) {
775 let font = FontRef::new(font_data).unwrap();
776 let shaper = Shaper::new(&font, ShaperMode::Nominal);
777 let class = &style::STYLE_CLASSES[style_class];
778 let unscaled_metrics =
779 metrics::compute_unscaled_style_metrics(&shaper, Default::default(), class);
780 let scale = metrics::Scale::new(
781 16.0,
782 font.head().unwrap().units_per_em() as i32,
783 Style::Normal,
784 Default::default(),
785 class.script.group,
786 );
787 let scaled_metrics = metrics::scale_style_metrics(&unscaled_metrics, scale);
788 let glyphs = font.outline_glyphs();
789 let glyph = glyphs.get(glyph_id).unwrap();
790 let mut outline = Outline::default();
791 outline.fill(&glyph, Default::default()).unwrap();
792 let mut axes = [
793 Axis::new(Axis::HORIZONTAL, outline.orientation),
794 Axis::new(Axis::VERTICAL, outline.orientation),
795 ];
796 for (dim, axis) in axes.iter_mut().enumerate() {
797 segments::compute_segments(&mut outline, axis, class.script.group);
798 segments::link_segments(
799 &outline,
800 axis,
801 scaled_metrics.axes[dim].scale,
802 class.script.group,
803 unscaled_metrics.axes[dim].max_width(),
804 );
805 compute_edges(
806 axis,
807 &scaled_metrics.axes[dim],
808 class.script.hint_top_to_bottom,
809 scaled_metrics.axes[1].scale,
810 class.script.group,
811 );
812 compute_blue_edges(
813 axis,
814 &scale,
815 &unscaled_metrics.axes[dim].blues,
816 &scaled_metrics.axes[dim].blues,
817 class.script.group,
818 );
819 }
820 assert_eq!(axes[Axis::HORIZONTAL].edges.as_slice(), expected_h_edges);
821 assert_eq!(axes[Axis::VERTICAL].edges.as_slice(), expected_v_edges);
822 }
823}