]>
Commit | Line | Data |
---|---|---|
cabec872 RR |
1 | /***************************************************************************/ |
2 | /* */ | |
3 | /* ahglyph.c */ | |
4 | /* */ | |
5 | /* Routines used to load and analyze a given glyph before hinting */ | |
6 | /* (body). */ | |
7 | /* */ | |
8 | /* Copyright 2000 Catharon Productions Inc. */ | |
9 | /* Author: David Turner */ | |
10 | /* */ | |
11 | /* This file is part of the Catharon Typography Project and shall only */ | |
12 | /* be used, modified, and distributed under the terms of the Catharon */ | |
13 | /* Open Source License that should come with this file under the name */ | |
14 | /* `CatharonLicense.txt'. By continuing to use, modify, or distribute */ | |
15 | /* this file you indicate that you have read the license and */ | |
16 | /* understand and accept it fully. */ | |
17 | /* */ | |
18 | /* Note that this license is compatible with the FreeType license. */ | |
19 | /* */ | |
20 | /***************************************************************************/ | |
21 | ||
22 | ||
23 | #ifdef FT_FLAT_COMPILE | |
24 | ||
25 | #include "ahglyph.h" | |
26 | #include "ahangles.h" | |
27 | #include "ahglobal.h" | |
28 | ||
29 | #else | |
30 | ||
31 | #include <autohint/ahglyph.h> | |
32 | #include <autohint/ahangles.h> | |
33 | #include <autohint/ahglobal.h> | |
34 | ||
35 | #endif | |
36 | ||
37 | ||
38 | #include <stdio.h> | |
39 | ||
40 | ||
41 | #define xxxAH_DEBUG_GLYPH | |
42 | ||
43 | ||
44 | /* compute the direction value of a given vector.. */ | |
45 | static | |
46 | AH_Direction ah_compute_direction( FT_Pos dx, | |
47 | FT_Pos dy ) | |
48 | { | |
49 | AH_Direction dir; | |
50 | FT_Pos ax = ABS( dx ); | |
51 | FT_Pos ay = ABS( dy ); | |
52 | ||
53 | ||
54 | dir = ah_dir_none; | |
55 | ||
56 | /* test for vertical direction */ | |
57 | if ( ax * 12 < ay ) | |
58 | { | |
59 | dir = dy > 0 ? ah_dir_up : ah_dir_down; | |
60 | } | |
61 | /* test for horizontal direction */ | |
62 | else if ( ay * 12 < ax ) | |
63 | { | |
64 | dir = dx > 0 ? ah_dir_right : ah_dir_left; | |
65 | } | |
66 | ||
67 | return dir; | |
68 | } | |
69 | ||
70 | ||
71 | /*************************************************************************/ | |
72 | /* */ | |
73 | /* <Function> */ | |
74 | /* ah_outline_new */ | |
75 | /* */ | |
76 | /* <Description> */ | |
77 | /* Creates a new and empty AH_Outline object. */ | |
78 | /* */ | |
79 | LOCAL_FUNC | |
80 | FT_Error ah_outline_new( FT_Memory memory, | |
81 | AH_Outline** aoutline ) | |
82 | { | |
83 | FT_Error error; | |
84 | AH_Outline* outline; | |
85 | ||
86 | ||
87 | if ( !ALLOC( outline, sizeof ( *outline ) ) ) | |
88 | { | |
89 | outline->memory = memory; | |
90 | *aoutline = outline; | |
91 | } | |
92 | ||
93 | return error; | |
94 | } | |
95 | ||
96 | ||
97 | /*************************************************************************/ | |
98 | /* */ | |
99 | /* <Function> */ | |
100 | /* ah_outline_done */ | |
101 | /* */ | |
102 | /* <Description> */ | |
103 | /* Destroys a given AH_Outline object. */ | |
104 | /* */ | |
105 | LOCAL_FUNC | |
106 | void ah_outline_done( AH_Outline* outline ) | |
107 | { | |
108 | FT_Memory memory = outline->memory; | |
109 | ||
110 | ||
111 | FREE( outline->horz_edges ); | |
112 | FREE( outline->horz_segments ); | |
113 | FREE( outline->contours ); | |
114 | FREE( outline->points ); | |
115 | ||
116 | FREE( outline ); | |
117 | } | |
118 | ||
119 | ||
120 | /*************************************************************************/ | |
121 | /* */ | |
122 | /* <Function> */ | |
123 | /* ah_outline_save */ | |
124 | /* */ | |
125 | /* <Description> */ | |
126 | /* Saves the content of a given AH_Outline object into a face's glyph */ | |
127 | /* slot. */ | |
128 | /* */ | |
129 | LOCAL_FUNC | |
130 | void ah_outline_save( AH_Outline* outline, | |
131 | AH_Loader* gloader ) | |
132 | { | |
133 | AH_Point* point = outline->points; | |
134 | AH_Point* limit = point + outline->num_points; | |
135 | FT_Vector* vec = gloader->current.outline.points; | |
136 | char* tag = gloader->current.outline.tags; | |
137 | ||
138 | ||
139 | /* we assume that the glyph loader has already been checked for storage */ | |
140 | for ( ; point < limit; point++, vec++, tag++ ) | |
141 | { | |
142 | vec->x = point->x; | |
143 | vec->y = point->y; | |
144 | ||
145 | if ( point->flags & ah_flah_conic ) | |
146 | tag[0] = FT_Curve_Tag_Conic; | |
147 | else if ( point->flags & ah_flah_cubic ) | |
148 | tag[0] = FT_Curve_Tag_Cubic; | |
149 | else | |
150 | tag[0] = FT_Curve_Tag_On; | |
151 | } | |
152 | } | |
153 | ||
154 | ||
155 | /*************************************************************************/ | |
156 | /* */ | |
157 | /* <Function> */ | |
158 | /* ah_outline_load */ | |
159 | /* */ | |
160 | /* <Description> */ | |
161 | /* Loads an unscaled outline from a glyph slot into an AH_Outline */ | |
162 | /* object. */ | |
163 | /* */ | |
164 | LOCAL_FUNC | |
165 | FT_Error ah_outline_load( AH_Outline* outline, | |
166 | FT_Face face ) | |
167 | { | |
168 | FT_Memory memory = outline->memory; | |
169 | FT_Error error = FT_Err_Ok; | |
170 | FT_Outline* source = &face->glyph->outline; | |
171 | FT_Int num_points = source->n_points; | |
172 | FT_Int num_contours = source->n_contours; | |
173 | AH_Point* points; | |
174 | ||
175 | ||
176 | /* check arguments */ | |
177 | if ( !face || | |
178 | !face->size || | |
179 | face->glyph->format != ft_glyph_format_outline ) | |
180 | return FT_Err_Invalid_Argument; | |
181 | ||
182 | /* first of all, reallocate the contours array if necessary */ | |
183 | if ( num_contours > outline->max_contours ) | |
184 | { | |
185 | FT_Int new_contours = ( num_contours + 3 ) & -4; | |
186 | ||
187 | ||
188 | if ( REALLOC_ARRAY( outline->contours, outline->max_contours, | |
189 | new_contours, AH_Point* ) ) | |
190 | goto Exit; | |
191 | ||
192 | outline->max_contours = new_contours; | |
193 | } | |
194 | ||
195 | /* then, realloc the points, segments & edges arrays if needed */ | |
196 | if ( num_points > outline->max_points ) | |
197 | { | |
198 | FT_Int news = ( num_points + 7 ) & -8; | |
199 | FT_Int max = outline->max_points; | |
200 | ||
201 | ||
202 | if ( REALLOC_ARRAY( outline->points, max, news, AH_Point ) || | |
203 | REALLOC_ARRAY( outline->horz_edges, max, news, AH_Edge ) || | |
204 | REALLOC_ARRAY( outline->horz_segments, max, news, AH_Segment ) ) | |
205 | goto Exit; | |
206 | ||
207 | /* readjust some pointers */ | |
208 | outline->vert_edges = outline->horz_edges + ( news >> 1 ); | |
209 | outline->vert_segments = outline->horz_segments + ( news >> 1 ); | |
210 | outline->max_points = news; | |
211 | } | |
212 | ||
213 | outline->num_points = num_points; | |
214 | outline->num_contours = num_contours; | |
215 | ||
216 | outline->num_hedges = 0; | |
217 | outline->num_vedges = 0; | |
218 | outline->num_hsegments = 0; | |
219 | outline->num_vsegments = 0; | |
220 | ||
221 | /* Compute the vertical and horizontal major directions; this is */ | |
222 | /* currently done by inspecting the `ft_outline_reverse_fill' flag. */ | |
223 | /* However, some fonts have improper glyphs, and it'd be a good idea */ | |
224 | /* to be able to re-compute these values on the fly. */ | |
225 | outline->vert_major_dir = ah_dir_up; | |
226 | outline->horz_major_dir = ah_dir_left; | |
227 | ||
228 | if ( source->flags & ft_outline_reverse_fill ) | |
229 | { | |
230 | outline->vert_major_dir = ah_dir_down; | |
231 | outline->horz_major_dir = ah_dir_right; | |
232 | } | |
233 | ||
234 | outline->x_scale = face->size->metrics.x_scale; | |
235 | outline->y_scale = face->size->metrics.y_scale; | |
236 | ||
237 | points = outline->points; | |
238 | ||
239 | { | |
240 | /* do one thing at a time -- it is easier to understand, and */ | |
241 | /* the code is clearer */ | |
242 | AH_Point* point = points; | |
243 | AH_Point* limit = point + outline->num_points; | |
244 | ||
245 | ||
246 | /* compute coordinates */ | |
247 | { | |
248 | FT_Vector* vec = source->points; | |
249 | FT_Fixed x_scale = outline->x_scale; | |
250 | FT_Fixed y_scale = outline->y_scale; | |
251 | ||
252 | ||
253 | for (; point < limit; vec++, point++ ) | |
254 | { | |
255 | point->fx = vec->x; | |
256 | point->fy = vec->y; | |
257 | point->ox = point->x = FT_MulFix( vec->x, x_scale ); | |
258 | point->oy = point->y = FT_MulFix( vec->y, y_scale ); | |
259 | ||
260 | point->flags = 0; | |
261 | } | |
262 | } | |
263 | ||
264 | /* compute Bezier flags */ | |
265 | { | |
266 | char* tag = source->tags; | |
267 | ||
268 | ||
269 | for ( point = points; point < limit; point++, tag++ ) | |
270 | { | |
271 | switch ( FT_CURVE_TAG( *tag ) ) | |
272 | { | |
273 | case FT_Curve_Tag_Conic: | |
274 | point->flags = ah_flah_conic; break; | |
275 | case FT_Curve_Tag_Cubic: | |
276 | point->flags = ah_flah_cubic; break; | |
277 | default: | |
278 | ; | |
279 | } | |
280 | } | |
281 | } | |
282 | ||
283 | /* compute `next' and `prev' */ | |
284 | { | |
285 | FT_Int contour_index; | |
286 | AH_Point* prev; | |
287 | AH_Point* first; | |
288 | AH_Point* end; | |
289 | ||
290 | ||
291 | contour_index = 0; | |
292 | ||
293 | first = points; | |
294 | end = points + source->contours[0]; | |
295 | prev = end; | |
296 | ||
297 | for ( point = points; point < limit; point++ ) | |
298 | { | |
299 | point->prev = prev; | |
300 | if ( point < end ) | |
301 | { | |
302 | point->next = point + 1; | |
303 | prev = point; | |
304 | } | |
305 | else | |
306 | { | |
307 | point->next = first; | |
308 | contour_index++; | |
309 | if ( point + 1 < limit ) | |
310 | { | |
311 | end = points + source->contours[contour_index]; | |
312 | first = point + 1; | |
313 | prev = end; | |
314 | } | |
315 | } | |
316 | } | |
317 | } | |
318 | ||
319 | /* set-up the contours array */ | |
320 | { | |
321 | AH_Point** contour = outline->contours; | |
322 | AH_Point** limit = contour + outline->num_contours; | |
323 | short* end = source->contours; | |
324 | short index = 0; | |
325 | ||
326 | ||
327 | for ( ; contour < limit; contour++, end++ ) | |
328 | { | |
329 | contour[0] = points + index; | |
330 | index = end[0] + 1; | |
331 | } | |
332 | } | |
333 | ||
334 | /* compute directions of in & out vectors */ | |
335 | { | |
336 | for ( point = points; point < limit; point++ ) | |
337 | { | |
338 | AH_Point* prev; | |
339 | AH_Point* next; | |
340 | FT_Vector vec; | |
341 | ||
342 | ||
343 | prev = point->prev; | |
344 | vec.x = point->fx - prev->fx; | |
345 | vec.y = point->fy - prev->fy; | |
346 | ||
347 | point->in_dir = ah_compute_direction( vec.x, vec.y ); | |
348 | ||
349 | #ifndef AH_OPTION_NO_WEAK_INTERPOLATION | |
350 | point->in_angle = ah_angle( &vec ); | |
351 | #endif | |
352 | ||
353 | next = point->next; | |
354 | vec.x = next->fx - point->fx; | |
355 | vec.y = next->fy - point->fy; | |
356 | ||
357 | point->out_dir = ah_compute_direction( vec.x, vec.y ); | |
358 | ||
359 | #ifndef AH_OPTION_NO_WEAK_INTERPOLATION | |
360 | point->out_angle = ah_angle( &vec ); | |
361 | ||
362 | { | |
363 | AH_Angle delta = point->in_angle - point->out_angle; | |
364 | ||
365 | ||
366 | if ( delta < 0 ) | |
367 | delta = -delta; | |
368 | if ( delta < 2 ) | |
369 | point->flags |= ah_flah_weak_interpolation; | |
370 | } | |
371 | ||
372 | #if 0 | |
373 | if ( point->flags & ( ah_flah_conic | ah_flah_cubic ) ) | |
374 | point->flags |= ah_flah_weak_interpolation; | |
375 | #endif | |
376 | ||
377 | #endif /* !AH_OPTION_NO_WEAK_INTERPOLATION */ | |
378 | ||
379 | #ifdef AH_OPTION_NO_STRONG_INTERPOLATION | |
380 | point->flags |= ah_flah_weak_interpolation; | |
381 | #endif | |
382 | } | |
383 | } | |
384 | } | |
385 | ||
386 | Exit: | |
387 | return error; | |
388 | } | |
389 | ||
390 | ||
391 | LOCAL_FUNC | |
392 | void ah_setup_uv( AH_Outline* outline, | |
393 | AH_UV source ) | |
394 | { | |
395 | AH_Point* point = outline->points; | |
396 | AH_Point* limit = point + outline->num_points; | |
397 | ||
398 | ||
399 | for ( ; point < limit; point++ ) | |
400 | { | |
401 | FT_Pos u, v; | |
402 | ||
403 | ||
404 | switch ( source ) | |
405 | { | |
406 | case ah_uv_fxy: | |
407 | u = point->fx; | |
408 | v = point->fy; | |
409 | break; | |
410 | case ah_uv_fyx: | |
411 | u = point->fy; | |
412 | v = point->fx; | |
413 | break; | |
414 | case ah_uv_oxy: | |
415 | u = point->ox; | |
416 | v = point->oy; | |
417 | break; | |
418 | case ah_uv_oyx: | |
419 | u = point->oy; | |
420 | v = point->ox; | |
421 | break; | |
422 | case ah_uv_yx: | |
423 | u = point->y; | |
424 | v = point->x; | |
425 | break; | |
426 | case ah_uv_ox: | |
427 | u = point->x; | |
428 | v = point->ox; | |
429 | break; | |
430 | case ah_uv_oy: | |
431 | u = point->y; | |
432 | v = point->oy; | |
433 | break; | |
434 | default: | |
435 | u = point->x; | |
436 | v = point->y; | |
437 | break; | |
438 | } | |
439 | point->u = u; | |
440 | point->v = v; | |
441 | } | |
442 | } | |
443 | ||
444 | ||
445 | LOCAL_FUNC | |
446 | void ah_outline_compute_segments( AH_Outline* outline ) | |
447 | { | |
448 | int dimension; | |
449 | AH_Segment* segments; | |
450 | FT_Int* p_num_segments; | |
451 | AH_Direction segment_dir; | |
452 | AH_Direction major_dir; | |
453 | ||
454 | ||
455 | segments = outline->horz_segments; | |
456 | p_num_segments = &outline->num_hsegments; | |
457 | major_dir = ah_dir_right; /* This value must be positive! */ | |
458 | segment_dir = major_dir; | |
459 | ||
460 | /* set up (u,v) in each point */ | |
461 | ah_setup_uv( outline, ah_uv_fyx ); | |
462 | ||
463 | for ( dimension = 1; dimension >= 0; dimension-- ) | |
464 | { | |
465 | AH_Point** contour = outline->contours; | |
466 | AH_Point** contour_limit = contour + outline->num_contours; | |
467 | AH_Segment* segment = segments; | |
468 | FT_Int num_segments = 0; | |
469 | ||
470 | #ifdef AH_HINT_METRICS | |
471 | AH_Point* min_point = 0; | |
472 | AH_Point* max_point = 0; | |
473 | FT_Pos min_coord = 32000; | |
474 | FT_Pos max_coord = -32000; | |
475 | #endif | |
476 | ||
477 | ||
478 | /* do each contour separately */ | |
479 | for ( ; contour < contour_limit; contour++ ) | |
480 | { | |
481 | AH_Point* point = contour[0]; | |
482 | AH_Point* last = point->prev; | |
483 | int on_edge = 0; | |
484 | FT_Pos min_pos = +32000; /* minimum segment pos != min_coord */ | |
485 | FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */ | |
486 | FT_Bool passed; | |
487 | ||
488 | ||
489 | #ifdef AH_HINT_METRICS | |
490 | if ( point->u < min_coord ) | |
491 | { | |
492 | min_coord = point->u; | |
493 | min_point = point; | |
494 | } | |
495 | if ( point->u > max_coord ) | |
496 | { | |
497 | max_coord = point->u; | |
498 | max_point = point; | |
499 | } | |
500 | #endif | |
501 | ||
502 | if ( point == last ) /* skip singletons -- just in case? */ | |
503 | continue; | |
504 | ||
505 | if ( ABS( last->out_dir ) == major_dir && | |
506 | ABS( point->out_dir ) == major_dir ) | |
507 | { | |
508 | /* we are already on an edge, try to locate its start */ | |
509 | last = point; | |
510 | ||
511 | for (;;) | |
512 | { | |
513 | point = point->prev; | |
514 | if ( ABS( point->out_dir ) != major_dir ) | |
515 | { | |
516 | point = point->next; | |
517 | break; | |
518 | } | |
519 | if ( point == last ) | |
520 | break; | |
521 | } | |
522 | ||
523 | } | |
524 | ||
525 | last = point; | |
526 | passed = 0; | |
527 | ||
528 | for (;;) | |
529 | { | |
530 | FT_Pos u, v; | |
531 | ||
532 | ||
533 | if ( on_edge ) | |
534 | { | |
535 | u = point->u; | |
536 | if ( u < min_pos ) | |
537 | min_pos = u; | |
538 | if ( u > max_pos ) | |
539 | max_pos = u; | |
540 | ||
541 | if ( point->out_dir != segment_dir || point == last ) | |
542 | { | |
543 | /* we are just leaving an edge; record a new segment! */ | |
544 | segment->last = point; | |
545 | segment->pos = ( min_pos + max_pos ) >> 1; | |
546 | ||
547 | /* a segment is round if either its first or last point */ | |
548 | /* is a control point */ | |
549 | if ( ( segment->first->flags | point->flags ) & | |
550 | ah_flah_control ) | |
551 | segment->flags |= ah_edge_round; | |
552 | ||
553 | /* compute segment size */ | |
554 | min_pos = max_pos = point->v; | |
555 | ||
556 | v = segment->first->v; | |
557 | if ( v < min_pos ) | |
558 | min_pos = v; | |
559 | if ( v > max_pos ) | |
560 | max_pos = v; | |
561 | ||
562 | segment->min_coord = min_pos; | |
563 | segment->max_coord = max_pos; | |
564 | ||
565 | on_edge = 0; | |
566 | num_segments++; | |
567 | segment++; | |
568 | /* fallthrough */ | |
569 | } | |
570 | } | |
571 | ||
572 | /* now exit if we are at the start/end point */ | |
573 | if ( point == last ) | |
574 | { | |
575 | if ( passed ) | |
576 | break; | |
577 | passed = 1; | |
578 | } | |
579 | ||
580 | if ( !on_edge && ABS( point->out_dir ) == major_dir ) | |
581 | { | |
582 | /* this is the start of a new segment! */ | |
583 | segment_dir = point->out_dir; | |
584 | ||
585 | /* clear all segment fields */ | |
586 | memset( segment, 0, sizeof ( *segment ) ); | |
587 | ||
588 | segment->dir = segment_dir; | |
589 | segment->flags = ah_edge_normal; | |
590 | min_pos = max_pos = point->u; | |
591 | segment->first = point; | |
592 | segment->last = point; | |
593 | segment->contour = contour; | |
594 | on_edge = 1; | |
595 | ||
596 | if ( point == max_point ) | |
597 | max_point = 0; | |
598 | ||
599 | if ( point == min_point ) | |
600 | min_point = 0; | |
601 | } | |
602 | ||
603 | point = point->next; | |
604 | } | |
605 | ||
606 | } /* contours */ | |
607 | ||
608 | #ifdef AH_HINT_METRICS | |
609 | /* we need to ensure that there are edges on the left-most and */ | |
610 | /* right-most points of the glyph in order to hint the metrics; */ | |
611 | /* we do this by inserting fake segments when needed */ | |
612 | if ( dimension == 0 ) | |
613 | { | |
614 | AH_Point* point = outline->points; | |
615 | AH_Point* limit = point + outline->num_points; | |
616 | ||
617 | AH_Point* min_point = 0; | |
618 | AH_Point* max_point = 0; | |
619 | FT_Pos min_pos = 32000; | |
620 | FT_Pos max_pos = -32000; | |
621 | ||
622 | ||
623 | /* compute minimum and maximum points */ | |
624 | for ( ; point < limit; point++ ) | |
625 | { | |
626 | FT_Pos x = point->fx; | |
627 | ||
628 | ||
629 | if ( x < min_pos ) | |
630 | { | |
631 | min_pos = x; | |
632 | min_point = point; | |
633 | } | |
634 | if ( x > max_pos ) | |
635 | { | |
636 | max_pos = x; | |
637 | max_point = point; | |
638 | } | |
639 | } | |
640 | ||
641 | /* insert minimum segment */ | |
642 | if ( min_point ) | |
643 | { | |
644 | /* clear all segment fields */ | |
645 | memset( segment, 0, sizeof ( *segment ) ); | |
646 | ||
647 | segment->dir = segment_dir; | |
648 | segment->flags = ah_edge_normal; | |
649 | segment->first = min_point; | |
650 | segment->last = min_point; | |
651 | segment->pos = min_pos; | |
652 | ||
653 | num_segments++; | |
654 | segment++; | |
655 | } | |
656 | ||
657 | /* insert maximum segment */ | |
658 | if ( max_point ) | |
659 | { | |
660 | /* clear all segment fields */ | |
661 | memset( segment, 0, sizeof ( *segment ) ); | |
662 | ||
663 | segment->dir = segment_dir; | |
664 | segment->flags = ah_edge_normal; | |
665 | segment->first = max_point; | |
666 | segment->last = max_point; | |
667 | segment->pos = max_pos; | |
668 | ||
669 | num_segments++; | |
670 | segment++; | |
671 | } | |
672 | } | |
673 | #endif /* AH_HINT_METRICS */ | |
674 | ||
675 | *p_num_segments = num_segments; | |
676 | ||
677 | segments = outline->vert_segments; | |
678 | major_dir = ah_dir_up; | |
679 | p_num_segments = &outline->num_vsegments; | |
680 | ah_setup_uv( outline, ah_uv_fxy ); | |
681 | } | |
682 | } | |
683 | ||
684 | ||
685 | LOCAL_FUNC | |
686 | void ah_outline_link_segments( AH_Outline* outline ) | |
687 | { | |
688 | AH_Segment* segments; | |
689 | AH_Segment* limit; | |
690 | int dimension; | |
691 | ||
692 | ||
693 | ah_setup_uv( outline, ah_uv_fyx ); | |
694 | ||
695 | segments = outline->horz_segments; | |
696 | limit = segments + outline->num_hsegments; | |
697 | ||
698 | for ( dimension = 1; dimension >= 0; dimension-- ) | |
699 | { | |
700 | AH_Segment* seg1; | |
701 | AH_Segment* seg2; | |
702 | ||
703 | ||
704 | /* now compare each segment to the others */ | |
705 | for ( seg1 = segments; seg1 < limit; seg1++ ) | |
706 | { | |
707 | FT_Pos best_score = 32000; | |
708 | AH_Segment* best_segment = 0; | |
709 | ||
710 | ||
711 | /* the fake segments are introduced to hint the metrics -- */ | |
712 | /* we must never link them to anything */ | |
713 | if ( seg1->first == seg1->last ) | |
714 | continue; | |
715 | ||
716 | for ( seg2 = segments; seg2 < limit; seg2++ ) | |
717 | if ( seg1 != seg2 && seg1->dir + seg2->dir == 0 ) | |
718 | { | |
719 | FT_Pos pos1 = seg1->pos; | |
720 | FT_Pos pos2 = seg2->pos; | |
721 | FT_Bool is_dir; | |
722 | FT_Bool is_pos; | |
723 | ||
724 | ||
725 | /* check that the segments are correctly oriented and */ | |
726 | /* positioned to form a black distance */ | |
727 | ||
728 | is_dir = ( seg1->dir == outline->horz_major_dir || | |
729 | seg1->dir == outline->vert_major_dir ); | |
730 | is_pos = pos1 > pos2; | |
731 | ||
732 | if ( pos1 == pos2 || !(is_dir ^ is_pos) ) | |
733 | continue; | |
734 | ||
735 | /* Check the two segments. We now have a better algorithm */ | |
736 | /* that doesn't rely on the segment points themselves but */ | |
737 | /* on their relative position. This gets rids of many */ | |
738 | /* unpleasant artefacts and incorrect stem/serifs */ | |
739 | /* computations. */ | |
740 | ||
741 | /* first of all, compute the size of the `common' height */ | |
742 | { | |
743 | FT_Pos min = seg1->min_coord; | |
744 | FT_Pos max = seg1->max_coord; | |
745 | FT_Pos len, score; | |
746 | FT_Pos size1, size2; | |
747 | ||
748 | ||
749 | size1 = max - min; | |
750 | size2 = seg2->max_coord - seg2->min_coord; | |
751 | ||
752 | if ( min < seg2->min_coord ) | |
753 | min = seg2->min_coord; | |
754 | ||
755 | if ( max < seg2->max_coord ) | |
756 | max = seg2->max_coord; | |
757 | ||
758 | len = max - min; | |
759 | score = seg2->pos - seg1->pos; | |
760 | if ( score < 0 ) | |
761 | score = -score; | |
762 | ||
763 | /* before comparing the scores, take care that the segments */ | |
764 | /* are really facing each other (often not for italics..) */ | |
765 | if ( 4 * len >= size1 && 4 * len >= size2 ) | |
766 | if ( score < best_score ) | |
767 | { | |
768 | best_score = score; | |
769 | best_segment = seg2; | |
770 | } | |
771 | } | |
772 | } | |
773 | ||
774 | if ( best_segment ) | |
775 | { | |
776 | seg1->link = best_segment; | |
777 | seg1->score = best_score; | |
778 | ||
779 | best_segment->num_linked++; | |
780 | } | |
781 | ||
782 | ||
783 | } /* edges 1 */ | |
784 | ||
785 | /* now, compute the `serif' segments */ | |
786 | for ( seg1 = segments; seg1 < limit; seg1++ ) | |
787 | { | |
788 | seg2 = seg1->link; | |
789 | ||
790 | if ( seg2 && seg2->link != seg1 ) | |
791 | { | |
792 | seg1->link = 0; | |
793 | seg1->serif = seg2->link; | |
794 | } | |
795 | } | |
796 | ||
797 | ah_setup_uv( outline, ah_uv_fxy ); | |
798 | ||
799 | segments = outline->vert_segments; | |
800 | limit = segments + outline->num_vsegments; | |
801 | } | |
802 | } | |
803 | ||
804 | ||
805 | #ifdef AH_DEBUG_GLYPH | |
806 | ||
807 | /* A function used to dump the array of linked segments */ | |
808 | void ah_dump_segments( AH_Outline* outline ) | |
809 | { | |
810 | AH_Segment* segments; | |
811 | AH_Segment* limit; | |
812 | AH_Point* points; | |
813 | FT_Int dimension; | |
814 | ||
815 | ||
816 | points = outline->points; | |
817 | segments = outline->horz_segments; | |
818 | limit = segments + outline->num_hsegments; | |
819 | ||
820 | for ( dimension = 1; dimension >= 0; dimension-- ) | |
821 | { | |
822 | AH_Segment* seg; | |
823 | ||
824 | ||
825 | printf ( "Table of %s segments:\n", | |
826 | !dimension ? "vertical" : "horizontal" ); | |
827 | printf ( " [ index | pos | dir | link | serif |" | |
828 | " numl | first | start ]\n" ); | |
829 | ||
830 | for ( seg = segments; seg < limit; seg++ ) | |
831 | { | |
832 | printf ( " [ %5d | %4d | %5s | %4d | %5d | %4d | %5d | %5d ]\n", | |
833 | seg - segments, | |
834 | (int)seg->pos, | |
835 | seg->dir == ah_dir_up | |
836 | ? "up" | |
837 | : ( seg->dir == ah_dir_down | |
838 | ? "down" | |
839 | : ( seg->dir == ah_dir_left | |
840 | ? "left" | |
841 | : ( seg->dir == ah_dir_right | |
842 | ? "right" | |
843 | : "none" ) ) ), | |
844 | seg->link ? (seg->link-segments) : -1, | |
845 | seg->serif ? (seg->serif-segments) : -1, | |
846 | (int)seg->num_linked, | |
847 | seg->first - points, | |
848 | seg->last - points ); | |
849 | } | |
850 | ||
851 | segments = outline->vert_segments; | |
852 | limit = segments + outline->num_vsegments; | |
853 | } | |
854 | } | |
855 | ||
856 | #endif /* AH_DEBUG_GLYPH */ | |
857 | ||
858 | ||
859 | static | |
860 | void ah_outline_compute_edges( AH_Outline* outline ) | |
861 | { | |
862 | AH_Edge* edges; | |
863 | AH_Segment* segments; | |
864 | AH_Segment* segment_limit; | |
865 | AH_Direction up_dir; | |
866 | FT_Int* p_num_edges; | |
867 | FT_Int dimension; | |
868 | FT_Fixed scale; | |
869 | FT_Pos edge_distance_threshold; | |
870 | ||
871 | ||
872 | edges = outline->horz_edges; | |
873 | segments = outline->horz_segments; | |
874 | segment_limit = segments + outline->num_hsegments; | |
875 | p_num_edges = &outline->num_hedges; | |
876 | up_dir = ah_dir_right; | |
877 | scale = outline->y_scale; | |
878 | ||
879 | for ( dimension = 1; dimension >= 0; dimension-- ) | |
880 | { | |
881 | AH_Edge* edge; | |
882 | AH_Edge* edge_limit; /* really == edge + num_edges */ | |
883 | AH_Segment* seg; | |
884 | ||
885 | ||
886 | /*********************************************************************/ | |
887 | /* */ | |
888 | /* We will begin by generating a sorted table of edges for the */ | |
889 | /* current direction. To do so, we simply scan each segment and try */ | |
890 | /* to find an edge in our table that corresponds to its position. */ | |
891 | /* */ | |
892 | /* If no edge is found, we create and insert a new edge in the */ | |
893 | /* sorted table. Otherwise, we simply add the segment to the edge's */ | |
894 | /* list which will be processed in the second step to compute the */ | |
895 | /* edge's properties. */ | |
896 | /* */ | |
897 | /* Note that the edges table is sorted along the segment/edge */ | |
898 | /* position. */ | |
899 | /* */ | |
900 | /*********************************************************************/ | |
901 | ||
902 | edge_distance_threshold = FT_MulFix( outline->edge_distance_threshold, | |
903 | scale ); | |
904 | if ( edge_distance_threshold > 64 / 4 ) | |
905 | edge_distance_threshold = 64 / 4; | |
906 | ||
907 | edge_limit = edges; | |
908 | for ( seg = segments; seg < segment_limit; seg++ ) | |
909 | { | |
910 | AH_Edge* found = 0; | |
911 | ||
912 | ||
913 | /* look for an edge corresponding to the segment */ | |
914 | for ( edge = edges; edge < edge_limit; edge++ ) | |
915 | { | |
916 | FT_Pos dist; | |
917 | ||
918 | ||
919 | dist = seg->pos - edge->fpos; | |
920 | if ( dist < 0 ) | |
921 | dist = -dist; | |
922 | ||
923 | dist = FT_MulFix( dist, scale ); | |
924 | if ( dist < edge_distance_threshold ) | |
925 | { | |
926 | found = edge; | |
927 | break; | |
928 | } | |
929 | } | |
930 | ||
931 | if ( !found ) | |
932 | { | |
933 | /* insert a new edge in the list and */ | |
934 | /* sort according to the position */ | |
935 | while ( edge > edges && edge[-1].fpos > seg->pos ) | |
936 | { | |
937 | edge[0] = edge[-1]; | |
938 | edge--; | |
939 | } | |
940 | edge_limit++; | |
941 | ||
942 | /* clear all edge fields */ | |
943 | memset( edge, 0, sizeof ( *edge ) ); | |
944 | ||
945 | /* add the segment to the new edge's list */ | |
946 | edge->first = seg; | |
947 | edge->last = seg; | |
948 | edge->fpos = seg->pos; | |
949 | edge->opos = edge->pos = FT_MulFix( seg->pos, scale ); | |
950 | seg->edge_next = seg; | |
951 | } | |
952 | else | |
953 | { | |
954 | /* if an edge was found, simply add the segment to the edge's */ | |
955 | /* list */ | |
956 | seg->edge_next = edge->first; | |
957 | edge->last->edge_next = seg; | |
958 | edge->last = seg; | |
959 | } | |
960 | } | |
961 | ||
962 | *p_num_edges = edge_limit - edges; | |
963 | ||
964 | ||
965 | /*********************************************************************/ | |
966 | /* */ | |
967 | /* Good, we will now compute each edge's properties according to */ | |
968 | /* segments found on its position. Basically, these are: */ | |
969 | /* */ | |
970 | /* - edge's main direction */ | |
971 | /* - stem edge, serif edge or both (which defaults to stem then) */ | |
972 | /* - rounded edge, straigth or both (which defaults to straight) */ | |
973 | /* - link for edge */ | |
974 | /* */ | |
975 | /*********************************************************************/ | |
976 | ||
977 | /* first of all, set the `edge' field in each segment -- this is */ | |
978 | /* required in order to compute edge links */ | |
979 | for ( edge = edges; edge < edge_limit; edge++ ) | |
980 | { | |
981 | seg = edge->first; | |
982 | if ( seg ) | |
983 | do | |
984 | { | |
985 | seg->edge = edge; | |
986 | seg = seg->edge_next; | |
987 | } | |
988 | while ( seg != edge->first ); | |
989 | } | |
990 | ||
991 | /* now, compute each edge properties */ | |
992 | for ( edge = edges; edge < edge_limit; edge++ ) | |
993 | { | |
994 | int is_round = 0; /* does it contain round segments? */ | |
995 | int is_straight = 0; /* does it contain straight segments? */ | |
996 | int ups = 0; /* number of upwards segments */ | |
997 | int downs = 0; /* number of downwards segments */ | |
998 | ||
999 | ||
1000 | seg = edge->first; | |
1001 | ||
1002 | do | |
1003 | { | |
1004 | FT_Bool is_serif; | |
1005 | ||
1006 | ||
1007 | /* check for roundness of segment */ | |
1008 | if ( seg->flags & ah_edge_round ) | |
1009 | is_round++; | |
1010 | else | |
1011 | is_straight++; | |
1012 | ||
1013 | /* check for segment direction */ | |
1014 | if ( seg->dir == up_dir ) | |
1015 | ups += seg->max_coord-seg->min_coord; | |
1016 | else | |
1017 | downs += seg->max_coord-seg->min_coord; | |
1018 | ||
1019 | /* check for links -- if seg->serif is set, then seg->link must */ | |
1020 | /* be ignored */ | |
1021 | is_serif = seg->serif && seg->serif->edge != edge; | |
1022 | ||
1023 | if ( seg->link || is_serif ) | |
1024 | { | |
1025 | AH_Edge* edge2; | |
1026 | AH_Segment* seg2; | |
1027 | ||
1028 | ||
1029 | edge2 = edge->link; | |
1030 | seg2 = seg->link; | |
1031 | ||
1032 | if ( is_serif ) | |
1033 | { | |
1034 | seg2 = seg->serif; | |
1035 | edge2 = edge->serif; | |
1036 | } | |
1037 | ||
1038 | if ( edge2 ) | |
1039 | { | |
1040 | FT_Pos edge_delta; | |
1041 | FT_Pos seg_delta; | |
1042 | ||
1043 | ||
1044 | edge_delta = edge->fpos - edge2->fpos; | |
1045 | if ( edge_delta < 0 ) | |
1046 | edge_delta = -edge_delta; | |
1047 | ||
1048 | seg_delta = seg->pos - seg2->pos; | |
1049 | if ( seg_delta < 0 ) | |
1050 | seg_delta = -seg_delta; | |
1051 | ||
1052 | if ( seg_delta < edge_delta ) | |
1053 | edge2 = seg2->edge; | |
1054 | } | |
1055 | else | |
1056 | edge2 = seg2->edge; | |
1057 | ||
1058 | if ( is_serif ) | |
1059 | edge->serif = edge2; | |
1060 | else | |
1061 | edge->link = edge2; | |
1062 | } | |
1063 | ||
1064 | seg = seg->edge_next; | |
1065 | ||
1066 | } while ( seg != edge->first ); | |
1067 | ||
1068 | /* set the round/straight flags */ | |
1069 | edge->flags = ah_edge_normal; | |
1070 | ||
1071 | if ( is_straight == 0 && is_round ) | |
1072 | edge->flags |= ah_edge_round; | |
1073 | ||
1074 | /* set the edge's main direction */ | |
1075 | edge->dir = ah_dir_none; | |
1076 | ||
1077 | if ( ups > downs ) | |
1078 | edge->dir = up_dir; | |
1079 | ||
1080 | else if ( ups < downs ) | |
1081 | edge->dir = - up_dir; | |
1082 | ||
1083 | else if ( ups == downs ) | |
1084 | edge->dir = 0; /* both up and down !! */ | |
1085 | ||
1086 | /* gets rid of serifs if link is set */ | |
1087 | /* XXX: This gets rid of many unpleasant artefacts! */ | |
1088 | /* Example: the `c' in cour.pfa at size 13 */ | |
1089 | ||
1090 | if ( edge->serif && edge->link ) | |
1091 | edge->serif = 0; | |
1092 | } | |
1093 | ||
1094 | edges = outline->vert_edges; | |
1095 | segments = outline->vert_segments; | |
1096 | segment_limit = segments + outline->num_vsegments; | |
1097 | p_num_edges = &outline->num_vedges; | |
1098 | up_dir = ah_dir_up; | |
1099 | scale = outline->x_scale; | |
1100 | } | |
1101 | } | |
1102 | ||
1103 | ||
1104 | /*************************************************************************/ | |
1105 | /* */ | |
1106 | /* <Function> */ | |
1107 | /* ah_outline_detect_features */ | |
1108 | /* */ | |
1109 | /* <Description> */ | |
1110 | /* Performs feature detection on a given AH_Outline object. */ | |
1111 | /* */ | |
1112 | LOCAL_FUNC | |
1113 | void ah_outline_detect_features( AH_Outline* outline ) | |
1114 | { | |
1115 | ah_outline_compute_segments( outline ); | |
1116 | ah_outline_link_segments ( outline ); | |
1117 | ah_outline_compute_edges ( outline ); | |
1118 | } | |
1119 | ||
1120 | ||
1121 | /*************************************************************************/ | |
1122 | /* */ | |
1123 | /* <Function> */ | |
1124 | /* ah_outline_compute_blue_edges */ | |
1125 | /* */ | |
1126 | /* <Description> */ | |
1127 | /* Computes the `blue edges' in a given outline (i.e. those that must */ | |
1128 | /* be snapped to a blue zone edge (top or bottom). */ | |
1129 | /* */ | |
1130 | LOCAL_FUNC | |
1131 | void ah_outline_compute_blue_edges( AH_Outline* outline, | |
1132 | AH_Face_Globals* face_globals ) | |
1133 | { | |
1134 | AH_Edge* edge = outline->horz_edges; | |
1135 | AH_Edge* limit = edge + outline->num_hedges; | |
1136 | AH_Globals* globals = &face_globals->design; | |
1137 | FT_Fixed y_scale = outline->y_scale; | |
1138 | ||
1139 | ||
1140 | /* compute for each horizontal edge, which blue zone is closer */ | |
1141 | for ( ; edge < limit; edge++ ) | |
1142 | { | |
1143 | AH_Blue blue; | |
1144 | FT_Pos* best_blue = 0; | |
1145 | FT_Pos best_dist; /* initial threshold */ | |
1146 | ||
1147 | ||
1148 | /* compute the initial threshold as a fraction of the EM size */ | |
1149 | best_dist = FT_MulFix( face_globals->face->units_per_EM / 40, y_scale ); | |
1150 | if ( best_dist > 64 / 4 ) | |
1151 | best_dist = 64 / 4; | |
1152 | ||
1153 | for ( blue = ah_blue_capital_top; blue < ah_blue_max; blue++ ) | |
1154 | { | |
1155 | /* if it is a top zone, check for right edges -- if it is a bottom */ | |
1156 | /* zone, check for left edges */ | |
1157 | /* */ | |
1158 | /* of course, that's for TrueType XXX */ | |
1159 | FT_Bool is_top_blue = AH_IS_TOP_BLUE( blue ); | |
1160 | FT_Bool is_major_dir = edge->dir == outline->horz_major_dir; | |
1161 | ||
1162 | ||
1163 | /* if it is a top zone, the edge must be against the major */ | |
1164 | /* direction; if it is a bottom zone, it must be in the major */ | |
1165 | /* direction */ | |
1166 | if ( is_top_blue ^ is_major_dir ) | |
1167 | { | |
1168 | FT_Pos dist; | |
1169 | FT_Pos* blue_pos = globals->blue_refs + blue; | |
1170 | ||
1171 | ||
1172 | /* first of all, compare it to the reference position */ | |
1173 | dist = edge->fpos - *blue_pos; | |
1174 | if ( dist < 0 ) | |
1175 | dist = -dist; | |
1176 | ||
1177 | dist = FT_MulFix( dist, y_scale ); | |
1178 | if ( dist < best_dist ) | |
1179 | { | |
1180 | best_dist = dist; | |
1181 | best_blue = blue_pos; | |
1182 | } | |
1183 | ||
1184 | /* now, compare it to the overshoot position if the edge is */ | |
1185 | /* rounded, and if the edge is over the reference position of a */ | |
1186 | /* top zone, or under the reference position of a bottom zone */ | |
1187 | if ( edge->flags & ah_edge_round && dist != 0 ) | |
1188 | { | |
1189 | FT_Bool is_under_ref = edge->fpos < *blue_pos; | |
1190 | ||
1191 | ||
1192 | if ( is_top_blue ^ is_under_ref ) | |
1193 | { | |
1194 | blue_pos = globals->blue_shoots + blue; | |
1195 | dist = edge->fpos - *blue_pos; | |
1196 | if ( dist < 0 ) | |
1197 | dist = -dist; | |
1198 | ||
1199 | dist = FT_MulFix( dist, y_scale ); | |
1200 | if ( dist < best_dist ) | |
1201 | { | |
1202 | best_dist = dist; | |
1203 | best_blue = blue_pos; | |
1204 | } | |
1205 | } | |
1206 | } | |
1207 | } | |
1208 | } | |
1209 | ||
1210 | if ( best_blue ) | |
1211 | edge->blue_edge = best_blue; | |
1212 | } | |
1213 | } | |
1214 | ||
1215 | ||
1216 | /*************************************************************************/ | |
1217 | /* */ | |
1218 | /* <Function> */ | |
1219 | /* ah_outline_scale_blue_edges */ | |
1220 | /* */ | |
1221 | /* <Description> */ | |
1222 | /* This functions must be called before hinting in order to re-adjust */ | |
1223 | /* the contents of the detected edges (basically change the `blue */ | |
1224 | /* edge' pointer from `design units' to `scaled ones'). */ | |
1225 | /* */ | |
1226 | LOCAL_FUNC | |
1227 | void ah_outline_scale_blue_edges( AH_Outline* outline, | |
1228 | AH_Face_Globals* globals ) | |
1229 | { | |
1230 | AH_Edge* edge = outline->horz_edges; | |
1231 | AH_Edge* limit = edge + outline->num_hedges; | |
1232 | FT_Int delta; | |
1233 | ||
1234 | ||
1235 | delta = globals->scaled.blue_refs - globals->design.blue_refs; | |
1236 | ||
1237 | for ( ; edge < limit; edge++ ) | |
1238 | { | |
1239 | if ( edge->blue_edge ) | |
1240 | edge->blue_edge += delta; | |
1241 | } | |
1242 | } | |
1243 | ||
1244 | ||
1245 | #ifdef AH_DEBUG_GLYPH | |
1246 | ||
1247 | void ah_dump_edges( AH_Outline* outline ) | |
1248 | { | |
1249 | AH_Edge* edges; | |
1250 | AH_Edge* limit; | |
1251 | AH_Segment* segments; | |
1252 | FT_Int dimension; | |
1253 | ||
1254 | ||
1255 | edges = outline->horz_edges; | |
1256 | limit = edges + outline->num_hedges; | |
1257 | segments = outline->horz_segments; | |
1258 | ||
1259 | for ( dimension = 1; dimension >= 0; dimension-- ) | |
1260 | { | |
1261 | AH_Edge* edge; | |
1262 | ||
1263 | ||
1264 | printf ( "Table of %s edges:\n", | |
1265 | !dimension ? "vertical" : "horizontal" ); | |
1266 | printf ( " [ index | pos | dir | link |" | |
1267 | " serif | blue | opos | pos ]\n" ); | |
1268 | ||
1269 | for ( edge = edges; edge < limit; edge++ ) | |
1270 | { | |
1271 | printf ( " [ %5d | %4d | %5s | %4d | %5d | %c | %5.2f | %5.2f ]\n", | |
1272 | edge - edges, | |
1273 | (int)edge->fpos, | |
1274 | edge->dir == ah_dir_up | |
1275 | ? "up" | |
1276 | : ( edge->dir == ah_dir_down | |
1277 | ? "down" | |
1278 | : ( edge->dir == ah_dir_left | |
1279 | ? "left" | |
1280 | : ( edge->dir == ah_dir_right | |
1281 | ? "right" | |
1282 | : "none" ) ) ), | |
1283 | edge->link ? ( edge->link - edges ) : -1, | |
1284 | edge->serif ? ( edge->serif - edges ) : -1, | |
1285 | edge->blue_edge ? 'y' : 'n', | |
1286 | edge->opos / 64.0, | |
1287 | edge->pos / 64.0 ); | |
1288 | } | |
1289 | ||
1290 | edges = outline->vert_edges; | |
1291 | limit = edges + outline->num_vedges; | |
1292 | segments = outline->vert_segments; | |
1293 | } | |
1294 | } | |
1295 | ||
1296 | #endif /* AH_DEBUG_GLYPH */ | |
1297 | ||
1298 | ||
1299 | /* END */ |