Add bytecode for LINK action.
[ttfautohint.git] / src / talatin.c
blob9c3d41663ab929ddd983fdde18a76896fbee5463
1 /* talatin.c */
3 /* originally file `aflatin.c' (2011-Mar-28) from FreeType */
5 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
7 #include <string.h>
9 #include <ft2build.h>
10 #include FT_ADVANCES_H
12 #include "talatin.h"
13 #include "tasort.h"
16 #ifdef TA_CONFIG_OPTION_USE_WARPER
17 #include "afwarp.h"
18 #endif
21 /* find segments and links, compute all stem widths, and initialize */
22 /* standard width and height for the glyph with given charcode */
24 void
25 ta_latin_metrics_init_widths(TA_LatinMetrics metrics,
26 FT_Face face,
27 FT_ULong charcode)
29 /* scan the array of segments in each direction */
30 TA_GlyphHintsRec hints[1];
33 ta_glyph_hints_init(hints);
35 metrics->axis[TA_DIMENSION_HORZ].width_count = 0;
36 metrics->axis[TA_DIMENSION_VERT].width_count = 0;
39 FT_Error error;
40 FT_UInt glyph_index;
41 int dim;
42 TA_LatinMetricsRec dummy[1];
43 TA_Scaler scaler = &dummy->root.scaler;
46 glyph_index = FT_Get_Char_Index(face, charcode);
47 if (glyph_index == 0)
48 goto Exit;
50 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
51 if (error || face->glyph->outline.n_points <= 0)
52 goto Exit;
54 memset(dummy, 0, sizeof (TA_LatinMetricsRec));
56 dummy->units_per_em = metrics->units_per_em;
58 scaler->x_scale = 0x10000L;
59 scaler->y_scale = 0x10000L;
60 scaler->x_delta = 0;
61 scaler->y_delta = 0;
63 scaler->face = face;
64 scaler->render_mode = FT_RENDER_MODE_NORMAL;
65 scaler->flags = 0;
67 ta_glyph_hints_rescale(hints, (TA_ScriptMetrics)dummy);
69 error = ta_glyph_hints_reload(hints, &face->glyph->outline);
70 if (error)
71 goto Exit;
73 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
75 TA_LatinAxis axis = &metrics->axis[dim];
76 TA_AxisHints axhints = &hints->axis[dim];
78 TA_Segment seg, limit, link;
79 FT_UInt num_widths = 0;
82 error = ta_latin_hints_compute_segments(hints, (TA_Dimension)dim);
83 if (error)
84 goto Exit;
86 ta_latin_hints_link_segments(hints, (TA_Dimension)dim);
88 seg = axhints->segments;
89 limit = seg + axhints->num_segments;
91 for (; seg < limit; seg++)
93 link = seg->link;
95 /* we only consider stem segments there! */
96 if (link
97 && link->link == seg
98 && link > seg)
100 FT_Pos dist;
103 dist = seg->pos - link->pos;
104 if (dist < 0)
105 dist = -dist;
107 if (num_widths < TA_LATIN_MAX_WIDTHS)
108 axis->widths[num_widths++].org = dist;
112 ta_sort_widths(num_widths, axis->widths);
113 axis->width_count = num_widths;
116 Exit:
117 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
119 TA_LatinAxis axis = &metrics->axis[dim];
120 FT_Pos stdw;
123 stdw = (axis->width_count > 0) ? axis->widths[0].org
124 : TA_LATIN_CONSTANT(metrics, 50);
126 /* let's try 20% of the smallest width */
127 axis->edge_distance_threshold = stdw / 5;
128 axis->standard_width = stdw;
129 axis->extra_light = 0;
133 ta_glyph_hints_done(hints);
137 #define TA_LATIN_MAX_TEST_CHARACTERS 12
140 static const char ta_latin_blue_chars[TA_LATIN_MAX_BLUES]
141 [TA_LATIN_MAX_TEST_CHARACTERS + 1] =
143 "THEZOCQS",
144 "HEZLOCUS",
145 "fijkdbh",
146 "xzroesc",
147 "xzroesc",
148 "pqgjy"
152 /* find all blue zones; flat segments give the reference points, */
153 /* round segments the overshoot positions */
155 static void
156 ta_latin_metrics_init_blues(TA_LatinMetrics metrics,
157 FT_Face face)
159 FT_Pos flats[TA_LATIN_MAX_TEST_CHARACTERS];
160 FT_Pos rounds[TA_LATIN_MAX_TEST_CHARACTERS];
161 FT_Int num_flats;
162 FT_Int num_rounds;
164 FT_Int bb;
165 TA_LatinBlue blue;
166 FT_Error error;
167 TA_LatinAxis axis = &metrics->axis[TA_DIMENSION_VERT];
168 FT_GlyphSlot glyph = face->glyph;
171 /* we compute the blues simply by loading each character from the */
172 /* `ta_latin_blue_chars[blues]' string, then finding its top-most or */
173 /* bottom-most points (depending on `TA_IS_TOP_BLUE') */
175 TA_LOG(("blue zones computation\n"));
176 TA_LOG(("------------------------------------------------\n"));
178 for (bb = 0; bb < TA_LATIN_BLUE_MAX; bb++)
180 const char* p = ta_latin_blue_chars[bb];
181 const char* limit = p + TA_LATIN_MAX_TEST_CHARACTERS;
182 FT_Pos* blue_ref;
183 FT_Pos* blue_shoot;
186 TA_LOG(("blue %3d: ", bb));
188 num_flats = 0;
189 num_rounds = 0;
191 for (; p < limit && *p; p++)
193 FT_UInt glyph_index;
194 FT_Pos best_y; /* same as points.y */
195 FT_Int best_point, best_first, best_last;
196 FT_Vector* points;
197 FT_Bool round = 0;
200 TA_LOG(("'%c'", *p));
202 /* load the character in the face -- skip unknown or empty ones */
203 glyph_index = FT_Get_Char_Index(face, (FT_UInt)*p);
204 if (glyph_index == 0)
205 continue;
207 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
208 if (error || glyph->outline.n_points <= 0)
209 continue;
211 /* now compute min or max point indices and coordinates */
212 points = glyph->outline.points;
213 best_point = -1;
214 best_y = 0; /* make compiler happy */
215 best_first = 0; /* ditto */
216 best_last = 0; /* ditto */
219 FT_Int nn;
220 FT_Int first = 0;
221 FT_Int last = -1;
224 for (nn = 0; nn < glyph->outline.n_contours; first = last + 1, nn++)
226 FT_Int old_best_point = best_point;
227 FT_Int pp;
230 last = glyph->outline.contours[nn];
232 /* avoid single-point contours since they are never rasterized; */
233 /* in some fonts, they correspond to mark attachment points */
234 /* which are way outside of the glyph's real outline */
235 if (last <= first)
236 continue;
238 if (TA_LATIN_IS_TOP_BLUE(bb))
240 for (pp = first; pp <= last; pp++)
241 if (best_point < 0
242 || points[pp].y > best_y)
244 best_point = pp;
245 best_y = points[pp].y;
248 else
250 for (pp = first; pp <= last; pp++)
251 if (best_point < 0
252 || points[pp].y < best_y)
254 best_point = pp;
255 best_y = points[pp].y;
259 if (best_point != old_best_point)
261 best_first = first;
262 best_last = last;
265 TA_LOG(("%5ld", best_y));
268 /* now check whether the point belongs to a straight or round */
269 /* segment; we first need to find in which contour the extremum */
270 /* lies, then inspect its previous and next points */
271 if (best_point >= 0)
273 FT_Int prev, next;
274 FT_Pos dist;
277 /* now look for the previous and next points that are not on the */
278 /* same Y coordinate and threshold the `closeness'... */
279 prev = best_point;
280 next = prev;
284 if (prev > best_first)
285 prev--;
286 else
287 prev = best_last;
289 dist = points[prev].y - best_y;
290 if (dist < -5 || dist > 5)
291 break;
292 } while (prev != best_point);
296 if (next < best_last)
297 next++;
298 else
299 next = best_first;
301 dist = points[next].y - best_y;
302 if (dist < -5 || dist > 5)
303 break;
304 } while (next != best_point);
306 /* now set the `round' flag depending on the segment's kind */
307 round = FT_BOOL(
308 FT_CURVE_TAG(glyph->outline.tags[prev]) != FT_CURVE_TAG_ON
309 || FT_CURVE_TAG(glyph->outline.tags[next]) != FT_CURVE_TAG_ON);
311 TA_LOG(("%c ", round ? 'r' : 'f'));
314 if (round)
315 rounds[num_rounds++] = best_y;
316 else
317 flats[num_flats++] = best_y;
320 TA_LOG(("\n"));
322 if (num_flats == 0 && num_rounds == 0)
324 /* we couldn't find a single glyph to compute this blue zone, */
325 /* we will simply ignore it then */
326 TA_LOG(("empty\n"));
327 continue;
330 /* we have computed the contents of the `rounds' and `flats' tables, */
331 /* now determine the reference and overshoot position of the blue -- */
332 /* we simply take the median value after a simple sort */
333 ta_sort_pos(num_rounds, rounds);
334 ta_sort_pos(num_flats, flats);
336 blue = &axis->blues[axis->blue_count];
337 blue_ref = &blue->ref.org;
338 blue_shoot = &blue->shoot.org;
340 axis->blue_count++;
342 if (num_flats == 0)
344 *blue_ref =
345 *blue_shoot = rounds[num_rounds / 2];
347 else if (num_rounds == 0)
349 *blue_ref =
350 *blue_shoot = flats[num_flats / 2];
352 else
354 *blue_ref = flats[num_flats / 2];
355 *blue_shoot = rounds[num_rounds / 2];
358 /* there are sometimes problems if the overshoot position of top */
359 /* zones is under its reference position, or the opposite for bottom */
360 /* zones; we must thus check everything there and correct the errors */
361 if (*blue_shoot != *blue_ref)
363 FT_Pos ref = *blue_ref;
364 FT_Pos shoot = *blue_shoot;
365 FT_Bool over_ref = FT_BOOL(shoot > ref);
368 if (TA_LATIN_IS_TOP_BLUE(bb) ^ over_ref)
369 *blue_ref =
370 *blue_shoot = (shoot + ref) / 2;
373 blue->flags = 0;
374 if (TA_LATIN_IS_TOP_BLUE(bb))
375 blue->flags |= TA_LATIN_BLUE_TOP;
377 /* the following flag is used later to adjust the y and x scales */
378 /* in order to optimize the pixel grid alignment */
379 /* of the top of small letters */
380 if (bb == TA_LATIN_BLUE_SMALL_TOP)
381 blue->flags |= TA_LATIN_BLUE_ADJUSTMENT;
383 TA_LOG(("-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot));
386 TA_LOG(("\n"));
388 return;
392 /* check whether all ASCII digits have the same advance width */
394 void
395 ta_latin_metrics_check_digits(TA_LatinMetrics metrics,
396 FT_Face face)
398 FT_UInt i;
399 FT_Bool started = 0, same_width = 1;
400 FT_Fixed advance, old_advance = 0;
403 /* digit `0' is 0x30 in all supported charmaps */
404 for (i = 0x30; i <= 0x39; i++)
406 FT_UInt glyph_index;
409 glyph_index = FT_Get_Char_Index(face, i);
410 if (glyph_index == 0)
411 continue;
413 if (FT_Get_Advance(face, glyph_index,
414 FT_LOAD_NO_SCALE
415 | FT_LOAD_NO_HINTING
416 | FT_LOAD_IGNORE_TRANSFORM,
417 &advance))
418 continue;
420 if (started)
422 if (advance != old_advance)
424 same_width = 0;
425 break;
428 else
430 old_advance = advance;
431 started = 1;
435 metrics->root.digits_have_same_width = same_width;
439 /* initialize global metrics */
441 FT_Error
442 ta_latin_metrics_init(TA_LatinMetrics metrics,
443 FT_Face face)
445 FT_Error error = FT_Err_Ok;
446 FT_CharMap oldmap = face->charmap;
447 FT_UInt ee;
449 static const FT_Encoding latin_encodings[] =
451 FT_ENCODING_UNICODE,
452 FT_ENCODING_APPLE_ROMAN,
453 FT_ENCODING_ADOBE_STANDARD,
454 FT_ENCODING_ADOBE_LATIN_1,
456 FT_ENCODING_NONE /* end of list */
460 metrics->units_per_em = face->units_per_EM;
462 /* do we have a latin charmap in there? */
463 for (ee = 0; latin_encodings[ee] != FT_ENCODING_NONE; ee++)
465 error = FT_Select_Charmap(face, latin_encodings[ee]);
466 if (!error)
467 break;
470 if (!error)
472 /* for now, compute the standard width and height from the `o' */
473 ta_latin_metrics_init_widths(metrics, face, 'o');
474 ta_latin_metrics_init_blues(metrics, face);
475 ta_latin_metrics_check_digits(metrics, face);
478 FT_Set_Charmap(face, oldmap);
479 return FT_Err_Ok;
483 /* adjust scaling value, then scale and shift widths */
484 /* and blue zones (if applicable) for given dimension */
486 static void
487 ta_latin_metrics_scale_dim(TA_LatinMetrics metrics,
488 TA_Scaler scaler,
489 TA_Dimension dim)
491 FT_Fixed scale;
492 FT_Pos delta;
493 TA_LatinAxis axis;
494 FT_UInt nn;
497 if (dim == TA_DIMENSION_HORZ)
499 scale = scaler->x_scale;
500 delta = scaler->x_delta;
502 else
504 scale = scaler->y_scale;
505 delta = scaler->y_delta;
508 axis = &metrics->axis[dim];
510 if (axis->org_scale == scale && axis->org_delta == delta)
511 return;
513 axis->org_scale = scale;
514 axis->org_delta = delta;
516 /* correct X and Y scale to optimize the alignment of the top of */
517 /* small letters to the pixel grid */
519 TA_LatinAxis Axis = &metrics->axis[TA_DIMENSION_VERT];
520 TA_LatinBlue blue = NULL;
523 for (nn = 0; nn < Axis->blue_count; nn++)
525 if (Axis->blues[nn].flags & TA_LATIN_BLUE_ADJUSTMENT)
527 blue = &Axis->blues[nn];
528 break;
532 if (blue)
534 FT_Pos scaled = FT_MulFix(blue->shoot.org, scaler->y_scale);
535 FT_Pos fitted = (scaled + 40) & ~63;
538 if (scaled != fitted)
540 if (dim == TA_DIMENSION_VERT)
541 scale = FT_MulDiv(scale, fitted, scaled);
546 axis->scale = scale;
547 axis->delta = delta;
549 if (dim == TA_DIMENSION_HORZ)
551 metrics->root.scaler.x_scale = scale;
552 metrics->root.scaler.x_delta = delta;
554 else
556 metrics->root.scaler.y_scale = scale;
557 metrics->root.scaler.y_delta = delta;
560 /* scale the widths */
561 for (nn = 0; nn < axis->width_count; nn++)
563 TA_Width width = axis->widths + nn;
566 width->cur = FT_MulFix(width->org, scale);
567 width->fit = width->cur;
570 /* an extra-light axis corresponds to a standard width that is */
571 /* smaller than 5/8 pixels */
572 axis->extra_light =
573 (FT_Bool)(FT_MulFix(axis->standard_width, scale) < 32 + 8);
575 if (dim == TA_DIMENSION_VERT)
577 /* scale the blue zones */
578 for (nn = 0; nn < axis->blue_count; nn++)
580 TA_LatinBlue blue = &axis->blues[nn];
581 FT_Pos dist;
584 blue->ref.cur = FT_MulFix(blue->ref.org, scale) + delta;
585 blue->ref.fit = blue->ref.cur;
586 blue->shoot.cur = FT_MulFix(blue->shoot.org, scale) + delta;
587 blue->shoot.fit = blue->shoot.cur;
588 blue->flags &= ~TA_LATIN_BLUE_ACTIVE;
590 /* a blue zone is only active if it is less than 3/4 pixels tall */
591 dist = FT_MulFix(blue->ref.org - blue->shoot.org, scale);
592 if (dist <= 48 && dist >= -48)
594 #if 0
595 FT_Pos delta1;
596 #endif
597 FT_Pos delta2;
600 /* use discrete values for blue zone widths */
602 #if 0
603 /* generic, original code */
604 delta1 = blue->shoot.org - blue->ref.org;
605 delta2 = delta1;
606 if (delta1 < 0)
607 delta2 = -delta2;
609 delta2 = FT_MulFix(delta2, scale);
611 if (delta2 < 32)
612 delta2 = 0;
613 else if (delta2 < 64)
614 delta2 = 32 + (((delta2 - 32) + 16) & ~31);
615 else
616 delta2 = TA_PIX_ROUND(delta2);
618 if (delta1 < 0)
619 delta2 = -delta2;
620 #else
621 /* simplified version due to abs(dist) <= 48 */
622 delta2 = dist;
623 if (dist < 0)
624 delta2 = -delta2;
626 if (delta2 < 32)
627 delta2 = 0;
628 else if (delta < 48)
629 delta2 = 32;
630 else
631 delta2 = 64;
633 if (dist < 0)
634 delta2 = -delta2;
635 #endif
637 blue->ref.fit = TA_PIX_ROUND(blue->ref.cur);
638 blue->shoot.fit = blue->ref.fit + delta2;
640 blue->flags |= TA_LATIN_BLUE_ACTIVE;
647 /* scale global values in both directions */
649 void
650 ta_latin_metrics_scale(TA_LatinMetrics metrics,
651 TA_Scaler scaler)
653 metrics->root.scaler.render_mode = scaler->render_mode;
654 metrics->root.scaler.face = scaler->face;
656 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_HORZ);
657 ta_latin_metrics_scale_dim(metrics, scaler, TA_DIMENSION_VERT);
661 /* walk over all contours and compute its segments */
663 FT_Error
664 ta_latin_hints_compute_segments(TA_GlyphHints hints,
665 TA_Dimension dim)
667 TA_AxisHints axis = &hints->axis[dim];
668 FT_Error error = FT_Err_Ok;
670 TA_Segment segment = NULL;
671 TA_SegmentRec seg0;
673 TA_Point* contour = hints->contours;
674 TA_Point* contour_limit = contour + hints->num_contours;
675 TA_Direction major_dir, segment_dir;
678 memset(&seg0, 0, sizeof (TA_SegmentRec));
679 seg0.score = 32000;
680 seg0.flags = TA_EDGE_NORMAL;
682 major_dir = (TA_Direction)TA_ABS(axis->major_dir);
683 segment_dir = major_dir;
685 axis->num_segments = 0;
687 /* set up (u,v) in each point */
688 if (dim == TA_DIMENSION_HORZ)
690 TA_Point point = hints->points;
691 TA_Point limit = point + hints->num_points;
694 for (; point < limit; point++)
696 point->u = point->fx;
697 point->v = point->fy;
700 else
702 TA_Point point = hints->points;
703 TA_Point limit = point + hints->num_points;
706 for (; point < limit; point++)
708 point->u = point->fy;
709 point->v = point->fx;
713 /* do each contour separately */
714 for (; contour < contour_limit; contour++)
716 TA_Point point = contour[0];
717 TA_Point last = point->prev;
719 int on_edge = 0;
721 FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
722 FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
723 FT_Bool passed;
726 if (point == last) /* skip singletons -- just in case */
727 continue;
729 if (TA_ABS(last->out_dir) == major_dir
730 && TA_ABS(point->out_dir) == major_dir)
732 /* we are already on an edge, try to locate its start */
733 last = point;
735 for (;;)
737 point = point->prev;
738 if (TA_ABS(point->out_dir) != major_dir)
740 point = point->next;
741 break;
743 if (point == last)
744 break;
748 last = point;
749 passed = 0;
751 for (;;)
753 FT_Pos u, v;
756 if (on_edge)
758 u = point->u;
759 if (u < min_pos)
760 min_pos = u;
761 if (u > max_pos)
762 max_pos = u;
764 if (point->out_dir != segment_dir
765 || point == last)
767 /* we are just leaving an edge; record a new segment! */
768 segment->last = point;
769 segment->pos = (FT_Short)((min_pos + max_pos) >> 1);
771 /* a segment is round if either its first or last point */
772 /* is a control point */
773 if ((segment->first->flags | point->flags) & TA_FLAG_CONTROL)
774 segment->flags |= TA_EDGE_ROUND;
776 /* compute segment size */
777 min_pos = max_pos = point->v;
779 v = segment->first->v;
780 if (v < min_pos)
781 min_pos = v;
782 if (v > max_pos)
783 max_pos = v;
785 segment->min_coord = (FT_Short)min_pos;
786 segment->max_coord = (FT_Short)max_pos;
787 segment->height = (FT_Short)(segment->max_coord -
788 segment->min_coord);
790 on_edge = 0;
791 segment = NULL;
792 /* fall through */
796 /* now exit if we are at the start/end point */
797 if (point == last)
799 if (passed)
800 break;
801 passed = 1;
804 if (!on_edge
805 && TA_ABS(point->out_dir) == major_dir)
807 /* this is the start of a new segment! */
808 segment_dir = (TA_Direction)point->out_dir;
810 /* clear all segment fields */
811 error = ta_axis_hints_new_segment(axis, &segment);
812 if (error)
813 goto Exit;
815 segment[0] = seg0;
816 segment->dir = (FT_Char)segment_dir;
817 min_pos = max_pos = point->u;
818 segment->first = point;
819 segment->last = point;
820 segment->contour = contour;
821 on_edge = 1;
824 point = point->next;
826 } /* contours */
829 /* now slightly increase the height of segments if this makes sense -- */
830 /* this is used to better detect and ignore serifs */
832 TA_Segment segments = axis->segments;
833 TA_Segment segments_end = segments + axis->num_segments;
836 for (segment = segments; segment < segments_end; segment++)
838 TA_Point first = segment->first;
839 TA_Point last = segment->last;
841 FT_Pos first_v = first->v;
842 FT_Pos last_v = last->v;
845 if (first == last)
846 continue;
848 if (first_v < last_v)
850 TA_Point p;
853 p = first->prev;
854 if (p->v < first_v)
855 segment->height = (FT_Short)(segment->height +
856 ((first_v - p->v) >> 1));
858 p = last->next;
859 if (p->v > last_v)
860 segment->height = (FT_Short)(segment->height +
861 ((p->v - last_v) >> 1));
863 else
865 TA_Point p;
868 p = first->prev;
869 if (p->v > first_v)
870 segment->height = (FT_Short)(segment->height +
871 ((p->v - first_v) >> 1));
873 p = last->next;
874 if (p->v < last_v)
875 segment->height = (FT_Short)(segment->height +
876 ((last_v - p->v) >> 1));
881 Exit:
882 return error;
886 /* link segments to form stems and serifs */
888 void
889 ta_latin_hints_link_segments(TA_GlyphHints hints,
890 TA_Dimension dim)
892 TA_AxisHints axis = &hints->axis[dim];
894 TA_Segment segments = axis->segments;
895 TA_Segment segment_limit = segments + axis->num_segments;
897 FT_Pos len_threshold, len_score;
898 TA_Segment seg1, seg2;
901 len_threshold = TA_LATIN_CONSTANT(hints->metrics, 8);
902 if (len_threshold == 0)
903 len_threshold = 1;
905 len_score = TA_LATIN_CONSTANT(hints->metrics, 6000);
907 /* now compare each segment to the others */
908 for (seg1 = segments; seg1 < segment_limit; seg1++)
910 /* the fake segments are introduced to hint the metrics -- */
911 /* we must never link them to anything */
912 if (seg1->dir != axis->major_dir
913 || seg1->first == seg1->last)
914 continue;
916 /* search for stems having opposite directions, */
917 /* with seg1 to the `left' of seg2 */
918 for (seg2 = segments; seg2 < segment_limit; seg2++)
920 FT_Pos pos1 = seg1->pos;
921 FT_Pos pos2 = seg2->pos;
924 if (seg1->dir + seg2->dir == 0
925 && pos2 > pos1)
927 /* compute distance between the two segments */
928 FT_Pos dist = pos2 - pos1;
929 FT_Pos min = seg1->min_coord;
930 FT_Pos max = seg1->max_coord;
931 FT_Pos len, score;
934 if (min < seg2->min_coord)
935 min = seg2->min_coord;
936 if (max > seg2->max_coord)
937 max = seg2->max_coord;
939 /* compute maximum coordinate difference of the two segments */
940 len = max - min;
941 if (len >= len_threshold)
943 /* small coordinate differences cause a higher score, and */
944 /* segments with a greater distance cause a higher score also */
945 score = dist + len_score / len;
947 /* and we search for the smallest score */
948 /* of the sum of the two values */
949 if (score < seg1->score)
951 seg1->score = score;
952 seg1->link = seg2;
955 if (score < seg2->score)
957 seg2->score = score;
958 seg2->link = seg1;
965 /* now compute the `serif' segments, cf. explanations in `tahints.h' */
966 for (seg1 = segments; seg1 < segment_limit; seg1++)
968 seg2 = seg1->link;
970 if (seg2)
972 if (seg2->link != seg1)
974 seg1->link = 0;
975 seg1->serif = seg2->link;
982 /* link segments to edges, using feature analysis for selection */
984 FT_Error
985 ta_latin_hints_compute_edges(TA_GlyphHints hints,
986 TA_Dimension dim)
988 TA_AxisHints axis = &hints->axis[dim];
989 FT_Error error = FT_Err_Ok;
990 TA_LatinAxis laxis = &((TA_LatinMetrics)hints->metrics)->axis[dim];
992 TA_Segment segments = axis->segments;
993 TA_Segment segment_limit = segments + axis->num_segments;
994 TA_Segment seg;
996 #if 0
997 TA_Direction up_dir;
998 #endif
999 FT_Fixed scale;
1000 FT_Pos edge_distance_threshold;
1001 FT_Pos segment_length_threshold;
1004 axis->num_edges = 0;
1006 scale = (dim == TA_DIMENSION_HORZ) ? hints->x_scale
1007 : hints->y_scale;
1009 #if 0
1010 up_dir = (dim == TA_DIMENSION_HORZ) ? TA_DIR_UP
1011 : TA_DIR_RIGHT;
1012 #endif
1014 /* we ignore all segments that are less than 1 pixel in length */
1015 /* to avoid many problems with serif fonts */
1016 /* (the corresponding threshold is computed in font units) */
1017 if (dim == TA_DIMENSION_HORZ)
1018 segment_length_threshold = FT_DivFix(64, hints->y_scale);
1019 else
1020 segment_length_threshold = 0;
1022 /********************************************************************/
1023 /* */
1024 /* We begin by generating a sorted table of edges for the current */
1025 /* direction. To do so, we simply scan each segment and try to find */
1026 /* an edge in our table that corresponds to its position. */
1027 /* */
1028 /* If no edge is found, we create and insert a new edge in the */
1029 /* sorted table. Otherwise, we simply add the segment to the edge's */
1030 /* list which gets processed in the second step to compute the */
1031 /* edge's properties. */
1032 /* */
1033 /* Note that the table of edges is sorted along the segment/edge */
1034 /* position. */
1035 /* */
1036 /********************************************************************/
1038 /* assure that edge distance threshold is at most 0.25px */
1039 edge_distance_threshold = FT_MulFix(laxis->edge_distance_threshold,
1040 scale);
1041 if (edge_distance_threshold > 64 / 4)
1042 edge_distance_threshold = 64 / 4;
1044 edge_distance_threshold = FT_DivFix(edge_distance_threshold,
1045 scale);
1047 for (seg = segments; seg < segment_limit; seg++)
1049 TA_Edge found = NULL;
1050 FT_Int ee;
1053 if (seg->height < segment_length_threshold)
1054 continue;
1056 /* a special case for serif edges: */
1057 /* if they are smaller than 1.5 pixels we ignore them */
1058 if (seg->serif
1059 && 2 * seg->height < 3 * segment_length_threshold)
1060 continue;
1062 /* look for an edge corresponding to the segment */
1063 for (ee = 0; ee < axis->num_edges; ee++)
1065 TA_Edge edge = axis->edges + ee;
1066 FT_Pos dist;
1069 dist = seg->pos - edge->fpos;
1070 if (dist < 0)
1071 dist = -dist;
1073 if (dist < edge_distance_threshold && edge->dir == seg->dir)
1075 found = edge;
1076 break;
1080 if (!found)
1082 TA_Edge edge;
1085 /* insert a new edge in the list and sort according to the position */
1086 error = ta_axis_hints_new_edge(axis, seg->pos,
1087 (TA_Direction)seg->dir,
1088 &edge);
1089 if (error)
1090 goto Exit;
1092 /* add the segment to the new edge's list */
1093 memset(edge, 0, sizeof (TA_EdgeRec));
1094 edge->first = seg;
1095 edge->last = seg;
1096 edge->dir = seg->dir;
1097 edge->fpos = seg->pos;
1098 edge->opos = FT_MulFix(seg->pos, scale);
1099 edge->pos = edge->opos;
1100 seg->edge_next = seg;
1102 else
1104 /* if an edge was found, simply add the segment to the edge's list */
1105 seg->edge_next = found->first;
1106 found->last->edge_next = seg;
1107 found->last = seg;
1111 /*****************************************************************/
1112 /* */
1113 /* Good, we now compute each edge's properties according to */
1114 /* the segments found on its position. Basically, these are */
1115 /* */
1116 /* - the edge's main direction */
1117 /* - stem edge, serif edge or both (which defaults to stem then) */
1118 /* - rounded edge, straight or both (which defaults to straight) */
1119 /* - link for edge */
1120 /* */
1121 /*****************************************************************/
1123 /* first of all, set the `edge' field in each segment -- this is */
1124 /* required in order to compute edge links */
1126 /* note that removing this loop and setting the `edge' field of each */
1127 /* segment directly in the code above slows down execution speed for */
1128 /* some reasons on platforms like the Sun */
1130 TA_Edge edges = axis->edges;
1131 TA_Edge edge_limit = edges + axis->num_edges;
1132 TA_Edge edge;
1135 for (edge = edges; edge < edge_limit; edge++)
1137 seg = edge->first;
1138 if (seg)
1141 seg->edge = edge;
1142 seg = seg->edge_next;
1143 } while (seg != edge->first);
1146 /* now compute each edge properties */
1147 for (edge = edges; edge < edge_limit; edge++)
1149 FT_Int is_round = 0; /* does it contain round segments? */
1150 FT_Int is_straight = 0; /* does it contain straight segments? */
1151 #if 0
1152 FT_Pos ups = 0; /* number of upwards segments */
1153 FT_Pos downs = 0; /* number of downwards segments */
1154 #endif
1157 seg = edge->first;
1161 FT_Bool is_serif;
1164 /* check for roundness of segment */
1165 if (seg->flags & TA_EDGE_ROUND)
1166 is_round++;
1167 else
1168 is_straight++;
1170 #if 0
1171 /* check for segment direction */
1172 if (seg->dir == up_dir)
1173 ups += seg->max_coord - seg->min_coord;
1174 else
1175 downs += seg->max_coord - seg->min_coord;
1176 #endif
1178 /* check for links -- */
1179 /* if seg->serif is set, then seg->link must be ignored */
1180 is_serif = (FT_Bool)(seg->serif
1181 && seg->serif->edge
1182 && seg->serif->edge != edge);
1184 if ((seg->link && seg->link->edge != NULL)
1185 || is_serif)
1187 TA_Edge edge2;
1188 TA_Segment seg2;
1191 edge2 = edge->link;
1192 seg2 = seg->link;
1194 if (is_serif)
1196 seg2 = seg->serif;
1197 edge2 = edge->serif;
1200 if (edge2)
1202 FT_Pos edge_delta;
1203 FT_Pos seg_delta;
1206 edge_delta = edge->fpos - edge2->fpos;
1207 if (edge_delta < 0)
1208 edge_delta = -edge_delta;
1210 seg_delta = seg->pos - seg2->pos;
1211 if (seg_delta < 0)
1212 seg_delta = -seg_delta;
1214 if (seg_delta < edge_delta)
1215 edge2 = seg2->edge;
1217 else
1218 edge2 = seg2->edge;
1220 if (is_serif)
1222 edge->serif = edge2;
1223 edge2->flags |= TA_EDGE_SERIF;
1225 else
1226 edge->link = edge2;
1229 seg = seg->edge_next;
1230 } while (seg != edge->first);
1232 /* set the round/straight flags */
1233 edge->flags = TA_EDGE_NORMAL;
1235 if (is_round > 0
1236 && is_round >= is_straight)
1237 edge->flags |= TA_EDGE_ROUND;
1239 #if 0
1240 /* set the edge's main direction */
1241 edge->dir = TA_DIR_NONE;
1243 if (ups > downs)
1244 edge->dir = (FT_Char)up_dir;
1246 else if (ups < downs)
1247 edge->dir = (FT_Char)-up_dir;
1249 else if (ups == downs)
1250 edge->dir = 0; /* both up and down! */
1251 #endif
1253 /* get rid of serifs if link is set */
1254 /* XXX: this gets rid of many unpleasant artefacts! */
1255 /* example: the `c' in cour.pfa at size 13 */
1257 if (edge->serif && edge->link)
1258 edge->serif = 0;
1262 Exit:
1263 return error;
1267 /* detect segments and edges for given dimension */
1269 FT_Error
1270 ta_latin_hints_detect_features(TA_GlyphHints hints,
1271 TA_Dimension dim)
1273 FT_Error error;
1276 error = ta_latin_hints_compute_segments(hints, dim);
1277 if (!error)
1279 ta_latin_hints_link_segments(hints, dim);
1281 error = ta_latin_hints_compute_edges(hints, dim);
1284 return error;
1288 /* compute all edges which lie within blue zones */
1290 void
1291 ta_latin_hints_compute_blue_edges(TA_GlyphHints hints,
1292 TA_LatinMetrics metrics)
1294 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1296 TA_Edge edge = axis->edges;
1297 TA_Edge edge_limit = edge + axis->num_edges;
1299 TA_LatinAxis latin = &metrics->axis[TA_DIMENSION_VERT];
1300 FT_Fixed scale = latin->scale;
1303 /* compute which blue zones are active, */
1304 /* i.e. have their scaled size < 3/4 pixels */
1306 /* for each horizontal edge search the blue zone which is closest */
1307 for (; edge < edge_limit; edge++)
1309 FT_Int bb;
1310 TA_Width best_blue = NULL;
1311 FT_Pos best_dist; /* initial threshold */
1313 FT_UInt best_blue_idx = 0;
1314 FT_Bool best_blue_is_shoot = 0;
1317 /* compute the initial threshold as a fraction of the EM size */
1318 /* (the value 40 is heuristic) */
1319 best_dist = FT_MulFix(metrics->units_per_em / 40, scale);
1321 /* assure a minimum distance of 0.5px */
1322 if (best_dist > 64 / 2)
1323 best_dist = 64 / 2;
1325 for (bb = 0; bb < TA_LATIN_BLUE_MAX; bb++)
1327 TA_LatinBlue blue = latin->blues + bb;
1328 FT_Bool is_top_blue, is_major_dir;
1331 /* skip inactive blue zones (i.e., those that are too large) */
1332 if (!(blue->flags & TA_LATIN_BLUE_ACTIVE))
1333 continue;
1335 /* if it is a top zone, check for right edges -- */
1336 /* if it is a bottom zone, check for left edges */
1337 is_top_blue = (FT_Byte)((blue->flags & TA_LATIN_BLUE_TOP) != 0);
1338 is_major_dir = FT_BOOL(edge->dir == axis->major_dir);
1340 /* if it is a top zone, the edge must be against the major */
1341 /* direction; if it is a bottom zone, it must be in the major */
1342 /* direction */
1343 if (is_top_blue ^ is_major_dir)
1345 FT_Pos dist;
1348 /* first of all, compare it to the reference position */
1349 dist = edge->fpos - blue->ref.org;
1350 if (dist < 0)
1351 dist = -dist;
1353 dist = FT_MulFix(dist, scale);
1354 if (dist < best_dist)
1356 best_dist = dist;
1357 best_blue = &blue->ref;
1359 best_blue_idx = bb;
1360 best_blue_is_shoot = 0;
1363 /* now compare it to the overshoot position and check whether */
1364 /* the edge is rounded, and whether the edge is over the */
1365 /* reference position of a top zone, or under the reference */
1366 /* position of a bottom zone */
1367 if (edge->flags & TA_EDGE_ROUND
1368 && dist != 0)
1370 FT_Bool is_under_ref = FT_BOOL(edge->fpos < blue->ref.org);
1373 if (is_top_blue ^ is_under_ref)
1375 dist = edge->fpos - blue->shoot.org;
1376 if (dist < 0)
1377 dist = -dist;
1379 dist = FT_MulFix(dist, scale);
1380 if (dist < best_dist)
1382 best_dist = dist;
1383 best_blue = &blue->shoot;
1385 best_blue_idx = bb;
1386 best_blue_is_shoot = 1;
1393 if (best_blue)
1395 edge->blue_edge = best_blue;
1396 edge->best_blue_idx = best_blue_idx;
1397 edge->best_blue_is_shoot = best_blue_is_shoot;
1403 /* initalize hinting engine */
1405 static FT_Error
1406 ta_latin_hints_init(TA_GlyphHints hints,
1407 TA_LatinMetrics metrics)
1409 FT_Render_Mode mode;
1410 FT_UInt32 scaler_flags, other_flags;
1411 FT_Face face = metrics->root.scaler.face;
1414 ta_glyph_hints_rescale(hints, (TA_ScriptMetrics)metrics);
1416 /* correct x_scale and y_scale if needed, since they may have */
1417 /* been modified by `ta_latin_metrics_scale_dim' above */
1418 hints->x_scale = metrics->axis[TA_DIMENSION_HORZ].scale;
1419 hints->x_delta = metrics->axis[TA_DIMENSION_HORZ].delta;
1420 hints->y_scale = metrics->axis[TA_DIMENSION_VERT].scale;
1421 hints->y_delta = metrics->axis[TA_DIMENSION_VERT].delta;
1423 /* compute flags depending on render mode, etc. */
1424 mode = metrics->root.scaler.render_mode;
1426 #if 0 /* #ifdef TA_CONFIG_OPTION_USE_WARPER */
1427 if (mode == FT_RENDER_MODE_LCD
1428 || mode == FT_RENDER_MODE_LCD_V)
1429 metrics->root.scaler.render_mode =
1430 mode = FT_RENDER_MODE_NORMAL;
1431 #endif
1433 scaler_flags = hints->scaler_flags;
1434 other_flags = 0;
1436 /* we snap the width of vertical stems for the monochrome */
1437 /* and horizontal LCD rendering targets only */
1438 if (mode == FT_RENDER_MODE_MONO
1439 || mode == FT_RENDER_MODE_LCD)
1440 other_flags |= TA_LATIN_HINTS_HORZ_SNAP;
1442 /* we snap the width of horizontal stems for the monochrome */
1443 /* and vertical LCD rendering targets only */
1444 if (mode == FT_RENDER_MODE_MONO
1445 || mode == FT_RENDER_MODE_LCD_V)
1446 other_flags |= TA_LATIN_HINTS_VERT_SNAP;
1448 /* we adjust stems to full pixels only if we don't use the `light' mode */
1449 if (mode != FT_RENDER_MODE_LIGHT)
1450 other_flags |= TA_LATIN_HINTS_STEM_ADJUST;
1452 if (mode == FT_RENDER_MODE_MONO)
1453 other_flags |= TA_LATIN_HINTS_MONO;
1455 /* in `light' hinting mode we disable horizontal hinting completely; */
1456 /* we also do it if the face is italic */
1457 if (mode == FT_RENDER_MODE_LIGHT
1458 || (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
1459 scaler_flags |= TA_SCALER_FLAG_NO_HORIZONTAL;
1461 hints->scaler_flags = scaler_flags;
1462 hints->other_flags = other_flags;
1464 return FT_Err_Ok;
1468 /* snap a given width in scaled coordinates to */
1469 /* one of the current standard widths */
1471 static FT_Pos
1472 ta_latin_snap_width(TA_Width widths,
1473 FT_Int count,
1474 FT_Pos width)
1476 int n;
1477 FT_Pos best = 64 + 32 + 2;
1478 FT_Pos reference = width;
1479 FT_Pos scaled;
1482 for (n = 0; n < count; n++)
1484 FT_Pos w;
1485 FT_Pos dist;
1488 w = widths[n].cur;
1489 dist = width - w;
1490 if (dist < 0)
1491 dist = -dist;
1492 if (dist < best)
1494 best = dist;
1495 reference = w;
1499 scaled = TA_PIX_ROUND(reference);
1501 if (width >= reference)
1503 if (width < scaled + 48)
1504 width = reference;
1506 else
1508 if (width > scaled - 48)
1509 width = reference;
1512 return width;
1516 /* compute the snapped width of a given stem, ignoring very thin ones */
1518 /* there is a lot of voodoo in this function; changing the hard-coded */
1519 /* parameters influence the whole hinting process */
1521 static FT_Pos
1522 ta_latin_compute_stem_width(TA_GlyphHints hints,
1523 TA_Dimension dim,
1524 FT_Pos width,
1525 FT_Byte base_flags,
1526 FT_Byte stem_flags)
1528 TA_LatinMetrics metrics = (TA_LatinMetrics) hints->metrics;
1529 TA_LatinAxis axis = &metrics->axis[dim];
1531 FT_Pos dist = width;
1532 FT_Int sign = 0;
1533 FT_Int vertical = (dim == TA_DIMENSION_VERT);
1536 if (!TA_LATIN_HINTS_DO_STEM_ADJUST(hints)
1537 || axis->extra_light)
1538 return width;
1540 if (dist < 0)
1542 dist = -width;
1543 sign = 1;
1546 if ((vertical && !TA_LATIN_HINTS_DO_VERT_SNAP(hints))
1547 || (!vertical && !TA_LATIN_HINTS_DO_HORZ_SNAP(hints)))
1549 /* smooth hinting process: very lightly quantize the stem width */
1551 /* leave the widths of serifs alone */
1552 if ((stem_flags & TA_EDGE_SERIF)
1553 && vertical
1554 && (dist < 3 * 64))
1555 goto Done_Width;
1556 else if (base_flags & TA_EDGE_ROUND)
1558 if (dist < 80)
1559 dist = 64;
1561 else if (dist < 56)
1562 dist = 56;
1564 if (axis->width_count > 0)
1566 FT_Pos delta;
1569 /* compare to standard width */
1570 delta = dist - axis->widths[0].cur;
1572 if (delta < 0)
1573 delta = -delta;
1575 if (delta < 40)
1577 dist = axis->widths[0].cur;
1578 if (dist < 48)
1579 dist = 48;
1581 goto Done_Width;
1584 if (dist < 3 * 64)
1586 delta = dist & 63;
1587 dist &= -64;
1589 if (delta < 10)
1590 dist += delta;
1591 else if (delta < 32)
1592 dist += 10;
1593 else if (delta < 54)
1594 dist += 54;
1595 else
1596 dist += delta;
1598 else
1599 dist = (dist + 32) & ~63;
1602 else
1604 /* strong hinting process: snap the stem width to integer pixels */
1606 FT_Pos org_dist = dist;
1609 dist = ta_latin_snap_width(axis->widths, axis->width_count, dist);
1611 if (vertical)
1613 /* in the case of vertical hinting, */
1614 /* always round the stem heights to integer pixels */
1616 if (dist >= 64)
1617 dist = (dist + 16) & ~63;
1618 else
1619 dist = 64;
1621 else
1623 if (TA_LATIN_HINTS_DO_MONO(hints))
1625 /* monochrome horizontal hinting: */
1626 /* snap widths to integer pixels with a different threshold */
1628 if (dist < 64)
1629 dist = 64;
1630 else
1631 dist = (dist + 32) & ~63;
1633 else
1635 /* for horizontal anti-aliased hinting, we adopt a more subtle */
1636 /* approach: we strengthen small stems, round stems whose size */
1637 /* is between 1 and 2 pixels to an integer, otherwise nothing */
1639 if (dist < 48)
1640 dist = (dist + 64) >> 1;
1642 else if (dist < 128)
1644 /* we only round to an integer width if the corresponding */
1645 /* distortion is less than 1/4 pixel -- otherwise, this */
1646 /* makes everything worse since the diagonals, which are */
1647 /* not hinted, appear a lot bolder or thinner than the */
1648 /* vertical stems */
1650 FT_Pos delta;
1653 dist = (dist + 22) & ~63;
1654 delta = dist - org_dist;
1655 if (delta < 0)
1656 delta = -delta;
1658 if (delta >= 16)
1660 dist = org_dist;
1661 if (dist < 48)
1662 dist = (dist + 64) >> 1;
1665 else
1666 /* round otherwise to prevent color fringes in LCD mode */
1667 dist = (dist + 32) & ~63;
1672 Done_Width:
1673 if (sign)
1674 dist = -dist;
1676 return dist;
1680 /* align one stem edge relative to the previous stem edge */
1682 static void
1683 ta_latin_align_linked_edge(TA_GlyphHints hints,
1684 TA_Dimension dim,
1685 TA_Edge base_edge,
1686 TA_Edge stem_edge)
1688 FT_Pos dist = stem_edge->opos - base_edge->opos;
1690 FT_Pos fitted_width = ta_latin_compute_stem_width(
1691 hints, dim, dist,
1692 base_edge->flags,
1693 stem_edge->flags);
1696 stem_edge->pos = base_edge->pos + fitted_width;
1698 TA_LOG((" LINK: edge %d (opos=%.2f) linked to %.2f,"
1699 " dist was %.2f, now %.2f\n",
1700 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
1701 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0));
1703 if (hints->recorder)
1704 hints->recorder(ta_link, hints, dim,
1705 (void*)base_edge, (void*)stem_edge, NULL);
1709 /* shift the coordinates of the `serif' edge by the same amount */
1710 /* as the corresponding `base' edge has been moved already */
1712 static void
1713 ta_latin_align_serif_edge(TA_GlyphHints hints,
1714 TA_Edge base,
1715 TA_Edge serif)
1717 FT_UNUSED(hints);
1719 serif->pos = base->pos + (serif->opos - base->opos);
1723 /* the main grid-fitting routine */
1725 void
1726 ta_latin_hint_edges(TA_GlyphHints hints,
1727 TA_Dimension dim)
1729 TA_AxisHints axis = &hints->axis[dim];
1731 TA_Edge edges = axis->edges;
1732 TA_Edge edge_limit = edges + axis->num_edges;
1733 FT_PtrDist n_edges;
1734 TA_Edge edge;
1736 TA_Edge anchor = NULL;
1737 FT_Int has_serifs = 0;
1740 TA_LOG(("%s edge hinting\n", dim == TA_DIMENSION_VERT ? "horizontal"
1741 : "vertical"));
1743 /* we begin by aligning all stems relative to the blue zone if needed -- */
1744 /* that's only for horizontal edges */
1746 if (dim == TA_DIMENSION_VERT
1747 && TA_HINTS_DO_BLUES(hints))
1749 for (edge = edges; edge < edge_limit; edge++)
1751 TA_Width blue;
1752 TA_Edge edge1, edge2; /* these edges form the stem to check */
1755 if (edge->flags & TA_EDGE_DONE)
1756 continue;
1758 blue = edge->blue_edge;
1759 edge1 = NULL;
1760 edge2 = edge->link;
1762 if (blue)
1763 edge1 = edge;
1765 /* flip edges if the other stem is aligned to a blue zone */
1766 else if (edge2 && edge2->blue_edge)
1768 blue = edge2->blue_edge;
1769 edge1 = edge2;
1770 edge2 = edge;
1773 if (!edge1)
1774 continue;
1776 TA_LOG((" BLUE: edge %d (opos=%.2f) snapped to %.2f, was %.2f\n",
1777 edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
1778 edge1->pos / 64.0));
1780 edge1->pos = blue->fit;
1781 edge1->flags |= TA_EDGE_DONE;
1783 if (hints->recorder)
1784 hints->recorder(ta_blue, hints, dim,
1785 (void*)edge1, NULL, NULL);
1787 if (edge2 && !edge2->blue_edge)
1789 ta_latin_align_linked_edge(hints, dim, edge1, edge2);
1790 edge2->flags |= TA_EDGE_DONE;
1793 if (!anchor)
1794 anchor = edge;
1798 /* now we align all other stem edges, */
1799 /* trying to maintain the relative order of stems in the glyph */
1800 for (edge = edges; edge < edge_limit; edge++)
1802 TA_Edge edge2;
1805 if (edge->flags & TA_EDGE_DONE)
1806 continue;
1808 /* skip all non-stem edges */
1809 edge2 = edge->link;
1810 if (!edge2)
1812 has_serifs++;
1813 continue;
1816 /* now align the stem */
1818 /* this should not happen, but it's better to be safe */
1819 if (edge2->blue_edge)
1821 TA_LOG((" ASSERTION FAILED for edge %d\n", edge2-edges));
1823 ta_latin_align_linked_edge(hints, dim, edge2, edge);
1824 edge->flags |= TA_EDGE_DONE;
1825 continue;
1828 if (!anchor)
1830 /* if we reach this if clause, no stem has been aligned yet */
1832 FT_Pos org_len, org_center, cur_len;
1833 FT_Pos cur_pos1, error1, error2, u_off, d_off;
1836 org_len = edge2->opos - edge->opos;
1837 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
1838 edge->flags, edge2->flags);
1840 /* some voodoo to specially round edges for small stem widths; */
1841 /* the idea is to align the center of a stem, */
1842 /* then shifting the stem edges to suitable positions */
1843 if (cur_len <= 64)
1845 /* width <= 1px */
1846 u_off = 32;
1847 d_off = 32;
1849 else
1851 /* 1px < width < 1.5px */
1852 u_off = 38;
1853 d_off = 26;
1856 if (cur_len < 96)
1858 org_center = edge->opos + (org_len >> 1);
1859 cur_pos1 = TA_PIX_ROUND(org_center);
1861 error1 = org_center - (cur_pos1 - u_off);
1862 if (error1 < 0)
1863 error1 = -error1;
1865 error2 = org_center - (cur_pos1 + d_off);
1866 if (error2 < 0)
1867 error2 = -error2;
1869 if (error1 < error2)
1870 cur_pos1 -= u_off;
1871 else
1872 cur_pos1 += d_off;
1874 edge->pos = cur_pos1 - cur_len / 2;
1875 edge2->pos = edge->pos + cur_len;
1877 else
1878 edge->pos = TA_PIX_ROUND(edge->opos);
1880 anchor = edge;
1881 edge->flags |= TA_EDGE_DONE;
1883 TA_LOG((" ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f)"
1884 " snapped to %.2f and %.2f\n",
1885 edge - edges, edge->opos / 64.0,
1886 edge2 - edges, edge2->opos / 64.0,
1887 edge->pos / 64.0, edge2->pos / 64.0));
1889 if (hints->recorder)
1890 hints->recorder(ta_anchor, hints, dim,
1891 (void*)edge, (void*)edge2, NULL);
1893 ta_latin_align_linked_edge(hints, dim, edge, edge2);
1895 else
1897 FT_Pos org_pos, org_len, org_center, cur_len;
1898 FT_Pos cur_pos1, cur_pos2, delta1, delta2;
1901 org_pos = anchor->pos + (edge->opos - anchor->opos);
1902 org_len = edge2->opos - edge->opos;
1903 org_center = org_pos + (org_len >> 1);
1905 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
1906 edge->flags, edge2->flags);
1908 if (edge2->flags & TA_EDGE_DONE)
1910 TA_LOG((" ADJUST: edge %d (pos=%.2f) moved to %.2f\n",
1911 edge - edges, edge->pos / 64.0,
1912 (edge2->pos - cur_len) / 64.0));
1914 edge->pos = edge2->pos - cur_len;
1916 if (hints->recorder)
1918 if (edge > edges)
1919 hints->recorder(ta_adjust_bound, hints, dim,
1920 (void*)edge, (void*)edge2, (void*)&edge[-1]);
1921 else
1922 hints->recorder(ta_adjust, hints, dim,
1923 (void*)edge, (void*)edge2, NULL);
1927 else if (cur_len < 96)
1929 FT_Pos u_off, d_off;
1932 cur_pos1 = TA_PIX_ROUND(org_center);
1934 if (cur_len <= 64)
1936 u_off = 32;
1937 d_off = 32;
1939 else
1941 u_off = 38;
1942 d_off = 26;
1945 delta1 = org_center - (cur_pos1 - u_off);
1946 if (delta1 < 0)
1947 delta1 = -delta1;
1949 delta2 = org_center - (cur_pos1 + d_off);
1950 if (delta2 < 0)
1951 delta2 = -delta2;
1953 if (delta1 < delta2)
1954 cur_pos1 -= u_off;
1955 else
1956 cur_pos1 += d_off;
1958 edge->pos = cur_pos1 - cur_len / 2;
1959 edge2->pos = cur_pos1 + cur_len / 2;
1961 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
1962 " snapped to %.2f and %.2f\n",
1963 edge - edges, edge->opos / 64.0,
1964 edge2 - edges, edge2->opos / 64.0,
1965 edge->pos / 64.0, edge2->pos / 64.0));
1967 if (hints->recorder)
1969 if (edge > edges)
1970 hints->recorder(ta_stem_bound, hints, dim,
1971 edge, edge2, &edge[-1]);
1972 else
1973 hints->recorder(ta_stem, hints, dim,
1974 (void*)edge, (void*)edge2, NULL);
1978 else
1980 org_pos = anchor->pos + (edge->opos - anchor->opos);
1981 org_len = edge2->opos - edge->opos;
1982 org_center = org_pos + (org_len >> 1);
1984 cur_len = ta_latin_compute_stem_width(hints, dim, org_len,
1985 edge->flags, edge2->flags);
1987 cur_pos1 = TA_PIX_ROUND(org_pos);
1988 delta1 = cur_pos1 + (cur_len >> 1) - org_center;
1989 if (delta1 < 0)
1990 delta1 = -delta1;
1992 cur_pos2 = TA_PIX_ROUND(org_pos + org_len) - cur_len;
1993 delta2 = cur_pos2 + (cur_len >> 1) - org_center;
1994 if (delta2 < 0)
1995 delta2 = -delta2;
1997 edge->pos = (delta1 < delta2) ? cur_pos1 : cur_pos2;
1998 edge2->pos = edge->pos + cur_len;
2000 TA_LOG((" STEM: edge %d (opos=%.2f) linked to %d (opos=%.2f)"
2001 " snapped to %.2f and %.2f\n",
2002 edge - edges, edge->opos / 64.0,
2003 edge2 - edges, edge2->opos / 64.0,
2004 edge->pos / 64.0, edge2->pos / 64.0));
2006 if (hints->recorder)
2008 if (edge > edges)
2009 hints->recorder(ta_stem_bound, hints, dim,
2010 edge, edge2, &edge[-1]);
2011 else
2012 hints->recorder(ta_stem, hints, dim,
2013 (void*)edge, (void*)edge2, NULL);
2017 edge->flags |= TA_EDGE_DONE;
2018 edge2->flags |= TA_EDGE_DONE;
2020 if (edge > edges
2021 && edge->pos < edge[-1].pos)
2023 TA_LOG((" BOUND: edge %d (pos=%.2f) moved to %.2f\n",
2024 edge - edges, edge->pos / 64.0, edge[-1].pos / 64.0));
2026 edge->pos = edge[-1].pos;
2028 if (hints->recorder)
2029 hints->recorder(ta_bound, hints, dim,
2030 (void*)edge, (void*)&edge[-1], NULL);
2035 /* make sure that lowercase m's maintain their symmetry */
2037 /* In general, lowercase m's have six vertical edges if they are sans */
2038 /* serif, or twelve if they are with serifs. This implementation is */
2039 /* based on that assumption, and seems to work very well with most */
2040 /* faces. However, if for a certain face this assumption is not */
2041 /* true, the m is just rendered like before. In addition, any stem */
2042 /* correction will only be applied to symmetrical glyphs (even if the */
2043 /* glyph is not an m), so the potential for unwanted distortion is */
2044 /* relatively low. */
2046 /* we don't handle horizontal edges since we can't easily assure that */
2047 /* the third (lowest) stem aligns with the base line; it might end up */
2048 /* one pixel higher or lower */
2050 n_edges = edge_limit - edges;
2051 if (dim == TA_DIMENSION_HORZ
2052 && (n_edges == 6 || n_edges == 12))
2054 TA_Edge edge1, edge2, edge3;
2055 FT_Pos dist1, dist2, span, delta;
2058 if (n_edges == 6)
2060 edge1 = edges;
2061 edge2 = edges + 2;
2062 edge3 = edges + 4;
2064 else
2066 edge1 = edges + 1;
2067 edge2 = edges + 5;
2068 edge3 = edges + 9;
2071 dist1 = edge2->opos - edge1->opos;
2072 dist2 = edge3->opos - edge2->opos;
2074 span = dist1 - dist2;
2075 if (span < 0)
2076 span = -span;
2078 if (span < 8)
2080 delta = edge3->pos - (2 * edge2->pos - edge1->pos);
2081 edge3->pos -= delta;
2082 if (edge3->link)
2083 edge3->link->pos -= delta;
2085 /* move the serifs along with the stem */
2086 if (n_edges == 12)
2088 (edges + 8)->pos -= delta;
2089 (edges + 11)->pos -= delta;
2092 edge3->flags |= TA_EDGE_DONE;
2093 if (edge3->link)
2094 edge3->link->flags |= TA_EDGE_DONE;
2098 if (has_serifs || !anchor)
2100 /* now hint the remaining edges (serifs and single) */
2101 /* in order to complete our processing */
2102 for (edge = edges; edge < edge_limit; edge++)
2104 FT_Pos delta;
2107 if (edge->flags & TA_EDGE_DONE)
2108 continue;
2110 delta = 1000;
2112 if (edge->serif)
2114 delta = edge->serif->opos - edge->opos;
2115 if (delta < 0)
2116 delta = -delta;
2119 if (delta < 64 + 16)
2121 ta_latin_align_serif_edge(hints, edge->serif, edge);
2123 TA_LOG((" SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f)"
2124 " aligned to %.2f\n",
2125 edge - edges, edge->opos / 64.0,
2126 edge->serif - edges, edge->serif->opos / 64.0,
2127 edge->pos / 64.0));
2129 if (hints->recorder)
2130 hints->recorder(ta_serif, hints, dim,
2131 (void*)edge, NULL, NULL);
2133 else if (!anchor)
2135 edge->pos = TA_PIX_ROUND(edge->opos);
2136 anchor = edge;
2138 TA_LOG((" SERIF_ANCHOR: edge %d (opos=%.2f) snapped to %.2f\n",
2139 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2141 if (hints->recorder)
2142 hints->recorder(ta_serif_anchor, hints, dim,
2143 (void*)edge, NULL, NULL);
2145 else
2147 TA_Edge before, after;
2150 for (before = edge - 1; before >= edges; before--)
2151 if (before->flags & TA_EDGE_DONE)
2152 break;
2154 for (after = edge + 1; after < edge_limit; after++)
2155 if (after->flags & TA_EDGE_DONE)
2156 break;
2158 if (before >= edges && before < edge
2159 && after < edge_limit && after > edge)
2161 if (after->opos == before->opos)
2162 edge->pos = before->pos;
2163 else
2164 edge->pos = before->pos + FT_MulDiv(edge->opos - before->opos,
2165 after->pos - before->pos,
2166 after->opos - before->opos);
2168 TA_LOG((" SERIF_LINK1: edge %d (opos=%.2f) snapped to %.2f"
2169 " from %d (opos=%.2f)\n",
2170 edge - edges, edge->opos / 64.0,
2171 edge->pos / 64.0,
2172 before - edges, before->opos / 64.0));
2174 if (hints->recorder)
2175 hints->recorder(ta_serif_link1, hints, dim,
2176 (void*)edge, NULL, NULL);
2178 else
2180 edge->pos = anchor->pos + ((edge->opos - anchor->opos + 16) & ~31);
2182 TA_LOG((" SERIF_LINK2: edge %d (opos=%.2f) snapped to %.2f\n",
2183 edge - edges, edge->opos / 64.0, edge->pos / 64.0));
2185 if (hints->recorder)
2186 hints->recorder(ta_serif_link2, hints, dim,
2187 (void*)edge, NULL, NULL);
2191 edge->flags |= TA_EDGE_DONE;
2193 if (edge > edges
2194 && edge->pos < edge[-1].pos)
2195 edge->pos = edge[-1].pos;
2197 if (edge + 1 < edge_limit
2198 && edge[1].flags & TA_EDGE_DONE
2199 && edge->pos > edge[1].pos)
2200 edge->pos = edge[1].pos;
2204 TA_LOG(("\n"));
2208 /* apply the complete hinting algorithm to a latin glyph */
2210 static FT_Error
2211 ta_latin_hints_apply(TA_GlyphHints hints,
2212 FT_Outline* outline,
2213 TA_LatinMetrics metrics)
2215 FT_Error error;
2216 int dim;
2219 error = ta_glyph_hints_reload(hints, outline);
2220 if (error)
2221 goto Exit;
2223 /* analyze glyph outline */
2224 #ifdef TA_CONFIG_OPTION_USE_WARPER
2225 if (metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT
2226 || TA_HINTS_DO_HORIZONTAL(hints))
2227 #else
2228 if (TA_HINTS_DO_HORIZONTAL(hints))
2229 #endif
2231 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_HORZ);
2232 if (error)
2233 goto Exit;
2236 if (TA_HINTS_DO_VERTICAL(hints))
2238 error = ta_latin_hints_detect_features(hints, TA_DIMENSION_VERT);
2239 if (error)
2240 goto Exit;
2242 ta_latin_hints_compute_blue_edges(hints, metrics);
2245 /* grid-fit the outline */
2246 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
2248 #ifdef TA_CONFIG_OPTION_USE_WARPER
2249 if (dim == TA_DIMENSION_HORZ
2250 && metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT)
2252 TA_WarperRec warper;
2253 FT_Fixed scale;
2254 FT_Pos delta;
2257 ta_warper_compute(&warper, hints, (TA_Dimension)dim, &scale, &delta);
2258 ta_glyph_hints_scale_dim(hints, (TA_Dimension)dim, scale, delta);
2260 continue;
2262 #endif
2264 if ((dim == TA_DIMENSION_HORZ && TA_HINTS_DO_HORIZONTAL(hints))
2265 || (dim == TA_DIMENSION_VERT && TA_HINTS_DO_VERTICAL(hints)))
2267 ta_latin_hint_edges(hints, (TA_Dimension)dim);
2268 ta_glyph_hints_align_edge_points(hints, (TA_Dimension)dim);
2269 ta_glyph_hints_align_strong_points(hints, (TA_Dimension)dim);
2270 ta_glyph_hints_align_weak_points(hints, (TA_Dimension)dim);
2273 ta_glyph_hints_save(hints, outline);
2275 Exit:
2276 return error;
2280 /* XXX: this should probably fine tuned to differentiate better between */
2281 /* scripts... */
2283 static const TA_Script_UniRangeRec ta_latin_uniranges[] =
2285 TA_UNIRANGE_REC(0x0020UL, 0x007FUL), /* Basic Latin (no control chars) */
2286 TA_UNIRANGE_REC(0x00A0UL, 0x00FFUL), /* Latin-1 Supplement (no control chars) */
2287 TA_UNIRANGE_REC(0x0100UL, 0x017FUL), /* Latin Extended-A */
2288 TA_UNIRANGE_REC(0x0180UL, 0x024FUL), /* Latin Extended-B */
2289 TA_UNIRANGE_REC(0x0250UL, 0x02AFUL), /* IPA Extensions */
2290 TA_UNIRANGE_REC(0x02B0UL, 0x02FFUL), /* Spacing Modifier Letters */
2291 TA_UNIRANGE_REC(0x0300UL, 0x036FUL), /* Combining Diacritical Marks */
2292 TA_UNIRANGE_REC(0x0370UL, 0x03FFUL), /* Greek and Coptic */
2293 TA_UNIRANGE_REC(0x0400UL, 0x04FFUL), /* Cyrillic */
2294 TA_UNIRANGE_REC(0x0500UL, 0x052FUL), /* Cyrillic Supplement */
2295 TA_UNIRANGE_REC(0x1D00UL, 0x1D7FUL), /* Phonetic Extensions */
2296 TA_UNIRANGE_REC(0x1D80UL, 0x1DBFUL), /* Phonetic Extensions Supplement */
2297 TA_UNIRANGE_REC(0x1DC0UL, 0x1DFFUL), /* Combining Diacritical Marks Supplement */
2298 TA_UNIRANGE_REC(0x1E00UL, 0x1EFFUL), /* Latin Extended Additional */
2299 TA_UNIRANGE_REC(0x1F00UL, 0x1FFFUL), /* Greek Extended */
2300 TA_UNIRANGE_REC(0x2000UL, 0x206FUL), /* General Punctuation */
2301 TA_UNIRANGE_REC(0x2070UL, 0x209FUL), /* Superscripts and Subscripts */
2302 TA_UNIRANGE_REC(0x20A0UL, 0x20CFUL), /* Currency Symbols */
2303 TA_UNIRANGE_REC(0x2150UL, 0x218FUL), /* Number Forms */
2304 TA_UNIRANGE_REC(0x2460UL, 0x24FFUL), /* Enclosed Alphanumerics */
2305 TA_UNIRANGE_REC(0x2C60UL, 0x2C7FUL), /* Latin Extended-C */
2306 TA_UNIRANGE_REC(0x2DE0UL, 0x2DFFUL), /* Cyrillic Extended-A */
2307 TA_UNIRANGE_REC(0xA640UL, 0xA69FUL), /* Cyrillic Extended-B */
2308 TA_UNIRANGE_REC(0xA720UL, 0xA7FFUL), /* Latin Extended-D */
2309 TA_UNIRANGE_REC(0xFB00UL, 0xFB06UL), /* Alphab. Present. Forms (Latin Ligs) */
2310 TA_UNIRANGE_REC(0x1D400UL, 0x1D7FFUL), /* Mathematical Alphanumeric Symbols */
2311 TA_UNIRANGE_REC(0UL, 0UL)
2315 const TA_ScriptClassRec ta_latin_script_class =
2317 TA_SCRIPT_LATIN,
2318 ta_latin_uniranges,
2320 sizeof (TA_LatinMetricsRec),
2322 (TA_Script_InitMetricsFunc)ta_latin_metrics_init,
2323 (TA_Script_ScaleMetricsFunc)ta_latin_metrics_scale,
2324 (TA_Script_DoneMetricsFunc)NULL,
2326 (TA_Script_InitHintsFunc)ta_latin_hints_init,
2327 (TA_Script_ApplyHintsFunc)ta_latin_hints_apply
2330 /* end of talatin.c */