1 /***************************************************************************/ 2 /* */ 3 /* cf2hints.c */ 4 /* */ 5 /* Adobe's code for handling CFF hints (body). */ 6 /* */ 7 /* Copyright 2007-2014 Adobe Systems Incorporated. */ 8 /* */ 9 /* This software, and all works of authorship, whether in source or */ 10 /* object code form as indicated by the copyright notice(s) included */ 11 /* herein (collectively, the "Work") is made available, and may only be */ 12 /* used, modified, and distributed under the FreeType Project License, */ 13 /* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ 14 /* FreeType Project License, each contributor to the Work hereby grants */ 15 /* to any individual or legal entity exercising permissions granted by */ 16 /* the FreeType Project License and this section (hereafter, "You" or */ 17 /* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ 18 /* royalty-free, irrevocable (except as stated in this section) patent */ 19 /* license to make, have made, use, offer to sell, sell, import, and */ 20 /* otherwise transfer the Work, where such license applies only to those */ 21 /* patent claims licensable by such contributor that are necessarily */ 22 /* infringed by their contribution(s) alone or by combination of their */ 23 /* contribution(s) with the Work to which such contribution(s) was */ 24 /* submitted. If You institute patent litigation against any entity */ 25 /* (including a cross-claim or counterclaim in a lawsuit) alleging that */ 26 /* the Work or a contribution incorporated within the Work constitutes */ 27 /* direct or contributory patent infringement, then any patent licenses */ 28 /* granted to You under this License for that Work shall terminate as of */ 29 /* the date such litigation is filed. */ 30 /* */ 31 /* By using, modifying, or distributing the Work you indicate that you */ 32 /* have read and understood the terms and conditions of the */ 33 /* FreeType Project License as well as those provided in this section, */ 34 /* and you accept them fully. */ 35 /* */ 36 /***************************************************************************/ 37 38 39 #include "cf2ft.h" 40 #include FT_INTERNAL_DEBUG_H 41 42 #include "cf2glue.h" 43 #include "cf2font.h" 44 #include "cf2hints.h" 45 #include "cf2intrp.h" 46 47 48 /*************************************************************************/ 49 /* */ 50 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 51 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 52 /* messages during execution. */ 53 /* */ 54 #undef FT_COMPONENT 55 #define FT_COMPONENT trace_cf2hints 56 57 58 typedef struct CF2_HintMoveRec_ 59 { 60 size_t j; /* index of upper hint map edge */ 61 CF2_Fixed moveUp; /* adjustment to optimum position */ 62 63 } CF2_HintMoveRec, *CF2_HintMove; 64 65 66 /* Compute angular momentum for winding order detection. It is called */ 67 /* for all lines and curves, but not necessarily in element order. */ 68 static CF2_Int cf2_getWindingMomentum(CF2_Fixed x1,CF2_Fixed y1,CF2_Fixed x2,CF2_Fixed y2)69 cf2_getWindingMomentum( CF2_Fixed x1, 70 CF2_Fixed y1, 71 CF2_Fixed x2, 72 CF2_Fixed y2 ) 73 { 74 /* cross product of pt1 position from origin with pt2 position from */ 75 /* pt1; we reduce the precision so that the result fits into 32 bits */ 76 77 return ( x1 >> 16 ) * ( SUB_INT32( y2, y1 ) >> 16 ) - 78 ( y1 >> 16 ) * ( SUB_INT32( x2, x1 ) >> 16 ); 79 } 80 81 82 /* 83 * Construct from a StemHint; this is used as a parameter to 84 * `cf2_blues_capture'. 85 * `hintOrigin' is the character space displacement of a seac accent. 86 * Adjust stem hint for darkening here. 87 * 88 */ 89 static void cf2_hint_init(CF2_Hint hint,const CF2_ArrStack stemHintArray,size_t indexStemHint,const CF2_Font font,CF2_Fixed hintOrigin,CF2_Fixed scale,FT_Bool bottom)90 cf2_hint_init( CF2_Hint hint, 91 const CF2_ArrStack stemHintArray, 92 size_t indexStemHint, 93 const CF2_Font font, 94 CF2_Fixed hintOrigin, 95 CF2_Fixed scale, 96 FT_Bool bottom ) 97 { 98 CF2_Fixed width; 99 const CF2_StemHintRec* stemHint; 100 101 102 FT_ZERO( hint ); 103 104 stemHint = (const CF2_StemHintRec*)cf2_arrstack_getPointer( 105 stemHintArray, 106 indexStemHint ); 107 108 width = SUB_INT32( stemHint->max, stemHint->min ); 109 110 if ( width == cf2_intToFixed( -21 ) ) 111 { 112 /* ghost bottom */ 113 114 if ( bottom ) 115 { 116 hint->csCoord = stemHint->max; 117 hint->flags = CF2_GhostBottom; 118 } 119 else 120 hint->flags = 0; 121 } 122 123 else if ( width == cf2_intToFixed( -20 ) ) 124 { 125 /* ghost top */ 126 127 if ( bottom ) 128 hint->flags = 0; 129 else 130 { 131 hint->csCoord = stemHint->min; 132 hint->flags = CF2_GhostTop; 133 } 134 } 135 136 else if ( width < 0 ) 137 { 138 /* inverted pair */ 139 140 /* 141 * Hints with negative widths were produced by an early version of a 142 * non-Adobe font tool. The Type 2 spec allows edge (ghost) hints 143 * with negative widths, but says 144 * 145 * All other negative widths have undefined meaning. 146 * 147 * CoolType has a silent workaround that negates the hint width; for 148 * permissive mode, we do the same here. 149 * 150 * Note: Such fonts cannot use ghost hints, but should otherwise work. 151 * Note: Some poor hints in our faux fonts can produce negative 152 * widths at some blends. For example, see a light weight of 153 * `u' in ASerifMM. 154 * 155 */ 156 if ( bottom ) 157 { 158 hint->csCoord = stemHint->max; 159 hint->flags = CF2_PairBottom; 160 } 161 else 162 { 163 hint->csCoord = stemHint->min; 164 hint->flags = CF2_PairTop; 165 } 166 } 167 168 else 169 { 170 /* normal pair */ 171 172 if ( bottom ) 173 { 174 hint->csCoord = stemHint->min; 175 hint->flags = CF2_PairBottom; 176 } 177 else 178 { 179 hint->csCoord = stemHint->max; 180 hint->flags = CF2_PairTop; 181 } 182 } 183 184 /* Now that ghost hints have been detected, adjust this edge for */ 185 /* darkening. Bottoms are not changed; tops are incremented by twice */ 186 /* `darkenY'. */ 187 if ( cf2_hint_isTop( hint ) ) 188 hint->csCoord = ADD_INT32( hint->csCoord, 2 * font->darkenY ); 189 190 hint->csCoord = ADD_INT32( hint->csCoord, hintOrigin ); 191 hint->scale = scale; 192 hint->index = indexStemHint; /* index in original stem hint array */ 193 194 /* if original stem hint has been used, use the same position */ 195 if ( hint->flags != 0 && stemHint->used ) 196 { 197 if ( cf2_hint_isTop( hint ) ) 198 hint->dsCoord = stemHint->maxDS; 199 else 200 hint->dsCoord = stemHint->minDS; 201 202 cf2_hint_lock( hint ); 203 } 204 else 205 hint->dsCoord = FT_MulFix( hint->csCoord, scale ); 206 } 207 208 209 /* initialize an invalid hint map element */ 210 static void cf2_hint_initZero(CF2_Hint hint)211 cf2_hint_initZero( CF2_Hint hint ) 212 { 213 FT_ZERO( hint ); 214 } 215 216 217 FT_LOCAL_DEF( FT_Bool ) cf2_hint_isValid(const CF2_Hint hint)218 cf2_hint_isValid( const CF2_Hint hint ) 219 { 220 return (FT_Bool)( hint->flags != 0 ); 221 } 222 223 224 static FT_Bool cf2_hint_isPair(const CF2_Hint hint)225 cf2_hint_isPair( const CF2_Hint hint ) 226 { 227 return (FT_Bool)( ( hint->flags & 228 ( CF2_PairBottom | CF2_PairTop ) ) != 0 ); 229 } 230 231 232 static FT_Bool cf2_hint_isPairTop(const CF2_Hint hint)233 cf2_hint_isPairTop( const CF2_Hint hint ) 234 { 235 return (FT_Bool)( ( hint->flags & CF2_PairTop ) != 0 ); 236 } 237 238 239 FT_LOCAL_DEF( FT_Bool ) cf2_hint_isTop(const CF2_Hint hint)240 cf2_hint_isTop( const CF2_Hint hint ) 241 { 242 return (FT_Bool)( ( hint->flags & 243 ( CF2_PairTop | CF2_GhostTop ) ) != 0 ); 244 } 245 246 247 FT_LOCAL_DEF( FT_Bool ) cf2_hint_isBottom(const CF2_Hint hint)248 cf2_hint_isBottom( const CF2_Hint hint ) 249 { 250 return (FT_Bool)( ( hint->flags & 251 ( CF2_PairBottom | CF2_GhostBottom ) ) != 0 ); 252 } 253 254 255 static FT_Bool cf2_hint_isLocked(const CF2_Hint hint)256 cf2_hint_isLocked( const CF2_Hint hint ) 257 { 258 return (FT_Bool)( ( hint->flags & CF2_Locked ) != 0 ); 259 } 260 261 262 static FT_Bool cf2_hint_isSynthetic(const CF2_Hint hint)263 cf2_hint_isSynthetic( const CF2_Hint hint ) 264 { 265 return (FT_Bool)( ( hint->flags & CF2_Synthetic ) != 0 ); 266 } 267 268 269 FT_LOCAL_DEF( void ) cf2_hint_lock(CF2_Hint hint)270 cf2_hint_lock( CF2_Hint hint ) 271 { 272 hint->flags |= CF2_Locked; 273 } 274 275 276 FT_LOCAL_DEF( void ) cf2_hintmap_init(CF2_HintMap hintmap,CF2_Font font,CF2_HintMap initialMap,CF2_ArrStack hintMoves,CF2_Fixed scale)277 cf2_hintmap_init( CF2_HintMap hintmap, 278 CF2_Font font, 279 CF2_HintMap initialMap, 280 CF2_ArrStack hintMoves, 281 CF2_Fixed scale ) 282 { 283 FT_ZERO( hintmap ); 284 285 /* copy parameters from font instance */ 286 hintmap->hinted = font->hinted; 287 hintmap->scale = scale; 288 hintmap->font = font; 289 hintmap->initialHintMap = initialMap; 290 /* will clear in `cf2_hintmap_adjustHints' */ 291 hintmap->hintMoves = hintMoves; 292 } 293 294 295 static FT_Bool cf2_hintmap_isValid(const CF2_HintMap hintmap)296 cf2_hintmap_isValid( const CF2_HintMap hintmap ) 297 { 298 return hintmap->isValid; 299 } 300 301 302 /* transform character space coordinate to device space using hint map */ 303 static CF2_Fixed cf2_hintmap_map(CF2_HintMap hintmap,CF2_Fixed csCoord)304 cf2_hintmap_map( CF2_HintMap hintmap, 305 CF2_Fixed csCoord ) 306 { 307 if ( hintmap->count == 0 || ! hintmap->hinted ) 308 { 309 /* there are no hints; use uniform scale and zero offset */ 310 return FT_MulFix( csCoord, hintmap->scale ); 311 } 312 else 313 { 314 /* start linear search from last hit */ 315 CF2_UInt i = hintmap->lastIndex; 316 317 318 FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES ); 319 320 /* search up */ 321 while ( i < hintmap->count - 1 && 322 csCoord >= hintmap->edge[i + 1].csCoord ) 323 i += 1; 324 325 /* search down */ 326 while ( i > 0 && csCoord < hintmap->edge[i].csCoord ) 327 i -= 1; 328 329 hintmap->lastIndex = i; 330 331 if ( i == 0 && csCoord < hintmap->edge[0].csCoord ) 332 { 333 /* special case for points below first edge: use uniform scale */ 334 return ADD_INT32( FT_MulFix( SUB_INT32( csCoord, 335 hintmap->edge[0].csCoord ), 336 hintmap->scale ), 337 hintmap->edge[0].dsCoord ); 338 } 339 else 340 { 341 /* 342 * Note: entries with duplicate csCoord are allowed. 343 * Use edge[i], the highest entry where csCoord >= entry[i].csCoord 344 */ 345 return ADD_INT32( FT_MulFix( SUB_INT32( csCoord, 346 hintmap->edge[i].csCoord ), 347 hintmap->edge[i].scale ), 348 hintmap->edge[i].dsCoord ); 349 } 350 } 351 } 352 353 354 /* 355 * This hinting policy moves a hint pair in device space so that one of 356 * its two edges is on a device pixel boundary (its fractional part is 357 * zero). `cf2_hintmap_insertHint' guarantees no overlap in CS 358 * space. Ensure here that there is no overlap in DS. 359 * 360 * In the first pass, edges are adjusted relative to adjacent hints. 361 * Those that are below have already been adjusted. Those that are 362 * above have not yet been adjusted. If a hint above blocks an 363 * adjustment to an optimal position, we will try again in a second 364 * pass. The second pass is top-down. 365 * 366 */ 367 368 static void cf2_hintmap_adjustHints(CF2_HintMap hintmap)369 cf2_hintmap_adjustHints( CF2_HintMap hintmap ) 370 { 371 size_t i, j; 372 373 374 cf2_arrstack_clear( hintmap->hintMoves ); /* working storage */ 375 376 /* 377 * First pass is bottom-up (font hint order) without look-ahead. 378 * Locked edges are already adjusted. 379 * Unlocked edges begin with dsCoord from `initialHintMap'. 380 * Save edges that are not optimally adjusted in `hintMoves' array, 381 * and process them in second pass. 382 */ 383 384 for ( i = 0; i < hintmap->count; i++ ) 385 { 386 FT_Bool isPair = cf2_hint_isPair( &hintmap->edge[i] ); 387 388 389 /* index of upper edge (same value for ghost hint) */ 390 j = isPair ? i + 1 : i; 391 392 FT_ASSERT( j < hintmap->count ); 393 FT_ASSERT( cf2_hint_isValid( &hintmap->edge[i] ) ); 394 FT_ASSERT( cf2_hint_isValid( &hintmap->edge[j] ) ); 395 FT_ASSERT( cf2_hint_isLocked( &hintmap->edge[i] ) == 396 cf2_hint_isLocked( &hintmap->edge[j] ) ); 397 398 if ( !cf2_hint_isLocked( &hintmap->edge[i] ) ) 399 { 400 /* hint edge is not locked, we can adjust it */ 401 CF2_Fixed fracDown = cf2_fixedFraction( hintmap->edge[i].dsCoord ); 402 CF2_Fixed fracUp = cf2_fixedFraction( hintmap->edge[j].dsCoord ); 403 404 /* calculate all four possibilities; moves down are negative */ 405 CF2_Fixed downMoveDown = 0 - fracDown; 406 CF2_Fixed upMoveDown = 0 - fracUp; 407 CF2_Fixed downMoveUp = ( fracDown == 0 ) 408 ? 0 409 : cf2_intToFixed( 1 ) - fracDown; 410 CF2_Fixed upMoveUp = ( fracUp == 0 ) 411 ? 0 412 : cf2_intToFixed( 1 ) - fracUp; 413 414 /* smallest move up */ 415 CF2_Fixed moveUp = FT_MIN( downMoveUp, upMoveUp ); 416 /* smallest move down */ 417 CF2_Fixed moveDown = FT_MAX( downMoveDown, upMoveDown ); 418 419 /* final amount to move edge or edge pair */ 420 CF2_Fixed move; 421 422 CF2_Fixed downMinCounter = CF2_MIN_COUNTER; 423 CF2_Fixed upMinCounter = CF2_MIN_COUNTER; 424 FT_Bool saveEdge = FALSE; 425 426 427 /* minimum counter constraint doesn't apply when adjacent edges */ 428 /* are synthetic */ 429 /* TODO: doesn't seem a big effect; for now, reduce the code */ 430 #if 0 431 if ( i == 0 || 432 cf2_hint_isSynthetic( &hintmap->edge[i - 1] ) ) 433 downMinCounter = 0; 434 435 if ( j >= hintmap->count - 1 || 436 cf2_hint_isSynthetic( &hintmap->edge[j + 1] ) ) 437 upMinCounter = 0; 438 #endif 439 440 /* is there room to move up? */ 441 /* there is if we are at top of array or the next edge is at or */ 442 /* beyond proposed move up? */ 443 if ( j >= hintmap->count - 1 || 444 hintmap->edge[j + 1].dsCoord >= 445 ADD_INT32( hintmap->edge[j].dsCoord, 446 moveUp + upMinCounter ) ) 447 { 448 /* there is room to move up; is there also room to move down? */ 449 if ( i == 0 || 450 hintmap->edge[i - 1].dsCoord <= 451 ADD_INT32( hintmap->edge[i].dsCoord, 452 moveDown - downMinCounter ) ) 453 { 454 /* move smaller absolute amount */ 455 move = ( -moveDown < moveUp ) ? moveDown : moveUp; /* optimum */ 456 } 457 else 458 move = moveUp; 459 } 460 else 461 { 462 /* is there room to move down? */ 463 if ( i == 0 || 464 hintmap->edge[i - 1].dsCoord <= 465 ADD_INT32( hintmap->edge[i].dsCoord, 466 moveDown - downMinCounter ) ) 467 { 468 move = moveDown; 469 /* true if non-optimum move */ 470 saveEdge = (FT_Bool)( moveUp < -moveDown ); 471 } 472 else 473 { 474 /* no room to move either way without overlapping or reducing */ 475 /* the counter too much */ 476 move = 0; 477 saveEdge = TRUE; 478 } 479 } 480 481 /* Identify non-moves and moves down that aren't optimal, and save */ 482 /* them for second pass. */ 483 /* Do this only if there is an unlocked edge above (which could */ 484 /* possibly move). */ 485 if ( saveEdge && 486 j < hintmap->count - 1 && 487 !cf2_hint_isLocked( &hintmap->edge[j + 1] ) ) 488 { 489 CF2_HintMoveRec savedMove; 490 491 492 savedMove.j = j; 493 /* desired adjustment in second pass */ 494 savedMove.moveUp = moveUp - move; 495 496 cf2_arrstack_push( hintmap->hintMoves, &savedMove ); 497 } 498 499 /* move the edge(s) */ 500 hintmap->edge[i].dsCoord = ADD_INT32( hintmap->edge[i].dsCoord, 501 move ); 502 if ( isPair ) 503 hintmap->edge[j].dsCoord = ADD_INT32( hintmap->edge[j].dsCoord, 504 move ); 505 } 506 507 /* assert there are no overlaps in device space */ 508 FT_ASSERT( i == 0 || 509 hintmap->edge[i - 1].dsCoord <= hintmap->edge[i].dsCoord ); 510 FT_ASSERT( i < j || 511 hintmap->edge[i].dsCoord <= hintmap->edge[j].dsCoord ); 512 513 /* adjust the scales, avoiding divide by zero */ 514 if ( i > 0 ) 515 { 516 if ( hintmap->edge[i].csCoord != hintmap->edge[i - 1].csCoord ) 517 hintmap->edge[i - 1].scale = 518 FT_DivFix( SUB_INT32( hintmap->edge[i].dsCoord, 519 hintmap->edge[i - 1].dsCoord ), 520 SUB_INT32( hintmap->edge[i].csCoord, 521 hintmap->edge[i - 1].csCoord ) ); 522 } 523 524 if ( isPair ) 525 { 526 if ( hintmap->edge[j].csCoord != hintmap->edge[j - 1].csCoord ) 527 hintmap->edge[j - 1].scale = 528 FT_DivFix( SUB_INT32( hintmap->edge[j].dsCoord, 529 hintmap->edge[j - 1].dsCoord ), 530 SUB_INT32( hintmap->edge[j].csCoord, 531 hintmap->edge[j - 1].csCoord ) ); 532 533 i += 1; /* skip upper edge on next loop */ 534 } 535 } 536 537 /* second pass tries to move non-optimal hints up, in case there is */ 538 /* room now */ 539 for ( i = cf2_arrstack_size( hintmap->hintMoves ); i > 0; i-- ) 540 { 541 CF2_HintMove hintMove = (CF2_HintMove) 542 cf2_arrstack_getPointer( hintmap->hintMoves, i - 1 ); 543 544 545 j = hintMove->j; 546 547 /* this was tested before the push, above */ 548 FT_ASSERT( j < hintmap->count - 1 ); 549 550 /* is there room to move up? */ 551 if ( hintmap->edge[j + 1].dsCoord >= 552 ADD_INT32( hintmap->edge[j].dsCoord, 553 hintMove->moveUp + CF2_MIN_COUNTER ) ) 554 { 555 /* there is more room now, move edge up */ 556 hintmap->edge[j].dsCoord = ADD_INT32( hintmap->edge[j].dsCoord, 557 hintMove->moveUp ); 558 559 if ( cf2_hint_isPair( &hintmap->edge[j] ) ) 560 { 561 FT_ASSERT( j > 0 ); 562 hintmap->edge[j - 1].dsCoord = 563 ADD_INT32( hintmap->edge[j - 1].dsCoord, hintMove->moveUp ); 564 } 565 } 566 } 567 } 568 569 570 /* insert hint edges into map, sorted by csCoord */ 571 static void cf2_hintmap_insertHint(CF2_HintMap hintmap,CF2_Hint bottomHintEdge,CF2_Hint topHintEdge)572 cf2_hintmap_insertHint( CF2_HintMap hintmap, 573 CF2_Hint bottomHintEdge, 574 CF2_Hint topHintEdge ) 575 { 576 CF2_UInt indexInsert; 577 578 /* set default values, then check for edge hints */ 579 FT_Bool isPair = TRUE; 580 CF2_Hint firstHintEdge = bottomHintEdge; 581 CF2_Hint secondHintEdge = topHintEdge; 582 583 584 /* one or none of the input params may be invalid when dealing with */ 585 /* edge hints; at least one edge must be valid */ 586 FT_ASSERT( cf2_hint_isValid( bottomHintEdge ) || 587 cf2_hint_isValid( topHintEdge ) ); 588 589 /* determine how many and which edges to insert */ 590 if ( !cf2_hint_isValid( bottomHintEdge ) ) 591 { 592 /* insert only the top edge */ 593 firstHintEdge = topHintEdge; 594 isPair = FALSE; 595 } 596 else if ( !cf2_hint_isValid( topHintEdge ) ) 597 { 598 /* insert only the bottom edge */ 599 isPair = FALSE; 600 } 601 602 /* paired edges must be in proper order */ 603 if ( isPair && 604 topHintEdge->csCoord < bottomHintEdge->csCoord ) 605 return; 606 607 /* linear search to find index value of insertion point */ 608 indexInsert = 0; 609 for ( ; indexInsert < hintmap->count; indexInsert++ ) 610 { 611 if ( hintmap->edge[indexInsert].csCoord >= firstHintEdge->csCoord ) 612 break; 613 } 614 615 /* 616 * Discard any hints that overlap in character space. Most often, this 617 * is while building the initial map, where captured hints from all 618 * zones are combined. Define overlap to include hints that `touch' 619 * (overlap zero). Hiragino Sans/Gothic fonts have numerous hints that 620 * touch. Some fonts have non-ideographic glyphs that overlap our 621 * synthetic hints. 622 * 623 * Overlap also occurs when darkening stem hints that are close. 624 * 625 */ 626 if ( indexInsert < hintmap->count ) 627 { 628 /* we are inserting before an existing edge: */ 629 /* verify that an existing edge is not the same */ 630 if ( hintmap->edge[indexInsert].csCoord == firstHintEdge->csCoord ) 631 return; /* ignore overlapping stem hint */ 632 633 /* verify that a new pair does not straddle the next edge */ 634 if ( isPair && 635 hintmap->edge[indexInsert].csCoord <= secondHintEdge->csCoord ) 636 return; /* ignore overlapping stem hint */ 637 638 /* verify that we are not inserting between paired edges */ 639 if ( cf2_hint_isPairTop( &hintmap->edge[indexInsert] ) ) 640 return; /* ignore overlapping stem hint */ 641 } 642 643 /* recompute device space locations using initial hint map */ 644 if ( cf2_hintmap_isValid( hintmap->initialHintMap ) && 645 !cf2_hint_isLocked( firstHintEdge ) ) 646 { 647 if ( isPair ) 648 { 649 /* Use hint map to position the center of stem, and nominal scale */ 650 /* to position the two edges. This preserves the stem width. */ 651 CF2_Fixed midpoint = 652 cf2_hintmap_map( 653 hintmap->initialHintMap, 654 ADD_INT32( secondHintEdge->csCoord, 655 firstHintEdge->csCoord ) / 2 ); 656 CF2_Fixed halfWidth = 657 FT_MulFix( SUB_INT32( secondHintEdge->csCoord, 658 firstHintEdge->csCoord ) / 2, 659 hintmap->scale ); 660 661 662 firstHintEdge->dsCoord = SUB_INT32( midpoint, halfWidth ); 663 secondHintEdge->dsCoord = ADD_INT32( midpoint, halfWidth ); 664 } 665 else 666 firstHintEdge->dsCoord = cf2_hintmap_map( hintmap->initialHintMap, 667 firstHintEdge->csCoord ); 668 } 669 670 /* 671 * Discard any hints that overlap in device space; this can occur 672 * because locked hints have been moved to align with blue zones. 673 * 674 * TODO: Although we might correct this later during adjustment, we 675 * don't currently have a way to delete a conflicting hint once it has 676 * been inserted. See v2.030 MinionPro-Regular, 12 ppem darkened, 677 * initial hint map for second path, glyph 945 (the perispomeni (tilde) 678 * in U+1F6E, Greek omega with psili and perispomeni). Darkening is 679 * 25. Pair 667,747 initially conflicts in design space with top edge 680 * 660. This is because 667 maps to 7.87, and the top edge was 681 * captured by a zone at 8.0. The pair is later successfully inserted 682 * in a zone without the top edge. In this zone it is adjusted to 8.0, 683 * and no longer conflicts with the top edge in design space. This 684 * means it can be included in yet a later zone which does have the top 685 * edge hint. This produces a small mismatch between the first and 686 * last points of this path, even though the hint masks are the same. 687 * The density map difference is tiny (1/256). 688 * 689 */ 690 691 if ( indexInsert > 0 ) 692 { 693 /* we are inserting after an existing edge */ 694 if ( firstHintEdge->dsCoord < hintmap->edge[indexInsert - 1].dsCoord ) 695 return; 696 } 697 698 if ( indexInsert < hintmap->count ) 699 { 700 /* we are inserting before an existing edge */ 701 if ( isPair ) 702 { 703 if ( secondHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord ) 704 return; 705 } 706 else 707 { 708 if ( firstHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord ) 709 return; 710 } 711 } 712 713 /* make room to insert */ 714 { 715 CF2_UInt iSrc = hintmap->count - 1; 716 CF2_UInt iDst = isPair ? hintmap->count + 1 : hintmap->count; 717 718 CF2_UInt count = hintmap->count - indexInsert; 719 720 721 if ( iDst >= CF2_MAX_HINT_EDGES ) 722 { 723 FT_TRACE4(( "cf2_hintmap_insertHint: too many hintmaps\n" )); 724 return; 725 } 726 727 while ( count-- ) 728 hintmap->edge[iDst--] = hintmap->edge[iSrc--]; 729 730 /* insert first edge */ 731 hintmap->edge[indexInsert] = *firstHintEdge; /* copy struct */ 732 hintmap->count += 1; 733 734 if ( isPair ) 735 { 736 /* insert second edge */ 737 hintmap->edge[indexInsert + 1] = *secondHintEdge; /* copy struct */ 738 hintmap->count += 1; 739 } 740 } 741 742 return; 743 } 744 745 746 /* 747 * Build a map from hints and mask. 748 * 749 * This function may recur one level if `hintmap->initialHintMap' is not yet 750 * valid. 751 * If `initialMap' is true, simply build initial map. 752 * 753 * Synthetic hints are used in two ways. A hint at zero is inserted, if 754 * needed, in the initial hint map, to prevent translations from 755 * propagating across the origin. If synthetic em box hints are enabled 756 * for ideographic dictionaries, then they are inserted in all hint 757 * maps, including the initial one. 758 * 759 */ 760 FT_LOCAL_DEF( void ) cf2_hintmap_build(CF2_HintMap hintmap,CF2_ArrStack hStemHintArray,CF2_ArrStack vStemHintArray,CF2_HintMask hintMask,CF2_Fixed hintOrigin,FT_Bool initialMap)761 cf2_hintmap_build( CF2_HintMap hintmap, 762 CF2_ArrStack hStemHintArray, 763 CF2_ArrStack vStemHintArray, 764 CF2_HintMask hintMask, 765 CF2_Fixed hintOrigin, 766 FT_Bool initialMap ) 767 { 768 FT_Byte* maskPtr; 769 770 CF2_Font font = hintmap->font; 771 CF2_HintMaskRec tempHintMask; 772 773 size_t bitCount, i; 774 FT_Byte maskByte; 775 776 777 /* check whether initial map is constructed */ 778 if ( !initialMap && !cf2_hintmap_isValid( hintmap->initialHintMap ) ) 779 { 780 /* make recursive call with initialHintMap and temporary mask; */ 781 /* temporary mask will get all bits set, below */ 782 cf2_hintmask_init( &tempHintMask, hintMask->error ); 783 cf2_hintmap_build( hintmap->initialHintMap, 784 hStemHintArray, 785 vStemHintArray, 786 &tempHintMask, 787 hintOrigin, 788 TRUE ); 789 } 790 791 if ( !cf2_hintmask_isValid( hintMask ) ) 792 { 793 /* without a hint mask, assume all hints are active */ 794 cf2_hintmask_setAll( hintMask, 795 cf2_arrstack_size( hStemHintArray ) + 796 cf2_arrstack_size( vStemHintArray ) ); 797 if ( !cf2_hintmask_isValid( hintMask ) ) 798 return; /* too many stem hints */ 799 } 800 801 /* begin by clearing the map */ 802 hintmap->count = 0; 803 hintmap->lastIndex = 0; 804 805 /* make a copy of the hint mask so we can modify it */ 806 tempHintMask = *hintMask; 807 maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); 808 809 /* use the hStem hints only, which are first in the mask */ 810 bitCount = cf2_arrstack_size( hStemHintArray ); 811 812 /* Defense-in-depth. Should never return here. */ 813 if ( bitCount > hintMask->bitCount ) 814 return; 815 816 /* synthetic embox hints get highest priority */ 817 if ( font->blues.doEmBoxHints ) 818 { 819 CF2_HintRec dummy; 820 821 822 cf2_hint_initZero( &dummy ); /* invalid hint map element */ 823 824 /* ghost bottom */ 825 cf2_hintmap_insertHint( hintmap, 826 &font->blues.emBoxBottomEdge, 827 &dummy ); 828 /* ghost top */ 829 cf2_hintmap_insertHint( hintmap, 830 &dummy, 831 &font->blues.emBoxTopEdge ); 832 } 833 834 /* insert hints captured by a blue zone or already locked (higher */ 835 /* priority) */ 836 for ( i = 0, maskByte = 0x80; i < bitCount; i++ ) 837 { 838 if ( maskByte & *maskPtr ) 839 { 840 /* expand StemHint into two `CF2_Hint' elements */ 841 CF2_HintRec bottomHintEdge, topHintEdge; 842 843 844 cf2_hint_init( &bottomHintEdge, 845 hStemHintArray, 846 i, 847 font, 848 hintOrigin, 849 hintmap->scale, 850 TRUE /* bottom */ ); 851 cf2_hint_init( &topHintEdge, 852 hStemHintArray, 853 i, 854 font, 855 hintOrigin, 856 hintmap->scale, 857 FALSE /* top */ ); 858 859 if ( cf2_hint_isLocked( &bottomHintEdge ) || 860 cf2_hint_isLocked( &topHintEdge ) || 861 cf2_blues_capture( &font->blues, 862 &bottomHintEdge, 863 &topHintEdge ) ) 864 { 865 /* insert captured hint into map */ 866 cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); 867 868 *maskPtr &= ~maskByte; /* turn off the bit for this hint */ 869 } 870 } 871 872 if ( ( i & 7 ) == 7 ) 873 { 874 /* move to next mask byte */ 875 maskPtr++; 876 maskByte = 0x80; 877 } 878 else 879 maskByte >>= 1; 880 } 881 882 /* initial hint map includes only captured hints plus maybe one at 0 */ 883 884 /* 885 * TODO: There is a problem here because we are trying to build a 886 * single hint map containing all captured hints. It is 887 * possible for there to be conflicts between captured hints, 888 * either because of darkening or because the hints are in 889 * separate hint zones (we are ignoring hint zones for the 890 * initial map). An example of the latter is MinionPro-Regular 891 * v2.030 glyph 883 (Greek Capital Alpha with Psili) at 15ppem. 892 * A stem hint for the psili conflicts with the top edge hint 893 * for the base character. The stem hint gets priority because 894 * of its sort order. In glyph 884 (Greek Capital Alpha with 895 * Psili and Oxia), the top of the base character gets a stem 896 * hint, and the psili does not. This creates different initial 897 * maps for the two glyphs resulting in different renderings of 898 * the base character. Will probably defer this either as not 899 * worth the cost or as a font bug. I don't think there is any 900 * good reason for an accent to be captured by an alignment 901 * zone. -darnold 2/12/10 902 */ 903 904 if ( initialMap ) 905 { 906 /* Apply a heuristic that inserts a point for (0,0), unless it's */ 907 /* already covered by a mapping. This locks the baseline for glyphs */ 908 /* that have no baseline hints. */ 909 910 if ( hintmap->count == 0 || 911 hintmap->edge[0].csCoord > 0 || 912 hintmap->edge[hintmap->count - 1].csCoord < 0 ) 913 { 914 /* all edges are above 0 or all edges are below 0; */ 915 /* construct a locked edge hint at 0 */ 916 917 CF2_HintRec edge, invalid; 918 919 920 cf2_hint_initZero( &edge ); 921 922 edge.flags = CF2_GhostBottom | 923 CF2_Locked | 924 CF2_Synthetic; 925 edge.scale = hintmap->scale; 926 927 cf2_hint_initZero( &invalid ); 928 cf2_hintmap_insertHint( hintmap, &edge, &invalid ); 929 } 930 } 931 else 932 { 933 /* insert remaining hints */ 934 935 maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); 936 937 for ( i = 0, maskByte = 0x80; i < bitCount; i++ ) 938 { 939 if ( maskByte & *maskPtr ) 940 { 941 CF2_HintRec bottomHintEdge, topHintEdge; 942 943 944 cf2_hint_init( &bottomHintEdge, 945 hStemHintArray, 946 i, 947 font, 948 hintOrigin, 949 hintmap->scale, 950 TRUE /* bottom */ ); 951 cf2_hint_init( &topHintEdge, 952 hStemHintArray, 953 i, 954 font, 955 hintOrigin, 956 hintmap->scale, 957 FALSE /* top */ ); 958 959 cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); 960 } 961 962 if ( ( i & 7 ) == 7 ) 963 { 964 /* move to next mask byte */ 965 maskPtr++; 966 maskByte = 0x80; 967 } 968 else 969 maskByte >>= 1; 970 } 971 } 972 973 /* 974 * Note: The following line is a convenient place to break when 975 * debugging hinting. Examine `hintmap->edge' for the list of 976 * enabled hints, then step over the call to see the effect of 977 * adjustment. We stop here first on the recursive call that 978 * creates the initial map, and then on each counter group and 979 * hint zone. 980 */ 981 982 /* adjust positions of hint edges that are not locked to blue zones */ 983 cf2_hintmap_adjustHints( hintmap ); 984 985 /* save the position of all hints that were used in this hint map; */ 986 /* if we use them again, we'll locate them in the same position */ 987 if ( !initialMap ) 988 { 989 for ( i = 0; i < hintmap->count; i++ ) 990 { 991 if ( !cf2_hint_isSynthetic( &hintmap->edge[i] ) ) 992 { 993 /* Note: include both valid and invalid edges */ 994 /* Note: top and bottom edges are copied back separately */ 995 CF2_StemHint stemhint = (CF2_StemHint) 996 cf2_arrstack_getPointer( hStemHintArray, 997 hintmap->edge[i].index ); 998 999 1000 if ( cf2_hint_isTop( &hintmap->edge[i] ) ) 1001 stemhint->maxDS = hintmap->edge[i].dsCoord; 1002 else 1003 stemhint->minDS = hintmap->edge[i].dsCoord; 1004 1005 stemhint->used = TRUE; 1006 } 1007 } 1008 } 1009 1010 /* hint map is ready to use */ 1011 hintmap->isValid = TRUE; 1012 1013 /* remember this mask has been used */ 1014 cf2_hintmask_setNew( hintMask, FALSE ); 1015 } 1016 1017 1018 FT_LOCAL_DEF( void ) cf2_glyphpath_init(CF2_GlyphPath glyphpath,CF2_Font font,CF2_OutlineCallbacks callbacks,CF2_Fixed scaleY,CF2_ArrStack hStemHintArray,CF2_ArrStack vStemHintArray,CF2_HintMask hintMask,CF2_Fixed hintOriginY,const CF2_Blues blues,const FT_Vector * fractionalTranslation)1019 cf2_glyphpath_init( CF2_GlyphPath glyphpath, 1020 CF2_Font font, 1021 CF2_OutlineCallbacks callbacks, 1022 CF2_Fixed scaleY, 1023 /* CF2_Fixed hShift, */ 1024 CF2_ArrStack hStemHintArray, 1025 CF2_ArrStack vStemHintArray, 1026 CF2_HintMask hintMask, 1027 CF2_Fixed hintOriginY, 1028 const CF2_Blues blues, 1029 const FT_Vector* fractionalTranslation ) 1030 { 1031 FT_ZERO( glyphpath ); 1032 1033 glyphpath->font = font; 1034 glyphpath->callbacks = callbacks; 1035 1036 cf2_arrstack_init( &glyphpath->hintMoves, 1037 font->memory, 1038 &font->error, 1039 sizeof ( CF2_HintMoveRec ) ); 1040 1041 cf2_hintmap_init( &glyphpath->initialHintMap, 1042 font, 1043 &glyphpath->initialHintMap, 1044 &glyphpath->hintMoves, 1045 scaleY ); 1046 cf2_hintmap_init( &glyphpath->firstHintMap, 1047 font, 1048 &glyphpath->initialHintMap, 1049 &glyphpath->hintMoves, 1050 scaleY ); 1051 cf2_hintmap_init( &glyphpath->hintMap, 1052 font, 1053 &glyphpath->initialHintMap, 1054 &glyphpath->hintMoves, 1055 scaleY ); 1056 1057 glyphpath->scaleX = font->innerTransform.a; 1058 glyphpath->scaleC = font->innerTransform.c; 1059 glyphpath->scaleY = font->innerTransform.d; 1060 1061 glyphpath->fractionalTranslation = *fractionalTranslation; 1062 1063 #if 0 1064 glyphpath->hShift = hShift; /* for fauxing */ 1065 #endif 1066 1067 glyphpath->hStemHintArray = hStemHintArray; 1068 glyphpath->vStemHintArray = vStemHintArray; 1069 glyphpath->hintMask = hintMask; /* ptr to current mask */ 1070 glyphpath->hintOriginY = hintOriginY; 1071 glyphpath->blues = blues; 1072 glyphpath->darken = font->darkened; /* TODO: should we make copies? */ 1073 glyphpath->xOffset = font->darkenX; 1074 glyphpath->yOffset = font->darkenY; 1075 glyphpath->miterLimit = 2 * FT_MAX( 1076 cf2_fixedAbs( glyphpath->xOffset ), 1077 cf2_fixedAbs( glyphpath->yOffset ) ); 1078 1079 /* .1 character space unit */ 1080 glyphpath->snapThreshold = cf2_doubleToFixed( 0.1 ); 1081 1082 glyphpath->moveIsPending = TRUE; 1083 glyphpath->pathIsOpen = FALSE; 1084 glyphpath->pathIsClosing = FALSE; 1085 glyphpath->elemIsQueued = FALSE; 1086 } 1087 1088 1089 FT_LOCAL_DEF( void ) cf2_glyphpath_finalize(CF2_GlyphPath glyphpath)1090 cf2_glyphpath_finalize( CF2_GlyphPath glyphpath ) 1091 { 1092 cf2_arrstack_finalize( &glyphpath->hintMoves ); 1093 } 1094 1095 1096 /* 1097 * Hint point in y-direction and apply outerTransform. 1098 * Input `current' hint map (which is actually delayed by one element). 1099 * Input x,y point in Character Space. 1100 * Output x,y point in Device Space, including translation. 1101 */ 1102 static void cf2_glyphpath_hintPoint(CF2_GlyphPath glyphpath,CF2_HintMap hintmap,FT_Vector * ppt,CF2_Fixed x,CF2_Fixed y)1103 cf2_glyphpath_hintPoint( CF2_GlyphPath glyphpath, 1104 CF2_HintMap hintmap, 1105 FT_Vector* ppt, 1106 CF2_Fixed x, 1107 CF2_Fixed y ) 1108 { 1109 FT_Vector pt; /* hinted point in upright DS */ 1110 1111 1112 pt.x = ADD_INT32( FT_MulFix( glyphpath->scaleX, x ), 1113 FT_MulFix( glyphpath->scaleC, y ) ); 1114 pt.y = cf2_hintmap_map( hintmap, y ); 1115 1116 ppt->x = ADD_INT32( 1117 FT_MulFix( glyphpath->font->outerTransform.a, pt.x ), 1118 ADD_INT32( 1119 FT_MulFix( glyphpath->font->outerTransform.c, pt.y ), 1120 glyphpath->fractionalTranslation.x ) ); 1121 ppt->y = ADD_INT32( 1122 FT_MulFix( glyphpath->font->outerTransform.b, pt.x ), 1123 ADD_INT32( 1124 FT_MulFix( glyphpath->font->outerTransform.d, pt.y ), 1125 glyphpath->fractionalTranslation.y ) ); 1126 } 1127 1128 1129 /* 1130 * From two line segments, (u1,u2) and (v1,v2), compute a point of 1131 * intersection on the corresponding lines. 1132 * Return false if no intersection is found, or if the intersection is 1133 * too far away from the ends of the line segments, u2 and v1. 1134 * 1135 */ 1136 static FT_Bool cf2_glyphpath_computeIntersection(CF2_GlyphPath glyphpath,const FT_Vector * u1,const FT_Vector * u2,const FT_Vector * v1,const FT_Vector * v2,FT_Vector * intersection)1137 cf2_glyphpath_computeIntersection( CF2_GlyphPath glyphpath, 1138 const FT_Vector* u1, 1139 const FT_Vector* u2, 1140 const FT_Vector* v1, 1141 const FT_Vector* v2, 1142 FT_Vector* intersection ) 1143 { 1144 /* 1145 * Let `u' be a zero-based vector from the first segment, `v' from the 1146 * second segment. 1147 * Let `w 'be the zero-based vector from `u1' to `v1'. 1148 * `perp' is the `perpendicular dot product'; see 1149 * http://mathworld.wolfram.com/PerpDotProduct.html. 1150 * `s' is the parameter for the parametric line for the first segment 1151 * (`u'). 1152 * 1153 * See notation in 1154 * http://softsurfer.com/Archive/algorithm_0104/algorithm_0104B.htm. 1155 * Calculations are done in 16.16, but must handle the squaring of 1156 * line lengths in character space. We scale all vectors by 1/32 to 1157 * avoid overflow. This allows values up to 4095 to be squared. The 1158 * scale factor cancels in the divide. 1159 * 1160 * TODO: the scale factor could be computed from UnitsPerEm. 1161 * 1162 */ 1163 1164 #define cf2_perp( a, b ) \ 1165 ( FT_MulFix( a.x, b.y ) - FT_MulFix( a.y, b.x ) ) 1166 1167 /* round and divide by 32 */ 1168 #define CF2_CS_SCALE( x ) \ 1169 ( ( (x) + 0x10 ) >> 5 ) 1170 1171 FT_Vector u, v, w; /* scaled vectors */ 1172 CF2_Fixed denominator, s; 1173 1174 1175 u.x = CF2_CS_SCALE( SUB_INT32( u2->x, u1->x ) ); 1176 u.y = CF2_CS_SCALE( SUB_INT32( u2->y, u1->y ) ); 1177 v.x = CF2_CS_SCALE( SUB_INT32( v2->x, v1->x ) ); 1178 v.y = CF2_CS_SCALE( SUB_INT32( v2->y, v1->y ) ); 1179 w.x = CF2_CS_SCALE( SUB_INT32( v1->x, u1->x ) ); 1180 w.y = CF2_CS_SCALE( SUB_INT32( v1->y, u1->y ) ); 1181 1182 denominator = cf2_perp( u, v ); 1183 1184 if ( denominator == 0 ) 1185 return FALSE; /* parallel or coincident lines */ 1186 1187 s = FT_DivFix( cf2_perp( w, v ), denominator ); 1188 1189 intersection->x = ADD_INT32( u1->x, 1190 FT_MulFix( s, SUB_INT32( u2->x, u1->x ) ) ); 1191 intersection->y = ADD_INT32( u1->y, 1192 FT_MulFix( s, SUB_INT32( u2->y, u1->y ) ) ); 1193 1194 1195 /* 1196 * Special case snapping for horizontal and vertical lines. 1197 * This cleans up intersections and reduces problems with winding 1198 * order detection. 1199 * Sample case is sbc cd KozGoPr6N-Medium.otf 20 16685. 1200 * Note: these calculations are in character space. 1201 * 1202 */ 1203 1204 if ( u1->x == u2->x && 1205 cf2_fixedAbs( SUB_INT32( intersection->x, 1206 u1->x ) ) < glyphpath->snapThreshold ) 1207 intersection->x = u1->x; 1208 if ( u1->y == u2->y && 1209 cf2_fixedAbs( SUB_INT32( intersection->y, 1210 u1->y ) ) < glyphpath->snapThreshold ) 1211 intersection->y = u1->y; 1212 1213 if ( v1->x == v2->x && 1214 cf2_fixedAbs( SUB_INT32( intersection->x, 1215 v1->x ) ) < glyphpath->snapThreshold ) 1216 intersection->x = v1->x; 1217 if ( v1->y == v2->y && 1218 cf2_fixedAbs( SUB_INT32( intersection->y, 1219 v1->y ) ) < glyphpath->snapThreshold ) 1220 intersection->y = v1->y; 1221 1222 /* limit the intersection distance from midpoint of u2 and v1 */ 1223 if ( cf2_fixedAbs( intersection->x - ADD_INT32( u2->x, v1->x ) / 2 ) > 1224 glyphpath->miterLimit || 1225 cf2_fixedAbs( intersection->y - ADD_INT32( u2->y, v1->y ) / 2 ) > 1226 glyphpath->miterLimit ) 1227 return FALSE; 1228 1229 return TRUE; 1230 } 1231 1232 1233 /* 1234 * Push the cached element (glyphpath->prevElem*) to the outline 1235 * consumer. When a darkening offset is used, the end point of the 1236 * cached element may be adjusted to an intersection point or we may 1237 * synthesize a connecting line to the current element. If we are 1238 * closing a subpath, we may also generate a connecting line to the start 1239 * point. 1240 * 1241 * This is where Character Space (CS) is converted to Device Space (DS) 1242 * using a hint map. This calculation must use a HintMap that was valid 1243 * at the time the element was saved. For the first point in a subpath, 1244 * that is a saved HintMap. For most elements, it just means the caller 1245 * has delayed building a HintMap from the current HintMask. 1246 * 1247 * Transform each point with outerTransform and call the outline 1248 * callbacks. This is a general 3x3 transform: 1249 * 1250 * x' = a*x + c*y + tx, y' = b*x + d*y + ty 1251 * 1252 * but it uses 4 elements from CF2_Font and the translation part 1253 * from CF2_GlyphPath. 1254 * 1255 */ 1256 static void cf2_glyphpath_pushPrevElem(CF2_GlyphPath glyphpath,CF2_HintMap hintmap,FT_Vector * nextP0,FT_Vector nextP1,FT_Bool close)1257 cf2_glyphpath_pushPrevElem( CF2_GlyphPath glyphpath, 1258 CF2_HintMap hintmap, 1259 FT_Vector* nextP0, 1260 FT_Vector nextP1, 1261 FT_Bool close ) 1262 { 1263 CF2_CallbackParamsRec params; 1264 1265 FT_Vector* prevP0; 1266 FT_Vector* prevP1; 1267 1268 FT_Vector intersection = { 0, 0 }; 1269 FT_Bool useIntersection = FALSE; 1270 1271 1272 FT_ASSERT( glyphpath->prevElemOp == CF2_PathOpLineTo || 1273 glyphpath->prevElemOp == CF2_PathOpCubeTo ); 1274 1275 if ( glyphpath->prevElemOp == CF2_PathOpLineTo ) 1276 { 1277 prevP0 = &glyphpath->prevElemP0; 1278 prevP1 = &glyphpath->prevElemP1; 1279 } 1280 else 1281 { 1282 prevP0 = &glyphpath->prevElemP2; 1283 prevP1 = &glyphpath->prevElemP3; 1284 } 1285 1286 /* optimization: if previous and next elements are offset by the same */ 1287 /* amount, then there will be no gap, and no need to compute an */ 1288 /* intersection. */ 1289 if ( prevP1->x != nextP0->x || prevP1->y != nextP0->y ) 1290 { 1291 /* previous element does not join next element: */ 1292 /* adjust end point of previous element to the intersection */ 1293 useIntersection = cf2_glyphpath_computeIntersection( glyphpath, 1294 prevP0, 1295 prevP1, 1296 nextP0, 1297 &nextP1, 1298 &intersection ); 1299 if ( useIntersection ) 1300 { 1301 /* modify the last point of the cached element (either line or */ 1302 /* curve) */ 1303 *prevP1 = intersection; 1304 } 1305 } 1306 1307 params.pt0 = glyphpath->currentDS; 1308 1309 switch( glyphpath->prevElemOp ) 1310 { 1311 case CF2_PathOpLineTo: 1312 params.op = CF2_PathOpLineTo; 1313 1314 /* note: pt2 and pt3 are unused */ 1315 1316 if ( close ) 1317 { 1318 /* use first hint map if closing */ 1319 cf2_glyphpath_hintPoint( glyphpath, 1320 &glyphpath->firstHintMap, 1321 ¶ms.pt1, 1322 glyphpath->prevElemP1.x, 1323 glyphpath->prevElemP1.y ); 1324 } 1325 else 1326 { 1327 cf2_glyphpath_hintPoint( glyphpath, 1328 hintmap, 1329 ¶ms.pt1, 1330 glyphpath->prevElemP1.x, 1331 glyphpath->prevElemP1.y ); 1332 } 1333 1334 /* output only non-zero length lines */ 1335 if ( params.pt0.x != params.pt1.x || params.pt0.y != params.pt1.y ) 1336 { 1337 glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); 1338 1339 glyphpath->currentDS = params.pt1; 1340 } 1341 break; 1342 1343 case CF2_PathOpCubeTo: 1344 params.op = CF2_PathOpCubeTo; 1345 1346 /* TODO: should we intersect the interior joins (p1-p2 and p2-p3)? */ 1347 cf2_glyphpath_hintPoint( glyphpath, 1348 hintmap, 1349 ¶ms.pt1, 1350 glyphpath->prevElemP1.x, 1351 glyphpath->prevElemP1.y ); 1352 cf2_glyphpath_hintPoint( glyphpath, 1353 hintmap, 1354 ¶ms.pt2, 1355 glyphpath->prevElemP2.x, 1356 glyphpath->prevElemP2.y ); 1357 cf2_glyphpath_hintPoint( glyphpath, 1358 hintmap, 1359 ¶ms.pt3, 1360 glyphpath->prevElemP3.x, 1361 glyphpath->prevElemP3.y ); 1362 1363 glyphpath->callbacks->cubeTo( glyphpath->callbacks, ¶ms ); 1364 1365 glyphpath->currentDS = params.pt3; 1366 1367 break; 1368 } 1369 1370 if ( !useIntersection || close ) 1371 { 1372 /* insert connecting line between end of previous element and start */ 1373 /* of current one */ 1374 /* note: at the end of a subpath, we might do both, so use `nextP0' */ 1375 /* before we change it, below */ 1376 1377 if ( close ) 1378 { 1379 /* if we are closing the subpath, then nextP0 is in the first */ 1380 /* hint zone */ 1381 cf2_glyphpath_hintPoint( glyphpath, 1382 &glyphpath->firstHintMap, 1383 ¶ms.pt1, 1384 nextP0->x, 1385 nextP0->y ); 1386 } 1387 else 1388 { 1389 cf2_glyphpath_hintPoint( glyphpath, 1390 hintmap, 1391 ¶ms.pt1, 1392 nextP0->x, 1393 nextP0->y ); 1394 } 1395 1396 if ( params.pt1.x != glyphpath->currentDS.x || 1397 params.pt1.y != glyphpath->currentDS.y ) 1398 { 1399 /* length is nonzero */ 1400 params.op = CF2_PathOpLineTo; 1401 params.pt0 = glyphpath->currentDS; 1402 1403 /* note: pt2 and pt3 are unused */ 1404 glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); 1405 1406 glyphpath->currentDS = params.pt1; 1407 } 1408 } 1409 1410 if ( useIntersection ) 1411 { 1412 /* return intersection point to caller */ 1413 *nextP0 = intersection; 1414 } 1415 } 1416 1417 1418 /* push a MoveTo element based on current point and offset of current */ 1419 /* element */ 1420 static void cf2_glyphpath_pushMove(CF2_GlyphPath glyphpath,FT_Vector start)1421 cf2_glyphpath_pushMove( CF2_GlyphPath glyphpath, 1422 FT_Vector start ) 1423 { 1424 CF2_CallbackParamsRec params; 1425 1426 1427 params.op = CF2_PathOpMoveTo; 1428 params.pt0 = glyphpath->currentDS; 1429 1430 /* Test if move has really happened yet; it would have called */ 1431 /* `cf2_hintmap_build' to set `isValid'. */ 1432 if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) ) 1433 { 1434 /* we are here iff first subpath is missing a moveto operator: */ 1435 /* synthesize first moveTo to finish initialization of hintMap */ 1436 cf2_glyphpath_moveTo( glyphpath, 1437 glyphpath->start.x, 1438 glyphpath->start.y ); 1439 } 1440 1441 cf2_glyphpath_hintPoint( glyphpath, 1442 &glyphpath->hintMap, 1443 ¶ms.pt1, 1444 start.x, 1445 start.y ); 1446 1447 /* note: pt2 and pt3 are unused */ 1448 glyphpath->callbacks->moveTo( glyphpath->callbacks, ¶ms ); 1449 1450 glyphpath->currentDS = params.pt1; 1451 glyphpath->offsetStart0 = start; 1452 } 1453 1454 1455 /* 1456 * All coordinates are in character space. 1457 * On input, (x1, y1) and (x2, y2) give line segment. 1458 * On output, (x, y) give offset vector. 1459 * We use a piecewise approximation to trig functions. 1460 * 1461 * TODO: Offset true perpendicular and proper length 1462 * supply the y-translation for hinting here, too, 1463 * that adds yOffset unconditionally to *y. 1464 */ 1465 static void cf2_glyphpath_computeOffset(CF2_GlyphPath glyphpath,CF2_Fixed x1,CF2_Fixed y1,CF2_Fixed x2,CF2_Fixed y2,CF2_Fixed * x,CF2_Fixed * y)1466 cf2_glyphpath_computeOffset( CF2_GlyphPath glyphpath, 1467 CF2_Fixed x1, 1468 CF2_Fixed y1, 1469 CF2_Fixed x2, 1470 CF2_Fixed y2, 1471 CF2_Fixed* x, 1472 CF2_Fixed* y ) 1473 { 1474 CF2_Fixed dx = SUB_INT32( x2, x1 ); 1475 CF2_Fixed dy = SUB_INT32( y2, y1 ); 1476 1477 1478 /* note: negative offsets don't work here; negate deltas to change */ 1479 /* quadrants, below */ 1480 if ( glyphpath->font->reverseWinding ) 1481 { 1482 dx = NEG_INT32( dx ); 1483 dy = NEG_INT32( dy ); 1484 } 1485 1486 *x = *y = 0; 1487 1488 if ( !glyphpath->darken ) 1489 return; 1490 1491 /* add momentum for this path element */ 1492 glyphpath->callbacks->windingMomentum = 1493 ADD_INT32( glyphpath->callbacks->windingMomentum, 1494 cf2_getWindingMomentum( x1, y1, x2, y2 ) ); 1495 1496 /* note: allow mixed integer and fixed multiplication here */ 1497 if ( dx >= 0 ) 1498 { 1499 if ( dy >= 0 ) 1500 { 1501 /* first quadrant, +x +y */ 1502 1503 if ( dx > MUL_INT32( 2, dy ) ) 1504 { 1505 /* +x */ 1506 *x = 0; 1507 *y = 0; 1508 } 1509 else if ( dy > MUL_INT32( 2, dx ) ) 1510 { 1511 /* +y */ 1512 *x = glyphpath->xOffset; 1513 *y = glyphpath->yOffset; 1514 } 1515 else 1516 { 1517 /* +x +y */ 1518 *x = FT_MulFix( cf2_doubleToFixed( 0.7 ), 1519 glyphpath->xOffset ); 1520 *y = FT_MulFix( cf2_doubleToFixed( 1.0 - 0.7 ), 1521 glyphpath->yOffset ); 1522 } 1523 } 1524 else 1525 { 1526 /* fourth quadrant, +x -y */ 1527 1528 if ( dx > MUL_INT32( -2, dy ) ) 1529 { 1530 /* +x */ 1531 *x = 0; 1532 *y = 0; 1533 } 1534 else if ( NEG_INT32( dy ) > MUL_INT32( 2, dx ) ) 1535 { 1536 /* -y */ 1537 *x = NEG_INT32( glyphpath->xOffset ); 1538 *y = glyphpath->yOffset; 1539 } 1540 else 1541 { 1542 /* +x -y */ 1543 *x = FT_MulFix( cf2_doubleToFixed( -0.7 ), 1544 glyphpath->xOffset ); 1545 *y = FT_MulFix( cf2_doubleToFixed( 1.0 - 0.7 ), 1546 glyphpath->yOffset ); 1547 } 1548 } 1549 } 1550 else 1551 { 1552 if ( dy >= 0 ) 1553 { 1554 /* second quadrant, -x +y */ 1555 1556 if ( NEG_INT32( dx ) > MUL_INT32( 2, dy ) ) 1557 { 1558 /* -x */ 1559 *x = 0; 1560 *y = MUL_INT32( 2, glyphpath->yOffset ); 1561 } 1562 else if ( dy > MUL_INT32( -2, dx ) ) 1563 { 1564 /* +y */ 1565 *x = glyphpath->xOffset; 1566 *y = glyphpath->yOffset; 1567 } 1568 else 1569 { 1570 /* -x +y */ 1571 *x = FT_MulFix( cf2_doubleToFixed( 0.7 ), 1572 glyphpath->xOffset ); 1573 *y = FT_MulFix( cf2_doubleToFixed( 1.0 + 0.7 ), 1574 glyphpath->yOffset ); 1575 } 1576 } 1577 else 1578 { 1579 /* third quadrant, -x -y */ 1580 1581 if ( NEG_INT32( dx ) > MUL_INT32( -2, dy ) ) 1582 { 1583 /* -x */ 1584 *x = 0; 1585 *y = MUL_INT32( 2, glyphpath->yOffset ); 1586 } 1587 else if ( NEG_INT32( dy ) > MUL_INT32( -2, dx ) ) 1588 { 1589 /* -y */ 1590 *x = NEG_INT32( glyphpath->xOffset ); 1591 *y = glyphpath->yOffset; 1592 } 1593 else 1594 { 1595 /* -x -y */ 1596 *x = FT_MulFix( cf2_doubleToFixed( -0.7 ), 1597 glyphpath->xOffset ); 1598 *y = FT_MulFix( cf2_doubleToFixed( 1.0 + 0.7 ), 1599 glyphpath->yOffset ); 1600 } 1601 } 1602 } 1603 } 1604 1605 1606 /* 1607 * The functions cf2_glyphpath_{moveTo,lineTo,curveTo,closeOpenPath} are 1608 * called by the interpreter with Character Space (CS) coordinates. Each 1609 * path element is placed into a queue of length one to await the 1610 * calculation of the following element. At that time, the darkening 1611 * offset of the following element is known and joins can be computed, 1612 * including possible modification of this element, before mapping to 1613 * Device Space (DS) and passing it on to the outline consumer. 1614 * 1615 */ 1616 FT_LOCAL_DEF( void ) cf2_glyphpath_moveTo(CF2_GlyphPath glyphpath,CF2_Fixed x,CF2_Fixed y)1617 cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath, 1618 CF2_Fixed x, 1619 CF2_Fixed y ) 1620 { 1621 cf2_glyphpath_closeOpenPath( glyphpath ); 1622 1623 /* save the parameters of the move for later, when we'll know how to */ 1624 /* offset it; */ 1625 /* also save last move point */ 1626 glyphpath->currentCS.x = glyphpath->start.x = x; 1627 glyphpath->currentCS.y = glyphpath->start.y = y; 1628 1629 glyphpath->moveIsPending = TRUE; 1630 1631 /* ensure we have a valid map with current mask */ 1632 if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) || 1633 cf2_hintmask_isNew( glyphpath->hintMask ) ) 1634 cf2_hintmap_build( &glyphpath->hintMap, 1635 glyphpath->hStemHintArray, 1636 glyphpath->vStemHintArray, 1637 glyphpath->hintMask, 1638 glyphpath->hintOriginY, 1639 FALSE ); 1640 1641 /* save a copy of current HintMap to use when drawing initial point */ 1642 glyphpath->firstHintMap = glyphpath->hintMap; /* structure copy */ 1643 } 1644 1645 1646 FT_LOCAL_DEF( void ) cf2_glyphpath_lineTo(CF2_GlyphPath glyphpath,CF2_Fixed x,CF2_Fixed y)1647 cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath, 1648 CF2_Fixed x, 1649 CF2_Fixed y ) 1650 { 1651 CF2_Fixed xOffset, yOffset; 1652 FT_Vector P0, P1; 1653 FT_Bool newHintMap; 1654 1655 /* 1656 * New hints will be applied after cf2_glyphpath_pushPrevElem has run. 1657 * In case this is a synthesized closing line, any new hints should be 1658 * delayed until this path is closed (`cf2_hintmask_isNew' will be 1659 * called again before the next line or curve). 1660 */ 1661 1662 /* true if new hint map not on close */ 1663 newHintMap = cf2_hintmask_isNew( glyphpath->hintMask ) && 1664 !glyphpath->pathIsClosing; 1665 1666 /* 1667 * Zero-length lines may occur in the charstring. Because we cannot 1668 * compute darkening offsets or intersections from zero-length lines, 1669 * it is best to remove them and avoid artifacts. However, zero-length 1670 * lines in CS at the start of a new hint map can generate non-zero 1671 * lines in DS due to hint substitution. We detect a change in hint 1672 * map here and pass those zero-length lines along. 1673 */ 1674 1675 /* 1676 * Note: Find explicitly closed paths here with a conditional 1677 * breakpoint using 1678 * 1679 * !gp->pathIsClosing && gp->start.x == x && gp->start.y == y 1680 * 1681 */ 1682 1683 if ( glyphpath->currentCS.x == x && 1684 glyphpath->currentCS.y == y && 1685 !newHintMap ) 1686 /* 1687 * Ignore zero-length lines in CS where the hint map is the same 1688 * because the line in DS will also be zero length. 1689 * 1690 * Ignore zero-length lines when we synthesize a closing line because 1691 * the close will be handled in cf2_glyphPath_pushPrevElem. 1692 */ 1693 return; 1694 1695 cf2_glyphpath_computeOffset( glyphpath, 1696 glyphpath->currentCS.x, 1697 glyphpath->currentCS.y, 1698 x, 1699 y, 1700 &xOffset, 1701 &yOffset ); 1702 1703 /* construct offset points */ 1704 P0.x = ADD_INT32( glyphpath->currentCS.x, xOffset ); 1705 P0.y = ADD_INT32( glyphpath->currentCS.y, yOffset ); 1706 P1.x = ADD_INT32( x, xOffset ); 1707 P1.y = ADD_INT32( y, yOffset ); 1708 1709 if ( glyphpath->moveIsPending ) 1710 { 1711 /* emit offset 1st point as MoveTo */ 1712 cf2_glyphpath_pushMove( glyphpath, P0 ); 1713 1714 glyphpath->moveIsPending = FALSE; /* adjust state machine */ 1715 glyphpath->pathIsOpen = TRUE; 1716 1717 glyphpath->offsetStart1 = P1; /* record second point */ 1718 } 1719 1720 if ( glyphpath->elemIsQueued ) 1721 { 1722 FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) || 1723 glyphpath->hintMap.count == 0 ); 1724 1725 cf2_glyphpath_pushPrevElem( glyphpath, 1726 &glyphpath->hintMap, 1727 &P0, 1728 P1, 1729 FALSE ); 1730 } 1731 1732 /* queue the current element with offset points */ 1733 glyphpath->elemIsQueued = TRUE; 1734 glyphpath->prevElemOp = CF2_PathOpLineTo; 1735 glyphpath->prevElemP0 = P0; 1736 glyphpath->prevElemP1 = P1; 1737 1738 /* update current map */ 1739 if ( newHintMap ) 1740 cf2_hintmap_build( &glyphpath->hintMap, 1741 glyphpath->hStemHintArray, 1742 glyphpath->vStemHintArray, 1743 glyphpath->hintMask, 1744 glyphpath->hintOriginY, 1745 FALSE ); 1746 1747 glyphpath->currentCS.x = x; /* pre-offset current point */ 1748 glyphpath->currentCS.y = y; 1749 } 1750 1751 1752 FT_LOCAL_DEF( void ) cf2_glyphpath_curveTo(CF2_GlyphPath glyphpath,CF2_Fixed x1,CF2_Fixed y1,CF2_Fixed x2,CF2_Fixed y2,CF2_Fixed x3,CF2_Fixed y3)1753 cf2_glyphpath_curveTo( CF2_GlyphPath glyphpath, 1754 CF2_Fixed x1, 1755 CF2_Fixed y1, 1756 CF2_Fixed x2, 1757 CF2_Fixed y2, 1758 CF2_Fixed x3, 1759 CF2_Fixed y3 ) 1760 { 1761 CF2_Fixed xOffset1, yOffset1, xOffset3, yOffset3; 1762 FT_Vector P0, P1, P2, P3; 1763 1764 1765 /* TODO: ignore zero length portions of curve?? */ 1766 cf2_glyphpath_computeOffset( glyphpath, 1767 glyphpath->currentCS.x, 1768 glyphpath->currentCS.y, 1769 x1, 1770 y1, 1771 &xOffset1, 1772 &yOffset1 ); 1773 cf2_glyphpath_computeOffset( glyphpath, 1774 x2, 1775 y2, 1776 x3, 1777 y3, 1778 &xOffset3, 1779 &yOffset3 ); 1780 1781 /* add momentum from the middle segment */ 1782 glyphpath->callbacks->windingMomentum = 1783 ADD_INT32( glyphpath->callbacks->windingMomentum, 1784 cf2_getWindingMomentum( x1, y1, x2, y2 ) ); 1785 1786 /* construct offset points */ 1787 P0.x = ADD_INT32( glyphpath->currentCS.x, xOffset1 ); 1788 P0.y = ADD_INT32( glyphpath->currentCS.y, yOffset1 ); 1789 P1.x = ADD_INT32( x1, xOffset1 ); 1790 P1.y = ADD_INT32( y1, yOffset1 ); 1791 /* note: preserve angle of final segment by using offset3 at both ends */ 1792 P2.x = ADD_INT32( x2, xOffset3 ); 1793 P2.y = ADD_INT32( y2, yOffset3 ); 1794 P3.x = ADD_INT32( x3, xOffset3 ); 1795 P3.y = ADD_INT32( y3, yOffset3 ); 1796 1797 if ( glyphpath->moveIsPending ) 1798 { 1799 /* emit offset 1st point as MoveTo */ 1800 cf2_glyphpath_pushMove( glyphpath, P0 ); 1801 1802 glyphpath->moveIsPending = FALSE; 1803 glyphpath->pathIsOpen = TRUE; 1804 1805 glyphpath->offsetStart1 = P1; /* record second point */ 1806 } 1807 1808 if ( glyphpath->elemIsQueued ) 1809 { 1810 FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) || 1811 glyphpath->hintMap.count == 0 ); 1812 1813 cf2_glyphpath_pushPrevElem( glyphpath, 1814 &glyphpath->hintMap, 1815 &P0, 1816 P1, 1817 FALSE ); 1818 } 1819 1820 /* queue the current element with offset points */ 1821 glyphpath->elemIsQueued = TRUE; 1822 glyphpath->prevElemOp = CF2_PathOpCubeTo; 1823 glyphpath->prevElemP0 = P0; 1824 glyphpath->prevElemP1 = P1; 1825 glyphpath->prevElemP2 = P2; 1826 glyphpath->prevElemP3 = P3; 1827 1828 /* update current map */ 1829 if ( cf2_hintmask_isNew( glyphpath->hintMask ) ) 1830 cf2_hintmap_build( &glyphpath->hintMap, 1831 glyphpath->hStemHintArray, 1832 glyphpath->vStemHintArray, 1833 glyphpath->hintMask, 1834 glyphpath->hintOriginY, 1835 FALSE ); 1836 1837 glyphpath->currentCS.x = x3; /* pre-offset current point */ 1838 glyphpath->currentCS.y = y3; 1839 } 1840 1841 1842 FT_LOCAL_DEF( void ) cf2_glyphpath_closeOpenPath(CF2_GlyphPath glyphpath)1843 cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath ) 1844 { 1845 if ( glyphpath->pathIsOpen ) 1846 { 1847 /* 1848 * A closing line in Character Space line is always generated below 1849 * with `cf2_glyphPath_lineTo'. It may be ignored later if it turns 1850 * out to be zero length in Device Space. 1851 */ 1852 glyphpath->pathIsClosing = TRUE; 1853 1854 cf2_glyphpath_lineTo( glyphpath, 1855 glyphpath->start.x, 1856 glyphpath->start.y ); 1857 1858 /* empty the final element from the queue and close the path */ 1859 if ( glyphpath->elemIsQueued ) 1860 cf2_glyphpath_pushPrevElem( glyphpath, 1861 &glyphpath->hintMap, 1862 &glyphpath->offsetStart0, 1863 glyphpath->offsetStart1, 1864 TRUE ); 1865 1866 /* reset state machine */ 1867 glyphpath->moveIsPending = TRUE; 1868 glyphpath->pathIsOpen = FALSE; 1869 glyphpath->pathIsClosing = FALSE; 1870 glyphpath->elemIsQueued = FALSE; 1871 } 1872 } 1873 1874 1875 /* END */ 1876