sdk-world v2026.11.05
PS2 SDK - world tier (copyleft-allowed, non-essential packages)
Loading...
Searching...
No Matches
afhints.c
1/****************************************************************************
2 *
3 * afhints.c
4 *
5 * Auto-fitter hinting routines (body).
6 *
7 * Copyright (C) 2003-2025 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
9 *
10 * This file is part of the FreeType project, and may only be used,
11 * modified, and distributed under the terms of the FreeType project
12 * license, LICENSE.TXT. By continuing to use, modify, or distribute
13 * this file you indicate that you have read the license and
14 * understand and accept it fully.
15 *
16 */
17
18
19#include "afhints.h"
20#include "aferrors.h"
23
24
25 /**************************************************************************
26 *
27 * The macro FT_COMPONENT is used in trace mode. It is an implicit
28 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
29 * messages during execution.
30 */
31#undef FT_COMPONENT
32#define FT_COMPONENT afhints
33
34
35 FT_LOCAL_DEF( void )
36 af_sort_pos( FT_UInt count,
37 FT_Pos* table )
38 {
39 FT_UInt i, j;
40 FT_Pos swap;
41
42
43 for ( i = 1; i < count; i++ )
44 {
45 for ( j = i; j > 0; j-- )
46 {
47 if ( table[j] >= table[j - 1] )
48 break;
49
50 swap = table[j];
51 table[j] = table[j - 1];
52 table[j - 1] = swap;
53 }
54 }
55 }
56
57
58 FT_LOCAL_DEF( void )
59 af_sort_and_quantize_widths( FT_UInt* count,
60 AF_Width table,
61 FT_Pos threshold )
62 {
63 FT_UInt i, j;
64 FT_UInt cur_idx;
65 FT_Pos cur_val;
66 FT_Pos sum;
67 AF_WidthRec swap;
68
69
70 if ( *count == 1 )
71 return;
72
73 /* sort */
74 for ( i = 1; i < *count; i++ )
75 {
76 for ( j = i; j > 0; j-- )
77 {
78 if ( table[j].org >= table[j - 1].org )
79 break;
80
81 swap = table[j];
82 table[j] = table[j - 1];
83 table[j - 1] = swap;
84 }
85 }
86
87 cur_idx = 0;
88 cur_val = table[cur_idx].org;
89
90 /* compute and use mean values for clusters not larger than */
91 /* `threshold'; this is very primitive and might not yield */
92 /* the best result, but normally, using reference character */
93 /* `o', `*count' is 2, so the code below is fully sufficient */
94 for ( i = 1; i < *count; i++ )
95 {
96 if ( table[i].org - cur_val > threshold ||
97 i == *count - 1 )
98 {
99 sum = 0;
100
101 /* fix loop for end of array */
102 if ( table[i].org - cur_val <= threshold &&
103 i == *count - 1 )
104 i++;
105
106 for ( j = cur_idx; j < i; j++ )
107 {
108 sum += table[j].org;
109 table[j].org = 0;
110 }
111 table[cur_idx].org = sum / (FT_Pos)j;
112
113 if ( i < *count - 1 )
114 {
115 cur_idx = i + 1;
116 cur_val = table[cur_idx].org;
117 }
118 }
119 }
120
121 cur_idx = 1;
122
123 /* compress array to remove zero values */
124 for ( i = 1; i < *count; i++ )
125 {
126 if ( table[i].org )
127 table[cur_idx++] = table[i];
128 }
129
130 *count = cur_idx;
131 }
132
133 /* Get new segment for given axis. */
134
135 FT_LOCAL_DEF( FT_Error )
136 af_axis_hints_new_segment( AF_AxisHints axis,
137 FT_Memory memory,
138 AF_Segment *asegment )
139 {
140 FT_Error error = FT_Err_Ok;
141 AF_Segment segment = NULL;
142
143
144 if ( axis->num_segments < AF_SEGMENTS_EMBEDDED )
145 {
146 if ( !axis->segments )
147 {
148 axis->segments = axis->embedded.segments;
149 axis->max_segments = AF_SEGMENTS_EMBEDDED;
150 }
151 }
152 else if ( axis->num_segments >= axis->max_segments )
153 {
154 FT_UInt old_max = axis->max_segments;
155 FT_UInt new_max = old_max;
156 FT_UInt big_max = FT_INT_MAX / sizeof ( *segment );
157
158
159 if ( old_max >= big_max )
160 {
161 error = FT_THROW( Out_Of_Memory );
162 goto Exit;
163 }
164
165 new_max += ( new_max >> 2 ) + 4;
166 if ( new_max < old_max || new_max > big_max )
167 new_max = big_max;
168
169 if ( axis->segments == axis->embedded.segments )
170 {
171 if ( FT_NEW_ARRAY( axis->segments, new_max ) )
172 goto Exit;
173 ft_memcpy( axis->segments, axis->embedded.segments,
174 sizeof ( axis->embedded.segments ) );
175 }
176 else
177 {
178 if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) )
179 goto Exit;
180 }
181
182 axis->max_segments = new_max;
183 }
184
185 segment = axis->segments + axis->num_segments++;
186
187 Exit:
188 *asegment = segment;
189 return error;
190 }
191
192
193 /* Get new edge for given axis, direction, and position, */
194 /* without initializing the edge itself. */
195
196 FT_LOCAL_DEF( FT_Error )
197 af_axis_hints_new_edge( AF_AxisHints axis,
198 FT_Int fpos,
199 AF_Direction dir,
200 FT_Bool top_to_bottom_hinting,
201 FT_Memory memory,
202 AF_Edge *anedge )
203 {
204 FT_Error error = FT_Err_Ok;
205 AF_Edge edge = NULL;
206 AF_Edge edges;
207
208
209 if ( axis->num_edges < AF_EDGES_EMBEDDED )
210 {
211 if ( !axis->edges )
212 {
213 axis->edges = axis->embedded.edges;
214 axis->max_edges = AF_EDGES_EMBEDDED;
215 }
216 }
217 else if ( axis->num_edges >= axis->max_edges )
218 {
219 FT_UInt old_max = axis->max_edges;
220 FT_UInt new_max = old_max;
221 FT_UInt big_max = FT_INT_MAX / sizeof ( *edge );
222
223
224 if ( old_max >= big_max )
225 {
226 error = FT_THROW( Out_Of_Memory );
227 goto Exit;
228 }
229
230 new_max += ( new_max >> 2 ) + 4;
231 if ( new_max < old_max || new_max > big_max )
232 new_max = big_max;
233
234 if ( axis->edges == axis->embedded.edges )
235 {
236 if ( FT_NEW_ARRAY( axis->edges, new_max ) )
237 goto Exit;
238 ft_memcpy( axis->edges, axis->embedded.edges,
239 sizeof ( axis->embedded.edges ) );
240 }
241 else
242 {
243 if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) )
244 goto Exit;
245 }
246
247 axis->max_edges = new_max;
248 }
249
250 edges = axis->edges;
251 edge = edges + axis->num_edges;
252
253 while ( edge > edges )
254 {
255 if ( top_to_bottom_hinting ? ( edge[-1].fpos > fpos )
256 : ( edge[-1].fpos < fpos ) )
257 break;
258
259 /* we want the edge with same position and minor direction */
260 /* to appear before those in the major one in the list */
261 if ( edge[-1].fpos == fpos && dir == axis->major_dir )
262 break;
263
264 edge[0] = edge[-1];
265 edge--;
266 }
267
268 axis->num_edges++;
269
270 Exit:
271 *anedge = edge;
272 return error;
273 }
274
275
276#ifdef FT_DEBUG_AUTOFIT
277
278#include FT_CONFIG_STANDARD_LIBRARY_H
279
280 /* The dump functions are used in the `ftgrid' demo program, too. */
281#define AF_DUMP( varformat ) \
282 do \
283 { \
284 if ( to_stdout ) \
285 printf varformat; \
286 else \
287 FT_TRACE7( varformat ); \
288 } while ( 0 )
289
290
291 static const char*
292 af_dir_str( AF_Direction dir )
293 {
294 const char* result;
295
296
297 switch ( dir )
298 {
299 case AF_DIR_UP:
300 result = "up";
301 break;
302 case AF_DIR_DOWN:
303 result = "down";
304 break;
305 case AF_DIR_LEFT:
306 result = "left";
307 break;
308 case AF_DIR_RIGHT:
309 result = "right";
310 break;
311 default:
312 result = "none";
313 }
314
315 return result;
316 }
317
318
319#define AF_INDEX_NUM( ptr, base ) (int)( (ptr) ? ( (ptr) - (base) ) : -1 )
320
321
322 static char*
323 af_print_idx( char* p,
324 size_t n,
325 int idx )
326 {
327 if ( idx == -1 )
328 {
329 p[0] = '-';
330 p[1] = '-';
331 p[2] = '\0';
332 }
333 else
334 ft_snprintf( p, n, "%d", idx );
335
336 return p;
337 }
338
339
340 static int
341 af_get_segment_index( AF_GlyphHints hints,
342 int point_idx,
343 int dimension )
344 {
345 AF_AxisHints axis = &hints->axis[dimension];
346 AF_Point point = hints->points + point_idx;
347 AF_Segment segments = axis->segments;
348 AF_Segment limit = segments + axis->num_segments;
349 AF_Segment segment;
350
351
352 for ( segment = segments; segment < limit; segment++ )
353 {
354 if ( segment->first <= segment->last )
355 {
356 if ( point >= segment->first && point <= segment->last )
357 break;
358 }
359 else
360 {
361 AF_Point p = segment->first;
362
363
364 for (;;)
365 {
366 if ( point == p )
367 goto Exit;
368
369 if ( p == segment->last )
370 break;
371
372 p = p->next;
373 }
374 }
375 }
376
377 Exit:
378 if ( segment == limit )
379 return -1;
380
381 return (int)( segment - segments );
382 }
383
384
385 static int
386 af_get_edge_index( AF_GlyphHints hints,
387 int segment_idx,
388 int dimension )
389 {
390 AF_AxisHints axis = &hints->axis[dimension];
391 AF_Edge edges = axis->edges;
392 AF_Segment segment = axis->segments + segment_idx;
393
394
395 return segment_idx == -1 ? -1 : AF_INDEX_NUM( segment->edge, edges );
396 }
397
398
399 static int
400 af_get_strong_edge_index( AF_GlyphHints hints,
401 AF_Edge* strong_edges,
402 int dimension )
403 {
404 AF_AxisHints axis = &hints->axis[dimension];
405 AF_Edge edges = axis->edges;
406
407
408 return AF_INDEX_NUM( strong_edges[dimension], edges );
409 }
410
411
412#ifdef __cplusplus
413 extern "C" {
414#endif
415 void
416 af_glyph_hints_dump_points( AF_GlyphHints hints,
417 FT_Bool to_stdout )
418 {
419 AF_Point points = hints->points;
420 AF_Point limit = points + hints->num_points;
421 AF_Point* contour = hints->contours;
422 AF_Point* climit = contour + hints->num_contours;
423 AF_Point point;
424
425
426 AF_DUMP(( "Table of points:\n" ));
427
428 if ( hints->num_points )
429 {
430 AF_DUMP(( " index hedge hseg vedge vseg flags "
431 /* " XXXXX XXXXX XXXXX XXXXX XXXXX XXXXXX" */
432 " xorg yorg xscale yscale xfit yfit "
433 /* " XXXXX XXXXX XXXX.XX XXXX.XX XXXX.XX XXXX.XX" */
434 " hbef haft vbef vaft" ));
435 /* " XXXXX XXXXX XXXXX XXXXX" */
436 }
437 else
438 AF_DUMP(( " (none)\n" ));
439
440 for ( point = points; point < limit; point++ )
441 {
442 int point_idx = AF_INDEX_NUM( point, points );
443 int segment_idx_0 = af_get_segment_index( hints, point_idx, 0 );
444 int segment_idx_1 = af_get_segment_index( hints, point_idx, 1 );
445
446 char buf1[16], buf2[16], buf3[16], buf4[16];
447 char buf5[16], buf6[16], buf7[16], buf8[16];
448
449
450 /* insert extra newline at the beginning of a contour */
451 if ( contour < climit && *contour == point )
452 {
453 AF_DUMP(( "\n" ));
454 contour++;
455 }
456
457 AF_DUMP(( " %5d %5s %5s %5s %5s %s"
458 " %5d %5d %7.2f %7.2f %7.2f %7.2f"
459 " %5s %5s %5s %5s\n",
460 point_idx,
461 af_print_idx( buf1, 16,
462 af_get_edge_index( hints, segment_idx_1, 1 ) ),
463 af_print_idx( buf2, 16, segment_idx_1 ),
464 af_print_idx( buf3, 16,
465 af_get_edge_index( hints, segment_idx_0, 0 ) ),
466 af_print_idx( buf4, 16, segment_idx_0 ),
467 ( point->flags & AF_FLAG_NEAR )
468 ? " near "
469 : ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
470 ? " weak "
471 : "strong",
472
473 point->fx,
474 point->fy,
475 (double)point->ox / 64,
476 (double)point->oy / 64,
477 (double)point->x / 64,
478 (double)point->y / 64,
479
480 af_print_idx( buf5, 16,
481 af_get_strong_edge_index( hints,
482 point->before,
483 1 ) ),
484 af_print_idx( buf6, 16,
485 af_get_strong_edge_index( hints,
486 point->after,
487 1 ) ),
488 af_print_idx( buf7, 16,
489 af_get_strong_edge_index( hints,
490 point->before,
491 0 ) ),
492 af_print_idx( buf8, 16,
493 af_get_strong_edge_index( hints,
494 point->after,
495 0 ) ) ));
496 }
497 AF_DUMP(( "\n" ));
498 }
499#ifdef __cplusplus
500 }
501#endif
502
503
504 static const char*
505 af_edge_flags_to_string( FT_UInt flags )
506 {
507 static char temp[32];
508 int pos = 0;
509
510
511 if ( flags & AF_EDGE_ROUND )
512 {
513 ft_memcpy( temp + pos, "round", 5 );
514 pos += 5;
515 }
516 if ( flags & AF_EDGE_SERIF )
517 {
518 if ( pos > 0 )
519 temp[pos++] = ' ';
520 ft_memcpy( temp + pos, "serif", 5 );
521 pos += 5;
522 }
523 if ( pos == 0 )
524 return "normal";
525
526 temp[pos] = '\0';
527
528 return temp;
529 }
530
531
532 /* Dump the array of linked segments. */
533
534#ifdef __cplusplus
535 extern "C" {
536#endif
537 void
538 af_glyph_hints_dump_segments( AF_GlyphHints hints,
539 FT_Bool to_stdout )
540 {
541 FT_Int dimension;
542
543
544 for ( dimension = 1; dimension >= 0; dimension-- )
545 {
546 AF_AxisHints axis = &hints->axis[dimension];
547 AF_Point points = hints->points;
548 AF_Edge edges = axis->edges;
549 AF_Segment segments = axis->segments;
550 AF_Segment limit = segments + axis->num_segments;
551 AF_Segment seg;
552
553 char buf1[16], buf2[16], buf3[16];
554
555
556 AF_DUMP(( "Table of %s segments:\n",
557 dimension == AF_DIMENSION_HORZ ? "vertical"
558 : "horizontal" ));
559 if ( axis->num_segments )
560 {
561 AF_DUMP(( " index pos delta dir from to "
562 /* " XXXXX XXXXX XXXXX XXXXX XXXX XXXX" */
563 " link serif edge"
564 /* " XXXX XXXXX XXXX" */
565 " height extra flags\n" ));
566 /* " XXXXXX XXXXX XXXXXXXXXXX" */
567 }
568 else
569 AF_DUMP(( " (none)\n" ));
570
571 for ( seg = segments; seg < limit; seg++ )
572 AF_DUMP(( " %5d %5d %5d %5s %4d %4d"
573 " %4s %5s %4s"
574 " %6d %5d %11s\n",
575 AF_INDEX_NUM( seg, segments ),
576 seg->pos,
577 seg->delta,
578 af_dir_str( (AF_Direction)seg->dir ),
579 AF_INDEX_NUM( seg->first, points ),
580 AF_INDEX_NUM( seg->last, points ),
581
582 af_print_idx( buf1, 16,
583 AF_INDEX_NUM( seg->link, segments ) ),
584 af_print_idx( buf2, 16,
585 AF_INDEX_NUM( seg->serif, segments ) ),
586 af_print_idx( buf3, 16,
587 AF_INDEX_NUM( seg->edge, edges ) ),
588
589 seg->height,
590 seg->height - ( seg->max_coord - seg->min_coord ),
591 af_edge_flags_to_string( seg->flags ) ));
592 AF_DUMP(( "\n" ));
593 }
594 }
595#ifdef __cplusplus
596 }
597#endif
598
599
600 /* Fetch number of segments. */
601
602#ifdef __cplusplus
603 extern "C" {
604#endif
605 FT_Error
606 af_glyph_hints_get_num_segments( AF_GlyphHints hints,
607 FT_Int dimension,
608 FT_UInt* num_segments )
609 {
610 AF_Dimension dim;
611 AF_AxisHints axis;
612
613
614 dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
615
616 axis = &hints->axis[dim];
617 *num_segments = axis->num_segments;
618
619 return FT_Err_Ok;
620 }
621#ifdef __cplusplus
622 }
623#endif
624
625
626 /* Fetch offset of segments into user supplied offset array. */
627
628#ifdef __cplusplus
629 extern "C" {
630#endif
631 FT_Error
632 af_glyph_hints_get_segment_offset( AF_GlyphHints hints,
633 FT_Int dimension,
634 FT_UInt idx,
635 FT_Pos *offset,
636 FT_Bool *is_blue,
637 FT_Pos *blue_offset )
638 {
639 AF_Dimension dim;
640 AF_AxisHints axis;
641 AF_Segment seg;
642
643
644 if ( !offset )
645 return FT_THROW( Invalid_Argument );
646
647 dim = ( dimension == 0 ) ? AF_DIMENSION_HORZ : AF_DIMENSION_VERT;
648
649 axis = &hints->axis[dim];
650
651 if ( idx >= axis->num_segments )
652 return FT_THROW( Invalid_Argument );
653
654 seg = &axis->segments[idx];
655 *offset = ( dim == AF_DIMENSION_HORZ ) ? seg->first->fx
656 : seg->first->fy;
657 if ( seg->edge )
658 *is_blue = FT_BOOL( seg->edge->blue_edge );
659 else
660 *is_blue = FALSE;
661
662 if ( *is_blue )
663 *blue_offset = seg->edge->blue_edge->org;
664 else
665 *blue_offset = 0;
666
667 return FT_Err_Ok;
668 }
669#ifdef __cplusplus
670 }
671#endif
672
673
674 /* Dump the array of linked edges. */
675
676#ifdef __cplusplus
677 extern "C" {
678#endif
679 void
680 af_glyph_hints_dump_edges( AF_GlyphHints hints,
681 FT_Bool to_stdout )
682 {
683 FT_Int dimension;
684
685
686 for ( dimension = 1; dimension >= 0; dimension-- )
687 {
688 AF_AxisHints axis = &hints->axis[dimension];
689 AF_Edge edges = axis->edges;
690 AF_Edge limit = edges + axis->num_edges;
691 AF_Edge edge;
692
693 char buf1[16], buf2[16];
694
695
696 /*
697 * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges
698 * since they have a constant X coordinate.
699 */
700 if ( dimension == AF_DIMENSION_HORZ )
701 AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
702 "vertical",
703 65536 * 64 / (double)hints->x_scale,
704 10 * (double)hints->x_scale / 65536 / 64 ));
705 else
706 AF_DUMP(( "Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
707 "horizontal",
708 65536 * 64 / (double)hints->y_scale,
709 10 * (double)hints->y_scale / 65536 / 64 ));
710
711 if ( axis->num_edges )
712 {
713 AF_DUMP(( " index pos dir link serif"
714 /* " XXXXX XXXX.XX XXXXX XXXX XXXXX" */
715 " blue opos pos flags\n" ));
716 /* " X XXXX.XX XXXX.XX XXXXXXXXXXX" */
717 }
718 else
719 AF_DUMP(( " (none)\n" ));
720
721 for ( edge = edges; edge < limit; edge++ )
722 AF_DUMP(( " %5d %7.2f %5s %4s %5s"
723 " %c %7.2f %7.2f %11s\n",
724 AF_INDEX_NUM( edge, edges ),
725 (double)(int)edge->opos / 64,
726 af_dir_str( (AF_Direction)edge->dir ),
727 af_print_idx( buf1, 16,
728 AF_INDEX_NUM( edge->link, edges ) ),
729 af_print_idx( buf2, 16,
730 AF_INDEX_NUM( edge->serif, edges ) ),
731
732 edge->blue_edge ? 'y' : 'n',
733 (double)edge->opos / 64,
734 (double)edge->pos / 64,
735 af_edge_flags_to_string( edge->flags ) ));
736 AF_DUMP(( "\n" ));
737 }
738 }
739#ifdef __cplusplus
740 }
741#endif
742
743#undef AF_DUMP
744
745#endif /* !FT_DEBUG_AUTOFIT */
746
747
748 /* Compute the direction value of a given vector. */
749
750 FT_LOCAL_DEF( AF_Direction )
751 af_direction_compute( FT_Pos dx,
752 FT_Pos dy )
753 {
754 FT_Pos ll, ss; /* long and short arm lengths */
755 AF_Direction dir; /* candidate direction */
756
757
758 if ( dy >= dx )
759 {
760 if ( dy >= -dx )
761 {
762 dir = AF_DIR_UP;
763 ll = dy;
764 ss = dx;
765 }
766 else
767 {
768 dir = AF_DIR_LEFT;
769 ll = -dx;
770 ss = dy;
771 }
772 }
773 else /* dy < dx */
774 {
775 if ( dy >= -dx )
776 {
777 dir = AF_DIR_RIGHT;
778 ll = dx;
779 ss = dy;
780 }
781 else
782 {
783 dir = AF_DIR_DOWN;
784 ll = -dy;
785 ss = dx;
786 }
787 }
788
789 /* return no direction if arm lengths do not differ enough */
790 /* (value 14 is heuristic, corresponding to approx. 4.1 degrees) */
791 /* the long arm is never negative */
792 if ( ll <= 14 * FT_ABS( ss ) )
793 dir = AF_DIR_NONE;
794
795 return dir;
796 }
797
798
799 FT_LOCAL_DEF( void )
800 af_glyph_hints_init( AF_GlyphHints hints,
801 FT_Memory memory )
802 {
803 /* no need to initialize the embedded items */
804 FT_MEM_ZERO( hints, sizeof ( *hints ) - sizeof ( hints->embedded ) );
805 hints->memory = memory;
806 }
807
808
809 FT_LOCAL_DEF( void )
810 af_glyph_hints_done( AF_GlyphHints hints )
811 {
812 FT_Memory memory;
813 int dim;
814
815
816 if ( !( hints && hints->memory ) )
817 return;
818
819 memory = hints->memory;
820
821 /*
822 * note that we don't need to free the segment and edge
823 * buffers since they are really within the hints->points array
824 */
825 for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
826 {
827 AF_AxisHints axis = &hints->axis[dim];
828
829
830 axis->num_segments = 0;
831 axis->max_segments = 0;
832 if ( axis->segments != axis->embedded.segments )
833 FT_FREE( axis->segments );
834
835 axis->num_edges = 0;
836 axis->max_edges = 0;
837 if ( axis->edges != axis->embedded.edges )
838 FT_FREE( axis->edges );
839 }
840
841 if ( hints->contours != hints->embedded.contours )
842 FT_FREE( hints->contours );
843 if ( hints->contour_y_minima != hints->embedded.contour_y_minima )
844 FT_FREE( hints->contour_y_minima );
845 if ( hints->contour_y_maxima != hints->embedded.contour_y_maxima )
846 FT_FREE( hints->contour_y_maxima );
847 hints->max_contours = 0;
848 hints->num_contours = 0;
849
850 if ( hints->points != hints->embedded.points )
851 FT_FREE( hints->points );
852 hints->max_points = 0;
853 hints->num_points = 0;
854
855 hints->memory = NULL;
856 }
857
858
859 /* Reset metrics. */
860
861 FT_LOCAL_DEF( void )
862 af_glyph_hints_rescale( AF_GlyphHints hints,
863 AF_StyleMetrics metrics )
864 {
865 hints->metrics = metrics;
866 hints->scaler_flags = metrics->scaler.flags;
867 }
868
869
870 /* Recompute all AF_Point in AF_GlyphHints from the definitions */
871 /* in a source outline. */
872
873 FT_LOCAL_DEF( FT_Error )
874 af_glyph_hints_reload( AF_GlyphHints hints,
875 FT_Outline* outline )
876 {
877 FT_Error error = FT_Err_Ok;
878 AF_Point points;
879 FT_Int old_max, new_max;
880 FT_Fixed x_scale = hints->x_scale;
881 FT_Fixed y_scale = hints->y_scale;
882 FT_Pos x_delta = hints->x_delta;
883 FT_Pos y_delta = hints->y_delta;
884 FT_Memory memory = hints->memory;
885
886
887 hints->num_points = 0;
888 hints->num_contours = 0;
889
890 hints->axis[0].num_segments = 0;
891 hints->axis[0].num_edges = 0;
892 hints->axis[1].num_segments = 0;
893 hints->axis[1].num_edges = 0;
894
895 /* first of all, reallocate the contours array if necessary */
896 new_max = outline->n_contours;
897 old_max = hints->max_contours;
898
899 if ( new_max <= AF_CONTOURS_EMBEDDED )
900 {
901 if ( !hints->contours )
902 {
903 hints->contours = hints->embedded.contours;
904 hints->contour_y_minima = hints->embedded.contour_y_minima;
905 hints->contour_y_maxima = hints->embedded.contour_y_maxima;
906
907 hints->max_contours = AF_CONTOURS_EMBEDDED;
908 }
909 }
910 else if ( new_max > old_max )
911 {
912 if ( hints->contours == hints->embedded.contours )
913 {
914 hints->contours = NULL;
915 hints->contour_y_minima = NULL;
916 hints->contour_y_maxima = NULL;
917 }
918
919 new_max = ( new_max + 3 ) & ~3; /* round up to a multiple of 4 */
920
921 if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
922 goto Exit;
923 if ( FT_RENEW_ARRAY( hints->contour_y_minima, old_max, new_max ) )
924 goto Exit;
925 if ( FT_RENEW_ARRAY( hints->contour_y_maxima, old_max, new_max ) )
926 goto Exit;
927
928 hints->max_contours = new_max;
929 }
930
931 /*
932 * then reallocate the points arrays if necessary --
933 * note that we reserve two additional point positions, used to
934 * hint metrics appropriately
935 */
936 new_max = outline->n_points + 2;
937 old_max = hints->max_points;
938
939 if ( new_max <= AF_POINTS_EMBEDDED )
940 {
941 if ( !hints->points )
942 {
943 hints->points = hints->embedded.points;
944 hints->max_points = AF_POINTS_EMBEDDED;
945 }
946 }
947 else if ( new_max > old_max )
948 {
949 if ( hints->points == hints->embedded.points )
950 hints->points = NULL;
951
952 new_max = ( new_max + 2 + 7 ) & ~7; /* round up to a multiple of 8 */
953
954 if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) )
955 goto Exit;
956
957 hints->max_points = new_max;
958 }
959
960 hints->num_points = outline->n_points;
961 hints->num_contours = outline->n_contours;
962
963 /* We can't rely on the value of `FT_Outline.flags' to know the fill */
964 /* direction used for a glyph, given that some fonts are broken (e.g., */
965 /* the Arphic ones). We thus recompute it each time we need to. */
966 /* */
967 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP;
968 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT;
969
970 if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT )
971 {
972 hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN;
973 hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT;
974 }
975
976 hints->x_scale = x_scale;
977 hints->y_scale = y_scale;
978 hints->x_delta = x_delta;
979 hints->y_delta = y_delta;
980
981 points = hints->points;
982 if ( hints->num_points == 0 )
983 goto Exit;
984
985 {
986 AF_Point point;
987 AF_Point point_limit = points + hints->num_points;
988
989 /* value 20 in `near_limit' is heuristic */
990 FT_UInt units_per_em = hints->metrics->scaler.face->units_per_EM;
991 FT_Int near_limit = 20 * units_per_em / 2048;
992
993
994 /* compute coordinates & Bezier flags, next and prev */
995 {
996 FT_Vector* vec = outline->points;
997 FT_Byte* tag = outline->tags;
998 FT_UShort endpoint = outline->contours[0];
999 AF_Point end = points + endpoint;
1000 AF_Point prev = end;
1001 FT_Int contour_index = 0;
1002
1003
1004 for ( point = points; point < point_limit; point++, vec++, tag++ )
1005 {
1006 FT_Pos out_x, out_y;
1007
1008
1009 point->in_dir = (FT_Char)AF_DIR_NONE;
1010 point->out_dir = (FT_Char)AF_DIR_NONE;
1011
1012 point->fx = (FT_Short)vec->x;
1013 point->fy = (FT_Short)vec->y;
1014 point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta;
1015 point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta;
1016
1017 end->fx = (FT_Short)outline->points[endpoint].x;
1018 end->fy = (FT_Short)outline->points[endpoint].y;
1019
1020 switch ( FT_CURVE_TAG( *tag ) )
1021 {
1022 case FT_CURVE_TAG_CONIC:
1023 point->flags = AF_FLAG_CONIC;
1024 break;
1025 case FT_CURVE_TAG_CUBIC:
1026 point->flags = AF_FLAG_CUBIC;
1027 break;
1028 default:
1029 point->flags = AF_FLAG_NONE;
1030 }
1031
1032 out_x = point->fx - prev->fx;
1033 out_y = point->fy - prev->fy;
1034
1035 if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
1036 prev->flags |= AF_FLAG_NEAR;
1037
1038 point->prev = prev;
1039 prev->next = point;
1040 prev = point;
1041
1042 if ( point == end )
1043 {
1044 if ( ++contour_index < outline->n_contours )
1045 {
1046 endpoint = outline->contours[contour_index];
1047 end = points + endpoint;
1048 prev = end;
1049 }
1050 }
1051
1052#ifdef FT_DEBUG_AUTOFIT
1053 point->before[0] = NULL;
1054 point->before[1] = NULL;
1055 point->after[0] = NULL;
1056 point->after[1] = NULL;
1057#endif
1058
1059 }
1060 }
1061
1062 /* set up the contours array */
1063 {
1064 AF_Point* contour = hints->contours;
1065 AF_Point* contour_limit = contour + hints->num_contours;
1066 FT_UShort* end = outline->contours;
1067 FT_Int idx = 0;
1068
1069
1070 for ( ; contour < contour_limit; contour++, end++ )
1071 {
1072 contour[0] = points + idx;
1073 idx = *end + 1;
1074 }
1075 }
1076
1077 {
1078 /*
1079 * Compute directions of `in' and `out' vectors.
1080 *
1081 * Note that distances between points that are very near to each
1082 * other are accumulated. In other words, the auto-hinter either
1083 * prepends the small vectors between near points to the first
1084 * non-near vector, or the sum of small vector lengths exceeds a
1085 * threshold, thus `grouping' the small vectors. All intermediate
1086 * points are tagged as weak; the directions are adjusted also to
1087 * be equal to the accumulated one.
1088 */
1089
1090 FT_Int near_limit2 = 2 * near_limit - 1;
1091
1092 AF_Point* contour;
1093 AF_Point* contour_limit = hints->contours + hints->num_contours;
1094
1095
1096 for ( contour = hints->contours; contour < contour_limit; contour++ )
1097 {
1098 AF_Point first = *contour;
1099 AF_Point next, prev, curr;
1100
1101 FT_Pos out_x, out_y;
1102
1103
1104 /* since the first point of a contour could be part of a */
1105 /* series of near points, go backwards to find the first */
1106 /* non-near point and adjust `first' */
1107
1108 point = first;
1109 prev = first->prev;
1110
1111 while ( prev != first )
1112 {
1113 out_x = point->fx - prev->fx;
1114 out_y = point->fy - prev->fy;
1115
1116 /*
1117 * We use Taxicab metrics to measure the vector length.
1118 *
1119 * Note that the accumulated distances so far could have the
1120 * opposite direction of the distance measured here. For this
1121 * reason we use `near_limit2' for the comparison to get a
1122 * non-near point even in the worst case.
1123 */
1124 if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit2 )
1125 break;
1126
1127 point = prev;
1128 prev = prev->prev;
1129 }
1130
1131 /* adjust first point */
1132 first = point;
1133
1134 /* now loop over all points of the contour to get */
1135 /* `in' and `out' vector directions */
1136
1137 curr = first;
1138
1139 /*
1140 * We abuse the `u' and `v' fields to store index deltas to the
1141 * next and previous non-near point, respectively.
1142 *
1143 * To avoid problems with not having non-near points, we point to
1144 * `first' by default as the next non-near point.
1145 *
1146 */
1147 curr->u = (FT_Pos)( first - curr );
1148 first->v = -curr->u;
1149
1150 out_x = 0;
1151 out_y = 0;
1152
1153 next = first;
1154 do
1155 {
1156 AF_Direction out_dir;
1157
1158
1159 point = next;
1160 next = point->next;
1161
1162 out_x += next->fx - point->fx;
1163 out_y += next->fy - point->fy;
1164
1165 if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
1166 {
1167 next->flags |= AF_FLAG_WEAK_INTERPOLATION;
1168 continue;
1169 }
1170
1171 curr->u = (FT_Pos)( next - curr );
1172 next->v = -curr->u;
1173
1174 out_dir = af_direction_compute( out_x, out_y );
1175
1176 /* adjust directions for all points inbetween; */
1177 /* the loop also updates position of `curr' */
1178 curr->out_dir = (FT_Char)out_dir;
1179 for ( curr = curr->next; curr != next; curr = curr->next )
1180 {
1181 curr->in_dir = (FT_Char)out_dir;
1182 curr->out_dir = (FT_Char)out_dir;
1183 }
1184 next->in_dir = (FT_Char)out_dir;
1185
1186 curr->u = (FT_Pos)( first - curr );
1187 first->v = -curr->u;
1188
1189 out_x = 0;
1190 out_y = 0;
1191
1192 } while ( next != first );
1193 }
1194
1195 /*
1196 * The next step is to `simplify' an outline's topology so that we
1197 * can identify local extrema more reliably: A series of
1198 * non-horizontal or non-vertical vectors pointing into the same
1199 * quadrant are handled as a single, long vector. From a
1200 * topological point of the view, the intermediate points are of no
1201 * interest and thus tagged as weak.
1202 */
1203
1204 for ( point = points; point < point_limit; point++ )
1205 {
1206 if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
1207 continue;
1208
1209 if ( point->in_dir == AF_DIR_NONE &&
1210 point->out_dir == AF_DIR_NONE )
1211 {
1212 /* check whether both vectors point into the same quadrant */
1213
1214 FT_Pos in_x, in_y;
1215 FT_Pos out_x, out_y;
1216
1217 AF_Point next_u = point + point->u;
1218 AF_Point prev_v = point + point->v;
1219
1220
1221 in_x = point->fx - prev_v->fx;
1222 in_y = point->fy - prev_v->fy;
1223
1224 out_x = next_u->fx - point->fx;
1225 out_y = next_u->fy - point->fy;
1226
1227 if ( ( in_x ^ out_x ) >= 0 && ( in_y ^ out_y ) >= 0 )
1228 {
1229 /* yes, so tag current point as weak */
1230 /* and update index deltas */
1231
1232 point->flags |= AF_FLAG_WEAK_INTERPOLATION;
1233
1234 prev_v->u = (FT_Pos)( next_u - prev_v );
1235 next_u->v = -prev_v->u;
1236 }
1237 }
1238 }
1239
1240 /*
1241 * Finally, check for remaining weak points. Everything else not
1242 * collected in edges so far is then implicitly classified as strong
1243 * points.
1244 */
1245
1246 for ( point = points; point < point_limit; point++ )
1247 {
1248 if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
1249 continue;
1250
1251 if ( point->flags & AF_FLAG_CONTROL )
1252 {
1253 /* control points are always weak */
1254 Is_Weak_Point:
1255 point->flags |= AF_FLAG_WEAK_INTERPOLATION;
1256 }
1257 else if ( point->out_dir == point->in_dir )
1258 {
1259 if ( point->out_dir != AF_DIR_NONE )
1260 {
1261 /* current point lies on a horizontal or */
1262 /* vertical segment (but doesn't start or end it) */
1263 goto Is_Weak_Point;
1264 }
1265
1266 {
1267 AF_Point next_u = point + point->u;
1268 AF_Point prev_v = point + point->v;
1269
1270
1271 if ( ft_corner_is_flat( point->fx - prev_v->fx,
1272 point->fy - prev_v->fy,
1273 next_u->fx - point->fx,
1274 next_u->fy - point->fy ) )
1275 {
1276 /* either the `in' or the `out' vector is much more */
1277 /* dominant than the other one, so tag current point */
1278 /* as weak and update index deltas */
1279
1280 prev_v->u = (FT_Pos)( next_u - prev_v );
1281 next_u->v = -prev_v->u;
1282
1283 goto Is_Weak_Point;
1284 }
1285 }
1286 }
1287 else if ( point->in_dir == -point->out_dir )
1288 {
1289 /* current point forms a spike */
1290 goto Is_Weak_Point;
1291 }
1292 }
1293 }
1294 }
1295
1296 Exit:
1297 return error;
1298 }
1299
1300
1301 /* Store the hinted outline in an FT_Outline structure. */
1302
1303 FT_LOCAL_DEF( void )
1304 af_glyph_hints_save( AF_GlyphHints hints,
1305 FT_Outline* outline )
1306 {
1307 AF_Point point = hints->points;
1308 AF_Point limit = point + hints->num_points;
1309 FT_Vector* vec = outline->points;
1310 FT_Byte* tag = outline->tags;
1311
1312
1313 for ( ; point < limit; point++, vec++, tag++ )
1314 {
1315 vec->x = point->x;
1316 vec->y = point->y;
1317
1318 if ( point->flags & AF_FLAG_CONIC )
1319 tag[0] = FT_CURVE_TAG_CONIC;
1320 else if ( point->flags & AF_FLAG_CUBIC )
1321 tag[0] = FT_CURVE_TAG_CUBIC;
1322 else
1323 tag[0] = FT_CURVE_TAG_ON;
1324 }
1325 }
1326
1327
1328 /****************************************************************
1329 *
1330 * EDGE POINT GRID-FITTING
1331 *
1332 ****************************************************************/
1333
1334
1335 /* Align all points of an edge to the same coordinate value, */
1336 /* either horizontally or vertically. */
1337
1338 FT_LOCAL_DEF( void )
1339 af_glyph_hints_align_edge_points( AF_GlyphHints hints,
1340 AF_Dimension dim )
1341 {
1342 AF_AxisHints axis = &hints->axis[dim];
1343 AF_Segment segments = axis->segments;
1344 AF_Segment segment_limit = FT_OFFSET( segments, axis->num_segments );
1345 AF_Segment seg;
1346
1347
1348 if ( dim == AF_DIMENSION_HORZ )
1349 {
1350 for ( seg = segments; seg < segment_limit; seg++ )
1351 {
1352 AF_Edge edge = seg->edge;
1353 AF_Point point, first, last;
1354
1355
1356 if ( !edge )
1357 continue;
1358
1359 first = seg->first;
1360 last = seg->last;
1361 point = first;
1362 for (;;)
1363 {
1364 point->x = edge->pos;
1365 point->flags |= AF_FLAG_TOUCH_X;
1366
1367 if ( point == last )
1368 break;
1369
1370 point = point->next;
1371 }
1372 }
1373 }
1374 else
1375 {
1376 for ( seg = segments; seg < segment_limit; seg++ )
1377 {
1378 AF_Edge edge = seg->edge;
1379 AF_Point point, first, last;
1380
1381
1382 if ( !edge )
1383 continue;
1384
1385 first = seg->first;
1386 last = seg->last;
1387 point = first;
1388 for (;;)
1389 {
1390 point->y = edge->pos;
1391 point->flags |= AF_FLAG_TOUCH_Y;
1392
1393 if ( point == last )
1394 break;
1395
1396 point = point->next;
1397 }
1398 }
1399 }
1400 }
1401
1402
1403 /****************************************************************
1404 *
1405 * STRONG POINT INTERPOLATION
1406 *
1407 ****************************************************************/
1408
1409
1410 /* Hint the strong points -- this is equivalent to the TrueType `IP' */
1411 /* hinting instruction. */
1412
1413 FT_LOCAL_DEF( void )
1414 af_glyph_hints_align_strong_points( AF_GlyphHints hints,
1415 AF_Dimension dim )
1416 {
1417 AF_Point points = hints->points;
1418 AF_Point point_limit = points + hints->num_points;
1419 AF_AxisHints axis = &hints->axis[dim];
1420 AF_Edge edges = axis->edges;
1421 AF_Edge edge_limit = FT_OFFSET( edges, axis->num_edges );
1422 FT_UInt touch_flag;
1423
1424
1425 if ( dim == AF_DIMENSION_HORZ )
1426 touch_flag = AF_FLAG_TOUCH_X;
1427 else
1428 touch_flag = AF_FLAG_TOUCH_Y;
1429
1430 if ( edges < edge_limit )
1431 {
1432 AF_Point point;
1433 AF_Edge edge;
1434
1435
1436 for ( point = points; point < point_limit; point++ )
1437 {
1438 FT_Pos u, ou, fu; /* point position */
1439 FT_Pos delta;
1440
1441
1442 if ( point->flags & touch_flag )
1443 continue;
1444
1445 /* if this point is candidate to weak interpolation, we */
1446 /* interpolate it after all strong points have been processed */
1447
1448 if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) )
1449 continue;
1450
1451 if ( dim == AF_DIMENSION_VERT )
1452 {
1453 u = point->fy;
1454 ou = point->oy;
1455 }
1456 else
1457 {
1458 u = point->fx;
1459 ou = point->ox;
1460 }
1461
1462 fu = u;
1463
1464 /* is the point before the first edge? */
1465 edge = edges;
1466 delta = edge->fpos - u;
1467 if ( delta >= 0 )
1468 {
1469 u = edge->pos - ( edge->opos - ou );
1470
1471#ifdef FT_DEBUG_AUTOFIT
1472 point->before[dim] = edge;
1473 point->after[dim] = NULL;
1474#endif
1475
1476 goto Store_Point;
1477 }
1478
1479 /* is the point after the last edge? */
1480 edge = edge_limit - 1;
1481 delta = u - edge->fpos;
1482 if ( delta >= 0 )
1483 {
1484 u = edge->pos + ( ou - edge->opos );
1485
1486#ifdef FT_DEBUG_AUTOFIT
1487 point->before[dim] = NULL;
1488 point->after[dim] = edge;
1489#endif
1490
1491 goto Store_Point;
1492 }
1493
1494 {
1495 FT_PtrDist min, max, mid;
1496 FT_Pos fpos;
1497
1498
1499 /* find enclosing edges */
1500 min = 0;
1501 max = edge_limit - edges;
1502
1503#if 1
1504 /* for a small number of edges, a linear search is better */
1505 if ( max <= 8 )
1506 {
1507 FT_PtrDist nn;
1508
1509
1510 for ( nn = 0; nn < max; nn++ )
1511 if ( edges[nn].fpos >= u )
1512 break;
1513
1514 if ( edges[nn].fpos == u )
1515 {
1516 u = edges[nn].pos;
1517 goto Store_Point;
1518 }
1519 min = nn;
1520 }
1521 else
1522#endif
1523 while ( min < max )
1524 {
1525 mid = ( max + min ) >> 1;
1526 edge = edges + mid;
1527 fpos = edge->fpos;
1528
1529 if ( u < fpos )
1530 max = mid;
1531 else if ( u > fpos )
1532 min = mid + 1;
1533 else
1534 {
1535 /* we are on the edge */
1536 u = edge->pos;
1537
1538#ifdef FT_DEBUG_AUTOFIT
1539 point->before[dim] = NULL;
1540 point->after[dim] = NULL;
1541#endif
1542
1543 goto Store_Point;
1544 }
1545 }
1546
1547 /* point is not on an edge */
1548 {
1549 AF_Edge before = edges + min - 1;
1550 AF_Edge after = edges + min + 0;
1551
1552
1553#ifdef FT_DEBUG_AUTOFIT
1554 point->before[dim] = before;
1555 point->after[dim] = after;
1556#endif
1557
1558 /* assert( before && after && before != after ) */
1559 if ( before->scale == 0 )
1560 before->scale = FT_DivFix( after->pos - before->pos,
1561 after->fpos - before->fpos );
1562
1563 u = before->pos + FT_MulFix( fu - before->fpos,
1564 before->scale );
1565 }
1566 }
1567
1568 Store_Point:
1569 /* save the point position */
1570 if ( dim == AF_DIMENSION_HORZ )
1571 point->x = u;
1572 else
1573 point->y = u;
1574
1575 point->flags |= touch_flag;
1576 }
1577 }
1578 }
1579
1580
1581 /****************************************************************
1582 *
1583 * WEAK POINT INTERPOLATION
1584 *
1585 ****************************************************************/
1586
1587
1588 /* Shift the original coordinates of all points between `p1' and */
1589 /* `p2' to get hinted coordinates, using the same difference as */
1590 /* given by `ref'. */
1591
1592 static void
1593 af_iup_shift( AF_Point p1,
1594 AF_Point p2,
1595 AF_Point ref )
1596 {
1597 AF_Point p;
1598 FT_Pos delta = ref->u - ref->v;
1599
1600
1601 if ( delta == 0 )
1602 return;
1603
1604 for ( p = p1; p < ref; p++ )
1605 p->u = p->v + delta;
1606
1607 for ( p = ref + 1; p <= p2; p++ )
1608 p->u = p->v + delta;
1609 }
1610
1611
1612 /* Interpolate the original coordinates of all points between `p1' and */
1613 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */
1614 /* reference points. The `u' and `v' members are the current and */
1615 /* original coordinate values, respectively. */
1616 /* */
1617 /* Details can be found in the TrueType bytecode specification. */
1618
1619 static void
1620 af_iup_interp( AF_Point p1,
1621 AF_Point p2,
1622 AF_Point ref1,
1623 AF_Point ref2 )
1624 {
1625 AF_Point p;
1626 FT_Pos u, v1, v2, u1, u2, d1, d2;
1627
1628
1629 if ( p1 > p2 )
1630 return;
1631
1632 if ( ref1->v > ref2->v )
1633 {
1634 p = ref1;
1635 ref1 = ref2;
1636 ref2 = p;
1637 }
1638
1639 v1 = ref1->v;
1640 v2 = ref2->v;
1641 u1 = ref1->u;
1642 u2 = ref2->u;
1643 d1 = u1 - v1;
1644 d2 = u2 - v2;
1645
1646 if ( u1 == u2 || v1 == v2 )
1647 {
1648 for ( p = p1; p <= p2; p++ )
1649 {
1650 u = p->v;
1651
1652 if ( u <= v1 )
1653 u += d1;
1654 else if ( u >= v2 )
1655 u += d2;
1656 else
1657 u = u1;
1658
1659 p->u = u;
1660 }
1661 }
1662 else
1663 {
1664 FT_Fixed scale = FT_DivFix( u2 - u1, v2 - v1 );
1665
1666
1667 for ( p = p1; p <= p2; p++ )
1668 {
1669 u = p->v;
1670
1671 if ( u <= v1 )
1672 u += d1;
1673 else if ( u >= v2 )
1674 u += d2;
1675 else
1676 u = u1 + FT_MulFix( u - v1, scale );
1677
1678 p->u = u;
1679 }
1680 }
1681 }
1682
1683
1684 /* Hint the weak points -- this is equivalent to the TrueType `IUP' */
1685 /* hinting instruction. */
1686
1687 FT_LOCAL_DEF( void )
1688 af_glyph_hints_align_weak_points( AF_GlyphHints hints,
1689 AF_Dimension dim )
1690 {
1691 AF_Point points = hints->points;
1692 AF_Point point_limit = points + hints->num_points;
1693 AF_Point* contour = hints->contours;
1694 AF_Point* contour_limit = contour + hints->num_contours;
1695 FT_UInt touch_flag;
1696 AF_Point point;
1697 AF_Point end_point;
1698 AF_Point first_point;
1699
1700
1701 /* PASS 1: Move segment points to edge positions */
1702
1703 if ( dim == AF_DIMENSION_HORZ )
1704 {
1705 touch_flag = AF_FLAG_TOUCH_X;
1706
1707 for ( point = points; point < point_limit; point++ )
1708 {
1709 point->u = point->x;
1710 point->v = point->ox;
1711 }
1712 }
1713 else
1714 {
1715 touch_flag = AF_FLAG_TOUCH_Y;
1716
1717 for ( point = points; point < point_limit; point++ )
1718 {
1719 point->u = point->y;
1720 point->v = point->oy;
1721 }
1722 }
1723
1724 for ( ; contour < contour_limit; contour++ )
1725 {
1726 AF_Point first_touched, last_touched;
1727
1728
1729 point = *contour;
1730 end_point = point->prev;
1731 first_point = point;
1732
1733 /* find first touched point */
1734 for (;;)
1735 {
1736 if ( point > end_point ) /* no touched point in contour */
1737 goto NextContour;
1738
1739 if ( point->flags & touch_flag )
1740 break;
1741
1742 point++;
1743 }
1744
1745 first_touched = point;
1746
1747 for (;;)
1748 {
1749 FT_ASSERT( point <= end_point &&
1750 ( point->flags & touch_flag ) != 0 );
1751
1752 /* skip any touched neighbours */
1753 while ( point < end_point &&
1754 ( point[1].flags & touch_flag ) != 0 )
1755 point++;
1756
1757 last_touched = point;
1758
1759 /* find the next touched point, if any */
1760 point++;
1761 for (;;)
1762 {
1763 if ( point > end_point )
1764 goto EndContour;
1765
1766 if ( ( point->flags & touch_flag ) != 0 )
1767 break;
1768
1769 point++;
1770 }
1771
1772 /* interpolate between last_touched and point */
1773 af_iup_interp( last_touched + 1, point - 1,
1774 last_touched, point );
1775 }
1776
1777 EndContour:
1778 /* special case: only one point was touched */
1779 if ( last_touched == first_touched )
1780 af_iup_shift( first_point, end_point, first_touched );
1781
1782 else /* interpolate the last part */
1783 {
1784 if ( last_touched < end_point )
1785 af_iup_interp( last_touched + 1, end_point,
1786 last_touched, first_touched );
1787
1788 if ( first_touched > points )
1789 af_iup_interp( first_point, first_touched - 1,
1790 last_touched, first_touched );
1791 }
1792
1793 NextContour:
1794 ;
1795 }
1796
1797 /* now save the interpolated values back to x/y */
1798 if ( dim == AF_DIMENSION_HORZ )
1799 {
1800 for ( point = points; point < point_limit; point++ )
1801 point->x = point->u;
1802 }
1803 else
1804 {
1805 for ( point = points; point < point_limit; point++ )
1806 point->y = point->u;
1807 }
1808 }
1809
1810
1811/* END */