1 /***************************************************************************/
5 /* PostScript hinting algorithm 3 (body). */
7 /* Copyright 2001, 2002 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
10 /* This file is part of the FreeType project, and may only be used */
11 /* modified and distributed under the terms of the FreeType project */
12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13 /* this file you indicate that you have read the license and */
14 /* understand and accept it fully. */
16 /***************************************************************************/
20 #include FT_INTERNAL_OBJECTS_H
21 #include FT_INTERNAL_DEBUG_H
26 #define FT_COMPONENT trace_pshalgo2
30 PSH3_Hint_Table ps3_debug_hint_table = 0;
31 PSH3_HintFunc ps3_debug_hint_func = 0;
32 PSH3_Glyph ps3_debug_glyph = 0;
36 #define COMPUTE_INFLEXS /* compute inflection points to optimize "S" and others */
37 #define STRONGER /* slightly increase the contrast of smooth hinting */
39 /*************************************************************************/
40 /*************************************************************************/
42 /***** BASIC HINTS RECORDINGS *****/
44 /*************************************************************************/
45 /*************************************************************************/
47 /* return true iff two stem hints overlap */
49 psh3_hint_overlap( PSH3_Hint hint1,
52 return ( hint1->org_pos + hint1->org_len >= hint2->org_pos &&
53 hint2->org_pos + hint2->org_len >= hint1->org_pos );
57 /* destroy hints table */
59 psh3_hint_table_done( PSH3_Hint_Table table,
62 FT_FREE( table->zones );
66 FT_FREE( table->sort );
67 FT_FREE( table->hints );
70 table->sort_global = 0;
74 /* deactivate all hints in a table */
76 psh3_hint_table_deactivate( PSH3_Hint_Table table )
78 FT_UInt count = table->max_hints;
79 PSH3_Hint hint = table->hints;
82 for ( ; count > 0; count--, hint++ )
84 psh3_hint_deactivate( hint );
90 /* internal function used to record a new hint */
92 psh3_hint_table_record( PSH3_Hint_Table table,
95 PSH3_Hint hint = table->hints + idx;
98 if ( idx >= table->max_hints )
100 FT_ERROR(( "psh3_hint_table_record: invalid hint index %d\n", idx ));
104 /* ignore active hints */
105 if ( psh3_hint_is_active( hint ) )
108 psh3_hint_activate( hint );
110 /* now scan the current active hint set in order to determine */
111 /* if we are overlapping with another segment */
113 PSH3_Hint* sorted = table->sort_global;
114 FT_UInt count = table->num_hints;
119 for ( ; count > 0; count--, sorted++ )
123 if ( psh3_hint_overlap( hint, hint2 ) )
125 hint->parent = hint2;
131 if ( table->num_hints < table->max_hints )
132 table->sort_global[table->num_hints++] = hint;
134 FT_ERROR(( "psh3_hint_table_record: too many sorted hints! BUG!\n" ));
139 psh3_hint_table_record_mask( PSH3_Hint_Table table,
142 FT_Int mask = 0, val = 0;
143 FT_Byte* cursor = hint_mask->bytes;
147 limit = hint_mask->num_bits;
149 for ( idx = 0; idx < limit; idx++ )
158 psh3_hint_table_record( table, idx );
165 /* create hints table */
167 psh3_hint_table_init( PSH3_Hint_Table table,
169 PS_Mask_Table hint_masks,
170 PS_Mask_Table counter_masks,
173 FT_UInt count = hints->num_hints;
176 FT_UNUSED( counter_masks );
179 /* allocate our tables */
180 if ( FT_NEW_ARRAY( table->sort, 2 * count ) ||
181 FT_NEW_ARRAY( table->hints, count ) ||
182 FT_NEW_ARRAY( table->zones, 2 * count + 1 ) )
185 table->max_hints = count;
186 table->sort_global = table->sort + count;
187 table->num_hints = 0;
188 table->num_zones = 0;
191 /* now, initialize the "hints" array */
193 PSH3_Hint write = table->hints;
194 PS_Hint read = hints->hints;
197 for ( ; count > 0; count--, write++, read++ )
199 write->org_pos = read->pos;
200 write->org_len = read->len;
201 write->flags = read->flags;
205 /* we now need to determine the initial "parent" stems; first */
206 /* activate the hints that are given by the initial hint masks */
209 FT_UInt Count = hint_masks->num_masks;
210 PS_Mask Mask = hint_masks->masks;
213 table->hint_masks = hint_masks;
215 for ( ; Count > 0; Count--, Mask++ )
216 psh3_hint_table_record_mask( table, Mask );
219 /* now, do a linear parse in case some hints were left alone */
220 if ( table->num_hints != table->max_hints )
222 FT_UInt Index, Count;
225 FT_ERROR(( "psh3_hint_table_init: missing/incorrect hint masks!\n" ));
226 Count = table->max_hints;
227 for ( Index = 0; Index < Count; Index++ )
228 psh3_hint_table_record( table, Index );
237 psh3_hint_table_activate_mask( PSH3_Hint_Table table,
240 FT_Int mask = 0, val = 0;
241 FT_Byte* cursor = hint_mask->bytes;
242 FT_UInt idx, limit, count;
245 limit = hint_mask->num_bits;
248 psh3_hint_table_deactivate( table );
250 for ( idx = 0; idx < limit; idx++ )
260 PSH3_Hint hint = &table->hints[idx];
263 if ( !psh3_hint_is_active( hint ) )
268 PSH3_Hint* sort = table->sort;
272 for ( count2 = count; count2 > 0; count2--, sort++ )
275 if ( psh3_hint_overlap( hint, hint2 ) )
276 FT_ERROR(( "psh3_hint_table_activate_mask:"
277 " found overlapping hints\n" ))
285 psh3_hint_activate( hint );
286 if ( count < table->max_hints )
287 table->sort[count++] = hint;
289 FT_ERROR(( "psh3_hint_tableactivate_mask:"
290 " too many active hints\n" ));
297 table->num_hints = count;
299 /* now, sort the hints; they are guaranteed to not overlap */
300 /* so we can compare their "org_pos" field directly */
303 PSH3_Hint hint1, hint2;
304 PSH3_Hint* sort = table->sort;
307 /* a simple bubble sort will do, since in 99% of cases, the hints */
308 /* will be already sorted -- and the sort will be linear */
309 for ( i1 = 1; i1 < (FT_Int)count; i1++ )
312 for ( i2 = i1 - 1; i2 >= 0; i2-- )
316 if ( hint2->org_pos < hint1->org_pos )
319 sort[i2 + 1] = hint2;
327 /*************************************************************************/
328 /*************************************************************************/
330 /***** HINTS GRID-FITTING AND OPTIMIZATION *****/
332 /*************************************************************************/
333 /*************************************************************************/
337 psh3_dimension_quantize_len( PSH_Dimension dim,
339 FT_Bool do_snapping )
345 FT_Pos delta = len - dim->stdw.widths[0].cur;
353 len = dim->stdw.widths[0].cur;
360 delta = ( len & 63 );
366 else if ( delta < 32 )
369 else if ( delta < 54 )
376 len = ( len + 32 ) & -64;
380 len = ( len + 32 ) & -64;
390 ps3_simple_scale( PSH3_Hint_Table table,
399 for ( count = 0; count < table->max_hints; count++ )
401 hint = table->hints + count;
403 hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta;
404 hint->cur_len = FT_MulFix( hint->org_len, scale );
406 if ( ps3_debug_hint_func )
407 ps3_debug_hint_func( hint, dimension );
411 #endif /* DEBUG_HINTER */
415 psh3_hint_snap_stem_side_delta( FT_Fixed pos,
418 FT_Fixed delta1 = ( ( pos + 32 ) & -64 ) - pos;
419 FT_Fixed delta2 = ( ( pos + len + 32 ) & -64 ) - pos - len;
422 if ( ABS( delta1 ) <= ABS( delta2 ) )
430 psh3_hint_align( PSH3_Hint hint,
435 PSH_Dimension dim = &globals->dimension[dimension];
436 FT_Fixed scale = dim->scale_mult;
437 FT_Fixed delta = dim->scale_delta;
440 if ( !psh3_hint_is_fitted( hint ) )
442 FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta;
443 FT_Pos len = FT_MulFix( hint->org_len, scale );
447 PSH_AlignmentRec align;
450 /* ignore stem alignments when requested through the hint flags */
451 if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
452 ( dimension == 1 && !glyph->do_vert_hints ) )
457 psh3_hint_set_fitted( hint );
461 /* perform stem snapping when requested - this is necessary
462 * for monochrome and LCD hinting modes only
464 do_snapping = ( dimension == 0 && glyph->do_horz_snapping ) ||
465 ( dimension == 1 && glyph->do_vert_snapping );
467 hint->cur_len = fit_len = len;
469 /* check blue zones for horizontal stems */
470 align.align = PSH_BLUE_ALIGN_NONE;
471 align.align_bot = align.align_top = 0;
473 if ( dimension == 1 )
474 psh_blues_snap_stem( &globals->blues,
475 hint->org_pos + hint->org_len,
479 switch ( align.align )
481 case PSH_BLUE_ALIGN_TOP:
482 /* the top of the stem is aligned against a blue zone */
483 hint->cur_pos = align.align_top - fit_len;
486 case PSH_BLUE_ALIGN_BOT:
487 /* the bottom of the stem is aligned against a blue zone */
488 hint->cur_pos = align.align_bot;
491 case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
492 /* both edges of the stem are aligned against blue zones */
493 hint->cur_pos = align.align_bot;
494 hint->cur_len = align.align_top - align.align_bot;
499 PSH3_Hint parent = hint->parent;
504 FT_Pos par_org_center, par_cur_center;
505 FT_Pos cur_org_center, cur_delta;
508 /* ensure that parent is already fitted */
509 if ( !psh3_hint_is_fitted( parent ) )
510 psh3_hint_align( parent, globals, dimension, glyph );
512 par_org_center = parent->org_pos + ( parent->org_len >> 1 );
513 par_cur_center = parent->cur_pos + ( parent->cur_len >> 1 );
514 cur_org_center = hint->org_pos + ( hint->org_len >> 1 );
516 cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
517 pos = par_cur_center + cur_delta - ( len >> 1 );
521 hint->cur_len = fit_len;
523 /* stem adjustment tries to snap stem widths to standard
524 * ones. this is important to prevent unpleasant rounding
527 if ( glyph->do_stem_adjust )
531 /* the stem is less than one pixel, we will center it
532 * around the nearest pixel center
535 pos = ( pos + (len >> 1) ) & -64;
537 /* this seems to be a bug !! */
538 pos = ( pos + ( (len >> 1) & -64 ) );
544 len = psh3_dimension_quantize_len( dim, len, 0 );
548 /* now that we have a good hinted stem width, try to position */
549 /* the stem along a pixel grid integer coordinate */
550 hint->cur_pos = pos + psh3_hint_snap_stem_side_delta( pos, len );
563 len = ( len + 32 ) & -64;
565 switch ( align.align )
567 case PSH_BLUE_ALIGN_TOP:
568 hint->cur_pos = align.align_top - len;
572 case PSH_BLUE_ALIGN_BOT:
576 case PSH_BLUE_ALIGN_BOT | PSH_BLUE_ALIGN_TOP:
584 pos = ( ( pos + ( len >> 1 ) ) & -64 ) + 32;
586 pos = ( pos + ( len >> 1 ) + 32 ) & -64;
588 hint->cur_pos = pos - ( len >> 1 );
593 psh3_hint_set_fitted( hint );
596 if ( ps3_debug_hint_func )
597 ps3_debug_hint_func( hint, dimension );
603 #if 0 /* not used for now, experimental */
606 * A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT)
610 psh3_hint_align_light( PSH3_Hint hint,
615 PSH_Dimension dim = &globals->dimension[dimension];
616 FT_Fixed scale = dim->scale_mult;
617 FT_Fixed delta = dim->scale_delta;
620 if ( !psh3_hint_is_fitted(hint) )
622 FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta;
623 FT_Pos len = FT_MulFix( hint->org_len, scale );
627 PSH_AlignmentRec align;
629 /* ignore stem alignments when requested through the hint flags */
630 if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
631 ( dimension == 1 && !glyph->do_vert_hints ) )
636 psh3_hint_set_fitted( hint );
642 hint->cur_len = fit_len;
644 /* check blue zones for horizontal stems */
645 align.align = PSH_BLUE_ALIGN_NONE;
646 align.align_bot = align.align_top = 0;
648 if ( dimension == 1 )
649 psh_blues_snap_stem( &globals->blues,
650 hint->org_pos + hint->org_len,
654 switch ( align.align )
656 case PSH_BLUE_ALIGN_TOP:
657 /* the top of the stem is aligned against a blue zone */
658 hint->cur_pos = align.align_top - fit_len;
661 case PSH_BLUE_ALIGN_BOT:
662 /* the bottom of the stem is aligned against a blue zone */
663 hint->cur_pos = align.align_bot;
666 case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
667 /* both edges of the stem are aligned against blue zones */
668 hint->cur_pos = align.align_bot;
669 hint->cur_len = align.align_top - align.align_bot;
674 PSH3_Hint parent = hint->parent;
679 FT_Pos par_org_center, par_cur_center;
680 FT_Pos cur_org_center, cur_delta;
683 /* ensure that parent is already fitted */
684 if ( !psh3_hint_is_fitted( parent ) )
685 psh3_hint_align_light( parent, globals, dimension, glyph );
687 par_org_center = parent->org_pos + ( parent->org_len / 2);
688 par_cur_center = parent->cur_pos + ( parent->cur_len / 2);
689 cur_org_center = hint->org_pos + ( hint->org_len / 2);
691 cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
692 pos = par_cur_center + cur_delta - ( len >> 1 );
695 /* Stems less than one pixel wide are easy - we want to
696 * make them as dark as possible, so they must fall within
697 * one pixel. If the stem is split between two pixels
698 * then snap the edge that is nearer to the pixel boundary
699 * to the pixel boundary
703 if ( ( pos + len + 63 ) / 64 != pos / 64 + 1 )
704 pos += psh3_hint_snap_stem_side_delta ( pos, len );
706 /* Position stems other to minimize the amount of mid-grays.
707 * There are, in general, two positions that do this,
708 * illustrated as A) and B) below.
712 * A) |--------------------------------|
713 * B) |--------------------------------|
714 * C) |--------------------------------|
716 * Position A) (split the excess stem equally) should be better
717 * for stems of width N + f where f < 0.5
719 * Position B) (split the deficiency equally) should be better
720 * for stems of width N + f where f > 0.5
722 * It turns out though that minimizing the total number of lit
723 * pixels is also important, so position C), with one edge
724 * aligned with a pixel boundary is actually preferable
725 * to A). There are also more possibile positions for C) than
726 * for A) or B), so it involves less distortion of the overall
731 FT_Fixed frac_len = len & 63;
732 FT_Fixed center = pos + ( len >> 1 );
733 FT_Fixed delta_a, delta_b;
735 if ( ( len / 64 ) & 1 )
737 delta_a = ( center & -64 ) + 32 - center;
738 delta_b = ( ( center + 32 ) & - 64 ) - center;
742 delta_a = ( ( center + 32 ) & - 64 ) - center;
743 delta_b = ( center & -64 ) + 32 - center;
746 /* We choose between B) and C) above based on the amount
747 * of fractinal stem width; for small amounts, choose
748 * C) always, for large amounts, B) always, and inbetween,
749 * pick whichever one involves less stem movement.
753 pos += psh3_hint_snap_stem_side_delta ( pos, len );
755 else if (frac_len < 48)
757 FT_Fixed side_delta = psh3_hint_snap_stem_side_delta ( pos, len );
759 if ( ABS( side_delta ) < ABS( delta_b ) )
774 psh3_hint_set_fitted( hint );
777 if ( ps3_debug_hint_func )
778 ps3_debug_hint_func( hint, dimension );
787 psh3_hint_table_align_hints( PSH3_Hint_Table table,
797 PSH_Dimension dim = &globals->dimension[dimension];
798 FT_Fixed scale = dim->scale_mult;
799 FT_Fixed delta = dim->scale_delta;
802 if ( ps_debug_no_vert_hints && dimension == 0 )
804 ps3_simple_scale( table, scale, delta, dimension );
808 if ( ps_debug_no_horz_hints && dimension == 1 )
810 ps3_simple_scale( table, scale, delta, dimension );
814 #endif /* DEBUG_HINTER*/
817 count = table->max_hints;
819 for ( ; count > 0; count--, hint++ )
820 psh3_hint_align( hint, globals, dimension, glyph );
824 /*************************************************************************/
825 /*************************************************************************/
827 /***** POINTS INTERPOLATION ROUTINES *****/
829 /*************************************************************************/
830 /*************************************************************************/
832 #define PSH3_ZONE_MIN -3200000L
833 #define PSH3_ZONE_MAX +3200000L
835 #define xxDEBUG_ZONES
843 psh3_print_zone( PSH3_Zone zone )
845 printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n",
846 zone->scale / 65536.0,
854 #define psh3_print_zone( x ) do { } while ( 0 )
856 #endif /* DEBUG_ZONES */
859 /*************************************************************************/
860 /*************************************************************************/
862 /***** HINTER GLYPH MANAGEMENT *****/
864 /*************************************************************************/
865 /*************************************************************************/
867 #ifdef COMPUTE_INFLEXS
869 /* compute all inflex points in a given glyph */
871 psh3_glyph_compute_inflections( PSH3_Glyph glyph )
876 for ( n = 0; n < glyph->num_contours; n++ )
878 PSH3_Point first, start, end, before, after;
879 FT_Angle angle_in, angle_seg, angle_out;
880 FT_Angle diff_in, diff_out;
884 /* we need at least 4 points to create an inflection point */
885 if ( glyph->contours[n].count < 4 )
888 /* compute first segment in contour */
889 first = glyph->contours[n].start;
898 } while ( PSH3_POINT_EQUAL_ORG( end, first ) );
900 angle_seg = PSH3_POINT_ANGLE( start, end );
902 /* extend the segment start whenever possible */
909 before = before->prev;
910 if ( before == first )
913 } while ( PSH3_POINT_EQUAL_ORG( before, start ) );
915 angle_in = PSH3_POINT_ANGLE( before, start );
917 } while ( angle_in == angle_seg );
920 diff_in = FT_Angle_Diff( angle_in, angle_seg );
922 /* now, process all segments in the contour */
925 /* first, extend current segment's end whenever possible */
933 if ( after == first )
936 } while ( PSH3_POINT_EQUAL_ORG( end, after ) );
938 angle_out = PSH3_POINT_ANGLE( end, after );
940 } while ( angle_out == angle_seg );
942 diff_out = FT_Angle_Diff( angle_seg, angle_out );
944 if ( ( diff_in ^ diff_out ) < 0 )
946 /* diff_in and diff_out have different signs, we have */
947 /* inflection points here... */
951 psh3_point_set_inflex( start );
954 while ( start != end );
956 psh3_point_set_inflex( start );
961 angle_seg = angle_out;
964 } while ( !finished );
971 #endif /* COMPUTE_INFLEXS */
975 psh3_glyph_done( PSH3_Glyph glyph )
977 FT_Memory memory = glyph->memory;
980 psh3_hint_table_done( &glyph->hint_tables[1], memory );
981 psh3_hint_table_done( &glyph->hint_tables[0], memory );
983 FT_FREE( glyph->points );
984 FT_FREE( glyph->contours );
986 glyph->num_points = 0;
987 glyph->num_contours = 0;
994 psh3_compute_dir( FT_Pos dx,
998 int result = PSH3_DIR_NONE;
1001 ax = ( dx >= 0 ) ? dx : -dx;
1002 ay = ( dy >= 0 ) ? dy : -dy;
1006 /* |dy| <<< |dx| means a near-horizontal segment */
1007 result = ( dx >= 0 ) ? PSH3_DIR_RIGHT : PSH3_DIR_LEFT;
1009 else if ( ax * 12 < ay )
1011 /* |dx| <<< |dy| means a near-vertical segment */
1012 result = ( dy >= 0 ) ? PSH3_DIR_UP : PSH3_DIR_DOWN;
1019 /* load outline point coordinates into hinter glyph */
1021 psh3_glyph_load_points( PSH3_Glyph glyph,
1024 FT_Vector* vec = glyph->outline->points;
1025 PSH3_Point point = glyph->points;
1026 FT_UInt count = glyph->num_points;
1029 for ( ; count > 0; count--, point++, vec++ )
1033 if ( dimension == 0 )
1035 point->org_u = vec->x;
1036 point->org_v = vec->y;
1040 point->org_u = vec->y;
1041 point->org_v = vec->x;
1045 point->org_x = vec->x;
1046 point->org_y = vec->y;
1053 /* save hinted point coordinates back to outline */
1055 psh3_glyph_save_points( PSH3_Glyph glyph,
1059 PSH3_Point point = glyph->points;
1060 FT_Vector* vec = glyph->outline->points;
1061 char* tags = glyph->outline->tags;
1064 for ( n = 0; n < glyph->num_points; n++ )
1066 if ( dimension == 0 )
1067 vec[n].x = point->cur_u;
1069 vec[n].y = point->cur_u;
1071 if ( psh3_point_is_strong( point ) )
1072 tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 );
1076 if ( dimension == 0 )
1078 point->cur_x = point->cur_u;
1079 point->flags_x = point->flags2 | point->flags;
1083 point->cur_y = point->cur_u;
1084 point->flags_y = point->flags2 | point->flags;
1095 psh3_glyph_init( PSH3_Glyph glyph,
1096 FT_Outline* outline,
1098 PSH_Globals globals )
1104 /* clear all fields */
1105 FT_MEM_ZERO( glyph, sizeof ( *glyph ) );
1107 memory = globals->memory;
1109 /* allocate and setup points + contours arrays */
1110 if ( FT_NEW_ARRAY( glyph->points, outline->n_points ) ||
1111 FT_NEW_ARRAY( glyph->contours, outline->n_contours ) )
1114 glyph->num_points = outline->n_points;
1115 glyph->num_contours = outline->n_contours;
1118 FT_UInt first = 0, next, n;
1119 PSH3_Point points = glyph->points;
1120 PSH3_Contour contour = glyph->contours;
1123 for ( n = 0; n < glyph->num_contours; n++ )
1129 next = outline->contours[n] + 1;
1130 count = next - first;
1132 contour->start = points + first;
1133 contour->count = (FT_UInt)count;
1137 point = points + first;
1139 point->prev = points + next - 1;
1140 point->contour = contour;
1142 for ( ; count > 1; count-- )
1144 point[0].next = point + 1;
1145 point[1].prev = point;
1147 point->contour = contour;
1149 point->next = points + first;
1158 PSH3_Point points = glyph->points;
1159 PSH3_Point point = points;
1160 FT_Vector* vec = outline->points;
1164 for ( n = 0; n < glyph->num_points; n++, point++ )
1166 FT_Int n_prev = point->prev - points;
1167 FT_Int n_next = point->next - points;
1168 FT_Pos dxi, dyi, dxo, dyo;
1171 if ( !( outline->tags[n] & FT_CURVE_TAG_ON ) )
1172 point->flags = PSH3_POINT_OFF;
1174 dxi = vec[n].x - vec[n_prev].x;
1175 dyi = vec[n].y - vec[n_prev].y;
1177 point->dir_in = (FT_Char)psh3_compute_dir( dxi, dyi );
1179 dxo = vec[n_next].x - vec[n].x;
1180 dyo = vec[n_next].y - vec[n].y;
1182 point->dir_out = (FT_Char)psh3_compute_dir( dxo, dyo );
1184 /* detect smooth points */
1185 if ( point->flags & PSH3_POINT_OFF )
1186 point->flags |= PSH3_POINT_SMOOTH;
1187 else if ( point->dir_in != PSH3_DIR_NONE ||
1188 point->dir_out != PSH3_DIR_NONE )
1190 if ( point->dir_in == point->dir_out )
1191 point->flags |= PSH3_POINT_SMOOTH;
1195 FT_Angle angle_in, angle_out, diff;
1198 angle_in = FT_Atan2( dxi, dyi );
1199 angle_out = FT_Atan2( dxo, dyo );
1201 diff = angle_in - angle_out;
1205 if ( diff > FT_ANGLE_PI )
1206 diff = FT_ANGLE_2PI - diff;
1208 if ( diff < FT_ANGLE_PI / 16 )
1209 point->flags |= PSH3_POINT_SMOOTH;
1214 glyph->memory = memory;
1215 glyph->outline = outline;
1216 glyph->globals = globals;
1218 #ifdef COMPUTE_INFLEXS
1219 psh3_glyph_load_points( glyph, 0 );
1220 psh3_glyph_compute_inflections( glyph );
1221 #endif /* COMPUTE_INFLEXS */
1223 /* now deal with hints tables */
1224 error = psh3_hint_table_init( &glyph->hint_tables [0],
1225 &ps_hints->dimension[0].hints,
1226 &ps_hints->dimension[0].masks,
1227 &ps_hints->dimension[0].counters,
1232 error = psh3_hint_table_init( &glyph->hint_tables [1],
1233 &ps_hints->dimension[1].hints,
1234 &ps_hints->dimension[1].masks,
1235 &ps_hints->dimension[1].counters,
1245 /* compute all extrema in a glyph for a given dimension */
1247 psh3_glyph_compute_extrema( PSH3_Glyph glyph )
1252 /* first of all, compute all local extrema */
1253 for ( n = 0; n < glyph->num_contours; n++ )
1255 PSH3_Point first = glyph->contours[n].start;
1256 PSH3_Point point, before, after;
1265 before = before->prev;
1266 if ( before == first )
1269 } while ( before->org_u == point->org_u );
1271 first = point = before->next;
1278 after = after->next;
1279 if ( after == first )
1282 } while ( after->org_u == point->org_u );
1284 if ( before->org_u < point->org_u )
1286 if ( after->org_u < point->org_u )
1292 else /* before->org_u > point->org_u */
1294 if ( after->org_u > point->org_u )
1300 psh3_point_set_extremum( point );
1301 point = point->next;
1303 } while ( point != after );
1307 before = after->prev;
1316 /* for each extrema, determine its direction along the */
1317 /* orthogonal axis */
1318 for ( n = 0; n < glyph->num_points; n++ )
1320 PSH3_Point point, before, after;
1323 point = &glyph->points[n];
1327 if ( psh3_point_is_extremum( point ) )
1331 before = before->prev;
1332 if ( before == point )
1335 } while ( before->org_v == point->org_v );
1339 after = after->next;
1340 if ( after == point )
1343 } while ( after->org_v == point->org_v );
1346 if ( before->org_v < point->org_v &&
1347 after->org_v > point->org_v )
1349 psh3_point_set_positive( point );
1351 else if ( before->org_v > point->org_v &&
1352 after->org_v < point->org_v )
1354 psh3_point_set_negative( point );
1363 #define PSH3_STRONG_THRESHOLD 30
1366 /* major_dir is the direction for points on the bottom/left of the stem; */
1367 /* Points on the top/right of the stem will have a direction of */
1371 psh3_hint_table_find_strong_point( PSH3_Hint_Table table,
1375 PSH3_Hint* sort = table->sort;
1376 FT_UInt num_hints = table->num_hints;
1377 FT_Int point_dir = 0;
1380 if ( PSH3_DIR_COMPARE( point->dir_in, major_dir ) )
1381 point_dir = point->dir_in;
1383 else if ( PSH3_DIR_COMPARE( point->dir_out, major_dir ) )
1384 point_dir = point->dir_out;
1391 for ( ; num_hints > 0; num_hints--, sort++ )
1393 PSH3_Hint hint = sort[0];
1397 if ( point_dir == major_dir )
1399 flag = PSH3_POINT_EDGE_MIN;
1400 d = point->org_u - hint->org_pos;
1402 if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
1405 psh3_point_set_strong( point );
1406 point->flags2 |= flag;
1411 else if ( point_dir == -major_dir )
1413 flag = PSH3_POINT_EDGE_MAX;
1414 d = point->org_u - hint->org_pos - hint->org_len;
1416 if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
1423 else if ( psh3_point_is_extremum( point ) )
1425 /* treat extrema as special cases for stem edge alignment */
1426 FT_UInt min_flag, max_flag;
1429 if ( major_dir == PSH3_DIR_HORIZONTAL )
1431 min_flag = PSH3_POINT_POSITIVE;
1432 max_flag = PSH3_POINT_NEGATIVE;
1436 min_flag = PSH3_POINT_NEGATIVE;
1437 max_flag = PSH3_POINT_POSITIVE;
1440 for ( ; num_hints > 0; num_hints--, sort++ )
1442 PSH3_Hint hint = sort[0];
1446 if ( point->flags2 & min_flag )
1448 flag = PSH3_POINT_EDGE_MIN;
1449 d = point->org_u - hint->org_pos;
1451 if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
1454 point->flags2 |= flag;
1456 psh3_point_set_strong( point );
1460 else if ( point->flags2 & max_flag )
1462 flag = PSH3_POINT_EDGE_MAX;
1463 d = point->org_u - hint->org_pos - hint->org_len;
1465 if ( ABS( d ) < PSH3_STRONG_THRESHOLD )
1469 if ( point->org_u >= hint->org_pos &&
1470 point->org_u <= hint->org_pos + hint->org_len )
1481 /* find strong points in a glyph */
1483 psh3_glyph_find_strong_points( PSH3_Glyph glyph,
1486 /* a point is strong if it is located on a stem */
1487 /* edge and has an "in" or "out" tangent to the hint's direction */
1489 PSH3_Hint_Table table = &glyph->hint_tables[dimension];
1490 PS_Mask mask = table->hint_masks->masks;
1491 FT_UInt num_masks = table->hint_masks->num_masks;
1493 FT_Int major_dir = dimension == 0 ? PSH3_DIR_VERTICAL
1494 : PSH3_DIR_HORIZONTAL;
1497 /* process secondary hints to "selected" points */
1498 if ( num_masks > 1 && glyph->num_points > 0 )
1500 first = mask->end_point;
1502 for ( ; num_masks > 1; num_masks--, mask++ )
1508 next = mask->end_point;
1509 count = next - first;
1512 PSH3_Point point = glyph->points + first;
1515 psh3_hint_table_activate_mask( table, mask );
1517 for ( ; count > 0; count--, point++ )
1518 psh3_hint_table_find_strong_point( table, point, major_dir );
1524 /* process primary hints for all points */
1525 if ( num_masks == 1 )
1527 FT_UInt count = glyph->num_points;
1528 PSH3_Point point = glyph->points;
1531 psh3_hint_table_activate_mask( table, table->hint_masks->masks );
1532 for ( ; count > 0; count--, point++ )
1534 if ( !psh3_point_is_strong( point ) )
1535 psh3_hint_table_find_strong_point( table, point, major_dir );
1539 /* now, certain points may have been attached to hint and */
1540 /* not marked as strong; update their flags then */
1542 FT_UInt count = glyph->num_points;
1543 PSH3_Point point = glyph->points;
1546 for ( ; count > 0; count--, point++ )
1547 if ( point->hint && !psh3_point_is_strong( point ) )
1548 psh3_point_set_strong( point );
1554 /* interpolate strong points with the help of hinted coordinates */
1556 psh3_glyph_interpolate_strong_points( PSH3_Glyph glyph,
1559 PSH_Dimension dim = &glyph->globals->dimension[dimension];
1560 FT_Fixed scale = dim->scale_mult;
1564 FT_UInt count = glyph->num_points;
1565 PSH3_Point point = glyph->points;
1568 for ( ; count > 0; count--, point++ )
1570 PSH3_Hint hint = point->hint;
1578 if ( psh3_point_is_edge_min( point ) )
1580 point->cur_u = hint->cur_pos;
1582 else if ( psh3_point_is_edge_max( point ) )
1584 point->cur_u = hint->cur_pos + hint->cur_len;
1588 delta = point->org_u - hint->org_pos;
1591 point->cur_u = hint->cur_pos + FT_MulFix( delta, scale );
1593 else if ( delta >= hint->org_len )
1594 point->cur_u = hint->cur_pos + hint->cur_len +
1595 FT_MulFix( delta - hint->org_len, scale );
1597 else if ( hint->org_len > 0 )
1598 point->cur_u = hint->cur_pos +
1599 FT_MulDiv( delta, hint->cur_len,
1602 point->cur_u = hint->cur_pos;
1604 psh3_point_set_fitted( point );
1612 psh3_glyph_interpolate_normal_points( PSH3_Glyph glyph,
1618 PSH_Dimension dim = &glyph->globals->dimension[dimension];
1619 FT_Fixed scale = dim->scale_mult;
1622 /* first technique: a point is strong if it is a local extrema */
1624 FT_UInt count = glyph->num_points;
1625 PSH3_Point point = glyph->points;
1628 for ( ; count > 0; count--, point++ )
1630 if ( psh3_point_is_strong( point ) )
1633 /* sometimes, some local extremas are smooth points */
1634 if ( psh3_point_is_smooth( point ) )
1636 if ( point->dir_in == PSH3_DIR_NONE ||
1637 point->dir_in != point->dir_out )
1640 if ( !psh3_point_is_extremum( point ) &&
1641 !psh3_point_is_inflex( point ) )
1644 point->flags &= ~PSH3_POINT_SMOOTH;
1647 /* find best enclosing point coordinates */
1649 PSH3_Point before = 0;
1650 PSH3_Point after = 0;
1652 FT_Pos diff_before = -32000;
1653 FT_Pos diff_after = 32000;
1654 FT_Pos u = point->org_u;
1656 FT_Int count2 = glyph->num_points;
1657 PSH3_Point cur = glyph->points;
1660 for ( ; count2 > 0; count2--, cur++ )
1662 if ( psh3_point_is_strong( cur ) )
1664 FT_Pos diff = cur->org_u - u;;
1669 if ( diff > diff_before )
1675 else if ( diff >= 0 )
1677 if ( diff < diff_after )
1691 /* we are before the first strong point coordinate; */
1692 /* simply translate the point */
1693 point->cur_u = after->cur_u +
1694 FT_MulFix( point->org_u - after->org_u, scale );
1698 /* we are after the last strong point coordinate; */
1699 /* simply translate the point */
1700 point->cur_u = before->cur_u +
1701 FT_MulFix( point->org_u - before->org_u, scale );
1705 if ( diff_before == 0 )
1706 point->cur_u = before->cur_u;
1708 else if ( diff_after == 0 )
1709 point->cur_u = after->cur_u;
1712 point->cur_u = before->cur_u +
1713 FT_MulDiv( u - before->org_u,
1714 after->cur_u - before->cur_u,
1715 after->org_u - before->org_u );
1718 psh3_point_set_fitted( point );
1728 /* interpolate other points */
1730 psh3_glyph_interpolate_other_points( PSH3_Glyph glyph,
1733 PSH_Dimension dim = &glyph->globals->dimension[dimension];
1734 FT_Fixed scale = dim->scale_mult;
1735 FT_Fixed delta = dim->scale_delta;
1736 PSH3_Contour contour = glyph->contours;
1737 FT_UInt num_contours = glyph->num_contours;
1740 for ( ; num_contours > 0; num_contours--, contour++ )
1742 PSH3_Point start = contour->start;
1743 PSH3_Point first, next, point;
1747 /* count the number of strong points in this contour */
1748 next = start + contour->count;
1752 for ( point = start; point < next; point++ )
1753 if ( psh3_point_is_fitted( point ) )
1761 /* if there are less than 2 fitted points in the contour, we */
1762 /* simply scale and eventually translate the contour points */
1763 if ( fit_count < 2 )
1765 if ( fit_count == 1 )
1766 delta = first->cur_u - FT_MulFix( first->org_u, scale );
1768 for ( point = start; point < next; point++ )
1769 if ( point != first )
1770 point->cur_u = FT_MulFix( point->org_u, scale ) + delta;
1775 /* there are more than 2 strong points in this contour; we */
1776 /* need to interpolate weak points between them */
1782 /* skip consecutive fitted points */
1786 if ( next == start )
1789 if ( !psh3_point_is_fitted( next ) )
1795 /* find next fitted point after unfitted one */
1799 if ( psh3_point_is_fitted( next ) )
1803 /* now interpolate between them */
1805 FT_Pos org_a, org_ab, cur_a, cur_ab;
1806 FT_Pos org_c, org_ac, cur_c;
1810 if ( first->org_u <= next->org_u )
1812 org_a = first->org_u;
1813 cur_a = first->cur_u;
1814 org_ab = next->org_u - org_a;
1815 cur_ab = next->cur_u - cur_a;
1819 org_a = next->org_u;
1820 cur_a = next->cur_u;
1821 org_ab = first->org_u - org_a;
1822 cur_ab = first->cur_u - cur_a;
1825 scale_ab = 0x10000L;
1827 scale_ab = FT_DivFix( cur_ab, org_ab );
1829 point = first->next;
1832 org_c = point->org_u;
1833 org_ac = org_c - org_a;
1837 /* on the left of the interpolation zone */
1838 cur_c = cur_a + FT_MulFix( org_ac, scale );
1840 else if ( org_ac >= org_ab )
1842 /* on the right on the interpolation zone */
1843 cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale );
1847 /* within the interpolation zone */
1848 cur_c = cur_a + FT_MulFix( org_ac, scale_ab );
1851 point->cur_u = cur_c;
1853 point = point->next;
1855 } while ( point != next );
1858 /* keep going until all points in the contours have been processed */
1861 } while ( first != start );
1869 /*************************************************************************/
1870 /*************************************************************************/
1872 /***** HIGH-LEVEL INTERFACE *****/
1874 /*************************************************************************/
1875 /*************************************************************************/
1878 ps3_hints_apply( PS_Hints ps_hints,
1879 FT_Outline* outline,
1880 PSH_Globals globals,
1881 FT_Render_Mode hint_mode )
1883 PSH3_GlyphRec glyphrec;
1884 PSH3_Glyph glyph = &glyphrec;
1894 memory = globals->memory;
1896 if ( ps3_debug_glyph )
1898 psh3_glyph_done( ps3_debug_glyph );
1899 FT_FREE( ps3_debug_glyph );
1902 if ( FT_NEW( glyph ) )
1905 ps3_debug_glyph = glyph;
1907 #endif /* DEBUG_HINTER */
1909 error = psh3_glyph_init( glyph, outline, ps_hints, globals );
1913 glyph->do_horz_hints = 1;
1914 glyph->do_vert_hints = 1;
1916 glyph->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
1917 hint_mode == FT_RENDER_MODE_LCD );
1919 glyph->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
1920 hint_mode == FT_RENDER_MODE_LCD_V );
1922 glyph->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
1924 for ( dimension = 0; dimension < 2; dimension++ )
1926 /* load outline coordinates into glyph */
1927 psh3_glyph_load_points( glyph, dimension );
1929 /* compute local extrema */
1930 psh3_glyph_compute_extrema( glyph );
1932 /* compute aligned stem/hints positions */
1933 psh3_hint_table_align_hints( &glyph->hint_tables[dimension],
1938 /* find strong points, align them, then interpolate others */
1939 psh3_glyph_find_strong_points( glyph, dimension );
1940 psh3_glyph_interpolate_strong_points( glyph, dimension );
1941 psh3_glyph_interpolate_normal_points( glyph, dimension );
1942 psh3_glyph_interpolate_other_points( glyph, dimension );
1944 /* save hinted coordinates back to outline */
1945 psh3_glyph_save_points( glyph, dimension );
1950 #ifndef DEBUG_HINTER
1951 psh3_glyph_done( glyph );