1/***************************************************************************/
2/* */
3/* pshalgo.c */
4/* */
5/* PostScript hinting algorithm (body). */
6/* */
7/* Copyright 2001-2018 by */
8/* David Turner, Robert Wilhelm, and Werner Lemberg. */
9/* */
10/* This file is part of the FreeType project, and may only be used */
11/* modified and distributed under the terms of the FreeType project */
12/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13/* this file you indicate that you have read the license and */
14/* understand and accept it fully. */
15/* */
16/***************************************************************************/
17
18
19#include <ft2build.h>
20#include FT_INTERNAL_OBJECTS_H
21#include FT_INTERNAL_DEBUG_H
22#include FT_INTERNAL_CALC_H
23#include "pshalgo.h"
24
25#include "pshnterr.h"
26
27
28#undef FT_COMPONENT
29#define FT_COMPONENT trace_pshalgo
30
31
32#ifdef DEBUG_HINTER
33 PSH_Hint_Table ps_debug_hint_table = NULL;
34 PSH_HintFunc ps_debug_hint_func = NULL;
35 PSH_Glyph ps_debug_glyph = NULL;
36#endif
37
38
39#define COMPUTE_INFLEXS /* compute inflection points to optimize `S' */
40 /* and similar glyphs */
41
42
43 /*************************************************************************/
44 /*************************************************************************/
45 /***** *****/
46 /***** BASIC HINTS RECORDINGS *****/
47 /***** *****/
48 /*************************************************************************/
49 /*************************************************************************/
50
51 /* return true if two stem hints overlap */
52 static FT_Int
53 psh_hint_overlap( PSH_Hint hint1,
54 PSH_Hint hint2 )
55 {
56 return hint1->org_pos + hint1->org_len >= hint2->org_pos &&
57 hint2->org_pos + hint2->org_len >= hint1->org_pos;
58 }
59
60
61 /* destroy hints table */
62 static void
63 psh_hint_table_done( PSH_Hint_Table table,
64 FT_Memory memory )
65 {
66 FT_FREE( table->zones );
67 table->num_zones = 0;
68 table->zone = NULL;
69
70 FT_FREE( table->sort );
71 FT_FREE( table->hints );
72 table->num_hints = 0;
73 table->max_hints = 0;
74 table->sort_global = NULL;
75 }
76
77
78 /* deactivate all hints in a table */
79 static void
80 psh_hint_table_deactivate( PSH_Hint_Table table )
81 {
82 FT_UInt count = table->max_hints;
83 PSH_Hint hint = table->hints;
84
85
86 for ( ; count > 0; count--, hint++ )
87 {
88 psh_hint_deactivate( hint );
89 hint->order = -1;
90 }
91 }
92
93
94 /* internal function to record a new hint */
95 static void
96 psh_hint_table_record( PSH_Hint_Table table,
97 FT_UInt idx )
98 {
99 PSH_Hint hint = table->hints + idx;
100
101
102 if ( idx >= table->max_hints )
103 {
104 FT_TRACE0(( "psh_hint_table_record: invalid hint index %d\n", idx ));
105 return;
106 }
107
108 /* ignore active hints */
109 if ( psh_hint_is_active( hint ) )
110 return;
111
112 psh_hint_activate( hint );
113
114 /* now scan the current active hint set to check */
115 /* whether `hint' overlaps with another hint */
116 {
117 PSH_Hint* sorted = table->sort_global;
118 FT_UInt count = table->num_hints;
119 PSH_Hint hint2;
120
121
122 hint->parent = NULL;
123 for ( ; count > 0; count--, sorted++ )
124 {
125 hint2 = sorted[0];
126
127 if ( psh_hint_overlap( hint, hint2 ) )
128 {
129 hint->parent = hint2;
130 break;
131 }
132 }
133 }
134
135 if ( table->num_hints < table->max_hints )
136 table->sort_global[table->num_hints++] = hint;
137 else
138 FT_TRACE0(( "psh_hint_table_record: too many sorted hints! BUG!\n" ));
139 }
140
141
142 static void
143 psh_hint_table_record_mask( PSH_Hint_Table table,
144 PS_Mask hint_mask )
145 {
146 FT_Int mask = 0, val = 0;
147 FT_Byte* cursor = hint_mask->bytes;
148 FT_UInt idx, limit;
149
150
151 limit = hint_mask->num_bits;
152
153 for ( idx = 0; idx < limit; idx++ )
154 {
155 if ( mask == 0 )
156 {
157 val = *cursor++;
158 mask = 0x80;
159 }
160
161 if ( val & mask )
162 psh_hint_table_record( table, idx );
163
164 mask >>= 1;
165 }
166 }
167
168
169 /* create hints table */
170 static FT_Error
171 psh_hint_table_init( PSH_Hint_Table table,
172 PS_Hint_Table hints,
173 PS_Mask_Table hint_masks,
174 PS_Mask_Table counter_masks,
175 FT_Memory memory )
176 {
177 FT_UInt count;
178 FT_Error error;
179
180 FT_UNUSED( counter_masks );
181
182
183 count = hints->num_hints;
184
185 /* allocate our tables */
186 if ( FT_NEW_ARRAY( table->sort, 2 * count ) ||
187 FT_NEW_ARRAY( table->hints, count ) ||
188 FT_NEW_ARRAY( table->zones, 2 * count + 1 ) )
189 goto Exit;
190
191 table->max_hints = count;
192 table->sort_global = table->sort + count;
193 table->num_hints = 0;
194 table->num_zones = 0;
195 table->zone = NULL;
196
197 /* initialize the `table->hints' array */
198 {
199 PSH_Hint write = table->hints;
200 PS_Hint read = hints->hints;
201
202
203 for ( ; count > 0; count--, write++, read++ )
204 {
205 write->org_pos = read->pos;
206 write->org_len = read->len;
207 write->flags = read->flags;
208 }
209 }
210
211 /* we now need to determine the initial `parent' stems; first */
212 /* activate the hints that are given by the initial hint masks */
213 if ( hint_masks )
214 {
215 PS_Mask mask = hint_masks->masks;
216
217
218 count = hint_masks->num_masks;
219 table->hint_masks = hint_masks;
220
221 for ( ; count > 0; count--, mask++ )
222 psh_hint_table_record_mask( table, mask );
223 }
224
225 /* finally, do a linear parse in case some hints were left alone */
226 if ( table->num_hints != table->max_hints )
227 {
228 FT_UInt idx;
229
230
231 FT_TRACE0(( "psh_hint_table_init: missing/incorrect hint masks\n" ));
232
233 count = table->max_hints;
234 for ( idx = 0; idx < count; idx++ )
235 psh_hint_table_record( table, idx );
236 }
237
238 Exit:
239 return error;
240 }
241
242
243 static void
244 psh_hint_table_activate_mask( PSH_Hint_Table table,
245 PS_Mask hint_mask )
246 {
247 FT_Int mask = 0, val = 0;
248 FT_Byte* cursor = hint_mask->bytes;
249 FT_UInt idx, limit, count;
250
251
252 limit = hint_mask->num_bits;
253 count = 0;
254
255 psh_hint_table_deactivate( table );
256
257 for ( idx = 0; idx < limit; idx++ )
258 {
259 if ( mask == 0 )
260 {
261 val = *cursor++;
262 mask = 0x80;
263 }
264
265 if ( val & mask )
266 {
267 PSH_Hint hint = &table->hints[idx];
268
269
270 if ( !psh_hint_is_active( hint ) )
271 {
272 FT_UInt count2;
273
274#if 0
275 PSH_Hint* sort = table->sort;
276 PSH_Hint hint2;
277
278
279 for ( count2 = count; count2 > 0; count2--, sort++ )
280 {
281 hint2 = sort[0];
282 if ( psh_hint_overlap( hint, hint2 ) )
283 FT_TRACE0(( "psh_hint_table_activate_mask:"
284 " found overlapping hints\n" ))
285 }
286#else
287 count2 = 0;
288#endif
289
290 if ( count2 == 0 )
291 {
292 psh_hint_activate( hint );
293 if ( count < table->max_hints )
294 table->sort[count++] = hint;
295 else
296 FT_TRACE0(( "psh_hint_tableactivate_mask:"
297 " too many active hints\n" ));
298 }
299 }
300 }
301
302 mask >>= 1;
303 }
304 table->num_hints = count;
305
306 /* now, sort the hints; they are guaranteed to not overlap */
307 /* so we can compare their "org_pos" field directly */
308 {
309 FT_Int i1, i2;
310 PSH_Hint hint1, hint2;
311 PSH_Hint* sort = table->sort;
312
313
314 /* a simple bubble sort will do, since in 99% of cases, the hints */
315 /* will be already sorted -- and the sort will be linear */
316 for ( i1 = 1; i1 < (FT_Int)count; i1++ )
317 {
318 hint1 = sort[i1];
319 for ( i2 = i1 - 1; i2 >= 0; i2-- )
320 {
321 hint2 = sort[i2];
322
323 if ( hint2->org_pos < hint1->org_pos )
324 break;
325
326 sort[i2 + 1] = hint2;
327 sort[i2] = hint1;
328 }
329 }
330 }
331 }
332
333
334 /*************************************************************************/
335 /*************************************************************************/
336 /***** *****/
337 /***** HINTS GRID-FITTING AND OPTIMIZATION *****/
338 /***** *****/
339 /*************************************************************************/
340 /*************************************************************************/
341
342#if 1
343 static FT_Pos
344 psh_dimension_quantize_len( PSH_Dimension dim,
345 FT_Pos len,
346 FT_Bool do_snapping )
347 {
348 if ( len <= 64 )
349 len = 64;
350 else
351 {
352 FT_Pos delta = len - dim->stdw.widths[0].cur;
353
354
355 if ( delta < 0 )
356 delta = -delta;
357
358 if ( delta < 40 )
359 {
360 len = dim->stdw.widths[0].cur;
361 if ( len < 48 )
362 len = 48;
363 }
364
365 if ( len < 3 * 64 )
366 {
367 delta = ( len & 63 );
368 len &= -64;
369
370 if ( delta < 10 )
371 len += delta;
372
373 else if ( delta < 32 )
374 len += 10;
375
376 else if ( delta < 54 )
377 len += 54;
378
379 else
380 len += delta;
381 }
382 else
383 len = FT_PIX_ROUND( len );
384 }
385
386 if ( do_snapping )
387 len = FT_PIX_ROUND( len );
388
389 return len;
390 }
391#endif /* 0 */
392
393
394#ifdef DEBUG_HINTER
395
396 static void
397 ps_simple_scale( PSH_Hint_Table table,
398 FT_Fixed scale,
399 FT_Fixed delta,
400 FT_Int dimension )
401 {
402 FT_UInt count;
403
404
405 for ( count = 0; count < table->max_hints; count++ )
406 {
407 PSH_Hint hint = table->hints + count;
408
409
410 hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta;
411 hint->cur_len = FT_MulFix( hint->org_len, scale );
412
413 if ( ps_debug_hint_func )
414 ps_debug_hint_func( hint, dimension );
415 }
416 }
417
418#endif /* DEBUG_HINTER */
419
420
421 static FT_Fixed
422 psh_hint_snap_stem_side_delta( FT_Fixed pos,
423 FT_Fixed len )
424 {
425 FT_Fixed delta1 = FT_PIX_ROUND( pos ) - pos;
426 FT_Fixed delta2 = FT_PIX_ROUND( pos + len ) - pos - len;
427
428
429 if ( FT_ABS( delta1 ) <= FT_ABS( delta2 ) )
430 return delta1;
431 else
432 return delta2;
433 }
434
435
436 static void
437 psh_hint_align( PSH_Hint hint,
438 PSH_Globals globals,
439 FT_Int dimension,
440 PSH_Glyph glyph )
441 {
442 PSH_Dimension dim = &globals->dimension[dimension];
443 FT_Fixed scale = dim->scale_mult;
444 FT_Fixed delta = dim->scale_delta;
445
446
447 if ( !psh_hint_is_fitted( hint ) )
448 {
449 FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta;
450 FT_Pos len = FT_MulFix( hint->org_len, scale );
451
452 FT_Int do_snapping;
453 FT_Pos fit_len;
454 PSH_AlignmentRec align;
455
456
457 /* ignore stem alignments when requested through the hint flags */
458 if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
459 ( dimension == 1 && !glyph->do_vert_hints ) )
460 {
461 hint->cur_pos = pos;
462 hint->cur_len = len;
463
464 psh_hint_set_fitted( hint );
465 return;
466 }
467
468 /* perform stem snapping when requested - this is necessary
469 * for monochrome and LCD hinting modes only
470 */
471 do_snapping = ( dimension == 0 && glyph->do_horz_snapping ) ||
472 ( dimension == 1 && glyph->do_vert_snapping );
473
474 hint->cur_len = fit_len = len;
475
476 /* check blue zones for horizontal stems */
477 align.align = PSH_BLUE_ALIGN_NONE;
478 align.align_bot = align.align_top = 0;
479
480 if ( dimension == 1 )
481 psh_blues_snap_stem( &globals->blues,
482 hint->org_pos + hint->org_len,
483 hint->org_pos,
484 &align );
485
486 switch ( align.align )
487 {
488 case PSH_BLUE_ALIGN_TOP:
489 /* the top of the stem is aligned against a blue zone */
490 hint->cur_pos = align.align_top - fit_len;
491 break;
492
493 case PSH_BLUE_ALIGN_BOT:
494 /* the bottom of the stem is aligned against a blue zone */
495 hint->cur_pos = align.align_bot;
496 break;
497
498 case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
499 /* both edges of the stem are aligned against blue zones */
500 hint->cur_pos = align.align_bot;
501 hint->cur_len = align.align_top - align.align_bot;
502 break;
503
504 default:
505 {
506 PSH_Hint parent = hint->parent;
507
508
509 if ( parent )
510 {
511 FT_Pos par_org_center, par_cur_center;
512 FT_Pos cur_org_center, cur_delta;
513
514
515 /* ensure that parent is already fitted */
516 if ( !psh_hint_is_fitted( parent ) )
517 psh_hint_align( parent, globals, dimension, glyph );
518
519 /* keep original relation between hints, this is, use the */
520 /* scaled distance between the centers of the hints to */
521 /* compute the new position */
522 par_org_center = parent->org_pos + ( parent->org_len >> 1 );
523 par_cur_center = parent->cur_pos + ( parent->cur_len >> 1 );
524 cur_org_center = hint->org_pos + ( hint->org_len >> 1 );
525
526 cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
527 pos = par_cur_center + cur_delta - ( len >> 1 );
528 }
529
530 hint->cur_pos = pos;
531 hint->cur_len = fit_len;
532
533 /* Stem adjustment tries to snap stem widths to standard
534 * ones. This is important to prevent unpleasant rounding
535 * artefacts.
536 */
537 if ( glyph->do_stem_adjust )
538 {
539 if ( len <= 64 )
540 {
541 /* the stem is less than one pixel; we will center it
542 * around the nearest pixel center
543 */
544 if ( len >= 32 )
545 {
546 /* This is a special case where we also widen the stem
547 * and align it to the pixel grid.
548 *
549 * stem_center = pos + (len/2)
550 * nearest_pixel_center = FT_ROUND(stem_center-32)+32
551 * new_pos = nearest_pixel_center-32
552 * = FT_ROUND(stem_center-32)
553 * = FT_FLOOR(stem_center-32+32)
554 * = FT_FLOOR(stem_center)
555 * new_len = 64
556 */
557 pos = FT_PIX_FLOOR( pos + ( len >> 1 ) );
558 len = 64;
559 }
560 else if ( len > 0 )
561 {
562 /* This is a very small stem; we simply align it to the
563 * pixel grid, trying to find the minimum displacement.
564 *
565 * left = pos
566 * right = pos + len
567 * left_nearest_edge = ROUND(pos)
568 * right_nearest_edge = ROUND(right)
569 *
570 * if ( ABS(left_nearest_edge - left) <=
571 * ABS(right_nearest_edge - right) )
572 * new_pos = left
573 * else
574 * new_pos = right
575 */
576 FT_Pos left_nearest = FT_PIX_ROUND( pos );
577 FT_Pos right_nearest = FT_PIX_ROUND( pos + len );
578 FT_Pos left_disp = left_nearest - pos;
579 FT_Pos right_disp = right_nearest - ( pos + len );
580
581
582 if ( left_disp < 0 )
583 left_disp = -left_disp;
584 if ( right_disp < 0 )
585 right_disp = -right_disp;
586 if ( left_disp <= right_disp )
587 pos = left_nearest;
588 else
589 pos = right_nearest;
590 }
591 else
592 {
593 /* this is a ghost stem; we simply round it */
594 pos = FT_PIX_ROUND( pos );
595 }
596 }
597 else
598 {
599 len = psh_dimension_quantize_len( dim, len, 0 );
600 }
601 }
602
603 /* now that we have a good hinted stem width, try to position */
604 /* the stem along a pixel grid integer coordinate */
605 hint->cur_pos = pos + psh_hint_snap_stem_side_delta( pos, len );
606 hint->cur_len = len;
607 }
608 }
609
610 if ( do_snapping )
611 {
612 pos = hint->cur_pos;
613 len = hint->cur_len;
614
615 if ( len < 64 )
616 len = 64;
617 else
618 len = FT_PIX_ROUND( len );
619
620 switch ( align.align )
621 {
622 case PSH_BLUE_ALIGN_TOP:
623 hint->cur_pos = align.align_top - len;
624 hint->cur_len = len;
625 break;
626
627 case PSH_BLUE_ALIGN_BOT:
628 hint->cur_len = len;
629 break;
630
631 case PSH_BLUE_ALIGN_BOT | PSH_BLUE_ALIGN_TOP:
632 /* don't touch */
633 break;
634
635
636 default:
637 hint->cur_len = len;
638 if ( len & 64 )
639 pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ) + 32;
640 else
641 pos = FT_PIX_ROUND( pos + ( len >> 1 ) );
642
643 hint->cur_pos = pos - ( len >> 1 );
644 hint->cur_len = len;
645 }
646 }
647
648 psh_hint_set_fitted( hint );
649
650#ifdef DEBUG_HINTER
651 if ( ps_debug_hint_func )
652 ps_debug_hint_func( hint, dimension );
653#endif
654 }
655 }
656
657
658#if 0 /* not used for now, experimental */
659
660 /*
661 * A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT)
662 * of stems
663 */
664 static void
665 psh_hint_align_light( PSH_Hint hint,
666 PSH_Globals globals,
667 FT_Int dimension,
668 PSH_Glyph glyph )
669 {
670 PSH_Dimension dim = &globals->dimension[dimension];
671 FT_Fixed scale = dim->scale_mult;
672 FT_Fixed delta = dim->scale_delta;
673
674
675 if ( !psh_hint_is_fitted( hint ) )
676 {
677 FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta;
678 FT_Pos len = FT_MulFix( hint->org_len, scale );
679
680 FT_Pos fit_len;
681
682 PSH_AlignmentRec align;
683
684
685 /* ignore stem alignments when requested through the hint flags */
686 if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
687 ( dimension == 1 && !glyph->do_vert_hints ) )
688 {
689 hint->cur_pos = pos;
690 hint->cur_len = len;
691
692 psh_hint_set_fitted( hint );
693 return;
694 }
695
696 fit_len = len;
697
698 hint->cur_len = fit_len;
699
700 /* check blue zones for horizontal stems */
701 align.align = PSH_BLUE_ALIGN_NONE;
702 align.align_bot = align.align_top = 0;
703
704 if ( dimension == 1 )
705 psh_blues_snap_stem( &globals->blues,
706 hint->org_pos + hint->org_len,
707 hint->org_pos,
708 &align );
709
710 switch ( align.align )
711 {
712 case PSH_BLUE_ALIGN_TOP:
713 /* the top of the stem is aligned against a blue zone */
714 hint->cur_pos = align.align_top - fit_len;
715 break;
716
717 case PSH_BLUE_ALIGN_BOT:
718 /* the bottom of the stem is aligned against a blue zone */
719 hint->cur_pos = align.align_bot;
720 break;
721
722 case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
723 /* both edges of the stem are aligned against blue zones */
724 hint->cur_pos = align.align_bot;
725 hint->cur_len = align.align_top - align.align_bot;
726 break;
727
728 default:
729 {
730 PSH_Hint parent = hint->parent;
731
732
733 if ( parent )
734 {
735 FT_Pos par_org_center, par_cur_center;
736 FT_Pos cur_org_center, cur_delta;
737
738
739 /* ensure that parent is already fitted */
740 if ( !psh_hint_is_fitted( parent ) )
741 psh_hint_align_light( parent, globals, dimension, glyph );
742
743 par_org_center = parent->org_pos + ( parent->org_len / 2 );
744 par_cur_center = parent->cur_pos + ( parent->cur_len / 2 );
745 cur_org_center = hint->org_pos + ( hint->org_len / 2 );
746
747 cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
748 pos = par_cur_center + cur_delta - ( len >> 1 );
749 }
750
751 /* Stems less than one pixel wide are easy -- we want to
752 * make them as dark as possible, so they must fall within
753 * one pixel. If the stem is split between two pixels
754 * then snap the edge that is nearer to the pixel boundary
755 * to the pixel boundary.
756 */
757 if ( len <= 64 )
758 {
759 if ( ( pos + len + 63 ) / 64 != pos / 64 + 1 )
760 pos += psh_hint_snap_stem_side_delta ( pos, len );
761 }
762
763 /* Position stems other to minimize the amount of mid-grays.
764 * There are, in general, two positions that do this,
765 * illustrated as A) and B) below.
766 *
767 * + + + +
768 *
769 * A) |--------------------------------|
770 * B) |--------------------------------|
771 * C) |--------------------------------|
772 *
773 * Position A) (split the excess stem equally) should be better
774 * for stems of width N + f where f < 0.5.
775 *
776 * Position B) (split the deficiency equally) should be better
777 * for stems of width N + f where f > 0.5.
778 *
779 * It turns out though that minimizing the total number of lit
780 * pixels is also important, so position C), with one edge
781 * aligned with a pixel boundary is actually preferable
782 * to A). There are also more possible positions for C) than
783 * for A) or B), so it involves less distortion of the overall
784 * character shape.
785 */
786 else /* len > 64 */
787 {
788 FT_Fixed frac_len = len & 63;
789 FT_Fixed center = pos + ( len >> 1 );
790 FT_Fixed delta_a, delta_b;
791
792
793 if ( ( len / 64 ) & 1 )
794 {
795 delta_a = FT_PIX_FLOOR( center ) + 32 - center;
796 delta_b = FT_PIX_ROUND( center ) - center;
797 }
798 else
799 {
800 delta_a = FT_PIX_ROUND( center ) - center;
801 delta_b = FT_PIX_FLOOR( center ) + 32 - center;
802 }
803
804 /* We choose between B) and C) above based on the amount
805 * of fractional stem width; for small amounts, choose
806 * C) always, for large amounts, B) always, and inbetween,
807 * pick whichever one involves less stem movement.
808 */
809 if ( frac_len < 32 )
810 {
811 pos += psh_hint_snap_stem_side_delta ( pos, len );
812 }
813 else if ( frac_len < 48 )
814 {
815 FT_Fixed side_delta = psh_hint_snap_stem_side_delta ( pos,
816 len );
817
818 if ( FT_ABS( side_delta ) < FT_ABS( delta_b ) )
819 pos += side_delta;
820 else
821 pos += delta_b;
822 }
823 else
824 {
825 pos += delta_b;
826 }
827 }
828
829 hint->cur_pos = pos;
830 }
831 } /* switch */
832
833 psh_hint_set_fitted( hint );
834
835#ifdef DEBUG_HINTER
836 if ( ps_debug_hint_func )
837 ps_debug_hint_func( hint, dimension );
838#endif
839 }
840 }
841
842#endif /* 0 */
843
844
845 static void
846 psh_hint_table_align_hints( PSH_Hint_Table table,
847 PSH_Globals globals,
848 FT_Int dimension,
849 PSH_Glyph glyph )
850 {
851 PSH_Hint hint;
852 FT_UInt count;
853
854#ifdef DEBUG_HINTER
855
856 PSH_Dimension dim = &globals->dimension[dimension];
857 FT_Fixed scale = dim->scale_mult;
858 FT_Fixed delta = dim->scale_delta;
859
860
861 if ( ps_debug_no_vert_hints && dimension == 0 )
862 {
863 ps_simple_scale( table, scale, delta, dimension );
864 return;
865 }
866
867 if ( ps_debug_no_horz_hints && dimension == 1 )
868 {
869 ps_simple_scale( table, scale, delta, dimension );
870 return;
871 }
872
873#endif /* DEBUG_HINTER*/
874
875 hint = table->hints;
876 count = table->max_hints;
877
878 for ( ; count > 0; count--, hint++ )
879 psh_hint_align( hint, globals, dimension, glyph );
880 }
881
882
883 /*************************************************************************/
884 /*************************************************************************/
885 /***** *****/
886 /***** POINTS INTERPOLATION ROUTINES *****/
887 /***** *****/
888 /*************************************************************************/
889 /*************************************************************************/
890
891#define xxDEBUG_ZONES
892
893
894#ifdef DEBUG_ZONES
895
896#include FT_CONFIG_STANDARD_LIBRARY_H
897
898 static void
899 psh_print_zone( PSH_Zone zone )
900 {
901 printf( "zone [scale,delta,min,max] = [%.5f,%.2f,%d,%d]\n",
902 zone->scale / 65536.0,
903 zone->delta / 64.0,
904 zone->min,
905 zone->max );
906 }
907
908#endif /* DEBUG_ZONES */
909
910
911 /*************************************************************************/
912 /*************************************************************************/
913 /***** *****/
914 /***** HINTER GLYPH MANAGEMENT *****/
915 /***** *****/
916 /*************************************************************************/
917 /*************************************************************************/
918
919#define psh_corner_is_flat ft_corner_is_flat
920#define psh_corner_orientation ft_corner_orientation
921
922
923#ifdef COMPUTE_INFLEXS
924
925 /* compute all inflex points in a given glyph */
926 static void
927 psh_glyph_compute_inflections( PSH_Glyph glyph )
928 {
929 FT_UInt n;
930
931
932 for ( n = 0; n < glyph->num_contours; n++ )
933 {
934 PSH_Point first, start, end, before, after;
935 FT_Pos in_x, in_y, out_x, out_y;
936 FT_Int orient_prev, orient_cur;
937 FT_Int finished = 0;
938
939
940 /* we need at least 4 points to create an inflection point */
941 if ( glyph->contours[n].count < 4 )
942 continue;
943
944 /* compute first segment in contour */
945 first = glyph->contours[n].start;
946
947 start = end = first;
948 do
949 {
950 end = end->next;
951 if ( end == first )
952 goto Skip;
953
954 in_x = end->org_u - start->org_u;
955 in_y = end->org_v - start->org_v;
956
957 } while ( in_x == 0 && in_y == 0 );
958
959 /* extend the segment start whenever possible */
960 before = start;
961 do
962 {
963 do
964 {
965 start = before;
966 before = before->prev;
967 if ( before == first )
968 goto Skip;
969
970 out_x = start->org_u - before->org_u;
971 out_y = start->org_v - before->org_v;
972
973 } while ( out_x == 0 && out_y == 0 );
974
975 orient_prev = psh_corner_orientation( in_x, in_y, out_x, out_y );
976
977 } while ( orient_prev == 0 );
978
979 first = start;
980 in_x = out_x;
981 in_y = out_y;
982
983 /* now, process all segments in the contour */
984 do
985 {
986 /* first, extend current segment's end whenever possible */
987 after = end;
988 do
989 {
990 do
991 {
992 end = after;
993 after = after->next;
994 if ( after == first )
995 finished = 1;
996
997 out_x = after->org_u - end->org_u;
998 out_y = after->org_v - end->org_v;
999
1000 } while ( out_x == 0 && out_y == 0 );
1001
1002 orient_cur = psh_corner_orientation( in_x, in_y, out_x, out_y );
1003
1004 } while ( orient_cur == 0 );
1005
1006 if ( ( orient_cur ^ orient_prev ) < 0 )
1007 {
1008 do
1009 {
1010 psh_point_set_inflex( start );
1011 start = start->next;
1012 }
1013 while ( start != end );
1014
1015 psh_point_set_inflex( start );
1016 }
1017
1018 start = end;
1019 end = after;
1020 orient_prev = orient_cur;
1021 in_x = out_x;
1022 in_y = out_y;
1023
1024 } while ( !finished );
1025
1026 Skip:
1027 ;
1028 }
1029 }
1030
1031#endif /* COMPUTE_INFLEXS */
1032
1033
1034 static void
1035 psh_glyph_done( PSH_Glyph glyph )
1036 {
1037 FT_Memory memory = glyph->memory;
1038
1039
1040 psh_hint_table_done( &glyph->hint_tables[1], memory );
1041 psh_hint_table_done( &glyph->hint_tables[0], memory );
1042
1043 FT_FREE( glyph->points );
1044 FT_FREE( glyph->contours );
1045
1046 glyph->num_points = 0;
1047 glyph->num_contours = 0;
1048
1049 glyph->memory = NULL;
1050 }
1051
1052
1053 static int
1054 psh_compute_dir( FT_Pos dx,
1055 FT_Pos dy )
1056 {
1057 FT_Pos ax, ay;
1058 int result = PSH_DIR_NONE;
1059
1060
1061 ax = FT_ABS( dx );
1062 ay = FT_ABS( dy );
1063
1064 if ( ay * 12 < ax )
1065 {
1066 /* |dy| <<< |dx| means a near-horizontal segment */
1067 result = ( dx >= 0 ) ? PSH_DIR_RIGHT : PSH_DIR_LEFT;
1068 }
1069 else if ( ax * 12 < ay )
1070 {
1071 /* |dx| <<< |dy| means a near-vertical segment */
1072 result = ( dy >= 0 ) ? PSH_DIR_UP : PSH_DIR_DOWN;
1073 }
1074
1075 return result;
1076 }
1077
1078
1079 /* load outline point coordinates into hinter glyph */
1080 static void
1081 psh_glyph_load_points( PSH_Glyph glyph,
1082 FT_Int dimension )
1083 {
1084 FT_Vector* vec = glyph->outline->points;
1085 PSH_Point point = glyph->points;
1086 FT_UInt count = glyph->num_points;
1087
1088
1089 for ( ; count > 0; count--, point++, vec++ )
1090 {
1091 point->flags2 = 0;
1092 point->hint = NULL;
1093 if ( dimension == 0 )
1094 {
1095 point->org_u = vec->x;
1096 point->org_v = vec->y;
1097 }
1098 else
1099 {
1100 point->org_u = vec->y;
1101 point->org_v = vec->x;
1102 }
1103
1104#ifdef DEBUG_HINTER
1105 point->org_x = vec->x;
1106 point->org_y = vec->y;
1107#endif
1108
1109 }
1110 }
1111
1112
1113 /* save hinted point coordinates back to outline */
1114 static void
1115 psh_glyph_save_points( PSH_Glyph glyph,
1116 FT_Int dimension )
1117 {
1118 FT_UInt n;
1119 PSH_Point point = glyph->points;
1120 FT_Vector* vec = glyph->outline->points;
1121 char* tags = glyph->outline->tags;
1122
1123
1124 for ( n = 0; n < glyph->num_points; n++ )
1125 {
1126 if ( dimension == 0 )
1127 vec[n].x = point->cur_u;
1128 else
1129 vec[n].y = point->cur_u;
1130
1131 if ( psh_point_is_strong( point ) )
1132 tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 );
1133
1134#ifdef DEBUG_HINTER
1135
1136 if ( dimension == 0 )
1137 {
1138 point->cur_x = point->cur_u;
1139 point->flags_x = point->flags2 | point->flags;
1140 }
1141 else
1142 {
1143 point->cur_y = point->cur_u;
1144 point->flags_y = point->flags2 | point->flags;
1145 }
1146
1147#endif
1148
1149 point++;
1150 }
1151 }
1152
1153
1154 static FT_Error
1155 psh_glyph_init( PSH_Glyph glyph,
1156 FT_Outline* outline,
1157 PS_Hints ps_hints,
1158 PSH_Globals globals )
1159 {
1160 FT_Error error;
1161 FT_Memory memory;
1162
1163
1164 /* clear all fields */
1165 FT_ZERO( glyph );
1166
1167 memory = glyph->memory = globals->memory;
1168
1169 /* allocate and setup points + contours arrays */
1170 if ( FT_NEW_ARRAY( glyph->points, outline->n_points ) ||
1171 FT_NEW_ARRAY( glyph->contours, outline->n_contours ) )
1172 goto Exit;
1173
1174 glyph->num_points = (FT_UInt)outline->n_points;
1175 glyph->num_contours = (FT_UInt)outline->n_contours;
1176
1177 {
1178 FT_UInt first = 0, next, n;
1179 PSH_Point points = glyph->points;
1180 PSH_Contour contour = glyph->contours;
1181
1182
1183 for ( n = 0; n < glyph->num_contours; n++ )
1184 {
1185 FT_UInt count;
1186 PSH_Point point;
1187
1188
1189 next = (FT_UInt)outline->contours[n] + 1;
1190 count = next - first;
1191
1192 contour->start = points + first;
1193 contour->count = count;
1194
1195 if ( count > 0 )
1196 {
1197 point = points + first;
1198
1199 point->prev = points + next - 1;
1200 point->contour = contour;
1201
1202 for ( ; count > 1; count-- )
1203 {
1204 point[0].next = point + 1;
1205 point[1].prev = point;
1206 point++;
1207 point->contour = contour;
1208 }
1209 point->next = points + first;
1210 }
1211
1212 contour++;
1213 first = next;
1214 }
1215 }
1216
1217 {
1218 PSH_Point points = glyph->points;
1219 PSH_Point point = points;
1220 FT_Vector* vec = outline->points;
1221 FT_UInt n;
1222
1223
1224 for ( n = 0; n < glyph->num_points; n++, point++ )
1225 {
1226 FT_Int n_prev = (FT_Int)( point->prev - points );
1227 FT_Int n_next = (FT_Int)( point->next - points );
1228 FT_Pos dxi, dyi, dxo, dyo;
1229
1230
1231 if ( !( outline->tags[n] & FT_CURVE_TAG_ON ) )
1232 point->flags = PSH_POINT_OFF;
1233
1234 dxi = vec[n].x - vec[n_prev].x;
1235 dyi = vec[n].y - vec[n_prev].y;
1236
1237 point->dir_in = (FT_Char)psh_compute_dir( dxi, dyi );
1238
1239 dxo = vec[n_next].x - vec[n].x;
1240 dyo = vec[n_next].y - vec[n].y;
1241
1242 point->dir_out = (FT_Char)psh_compute_dir( dxo, dyo );
1243
1244 /* detect smooth points */
1245 if ( point->flags & PSH_POINT_OFF )
1246 point->flags |= PSH_POINT_SMOOTH;
1247
1248 else if ( point->dir_in == point->dir_out )
1249 {
1250 if ( point->dir_out != PSH_DIR_NONE ||
1251 psh_corner_is_flat( dxi, dyi, dxo, dyo ) )
1252 point->flags |= PSH_POINT_SMOOTH;
1253 }
1254 }
1255 }
1256
1257 glyph->outline = outline;
1258 glyph->globals = globals;
1259
1260#ifdef COMPUTE_INFLEXS
1261 psh_glyph_load_points( glyph, 0 );
1262 psh_glyph_compute_inflections( glyph );
1263#endif /* COMPUTE_INFLEXS */
1264
1265 /* now deal with hints tables */
1266 error = psh_hint_table_init( &glyph->hint_tables [0],
1267 &ps_hints->dimension[0].hints,
1268 &ps_hints->dimension[0].masks,
1269 &ps_hints->dimension[0].counters,
1270 memory );
1271 if ( error )
1272 goto Exit;
1273
1274 error = psh_hint_table_init( &glyph->hint_tables [1],
1275 &ps_hints->dimension[1].hints,
1276 &ps_hints->dimension[1].masks,
1277 &ps_hints->dimension[1].counters,
1278 memory );
1279 if ( error )
1280 goto Exit;
1281
1282 Exit:
1283 return error;
1284 }
1285
1286
1287 /* compute all extrema in a glyph for a given dimension */
1288 static void
1289 psh_glyph_compute_extrema( PSH_Glyph glyph )
1290 {
1291 FT_UInt n;
1292
1293
1294 /* first of all, compute all local extrema */
1295 for ( n = 0; n < glyph->num_contours; n++ )
1296 {
1297 PSH_Point first = glyph->contours[n].start;
1298 PSH_Point point, before, after;
1299
1300
1301 if ( glyph->contours[n].count == 0 )
1302 continue;
1303
1304 point = first;
1305 before = point;
1306
1307 do
1308 {
1309 before = before->prev;
1310 if ( before == first )
1311 goto Skip;
1312
1313 } while ( before->org_u == point->org_u );
1314
1315 first = point = before->next;
1316
1317 for (;;)
1318 {
1319 after = point;
1320 do
1321 {
1322 after = after->next;
1323 if ( after == first )
1324 goto Next;
1325
1326 } while ( after->org_u == point->org_u );
1327
1328 if ( before->org_u < point->org_u )
1329 {
1330 if ( after->org_u < point->org_u )
1331 {
1332 /* local maximum */
1333 goto Extremum;
1334 }
1335 }
1336 else /* before->org_u > point->org_u */
1337 {
1338 if ( after->org_u > point->org_u )
1339 {
1340 /* local minimum */
1341 Extremum:
1342 do
1343 {
1344 psh_point_set_extremum( point );
1345 point = point->next;
1346
1347 } while ( point != after );
1348 }
1349 }
1350
1351 before = after->prev;
1352 point = after;
1353
1354 } /* for */
1355
1356 Next:
1357 ;
1358 }
1359
1360 /* for each extremum, determine its direction along the */
1361 /* orthogonal axis */
1362 for ( n = 0; n < glyph->num_points; n++ )
1363 {
1364 PSH_Point point, before, after;
1365
1366
1367 point = &glyph->points[n];
1368 before = point;
1369 after = point;
1370
1371 if ( psh_point_is_extremum( point ) )
1372 {
1373 do
1374 {
1375 before = before->prev;
1376 if ( before == point )
1377 goto Skip;
1378
1379 } while ( before->org_v == point->org_v );
1380
1381 do
1382 {
1383 after = after->next;
1384 if ( after == point )
1385 goto Skip;
1386
1387 } while ( after->org_v == point->org_v );
1388 }
1389
1390 if ( before->org_v < point->org_v &&
1391 after->org_v > point->org_v )
1392 {
1393 psh_point_set_positive( point );
1394 }
1395 else if ( before->org_v > point->org_v &&
1396 after->org_v < point->org_v )
1397 {
1398 psh_point_set_negative( point );
1399 }
1400
1401 Skip:
1402 ;
1403 }
1404 }
1405
1406
1407 /* major_dir is the direction for points on the bottom/left of the stem; */
1408 /* Points on the top/right of the stem will have a direction of */
1409 /* -major_dir. */
1410
1411 static void
1412 psh_hint_table_find_strong_points( PSH_Hint_Table table,
1413 PSH_Point point,
1414 FT_UInt count,
1415 FT_Int threshold,
1416 FT_Int major_dir )
1417 {
1418 PSH_Hint* sort = table->sort;
1419 FT_UInt num_hints = table->num_hints;
1420
1421
1422 for ( ; count > 0; count--, point++ )
1423 {
1424 FT_Int point_dir = 0;
1425 FT_Pos org_u = point->org_u;
1426
1427
1428 if ( psh_point_is_strong( point ) )
1429 continue;
1430
1431 if ( PSH_DIR_COMPARE( point->dir_in, major_dir ) )
1432 point_dir = point->dir_in;
1433
1434 else if ( PSH_DIR_COMPARE( point->dir_out, major_dir ) )
1435 point_dir = point->dir_out;
1436
1437 if ( point_dir )
1438 {
1439 if ( point_dir == major_dir )
1440 {
1441 FT_UInt nn;
1442
1443
1444 for ( nn = 0; nn < num_hints; nn++ )
1445 {
1446 PSH_Hint hint = sort[nn];
1447 FT_Pos d = org_u - hint->org_pos;
1448
1449
1450 if ( d < threshold && -d < threshold )
1451 {
1452 psh_point_set_strong( point );
1453 point->flags2 |= PSH_POINT_EDGE_MIN;
1454 point->hint = hint;
1455 break;
1456 }
1457 }
1458 }
1459 else if ( point_dir == -major_dir )
1460 {
1461 FT_UInt nn;
1462
1463
1464 for ( nn = 0; nn < num_hints; nn++ )
1465 {
1466 PSH_Hint hint = sort[nn];
1467 FT_Pos d = org_u - hint->org_pos - hint->org_len;
1468
1469
1470 if ( d < threshold && -d < threshold )
1471 {
1472 psh_point_set_strong( point );
1473 point->flags2 |= PSH_POINT_EDGE_MAX;
1474 point->hint = hint;
1475 break;
1476 }
1477 }
1478 }
1479 }
1480
1481#if 1
1482 else if ( psh_point_is_extremum( point ) )
1483 {
1484 /* treat extrema as special cases for stem edge alignment */
1485 FT_UInt nn, min_flag, max_flag;
1486
1487
1488 if ( major_dir == PSH_DIR_HORIZONTAL )
1489 {
1490 min_flag = PSH_POINT_POSITIVE;
1491 max_flag = PSH_POINT_NEGATIVE;
1492 }
1493 else
1494 {
1495 min_flag = PSH_POINT_NEGATIVE;
1496 max_flag = PSH_POINT_POSITIVE;
1497 }
1498
1499 if ( point->flags2 & min_flag )
1500 {
1501 for ( nn = 0; nn < num_hints; nn++ )
1502 {
1503 PSH_Hint hint = sort[nn];
1504 FT_Pos d = org_u - hint->org_pos;
1505
1506
1507 if ( d < threshold && -d < threshold )
1508 {
1509 point->flags2 |= PSH_POINT_EDGE_MIN;
1510 point->hint = hint;
1511 psh_point_set_strong( point );
1512 break;
1513 }
1514 }
1515 }
1516 else if ( point->flags2 & max_flag )
1517 {
1518 for ( nn = 0; nn < num_hints; nn++ )
1519 {
1520 PSH_Hint hint = sort[nn];
1521 FT_Pos d = org_u - hint->org_pos - hint->org_len;
1522
1523
1524 if ( d < threshold && -d < threshold )
1525 {
1526 point->flags2 |= PSH_POINT_EDGE_MAX;
1527 point->hint = hint;
1528 psh_point_set_strong( point );
1529 break;
1530 }
1531 }
1532 }
1533
1534 if ( !point->hint )
1535 {
1536 for ( nn = 0; nn < num_hints; nn++ )
1537 {
1538 PSH_Hint hint = sort[nn];
1539
1540
1541 if ( org_u >= hint->org_pos &&
1542 org_u <= hint->org_pos + hint->org_len )
1543 {
1544 point->hint = hint;
1545 break;
1546 }
1547 }
1548 }
1549 }
1550
1551#endif /* 1 */
1552 }
1553 }
1554
1555
1556 /* the accepted shift for strong points in fractional pixels */
1557#define PSH_STRONG_THRESHOLD 32
1558
1559 /* the maximum shift value in font units */
1560#define PSH_STRONG_THRESHOLD_MAXIMUM 30
1561
1562
1563 /* find strong points in a glyph */
1564 static void
1565 psh_glyph_find_strong_points( PSH_Glyph glyph,
1566 FT_Int dimension )
1567 {
1568 /* a point is `strong' if it is located on a stem edge and */
1569 /* has an `in' or `out' tangent parallel to the hint's direction */
1570
1571 PSH_Hint_Table table = &glyph->hint_tables[dimension];
1572 PS_Mask mask = table->hint_masks->masks;
1573 FT_UInt num_masks = table->hint_masks->num_masks;
1574 FT_UInt first = 0;
1575 FT_Int major_dir = ( dimension == 0 ) ? PSH_DIR_VERTICAL
1576 : PSH_DIR_HORIZONTAL;
1577 PSH_Dimension dim = &glyph->globals->dimension[dimension];
1578 FT_Fixed scale = dim->scale_mult;
1579 FT_Int threshold;
1580
1581
1582 threshold = (FT_Int)FT_DivFix( PSH_STRONG_THRESHOLD, scale );
1583 if ( threshold > PSH_STRONG_THRESHOLD_MAXIMUM )
1584 threshold = PSH_STRONG_THRESHOLD_MAXIMUM;
1585
1586 /* process secondary hints to `selected' points */
1587 if ( num_masks > 1 && glyph->num_points > 0 )
1588 {
1589 /* the `endchar' op can reduce the number of points */
1590 first = mask->end_point > glyph->num_points
1591 ? glyph->num_points
1592 : mask->end_point;
1593 mask++;
1594 for ( ; num_masks > 1; num_masks--, mask++ )
1595 {
1596 FT_UInt next = FT_MIN( mask->end_point, glyph->num_points );
1597
1598
1599 if ( next > first )
1600 {
1601 FT_UInt count = next - first;
1602 PSH_Point point = glyph->points + first;
1603
1604
1605 psh_hint_table_activate_mask( table, mask );
1606
1607 psh_hint_table_find_strong_points( table, point, count,
1608 threshold, major_dir );
1609 }
1610 first = next;
1611 }
1612 }
1613
1614 /* process primary hints for all points */
1615 if ( num_masks == 1 )
1616 {
1617 FT_UInt count = glyph->num_points;
1618 PSH_Point point = glyph->points;
1619
1620
1621 psh_hint_table_activate_mask( table, table->hint_masks->masks );
1622
1623 psh_hint_table_find_strong_points( table, point, count,
1624 threshold, major_dir );
1625 }
1626
1627 /* now, certain points may have been attached to a hint and */
1628 /* not marked as strong; update their flags then */
1629 {
1630 FT_UInt count = glyph->num_points;
1631 PSH_Point point = glyph->points;
1632
1633
1634 for ( ; count > 0; count--, point++ )
1635 if ( point->hint && !psh_point_is_strong( point ) )
1636 psh_point_set_strong( point );
1637 }
1638 }
1639
1640
1641 /* find points in a glyph which are in a blue zone and have `in' or */
1642 /* `out' tangents parallel to the horizontal axis */
1643 static void
1644 psh_glyph_find_blue_points( PSH_Blues blues,
1645 PSH_Glyph glyph )
1646 {
1647 PSH_Blue_Table table;
1648 PSH_Blue_Zone zone;
1649 FT_UInt glyph_count = glyph->num_points;
1650 FT_UInt blue_count;
1651 PSH_Point point = glyph->points;
1652
1653
1654 for ( ; glyph_count > 0; glyph_count--, point++ )
1655 {
1656 FT_Pos y;
1657
1658
1659 /* check tangents */
1660 if ( !PSH_DIR_COMPARE( point->dir_in, PSH_DIR_HORIZONTAL ) &&
1661 !PSH_DIR_COMPARE( point->dir_out, PSH_DIR_HORIZONTAL ) )
1662 continue;
1663
1664 /* skip strong points */
1665 if ( psh_point_is_strong( point ) )
1666 continue;
1667
1668 y = point->org_u;
1669
1670 /* look up top zones */
1671 table = &blues->normal_top;
1672 blue_count = table->count;
1673 zone = table->zones;
1674
1675 for ( ; blue_count > 0; blue_count--, zone++ )
1676 {
1677 FT_Pos delta = y - zone->org_bottom;
1678
1679
1680 if ( delta < -blues->blue_fuzz )
1681 break;
1682
1683 if ( y <= zone->org_top + blues->blue_fuzz )
1684 if ( blues->no_overshoots || delta <= blues->blue_threshold )
1685 {
1686 point->cur_u = zone->cur_bottom;
1687 psh_point_set_strong( point );
1688 psh_point_set_fitted( point );
1689 }
1690 }
1691
1692 /* look up bottom zones */
1693 table = &blues->normal_bottom;
1694 blue_count = table->count;
1695 zone = table->zones + blue_count - 1;
1696
1697 for ( ; blue_count > 0; blue_count--, zone-- )
1698 {
1699 FT_Pos delta = zone->org_top - y;
1700
1701
1702 if ( delta < -blues->blue_fuzz )
1703 break;
1704
1705 if ( y >= zone->org_bottom - blues->blue_fuzz )
1706 if ( blues->no_overshoots || delta < blues->blue_threshold )
1707 {
1708 point->cur_u = zone->cur_top;
1709 psh_point_set_strong( point );
1710 psh_point_set_fitted( point );
1711 }
1712 }
1713 }
1714 }
1715
1716
1717 /* interpolate strong points with the help of hinted coordinates */
1718 static void
1719 psh_glyph_interpolate_strong_points( PSH_Glyph glyph,
1720 FT_Int dimension )
1721 {
1722 PSH_Dimension dim = &glyph->globals->dimension[dimension];
1723 FT_Fixed scale = dim->scale_mult;
1724
1725 FT_UInt count = glyph->num_points;
1726 PSH_Point point = glyph->points;
1727
1728
1729 for ( ; count > 0; count--, point++ )
1730 {
1731 PSH_Hint hint = point->hint;
1732
1733
1734 if ( hint )
1735 {
1736 FT_Pos delta;
1737
1738
1739 if ( psh_point_is_edge_min( point ) )
1740 point->cur_u = hint->cur_pos;
1741
1742 else if ( psh_point_is_edge_max( point ) )
1743 point->cur_u = hint->cur_pos + hint->cur_len;
1744
1745 else
1746 {
1747 delta = point->org_u - hint->org_pos;
1748
1749 if ( delta <= 0 )
1750 point->cur_u = hint->cur_pos + FT_MulFix( delta, scale );
1751
1752 else if ( delta >= hint->org_len )
1753 point->cur_u = hint->cur_pos + hint->cur_len +
1754 FT_MulFix( delta - hint->org_len, scale );
1755
1756 else /* hint->org_len > 0 */
1757 point->cur_u = hint->cur_pos +
1758 FT_MulDiv( delta, hint->cur_len,
1759 hint->org_len );
1760 }
1761 psh_point_set_fitted( point );
1762 }
1763 }
1764 }
1765
1766
1767#define PSH_MAX_STRONG_INTERNAL 16
1768
1769 static void
1770 psh_glyph_interpolate_normal_points( PSH_Glyph glyph,
1771 FT_Int dimension )
1772 {
1773
1774#if 1
1775 /* first technique: a point is strong if it is a local extremum */
1776
1777 PSH_Dimension dim = &glyph->globals->dimension[dimension];
1778 FT_Fixed scale = dim->scale_mult;
1779 FT_Memory memory = glyph->memory;
1780
1781 PSH_Point* strongs = NULL;
1782 PSH_Point strongs_0[PSH_MAX_STRONG_INTERNAL];
1783 FT_UInt num_strongs = 0;
1784
1785 PSH_Point points = glyph->points;
1786 PSH_Point points_end = points + glyph->num_points;
1787 PSH_Point point;
1788
1789
1790 /* first count the number of strong points */
1791 for ( point = points; point < points_end; point++ )
1792 {
1793 if ( psh_point_is_strong( point ) )
1794 num_strongs++;
1795 }
1796
1797 if ( num_strongs == 0 ) /* nothing to do here */
1798 return;
1799
1800 /* allocate an array to store a list of points, */
1801 /* stored in increasing org_u order */
1802 if ( num_strongs <= PSH_MAX_STRONG_INTERNAL )
1803 strongs = strongs_0;
1804 else
1805 {
1806 FT_Error error;
1807
1808
1809 if ( FT_NEW_ARRAY( strongs, num_strongs ) )
1810 return;
1811 }
1812
1813 num_strongs = 0;
1814 for ( point = points; point < points_end; point++ )
1815 {
1816 PSH_Point* insert;
1817
1818
1819 if ( !psh_point_is_strong( point ) )
1820 continue;
1821
1822 for ( insert = strongs + num_strongs; insert > strongs; insert-- )
1823 {
1824 if ( insert[-1]->org_u <= point->org_u )
1825 break;
1826
1827 insert[0] = insert[-1];
1828 }
1829 insert[0] = point;
1830 num_strongs++;
1831 }
1832
1833 /* now try to interpolate all normal points */
1834 for ( point = points; point < points_end; point++ )
1835 {
1836 if ( psh_point_is_strong( point ) )
1837 continue;
1838
1839 /* sometimes, some local extrema are smooth points */
1840 if ( psh_point_is_smooth( point ) )
1841 {
1842 if ( point->dir_in == PSH_DIR_NONE ||
1843 point->dir_in != point->dir_out )
1844 continue;
1845
1846 if ( !psh_point_is_extremum( point ) &&
1847 !psh_point_is_inflex( point ) )
1848 continue;
1849
1850 point->flags &= ~PSH_POINT_SMOOTH;
1851 }
1852
1853 /* find best enclosing point coordinates then interpolate */
1854 {
1855 PSH_Point before, after;
1856 FT_UInt nn;
1857
1858
1859 for ( nn = 0; nn < num_strongs; nn++ )
1860 if ( strongs[nn]->org_u > point->org_u )
1861 break;
1862
1863 if ( nn == 0 ) /* point before the first strong point */
1864 {
1865 after = strongs[0];
1866
1867 point->cur_u = after->cur_u +
1868 FT_MulFix( point->org_u - after->org_u,
1869 scale );
1870 }
1871 else
1872 {
1873 before = strongs[nn - 1];
1874
1875 for ( nn = num_strongs; nn > 0; nn-- )
1876 if ( strongs[nn - 1]->org_u < point->org_u )
1877 break;
1878
1879 if ( nn == num_strongs ) /* point is after last strong point */
1880 {
1881 before = strongs[nn - 1];
1882
1883 point->cur_u = before->cur_u +
1884 FT_MulFix( point->org_u - before->org_u,
1885 scale );
1886 }
1887 else
1888 {
1889 FT_Pos u;
1890
1891
1892 after = strongs[nn];
1893
1894 /* now interpolate point between before and after */
1895 u = point->org_u;
1896
1897 if ( u == before->org_u )
1898 point->cur_u = before->cur_u;
1899
1900 else if ( u == after->org_u )
1901 point->cur_u = after->cur_u;
1902
1903 else
1904 point->cur_u = before->cur_u +
1905 FT_MulDiv( u - before->org_u,
1906 after->cur_u - before->cur_u,
1907 after->org_u - before->org_u );
1908 }
1909 }
1910 psh_point_set_fitted( point );
1911 }
1912 }
1913
1914 if ( strongs != strongs_0 )
1915 FT_FREE( strongs );
1916
1917#endif /* 1 */
1918
1919 }
1920
1921
1922 /* interpolate other points */
1923 static void
1924 psh_glyph_interpolate_other_points( PSH_Glyph glyph,
1925 FT_Int dimension )
1926 {
1927 PSH_Dimension dim = &glyph->globals->dimension[dimension];
1928 FT_Fixed scale = dim->scale_mult;
1929 FT_Fixed delta = dim->scale_delta;
1930 PSH_Contour contour = glyph->contours;
1931 FT_UInt num_contours = glyph->num_contours;
1932
1933
1934 for ( ; num_contours > 0; num_contours--, contour++ )
1935 {
1936 PSH_Point start = contour->start;
1937 PSH_Point first, next, point;
1938 FT_UInt fit_count;
1939
1940
1941 /* count the number of strong points in this contour */
1942 next = start + contour->count;
1943 fit_count = 0;
1944 first = NULL;
1945
1946 for ( point = start; point < next; point++ )
1947 if ( psh_point_is_fitted( point ) )
1948 {
1949 if ( !first )
1950 first = point;
1951
1952 fit_count++;
1953 }
1954
1955 /* if there are less than 2 fitted points in the contour, we */
1956 /* simply scale and eventually translate the contour points */
1957 if ( fit_count < 2 )
1958 {
1959 if ( fit_count == 1 )
1960 delta = first->cur_u - FT_MulFix( first->org_u, scale );
1961
1962 for ( point = start; point < next; point++ )
1963 if ( point != first )
1964 point->cur_u = FT_MulFix( point->org_u, scale ) + delta;
1965
1966 goto Next_Contour;
1967 }
1968
1969 /* there are more than 2 strong points in this contour; we */
1970 /* need to interpolate weak points between them */
1971 start = first;
1972 do
1973 {
1974 /* skip consecutive fitted points */
1975 for (;;)
1976 {
1977 next = first->next;
1978 if ( next == start )
1979 goto Next_Contour;
1980
1981 if ( !psh_point_is_fitted( next ) )
1982 break;
1983
1984 first = next;
1985 }
1986
1987 /* find next fitted point after unfitted one */
1988 for (;;)
1989 {
1990 next = next->next;
1991 if ( psh_point_is_fitted( next ) )
1992 break;
1993 }
1994
1995 /* now interpolate between them */
1996 {
1997 FT_Pos org_a, org_ab, cur_a, cur_ab;
1998 FT_Pos org_c, org_ac, cur_c;
1999 FT_Fixed scale_ab;
2000
2001
2002 if ( first->org_u <= next->org_u )
2003 {
2004 org_a = first->org_u;
2005 cur_a = first->cur_u;
2006 org_ab = next->org_u - org_a;
2007 cur_ab = next->cur_u - cur_a;
2008 }
2009 else
2010 {
2011 org_a = next->org_u;
2012 cur_a = next->cur_u;
2013 org_ab = first->org_u - org_a;
2014 cur_ab = first->cur_u - cur_a;
2015 }
2016
2017 scale_ab = 0x10000L;
2018 if ( org_ab > 0 )
2019 scale_ab = FT_DivFix( cur_ab, org_ab );
2020
2021 point = first->next;
2022 do
2023 {
2024 org_c = point->org_u;
2025 org_ac = org_c - org_a;
2026
2027 if ( org_ac <= 0 )
2028 {
2029 /* on the left of the interpolation zone */
2030 cur_c = cur_a + FT_MulFix( org_ac, scale );
2031 }
2032 else if ( org_ac >= org_ab )
2033 {
2034 /* on the right on the interpolation zone */
2035 cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale );
2036 }
2037 else
2038 {
2039 /* within the interpolation zone */
2040 cur_c = cur_a + FT_MulFix( org_ac, scale_ab );
2041 }
2042
2043 point->cur_u = cur_c;
2044
2045 point = point->next;
2046
2047 } while ( point != next );
2048 }
2049
2050 /* keep going until all points in the contours have been processed */
2051 first = next;
2052
2053 } while ( first != start );
2054
2055 Next_Contour:
2056 ;
2057 }
2058 }
2059
2060
2061 /*************************************************************************/
2062 /*************************************************************************/
2063 /***** *****/
2064 /***** HIGH-LEVEL INTERFACE *****/
2065 /***** *****/
2066 /*************************************************************************/
2067 /*************************************************************************/
2068
2069 FT_Error
2070 ps_hints_apply( PS_Hints ps_hints,
2071 FT_Outline* outline,
2072 PSH_Globals globals,
2073 FT_Render_Mode hint_mode )
2074 {
2075 PSH_GlyphRec glyphrec;
2076 PSH_Glyph glyph = &glyphrec;
2077 FT_Error error;
2078#ifdef DEBUG_HINTER
2079 FT_Memory memory;
2080#endif
2081 FT_Int dimension;
2082
2083
2084 /* something to do? */
2085 if ( outline->n_points == 0 || outline->n_contours == 0 )
2086 return FT_Err_Ok;
2087
2088#ifdef DEBUG_HINTER
2089
2090 memory = globals->memory;
2091
2092 if ( ps_debug_glyph )
2093 {
2094 psh_glyph_done( ps_debug_glyph );
2095 FT_FREE( ps_debug_glyph );
2096 }
2097
2098 if ( FT_NEW( glyph ) )
2099 return error;
2100
2101 ps_debug_glyph = glyph;
2102
2103#endif /* DEBUG_HINTER */
2104
2105 error = psh_glyph_init( glyph, outline, ps_hints, globals );
2106 if ( error )
2107 goto Exit;
2108
2109 /* try to optimize the y_scale so that the top of non-capital letters
2110 * is aligned on a pixel boundary whenever possible
2111 */
2112 {
2113 PSH_Dimension dim_x = &glyph->globals->dimension[0];
2114 PSH_Dimension dim_y = &glyph->globals->dimension[1];
2115
2116 FT_Fixed x_scale = dim_x->scale_mult;
2117 FT_Fixed y_scale = dim_y->scale_mult;
2118
2119 FT_Fixed old_x_scale = x_scale;
2120 FT_Fixed old_y_scale = y_scale;
2121
2122 FT_Fixed scaled;
2123 FT_Fixed fitted;
2124
2125 FT_Bool rescale = FALSE;
2126
2127
2128 scaled = FT_MulFix( globals->blues.normal_top.zones->org_ref, y_scale );
2129 fitted = FT_PIX_ROUND( scaled );
2130
2131 if ( fitted != 0 && scaled != fitted )
2132 {
2133 rescale = TRUE;
2134
2135 y_scale = FT_MulDiv( y_scale, fitted, scaled );
2136
2137 if ( fitted < scaled )
2138 x_scale -= x_scale / 50;
2139
2140 psh_globals_set_scale( glyph->globals, x_scale, y_scale, 0, 0 );
2141 }
2142
2143 glyph->do_horz_hints = 1;
2144 glyph->do_vert_hints = 1;
2145
2146 glyph->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
2147 hint_mode == FT_RENDER_MODE_LCD );
2148
2149 glyph->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
2150 hint_mode == FT_RENDER_MODE_LCD_V );
2151
2152 glyph->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
2153
2154 for ( dimension = 0; dimension < 2; dimension++ )
2155 {
2156 /* load outline coordinates into glyph */
2157 psh_glyph_load_points( glyph, dimension );
2158
2159 /* compute local extrema */
2160 psh_glyph_compute_extrema( glyph );
2161
2162 /* compute aligned stem/hints positions */
2163 psh_hint_table_align_hints( &glyph->hint_tables[dimension],
2164 glyph->globals,
2165 dimension,
2166 glyph );
2167
2168 /* find strong points, align them, then interpolate others */
2169 psh_glyph_find_strong_points( glyph, dimension );
2170 if ( dimension == 1 )
2171 psh_glyph_find_blue_points( &globals->blues, glyph );
2172 psh_glyph_interpolate_strong_points( glyph, dimension );
2173 psh_glyph_interpolate_normal_points( glyph, dimension );
2174 psh_glyph_interpolate_other_points( glyph, dimension );
2175
2176 /* save hinted coordinates back to outline */
2177 psh_glyph_save_points( glyph, dimension );
2178
2179 if ( rescale )
2180 psh_globals_set_scale( glyph->globals,
2181 old_x_scale, old_y_scale, 0, 0 );
2182 }
2183 }
2184
2185 Exit:
2186
2187#ifndef DEBUG_HINTER
2188 psh_glyph_done( glyph );
2189#endif
2190
2191 return error;
2192 }
2193
2194
2195/* END */
2196