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