blob: 95f167a66dd32344ad653f0068ffbeafd081ea0b [file] [log] [blame]
David Turnera83bc082001-10-18 11:38:43 +00001#include <ft2build.h>
2#include FT_INTERNAL_OBJECTS_H
3#include FT_INTERNAL_DEBUG_H
4#include "pshalgo2.h"
5
6
7#ifdef DEBUG_HINTER
8 extern PSH2_Hint_Table ps2_debug_hint_table = 0;
9 extern PSH2_HintFunc ps2_debug_hint_func = 0;
10 extern PSH2_Glyph ps2_debug_glyph = 0;
11#endif
12
13 /************************************************************************/
14 /************************************************************************/
15 /***** *****/
16 /***** BASIC HINTS RECORDINGS *****/
17 /***** *****/
18 /************************************************************************/
19 /************************************************************************/
20
21 /* return true iff two stem hints overlap */
22 static FT_Int
23 psh2_hint_overlap( PSH2_Hint hint1,
24 PSH2_Hint hint2 )
25 {
26 return ( hint1->org_pos + hint1->org_len >= hint2->org_pos &&
27 hint2->org_pos + hint2->org_len >= hint1->org_pos );
28 }
29
30
31 /* destroy hints table */
32 static void
33 psh2_hint_table_done( PSH2_Hint_Table table,
34 FT_Memory memory )
35 {
36 FREE( table->zones );
37 table->num_zones = 0;
38 table->zone = 0;
39
40 FREE( table->sort );
41 FREE( table->hints );
42 table->num_hints = 0;
43 table->max_hints = 0;
44 table->sort_global = 0;
45 }
46
47
48 /* deactivate all hints in a table */
49 static void
50 psh2_hint_table_deactivate( PSH2_Hint_Table table )
51 {
52 FT_UInt count = table->max_hints;
53 PSH2_Hint hint = table->hints;
54
55 for ( ; count > 0; count--, hint++ )
56 {
57 psh2_hint_deactivate(hint);
58 hint->order = -1;
59 }
60 }
61
62
63 /* internal function used to record a new hint */
64 static void
65 psh2_hint_table_record( PSH2_Hint_Table table,
66 FT_UInt index )
67 {
68 PSH2_Hint hint = table->hints + index;
69
70 if ( index >= table->max_hints )
71 {
72 FT_ERROR(( "%s.activate: invalid hint index %d\n", index ));
73 return;
74 }
75
76 /* ignore active hints */
77 if ( psh2_hint_is_active(hint) )
78 return;
79
80 psh2_hint_activate(hint);
81
82 /* now scan the current active hint set in order to determine */
83 /* if we're overlapping with another segment.. */
84 {
85 PSH2_Hint* sorted = table->sort_global;
86 FT_UInt count = table->num_hints;
87 PSH2_Hint hint2;
88
89 hint->parent = 0;
90 for ( ; count > 0; count--, sorted++ )
91 {
92 hint2 = sorted[0];
93
94 if ( psh2_hint_overlap( hint, hint2 ) )
95 {
96 hint->parent = hint2;
97 break;
98 }
99 }
100 }
101
102 if ( table->num_hints < table->max_hints )
103 table->sort_global[ table->num_hints++ ] = hint;
104 else
105 {
106 FT_ERROR(( "%s.activate: too many sorted hints !! BUG !!\n",
107 "ps.fitter" ));
108 }
109 }
110
111
112 static void
113 psh2_hint_table_record_mask( PSH2_Hint_Table table,
114 PS_Mask hint_mask )
115 {
116 FT_Int mask = 0, val = 0;
117 FT_Byte* cursor = hint_mask->bytes;
118 FT_UInt index, limit;
119
120 limit = hint_mask->num_bits;
121
David Turnera83bc082001-10-18 11:38:43 +0000122 for ( index = 0; index < limit; index++ )
123 {
124 if ( mask == 0 )
125 {
126 val = *cursor++;
127 mask = 0x80;
128 }
129
130 if ( val & mask )
131 psh2_hint_table_record( table, index );
132
133 mask >>= 1;
134 }
135 }
136
137
138 /* create hints table */
139 static FT_Error
140 psh2_hint_table_init( PSH2_Hint_Table table,
141 PS_Hint_Table hints,
142 PS_Mask_Table hint_masks,
143 PS_Mask_Table counter_masks,
144 FT_Memory memory )
145 {
146 FT_UInt count = hints->num_hints;
147 FT_Error error;
148
149 FT_UNUSED(counter_masks);
150
151 /* allocate our tables */
152 if ( ALLOC_ARRAY( table->sort, 2*count, PSH2_Hint ) ||
153 ALLOC_ARRAY( table->hints, count, PSH2_HintRec ) ||
154 ALLOC_ARRAY( table->zones, 2*count+1, PSH2_ZoneRec ) )
155 goto Exit;
156
157 table->max_hints = count;
158 table->sort_global = table->sort + count;
159 table->num_hints = 0;
160 table->num_zones = 0;
161 table->zone = 0;
162
163 /* now, initialise the "hints" array */
164 {
165 PSH2_Hint write = table->hints;
166 PS_Hint read = hints->hints;
167
168 for ( ; count > 0; count--, write++, read++ )
169 {
170 write->org_pos = read->pos;
171 write->org_len = read->len;
172 write->flags = read->flags;
173 }
174 }
175
176 /* we now need to determine the initial "parent" stems, first */
177 /* activate the hints that are given by the initial hint masks */
178 if ( hint_masks )
179 {
180 FT_UInt count = hint_masks->num_masks;
181 PS_Mask mask = hint_masks->masks;
182
183 table->hint_masks = hint_masks;
184
185 for ( ; count > 0; count--, mask++ )
186 psh2_hint_table_record_mask( table, mask );
187 }
188
189 /* now, do a linear parse in case some hints were left alone */
190 if ( table->num_hints != table->max_hints )
191 {
192 FT_UInt index, count;
193
194 FT_ERROR(( "%s.init: missing/incorrect hint masks !!\n" ));
195 count = table->max_hints;
196 for ( index = 0; index < count; index++ )
197 psh2_hint_table_record( table, index );
198 }
199
200 Exit:
201 return error;
202 }
203
204
205
206 static void
207 psh2_hint_table_activate_mask( PSH2_Hint_Table table,
208 PS_Mask hint_mask )
209 {
210 FT_Int mask = 0, val = 0;
211 FT_Byte* cursor = hint_mask->bytes;
212 FT_UInt index, limit, count;
213
214 limit = hint_mask->num_bits;
215 count = 0;
216
217 psh2_hint_table_deactivate( table );
218
219 for ( index = 0; index < limit; index++ )
220 {
221 if ( mask == 0 )
222 {
223 val = *cursor++;
224 mask = 0x80;
225 }
226
227 if ( val & mask )
228 {
229 PSH2_Hint hint = &table->hints[index];
230
231 if ( !psh2_hint_is_active(hint) )
232 {
David Turnera83bc082001-10-18 11:38:43 +0000233 FT_UInt count2;
David Turnera83bc082001-10-18 11:38:43 +0000234
235#if 0
David Turner7e4b52d2001-10-19 09:17:49 +0000236 PSH2_Hint* sort = table->sort;
David Turnerf373e2d2001-10-18 13:20:01 +0000237 PSH2_Hint hint2;
238
David Turnera83bc082001-10-18 11:38:43 +0000239 for ( count2 = count; count2 > 0; count2--, sort++ )
240 {
241 hint2 = sort[0];
242 if ( psh2_hint_overlap( hint, hint2 ) )
243 {
244 FT_ERROR(( "%s.activate_mask: found overlapping hints\n",
245 "psf.hint" ));
246 }
247 }
248#else
249 count2 = 0;
250#endif
251
252 if ( count2 == 0 )
253 {
254 psh2_hint_activate( hint );
255 if ( count < table->max_hints )
256 table->sort[count++] = hint;
257 else
258 {
259 FT_ERROR(( "%s.activate_mask: too many active hints\n",
260 "psf.hint" ));
261 }
262 }
263 }
264 }
265
266 mask >>= 1;
267 }
268 table->num_hints = count;
269
270 /* now, sort the hints, they're guaranteed to not overlap */
271 /* so we can compare their "org_pos" field directly.. */
272 {
273 FT_Int i1, i2;
274 PSH2_Hint hint1, hint2;
275 PSH2_Hint* sort = table->sort;
276
277 /* a simple bubble sort will do, since in 99% of cases, the hints */
278 /* will be already sorted.. and the sort will be linear */
279 for ( i1 = 1; i1 < (FT_Int)count; i1++ )
280 {
281 hint1 = sort[i1];
282 for ( i2 = i1-1; i2 >= 0; i2-- )
283 {
284 hint2 = sort[i2];
285 if ( hint2->org_pos < hint1->org_pos )
286 break;
287
288 sort[i1] = hint2;
289 sort[i2] = hint1;
290 }
291 }
292 }
293 }
294
295
296
297
298
299
300
301 /************************************************************************/
302 /************************************************************************/
303 /***** *****/
304 /***** HINTS GRID-FITTING AND OPTIMISATION *****/
305 /***** *****/
306 /************************************************************************/
307 /************************************************************************/
308
309#ifdef DEBUG_HINTER
310 static void
David Turner9d7e5e82001-10-29 17:22:12 +0000311 ps2_simple_scale( PSH2_Hint_Table table,
David Turnera83bc082001-10-18 11:38:43 +0000312 FT_Fixed scale,
313 FT_Fixed delta,
David Turner12d40da2001-10-21 15:41:11 +0000314 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +0000315 {
316 PSH2_Hint hint;
317 FT_UInt count;
318
319 for ( count = 0; count < table->max_hints; count++ )
320 {
321 hint = table->hints + count;
322
323 hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta;
324 hint->cur_len = FT_MulFix( hint->org_len, scale );
325
326 if (ps2_debug_hint_func)
327 ps2_debug_hint_func( hint, vertical );
328 }
329 }
330#endif
331
332
333 static void
334 psh2_hint_align( PSH2_Hint hint,
335 PSH_Globals globals,
David Turner12d40da2001-10-21 15:41:11 +0000336 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +0000337 {
338 PSH_Dimension dim = &globals->dimension[vertical];
339 FT_Fixed scale = dim->scale_mult;
340 FT_Fixed delta = dim->scale_delta;
341
342 if ( !psh2_hint_is_fitted(hint) )
343 {
344 FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta;
345 FT_Pos len = FT_MulFix( hint->org_len, scale );
346
347 FT_Pos fit_center;
348 FT_Pos fit_len;
349
350 PSH_AlignmentRec align;
351
352 /* compute fitted width/height */
353 fit_len = 0;
354 if ( hint->org_len )
355 {
356 fit_len = psh_dimension_snap_width( dim, hint->org_len );
357 if ( fit_len < 64 )
358 fit_len = 64;
359 else
360 fit_len = (fit_len + 32 ) & -64;
361 }
362
363 hint->cur_len = fit_len;
364
365 /* check blue zones for horizontal stems */
366 align.align = 0;
David Turner12d40da2001-10-21 15:41:11 +0000367 align.align_bot = align.align_top = 0;
368
David Turnera83bc082001-10-18 11:38:43 +0000369 if (!vertical)
370 {
371 psh_blues_snap_stem( &globals->blues,
372 hint->org_pos + hint->org_len,
373 hint->org_pos,
374 &align );
375 }
376
377 switch (align.align)
378 {
379 case PSH_BLUE_ALIGN_TOP:
380 {
381 /* the top of the stem is aligned against a blue zone */
382 hint->cur_pos = align.align_top - fit_len;
383 break;
384 }
385
386 case PSH_BLUE_ALIGN_BOT:
387 {
388 /* the bottom of the stem is aligned against a blue zone */
389 hint->cur_pos = align.align_bot;
390 break;
391 }
392
393 case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
394 {
395 /* both edges of the stem are aligned against blue zones */
396 hint->cur_pos = align.align_bot;
397 hint->cur_len = align.align_top - align.align_bot;
398 break;
399 }
400
401 default:
402 {
403 PSH2_Hint parent = hint->parent;
404
405 if ( parent )
406 {
407 FT_Pos par_org_center, par_cur_center;
408 FT_Pos cur_org_center, cur_delta;
409
410 /* ensure that parent is already fitted */
411 if ( !psh2_hint_is_fitted(parent) )
412 psh2_hint_align( parent, globals, vertical );
413
414 par_org_center = parent->org_pos + (parent->org_len/2);
415 par_cur_center = parent->cur_pos + (parent->cur_len/2);
416 cur_org_center = hint->org_pos + (hint->org_len/2);
417
418 cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
419#if 0
420 if ( cur_delta >= 0 )
421 cur_delta = (cur_delta+16) & -64;
422 else
423 cur_delta = -((-cur_delta+16) & -64);
424#endif
425 pos = par_cur_center + cur_delta - (len >> 1);
426 }
427
428 /* normal processing */
429 if ( (fit_len/64) & 1 )
430 {
431 /* odd number of pixels */
432 fit_center = ((pos + (len >> 1)) & -64) + 32;
433 }
434 else
435 {
436 /* even number of pixels */
437 fit_center = (pos + (len >> 1) + 32) & -64;
438 }
439
440 hint->cur_pos = fit_center - (fit_len >> 1);
441 }
442 }
443
444 psh2_hint_set_fitted(hint);
445
446#ifdef DEBUG_HINTER
447 if (ps2_debug_hint_func)
448 ps2_debug_hint_func( hint, vertical );
449#endif
450 }
451 }
452
453
454 static void
455 psh2_hint_table_align_hints( PSH2_Hint_Table table,
456 PSH_Globals globals,
David Turner12d40da2001-10-21 15:41:11 +0000457 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +0000458 {
David Turnera83bc082001-10-18 11:38:43 +0000459 PSH2_Hint hint;
460 FT_UInt count;
461
462#ifdef DEBUG_HINTER
David Turnera11cd4e2001-10-26 07:21:38 +0000463 PSH_Dimension dim = &globals->dimension[vertical];
464 FT_Fixed scale = dim->scale_mult;
465 FT_Fixed delta = dim->scale_delta;
466
David Turnera83bc082001-10-18 11:38:43 +0000467 if ( ps_debug_no_vert_hints && vertical )
468 {
David Turner9d7e5e82001-10-29 17:22:12 +0000469 ps2_simple_scale( table, scale, delta, vertical );
David Turnera83bc082001-10-18 11:38:43 +0000470 return;
471 }
472
473 if ( ps_debug_no_horz_hints && !vertical )
474 {
David Turner9d7e5e82001-10-29 17:22:12 +0000475 ps2_simple_scale( table, scale, delta, vertical );
David Turnera83bc082001-10-18 11:38:43 +0000476 return;
477 }
478#endif
479
480 hint = table->hints;
481 count = table->max_hints;
482 for ( ; count > 0; count--, hint++ )
483 psh2_hint_align( hint, globals, vertical );
484 }
485
486
487 /************************************************************************/
488 /************************************************************************/
489 /***** *****/
490 /***** POINTS INTERPOLATION ROUTINES *****/
491 /***** *****/
492 /************************************************************************/
493 /************************************************************************/
494
495#define PSH2_ZONE_MIN -3200000
496#define PSH2_ZONE_MAX +3200000
497
498
499#define xxDEBUG_ZONES
500
501#ifdef DEBUG_ZONES
502
503#include <stdio.h>
504
505 static void
506 print_zone( PSH2_Zone zone )
507 {
508 printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n",
509 zone->scale/65536.0,
510 zone->delta/64.0,
511 zone->min,
512 zone->max );
513 }
514
515#else
516# define print_zone(x) do { } while (0)
517#endif
518
David Turner7e4b52d2001-10-19 09:17:49 +0000519#if 0
David Turnera83bc082001-10-18 11:38:43 +0000520 /* setup interpolation zones once the hints have been grid-fitted */
521 /* by the optimizer.. */
522 static void
523 psh2_hint_table_setup_zones( PSH2_Hint_Table table,
524 FT_Fixed scale,
525 FT_Fixed delta )
526 {
527 FT_UInt count;
528 PSH2_Zone zone;
529 PSH2_Hint *sort, hint, hint2;
530
531 zone = table->zones;
532
533 /* special case, no hints defined */
534 if ( table->num_hints == 0 )
535 {
536 zone->scale = scale;
537 zone->delta = delta;
538 zone->min = PSH2_ZONE_MIN;
539 zone->max = PSH2_ZONE_MAX;
540
541 table->num_zones = 1;
542 table->zone = zone;
543 return;
544 }
545
546 /* the first zone is before the first hint */
547 /* x' = (x-x0)*s + x0' = x*s + ( x0' - x0*s ) */
548 sort = table->sort;
549 hint = sort[0];
550
551 zone->scale = scale;
552 zone->delta = hint->cur_pos - FT_MulFix( hint->org_pos, scale );
553 zone->min = PSH2_ZONE_MIN;
554 zone->max = hint->org_pos;
555
556 print_zone( zone );
557
558 zone++;
559
560 for ( count = table->num_hints; count > 0; count-- )
561 {
562 FT_Fixed scale2;
563
564 if ( hint->org_len > 0 )
565 {
566 /* setup a zone for inner-stem interpolation */
567 /* (x' - x0') = (x - x0)*(x1'-x0')/(x1-x0) */
568 /* x' = x*s2 + x0' - x0*s2 */
569
570 scale2 = FT_DivFix( hint->cur_len, hint->org_len );
571 zone->scale = scale2;
572 zone->min = hint->org_pos;
573 zone->max = hint->org_pos + hint->org_len;
574 zone->delta = hint->cur_pos - FT_MulFix( zone->min, scale2 );
575
576 print_zone( zone );
577
578 zone++;
579 }
580
581 if ( count == 1 )
582 break;
583
584 sort++;
585 hint2 = sort[0];
586
587 /* setup zone for inter-stem interpolation */
588 /* (x'-x1') = (x-x1)*(x2'-x1')/(x2-x1) */
589 /* x' = x*s3 + x1' - x1*s3 */
590 scale2 = FT_DivFix( hint2->cur_pos - (hint->cur_pos + hint->cur_len),
591 hint2->org_pos - (hint->org_pos + hint->org_len) );
592 zone->scale = scale2;
593 zone->min = hint->org_pos + hint->org_len;
594 zone->max = hint2->org_pos;
595 zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale2 );
596
597 print_zone( zone );
598
599 zone++;
600
601 hint = hint2;
602 }
603
604 /* the last zone */
605 zone->scale = scale;
606 zone->min = hint->org_pos + hint->org_len;
607 zone->max = PSH2_ZONE_MAX;
608 zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale );
609
610 print_zone( zone );
611
612 zone++;
613
614 table->num_zones = zone - table->zones;
615 table->zone = table->zones;
616 }
David Turner7e4b52d2001-10-19 09:17:49 +0000617#endif
David Turnera83bc082001-10-18 11:38:43 +0000618
David Turner12d40da2001-10-21 15:41:11 +0000619#if 0
David Turnera83bc082001-10-18 11:38:43 +0000620 /* tune a single coordinate with the current interpolation zones */
621 static FT_Pos
622 psh2_hint_table_tune_coord( PSH2_Hint_Table table,
623 FT_Int coord )
624 {
625 PSH2_Zone zone;
626
627 zone = table->zone;
628
629 if ( coord < zone->min )
630 {
631 do
632 {
633 if ( zone == table->zones )
634 break;
635
636 zone--;
637 }
638 while ( coord < zone->min );
639 table->zone = zone;
640 }
641 else if ( coord > zone->max )
642 {
643 do
644 {
645 if ( zone == table->zones + table->num_zones - 1 )
646 break;
647
648 zone++;
649 }
650 while ( coord > zone->max );
651 table->zone = zone;
652 }
653
654 return FT_MulFix( coord, zone->scale ) + zone->delta;
655 }
David Turner12d40da2001-10-21 15:41:11 +0000656#endif
David Turnera83bc082001-10-18 11:38:43 +0000657
658#if 0
659 /* tune a given outline with current interpolation zones */
660 /* the function only works in a single dimension.. */
661 static void
662 psh2_hint_table_tune_outline( PSH2_Hint_Table table,
663 FT_Outline* outline,
664 PSH_Globals globals,
David Turner12d40da2001-10-21 15:41:11 +0000665 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +0000666
667 {
668 FT_UInt count, first, last;
669 PS_Mask_Table hint_masks = table->hint_masks;
670 PS_Mask mask;
671 PSH_Dimension dim = &globals->dimension[vertical];
672 FT_Fixed scale = dim->scale_mult;
673 FT_Fixed delta = dim->scale_delta;
674
675 if ( hint_masks && hint_masks->num_masks > 0 )
676 {
677 first = 0;
678 mask = hint_masks->masks;
679 count = hint_masks->num_masks;
680 for ( ; count > 0; count--, mask++ )
681 {
682 last = mask->end_point;
683
684 if ( last > first )
685 {
686 FT_Vector* vec;
687 FT_Int count2;
688
689 psh2_hint_table_activate_mask( table, mask );
690 psh2_hint_table_optimize( table, globals, outline, vertical );
691 psh2_hint_table_setup_zones( table, scale, delta );
692 last = mask->end_point;
693
694 vec = outline->points + first;
695 count2 = last - first;
696 for ( ; count2 > 0; count2--, vec++ )
697 {
698 FT_Pos x, *px;
699
700 px = vertical ? &vec->x : &vec->y;
701 x = *px;
702
703 *px = psh2_hint_table_tune_coord( table, (FT_Int)x );
704 }
705 }
706
707 first = last;
708 }
709 }
710 else /* no hints in this glyph, simply scale the outline */
711 {
712 FT_Vector* vec;
713
714 vec = outline->points;
715 count = outline->n_points;
716
717 if ( vertical )
718 {
719 for ( ; count > 0; count--, vec++ )
720 vec->x = FT_MulFix( vec->x, scale ) + delta;
721 }
722 else
723 {
724 for ( ; count > 0; count--, vec++ )
725 vec->y = FT_MulFix( vec->y, scale ) + delta;
726 }
727 }
728 }
729#endif
730
731 /************************************************************************/
732 /************************************************************************/
733 /***** *****/
734 /***** HINTER GLYPH MANAGEMENT *****/
735 /***** *****/
736 /************************************************************************/
737 /************************************************************************/
738
739 static int
740 psh2_point_is_extremum( PSH2_Point point )
741 {
742 PSH2_Point before = point;
743 PSH2_Point after = point;
744 FT_Pos d_before;
745 FT_Pos d_after;
746
747 do
748 {
749 before = before->prev;
750 if ( before == point )
751 return 0;
752
753 d_before = before->org_u - point->org_u;
754 }
755 while ( d_before == 0 );
756
757 do
758 {
759 after = after->next;
760 if ( after == point )
761 return 0;
762
763 d_after = after->org_u - point->org_u;
764 }
765 while ( d_after == 0 );
766
767 return ( ( d_before > 0 && d_after > 0 ) ||
768 ( d_before < 0 && d_after < 0 ) );
769 }
770
771
772
773 static void
774 psh2_glyph_done( PSH2_Glyph glyph )
775 {
776 FT_Memory memory = glyph->memory;
777
778 psh2_hint_table_done( &glyph->hint_tables[1], memory );
779 psh2_hint_table_done( &glyph->hint_tables[0], memory );
780
781 FREE( glyph->points );
782 FREE( glyph->contours );
783
784 glyph->num_points = 0;
785 glyph->num_contours = 0;
786
787 glyph->memory = 0;
788 }
789
790
791 static int
792 psh2_compute_dir( FT_Pos dx, FT_Pos dy )
793 {
794 FT_Pos ax, ay;
795 int result = PSH2_DIR_NONE;
796
797 ax = ( dx >= 0 ) ? dx : -dx;
798 ay = ( dy >= 0 ) ? dy : -dy;
799
800 if ( ay*12 < ax )
801 {
802 /* |dy| <<< |dx| means a near-horizontal segment */
803 result = ( dx >= 0 ) ? PSH2_DIR_RIGHT : PSH2_DIR_LEFT;
804 }
805 else if ( ax*12 < ay )
806 {
807 /* |dx| <<< |dy| means a near-vertical segment */
808 result = ( dy >= 0 ) ? PSH2_DIR_UP : PSH2_DIR_DOWN;
809 }
810 return result;
811 }
812
813
814 static FT_Error
815 psh2_glyph_init( PSH2_Glyph glyph,
816 FT_Outline* outline,
817 PS_Hints ps_hints,
818 PSH_Globals globals )
819 {
820 FT_Error error;
821 FT_Memory memory;
822
823 /* clear all fields */
824 memset( glyph, 0, sizeof(*glyph) );
825
826 memory = globals->memory;
827
828 /* allocate and setup points + contours arrays */
829 if ( ALLOC_ARRAY( glyph->points, outline->n_points, PSH2_PointRec ) ||
830 ALLOC_ARRAY( glyph->contours, outline->n_contours, PSH2_ContourRec ) )
831 goto Exit;
832
833 glyph->num_points = outline->n_points;
834 glyph->num_contours = outline->n_contours;
835
836 {
837 FT_UInt first = 0, next, n;
838 PSH2_Point points = glyph->points;
839 PSH2_Contour contour = glyph->contours;
840
841 for ( n = 0; n < glyph->num_contours; n++ )
842 {
843 FT_Int count;
844 PSH2_Point point;
845
846 next = outline->contours[n] + 1;
847 count = next - first;
848
849 contour->start = points + first;
850 contour->count = (FT_UInt)count;
851
852 if ( count > 0 )
853 {
854 point = points + first;
855
856 point->prev = points + next - 1;
857 point->contour = contour;
858 for ( ; count > 1; count-- )
859 {
860 point[0].next = point + 1;
861 point[1].prev = point;
862 point++;
863 point->contour = contour;
864 }
865 point->next = points + first;
866 }
867
868 contour++;
869 first = next;
870 }
871 }
872
873 {
874 PSH2_Point points = glyph->points;
875 PSH2_Point point = points;
876 FT_Vector* vec = outline->points;
877 FT_UInt n;
878
879 for ( n = 0; n < glyph->num_points; n++, point++ )
880 {
881 FT_Int n_prev = point->prev - points;
882 FT_Int n_next = point->next - points;
883 FT_Pos dxi, dyi, dxo, dyo;
884
885 if ( !(outline->tags[n] & FT_Curve_Tag_On) )
886 point->flags = PSH2_POINT_OFF;
887
888 dxi = vec[n].x - vec[n_prev].x;
889 dyi = vec[n].y - vec[n_prev].y;
890
David Turner12d40da2001-10-21 15:41:11 +0000891 point->dir_in = (FT_Char) psh2_compute_dir( dxi, dyi );
David Turnera83bc082001-10-18 11:38:43 +0000892
893 dxo = vec[n_next].x - vec[n].x;
894 dyo = vec[n_next].y - vec[n].y;
895
David Turner12d40da2001-10-21 15:41:11 +0000896 point->dir_out = (FT_Char) psh2_compute_dir( dxo, dyo );
David Turnera83bc082001-10-18 11:38:43 +0000897
898 /* detect smooth points */
899 if ( point->flags & PSH2_POINT_OFF )
900 {
901 point->flags |= PSH2_POINT_SMOOTH;
902 }
903 else if ( point->dir_in != PSH2_DIR_NONE ||
904 point->dir_out != PSH2_DIR_NONE )
905 {
906 if ( point->dir_in == point->dir_out )
907 point->flags |= PSH2_POINT_SMOOTH;
908 }
909 else
910 {
911 FT_Angle angle_in, angle_out, diff;
912
913 angle_in = FT_Atan2( dxi, dyi );
914 angle_out = FT_Atan2( dxo, dyo );
915
916 diff = angle_in - angle_out;
917 if ( diff < 0 )
918 diff = -diff;
919
920 if ( diff > FT_ANGLE_PI )
921 diff = FT_ANGLE_2PI - diff;
922
923 if ( (diff < FT_ANGLE_PI/16) )
924 point->flags |= PSH2_POINT_SMOOTH;
925 }
926 }
927 }
928
929 glyph->memory = memory;
930 glyph->outline = outline;
931 glyph->globals = globals;
932
933 /* now deal with hints tables */
934 error = psh2_hint_table_init( &glyph->hint_tables [0],
935 &ps_hints->dimension[0].hints,
936 &ps_hints->dimension[0].masks,
937 &ps_hints->dimension[0].counters,
938 memory );
939 if (error) goto Exit;
940
941 error = psh2_hint_table_init( &glyph->hint_tables [1],
942 &ps_hints->dimension[1].hints,
943 &ps_hints->dimension[1].masks,
944 &ps_hints->dimension[1].counters,
945 memory );
946 if (error) goto Exit;
947
948 Exit:
949 return error;
950 }
951
952
953 /* load outline point coordinates into hinter glyph */
954 static void
955 psh2_glyph_load_points( PSH2_Glyph glyph,
David Turner12d40da2001-10-21 15:41:11 +0000956 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +0000957 {
958 FT_Vector* vec = glyph->outline->points;
959 PSH2_Point point = glyph->points;
960 FT_UInt count = glyph->num_points;
961
962 for ( ; count > 0; count--, point++, vec++ )
963 {
964 point->flags &= PSH2_POINT_OFF | PSH2_POINT_SMOOTH;
965 point->hint = 0;
966 if (vertical)
967 point->org_u = vec->x;
968 else
969 point->org_u = vec->y;
970
971#ifdef DEBUG_HINTER
972 point->org_x = vec->x;
973 point->org_y = vec->y;
974#endif
975 }
976 }
977
978
979 /* save hinted point coordinates back to outline */
980 static void
981 psh2_glyph_save_points( PSH2_Glyph glyph,
David Turner12d40da2001-10-21 15:41:11 +0000982 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +0000983 {
984 FT_UInt n;
985 PSH2_Point point = glyph->points;
986 FT_Vector* vec = glyph->outline->points;
987 char* tags = glyph->outline->tags;
988
989 for ( n = 0; n < glyph->num_points; n++ )
990 {
991 if (vertical)
992 vec[n].x = point->cur_u;
993 else
994 vec[n].y = point->cur_u;
995
996 if ( psh2_point_is_strong(point) )
997 tags[n] |= vertical ? 32 : 64;
998
999#ifdef DEBUG_HINTER
1000 if (vertical)
1001 {
1002 point->cur_x = point->cur_u;
1003 point->flags_x = point->flags;
1004 }
1005 else
1006 {
1007 point->cur_y = point->cur_u;
1008 point->flags_y = point->flags;
1009 }
1010#endif
1011 point++;
1012 }
1013 }
1014
1015
1016 static void
1017 psh2_hint_table_find_strong_point( PSH2_Hint_Table table,
1018 PSH2_Point point,
1019 FT_Int major_dir )
1020 {
1021 PSH2_Hint* sort = table->sort;
1022 FT_UInt num_hints = table->num_hints;
1023
1024 for ( ; num_hints > 0; num_hints--, sort++ )
1025 {
1026 PSH2_Hint hint = sort[0];
1027
1028 if ( ABS(point->dir_in) == major_dir ||
1029 ABS(point->dir_out) == major_dir )
1030 {
1031 FT_Pos d;
1032
1033 d = point->org_u - hint->org_pos;
1034 if ( ABS(d) < 3 )
1035 {
1036 Is_Strong:
1037 psh2_point_set_strong(point);
1038 point->hint = hint;
1039 break;
1040 }
1041
1042 d -= hint->org_len;
1043 if ( ABS(d) < 3 )
1044 goto Is_Strong;
1045 }
1046
1047#if 1
1048 if ( point->org_u >= hint->org_pos &&
1049 point->org_u <= hint->org_pos + hint->org_len &&
1050 psh2_point_is_extremum( point ) )
1051 {
1052 /* attach to hint, but don't mark as strong */
1053 point->hint = hint;
1054 break;
1055 }
1056#endif
1057 }
1058 }
1059
1060
1061
1062 /* find strong points in a glyph */
1063 static void
1064 psh2_glyph_find_strong_points( PSH2_Glyph glyph,
David Turner12d40da2001-10-21 15:41:11 +00001065 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +00001066 {
1067 /* a point is strong if it is located on a stem */
1068 /* edge and has an "in" or "out" tangent to the hint's direction */
1069 {
1070 PSH2_Hint_Table table = &glyph->hint_tables[vertical];
1071 PS_Mask mask = table->hint_masks->masks;
1072 FT_UInt num_masks = table->hint_masks->num_masks;
1073 FT_UInt first = 0;
1074 FT_Int major_dir = vertical ? PSH2_DIR_UP : PSH2_DIR_RIGHT;
David Turnera83bc082001-10-18 11:38:43 +00001075
1076 /* process secondary hints to "selected" points */
1077 if ( num_masks > 1 )
1078 {
1079 mask++;
1080 for ( ; num_masks > 1; num_masks--, mask++ )
1081 {
1082 FT_UInt next;
1083 FT_Int count;
1084
1085 next = mask->end_point;
1086 count = next - first;
1087 if ( count > 0 )
1088 {
1089 PSH2_Point point = glyph->points + first;
1090
1091 psh2_hint_table_activate_mask( table, mask );
1092
1093 for ( ; count > 0; count--, point++ )
1094 psh2_hint_table_find_strong_point( table, point, major_dir );
1095 }
1096 first = next;
1097 }
1098 }
1099
1100 /* process primary hints for all points */
1101 if ( num_masks == 1 )
1102 {
1103 FT_UInt count = glyph->num_points;
1104 PSH2_Point point = glyph->points;
1105
1106 psh2_hint_table_activate_mask( table, table->hint_masks->masks );
1107 for ( ; count > 0; count--, point++ )
1108 {
1109 if ( !psh2_point_is_strong(point) )
1110 psh2_hint_table_find_strong_point( table, point, major_dir );
1111 }
1112 }
1113
1114 /* now, certain points may have been attached to hint and */
1115 /* not marked as strong, update their flags then.. */
1116 {
1117 FT_UInt count = glyph->num_points;
1118 PSH2_Point point = glyph->points;
1119
1120 for ( ; count > 0; count--, point++ )
1121 if ( point->hint && !psh2_point_is_strong(point) )
1122 psh2_point_set_strong(point);
1123 }
1124 }
1125 }
1126
1127
1128
1129 /* interpolate strong points with the help of hinted coordinates */
1130 static void
1131 psh2_glyph_interpolate_strong_points( PSH2_Glyph glyph,
David Turner12d40da2001-10-21 15:41:11 +00001132 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +00001133 {
1134 PSH_Dimension dim = &glyph->globals->dimension[vertical];
1135 FT_Fixed scale = dim->scale_mult;
David Turnera83bc082001-10-18 11:38:43 +00001136
1137 {
1138 FT_UInt count = glyph->num_points;
1139 PSH2_Point point = glyph->points;
1140
1141 for ( ; count > 0; count--, point++ )
1142 {
1143 PSH2_Hint hint = point->hint;
1144
1145 if ( hint )
1146 {
1147 FT_Pos delta;
1148
1149 delta = point->org_u - hint->org_pos;
1150
1151 if ( delta <= 0 )
1152 point->cur_u = hint->cur_pos + FT_MulFix( delta, scale );
1153
1154 else if ( delta >= hint->org_len )
1155 point->cur_u = hint->cur_pos + hint->cur_len +
1156 FT_MulFix( delta - hint->org_len, scale );
1157
1158 else if ( hint->org_len > 0 )
1159 point->cur_u = hint->cur_pos +
1160 FT_MulDiv( delta, hint->cur_len, hint->org_len );
1161 else
1162 point->cur_u = hint->cur_pos;
1163
1164 psh2_point_set_fitted(point);
1165 }
1166 }
1167 }
1168 }
1169
1170
1171 static void
1172 psh2_glyph_interpolate_normal_points( PSH2_Glyph glyph,
David Turner12d40da2001-10-21 15:41:11 +00001173 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +00001174 {
1175#if 1
1176 PSH_Dimension dim = &glyph->globals->dimension[vertical];
1177 FT_Fixed scale = dim->scale_mult;
1178
1179 /* first technique: a point is strong if it is a local extrema */
1180 {
1181 FT_UInt count = glyph->num_points;
1182 PSH2_Point point = glyph->points;
1183
1184 for ( ; count > 0; count--, point++ )
1185 {
1186 if ( psh2_point_is_strong(point) )
1187 continue;
1188
1189 /* sometimes, some local extremas are smooth points */
1190 if ( psh2_point_is_smooth(point) )
1191 {
1192 if ( point->dir_in == PSH2_DIR_NONE ||
1193 point->dir_in != point->dir_out )
1194 continue;
1195
1196 if ( !psh2_point_is_extremum( point ) )
1197 continue;
1198
1199 point->flags &= ~PSH2_POINT_SMOOTH;
1200 }
1201
1202 /* find best enclosing point coordinates */
1203 {
1204 PSH2_Point before = 0;
1205 PSH2_Point after = 0;
1206
1207 FT_Pos diff_before = -32000;
1208 FT_Pos diff_after = 32000;
1209 FT_Pos u = point->org_u;
1210
1211 FT_Int count2 = glyph->num_points;
1212 PSH2_Point cur = glyph->points;
1213
1214 for ( ; count2 > 0; count2--, cur++ )
1215 {
1216 if ( psh2_point_is_strong(cur) )
1217 {
1218 FT_Pos diff = cur->org_u - u;;
1219
1220 if ( diff <= 0 )
1221 {
1222 if ( diff > diff_before )
1223 {
1224 diff_before = diff;
1225 before = cur;
1226 }
1227 }
1228 else if ( diff >= 0 )
1229 {
1230 if ( diff < diff_after )
1231 {
1232 diff_after = diff;
1233 after = cur;
1234 }
1235 }
1236 }
1237 }
1238
1239 if ( !before )
1240 {
1241 if ( !after )
1242 continue;
1243
1244 /* we're before the first strong point coordinate */
1245 /* simply translate the point.. */
1246 point->cur_u = after->cur_u +
1247 FT_MulFix( point->org_u - after->org_u, scale );
1248 }
1249 else if ( !after )
1250 {
1251 /* we're after the last strong point coordinate */
1252 /* simply translate the point.. */
1253 point->cur_u = before->cur_u +
1254 FT_MulFix( point->org_u - before->org_u, scale );
1255 }
1256 else
1257 {
1258 if ( diff_before == 0 )
1259 point->cur_u = before->cur_u;
1260
1261 else if ( diff_after == 0 )
1262 point->cur_u = after->cur_u;
1263
1264 else
1265 point->cur_u = before->cur_u +
1266 FT_MulDiv( u - before->org_u,
1267 after->cur_u - before->cur_u,
1268 after->org_u - before->org_u );
1269 }
1270
1271 psh2_point_set_fitted(point);
1272 }
1273 }
1274 }
1275#endif
1276 }
1277
1278
1279
1280 /* interpolate other points */
1281 static void
1282 psh2_glyph_interpolate_other_points( PSH2_Glyph glyph,
David Turner12d40da2001-10-21 15:41:11 +00001283 FT_Int vertical )
David Turnera83bc082001-10-18 11:38:43 +00001284 {
1285 PSH_Dimension dim = &glyph->globals->dimension[vertical];
1286 FT_Fixed scale = dim->scale_mult;
1287 FT_Fixed delta = dim->scale_delta;
1288 PSH2_Contour contour = glyph->contours;
1289 FT_UInt num_contours = glyph->num_contours;
1290
1291 for ( ; num_contours > 0; num_contours--, contour++ )
1292 {
1293 PSH2_Point start = contour->start;
1294 PSH2_Point first, next, point;
1295 FT_UInt fit_count;
1296
1297 /* count the number of strong points in this contour */
1298 next = start + contour->count;
1299 fit_count = 0;
1300 first = 0;
1301
1302 for ( point = start; point < next; point++ )
1303 if ( psh2_point_is_fitted(point) )
1304 {
1305 if ( !first )
1306 first = point;
1307
1308 fit_count++;
1309 }
1310
1311 /* if there is less than 2 fitted points in the contour, we'll */
1312 /* simply scale and eventually translate the contour points */
1313 if ( fit_count < 2 )
1314 {
1315 if ( fit_count == 1 )
1316 delta = first->cur_u - FT_MulFix( first->org_u, scale );
1317
1318 for ( point = start; point < next; point++ )
1319 if ( point != first )
1320 point->cur_u = FT_MulFix( point->org_u, scale ) + delta;
1321
1322 goto Next_Contour;
1323 }
1324
1325 /* there are more than 2 strong points in this contour, we'll */
1326 /* need to interpolate weak points between them.. */
1327 start = first;
1328 do
1329 {
1330 point = first;
1331
1332 /* skip consecutive fitted points */
1333 for (;;)
1334 {
1335 next = first->next;
1336 if ( next == start )
1337 goto Next_Contour;
1338
1339 if ( !psh2_point_is_fitted(next) )
1340 break;
1341
1342 first = next;
1343 }
1344
1345 /* find next fitted point after unfitted one */
1346 for (;;)
1347 {
1348 next = next->next;
1349 if ( psh2_point_is_fitted(next) )
1350 break;
1351 }
1352
1353 /* now interpolate between them */
1354 {
1355 FT_Pos org_a, org_ab, cur_a, cur_ab;
1356 FT_Pos org_c, org_ac, cur_c;
1357 FT_Fixed scale_ab;
1358
1359 if ( first->org_u <= next->org_u )
1360 {
1361 org_a = first->org_u;
1362 cur_a = first->cur_u;
1363 org_ab = next->org_u - org_a;
1364 cur_ab = next->cur_u - cur_a;
1365 }
1366 else
1367 {
1368 org_a = next->org_u;
1369 cur_a = next->cur_u;
1370 org_ab = first->org_u - org_a;
1371 cur_ab = first->cur_u - cur_a;
1372 }
1373
1374 scale_ab = 0x10000L;
1375 if ( org_ab > 0 )
1376 scale_ab = FT_DivFix( cur_ab, org_ab );
1377
1378 point = first->next;
1379 do
1380 {
1381 org_c = point->org_u;
1382 org_ac = org_c - org_a;
1383
1384 if ( org_ac <= 0 )
1385 {
1386 /* on the left of the interpolation zone */
1387 cur_c = cur_a + FT_MulFix( org_ac, scale );
1388 }
1389 else if ( org_ac >= org_ab )
1390 {
1391 /* on the right on the interpolation zone */
1392 cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale );
1393 }
1394 else
1395 {
1396 /* within the interpolation zone */
1397 cur_c = cur_a + FT_MulFix( org_ac, scale_ab );
1398 }
1399
1400 point->cur_u = cur_c;
1401
1402 point = point->next;
1403 }
1404 while ( point != next );
1405 }
1406
1407 /* keep going until all points in the contours have been processed */
1408 first = next;
1409 }
1410 while ( first != start );
1411
1412 Next_Contour:
1413 ;
1414 }
1415 }
1416
1417 /************************************************************************/
1418 /************************************************************************/
1419 /***** *****/
1420 /***** HIGH-LEVEL INTERFACE *****/
1421 /***** *****/
1422 /************************************************************************/
1423 /************************************************************************/
1424
1425 FT_Error
1426 ps2_hints_apply( PS_Hints ps_hints,
1427 FT_Outline* outline,
1428 PSH_Globals globals )
1429 {
1430 PSH2_Glyph glyph;
1431 PSH2_GlyphRec glyphrec;
1432 FT_Error error;
1433 FT_Memory memory;
1434 FT_Int dimension;
1435
1436 memory = globals->memory;
1437
David Turner9d7e5e82001-10-29 17:22:12 +00001438 FT_UNUSED(glyphrec);
1439
1440
David Turnera83bc082001-10-18 11:38:43 +00001441#ifdef DEBUG_HINTER
1442 if ( ps2_debug_glyph )
1443 {
1444 psh2_glyph_done( ps2_debug_glyph );
1445 FREE( ps2_debug_glyph );
1446 }
1447
1448 if ( ALLOC( glyph, sizeof(*glyph) ) )
1449 return error;
1450
1451 ps2_debug_glyph = glyph;
1452#else
1453 glyph = &glyphrec;
1454#endif
David Turner9d7e5e82001-10-29 17:22:12 +00001455
David Turnera83bc082001-10-18 11:38:43 +00001456 error = psh2_glyph_init( glyph, outline, ps_hints, globals );
1457 if (error) goto Exit;
1458
1459 for ( dimension = 1; dimension >= 0; dimension-- )
1460 {
1461 /* load outline coordinates into glyph */
1462 psh2_glyph_load_points( glyph, dimension );
1463
1464 /* compute aligned stem/hints positions */
1465 psh2_hint_table_align_hints( &glyph->hint_tables[dimension],
1466 glyph->globals,
1467 dimension );
1468
1469 /* find strong points, align them, then interpolate others */
1470 psh2_glyph_find_strong_points( glyph, dimension );
1471 psh2_glyph_interpolate_strong_points( glyph, dimension );
1472 psh2_glyph_interpolate_normal_points( glyph, dimension );
1473 psh2_glyph_interpolate_other_points( glyph, dimension );
1474
1475 /* save hinted coordinates back to outline */
1476 psh2_glyph_save_points( glyph, dimension );
1477 }
1478
1479 Exit:
1480#ifndef DEBUG_HINTER
1481 psh2_glyph_done( glyph );
1482#endif
1483 return error;
1484 }