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