1 /**************************************************************************** 2 * 3 * ftoutln.c 4 * 5 * FreeType outline management (body). 6 * 7 * Copyright (C) 1996-2019 by 8 * David Turner, Robert Wilhelm, and Werner Lemberg. 9 * 10 * This file is part of the FreeType project, and may only be used, 11 * modified, and distributed under the terms of the FreeType project 12 * license, LICENSE.TXT. By continuing to use, modify, or distribute 13 * this file you indicate that you have read the license and 14 * understand and accept it fully. 15 * 16 */ 17 18 19 #include <ft2build.h> 20 #include FT_OUTLINE_H 21 #include FT_INTERNAL_OBJECTS_H 22 #include FT_INTERNAL_CALC_H 23 #include FT_INTERNAL_DEBUG_H 24 #include FT_TRIGONOMETRY_H 25 26 27 /************************************************************************** 28 * 29 * The macro FT_COMPONENT is used in trace mode. It is an implicit 30 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 31 * messages during execution. 32 */ 33 #undef FT_COMPONENT 34 #define FT_COMPONENT outline 35 36 37 static 38 const FT_Outline null_outline = { 0, 0, NULL, NULL, NULL, 0 }; 39 40 41 /* documentation is in ftoutln.h */ 42 43 FT_EXPORT_DEF( FT_Error ) FT_Outline_Decompose(FT_Outline * outline,const FT_Outline_Funcs * func_interface,void * user)44 FT_Outline_Decompose( FT_Outline* outline, 45 const FT_Outline_Funcs* func_interface, 46 void* user ) 47 { 48 #undef SCALED 49 #define SCALED( x ) ( (x) * ( 1L << shift ) - delta ) 50 51 FT_Vector v_last; 52 FT_Vector v_control; 53 FT_Vector v_start; 54 55 FT_Vector* point; 56 FT_Vector* limit; 57 char* tags; 58 59 FT_Error error; 60 61 FT_Int n; /* index of contour in outline */ 62 FT_UInt first; /* index of first point in contour */ 63 FT_Int tag; /* current point's state */ 64 65 FT_Int shift; 66 FT_Pos delta; 67 68 69 if ( !outline ) 70 return FT_THROW( Invalid_Outline ); 71 72 if ( !func_interface ) 73 return FT_THROW( Invalid_Argument ); 74 75 shift = func_interface->shift; 76 delta = func_interface->delta; 77 first = 0; 78 79 for ( n = 0; n < outline->n_contours; n++ ) 80 { 81 FT_Int last; /* index of last point in contour */ 82 83 84 FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n )); 85 86 last = outline->contours[n]; 87 if ( last < 0 ) 88 goto Invalid_Outline; 89 limit = outline->points + last; 90 91 v_start = outline->points[first]; 92 v_start.x = SCALED( v_start.x ); 93 v_start.y = SCALED( v_start.y ); 94 95 v_last = outline->points[last]; 96 v_last.x = SCALED( v_last.x ); 97 v_last.y = SCALED( v_last.y ); 98 99 v_control = v_start; 100 101 point = outline->points + first; 102 tags = outline->tags + first; 103 tag = FT_CURVE_TAG( tags[0] ); 104 105 /* A contour cannot start with a cubic control point! */ 106 if ( tag == FT_CURVE_TAG_CUBIC ) 107 goto Invalid_Outline; 108 109 /* check first point to determine origin */ 110 if ( tag == FT_CURVE_TAG_CONIC ) 111 { 112 /* first point is conic control. Yes, this happens. */ 113 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) 114 { 115 /* start at last point if it is on the curve */ 116 v_start = v_last; 117 limit--; 118 } 119 else 120 { 121 /* if both first and last points are conic, */ 122 /* start at their middle and record its position */ 123 /* for closure */ 124 v_start.x = ( v_start.x + v_last.x ) / 2; 125 v_start.y = ( v_start.y + v_last.y ) / 2; 126 127 /* v_last = v_start; */ 128 } 129 point--; 130 tags--; 131 } 132 133 FT_TRACE5(( " move to (%.2f, %.2f)\n", 134 v_start.x / 64.0, v_start.y / 64.0 )); 135 error = func_interface->move_to( &v_start, user ); 136 if ( error ) 137 goto Exit; 138 139 while ( point < limit ) 140 { 141 point++; 142 tags++; 143 144 tag = FT_CURVE_TAG( tags[0] ); 145 switch ( tag ) 146 { 147 case FT_CURVE_TAG_ON: /* emit a single line_to */ 148 { 149 FT_Vector vec; 150 151 152 vec.x = SCALED( point->x ); 153 vec.y = SCALED( point->y ); 154 155 FT_TRACE5(( " line to (%.2f, %.2f)\n", 156 vec.x / 64.0, vec.y / 64.0 )); 157 error = func_interface->line_to( &vec, user ); 158 if ( error ) 159 goto Exit; 160 continue; 161 } 162 163 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ 164 v_control.x = SCALED( point->x ); 165 v_control.y = SCALED( point->y ); 166 167 Do_Conic: 168 if ( point < limit ) 169 { 170 FT_Vector vec; 171 FT_Vector v_middle; 172 173 174 point++; 175 tags++; 176 tag = FT_CURVE_TAG( tags[0] ); 177 178 vec.x = SCALED( point->x ); 179 vec.y = SCALED( point->y ); 180 181 if ( tag == FT_CURVE_TAG_ON ) 182 { 183 FT_TRACE5(( " conic to (%.2f, %.2f)" 184 " with control (%.2f, %.2f)\n", 185 vec.x / 64.0, vec.y / 64.0, 186 v_control.x / 64.0, v_control.y / 64.0 )); 187 error = func_interface->conic_to( &v_control, &vec, user ); 188 if ( error ) 189 goto Exit; 190 continue; 191 } 192 193 if ( tag != FT_CURVE_TAG_CONIC ) 194 goto Invalid_Outline; 195 196 v_middle.x = ( v_control.x + vec.x ) / 2; 197 v_middle.y = ( v_control.y + vec.y ) / 2; 198 199 FT_TRACE5(( " conic to (%.2f, %.2f)" 200 " with control (%.2f, %.2f)\n", 201 v_middle.x / 64.0, v_middle.y / 64.0, 202 v_control.x / 64.0, v_control.y / 64.0 )); 203 error = func_interface->conic_to( &v_control, &v_middle, user ); 204 if ( error ) 205 goto Exit; 206 207 v_control = vec; 208 goto Do_Conic; 209 } 210 211 FT_TRACE5(( " conic to (%.2f, %.2f)" 212 " with control (%.2f, %.2f)\n", 213 v_start.x / 64.0, v_start.y / 64.0, 214 v_control.x / 64.0, v_control.y / 64.0 )); 215 error = func_interface->conic_to( &v_control, &v_start, user ); 216 goto Close; 217 218 default: /* FT_CURVE_TAG_CUBIC */ 219 { 220 FT_Vector vec1, vec2; 221 222 223 if ( point + 1 > limit || 224 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) 225 goto Invalid_Outline; 226 227 point += 2; 228 tags += 2; 229 230 vec1.x = SCALED( point[-2].x ); 231 vec1.y = SCALED( point[-2].y ); 232 233 vec2.x = SCALED( point[-1].x ); 234 vec2.y = SCALED( point[-1].y ); 235 236 if ( point <= limit ) 237 { 238 FT_Vector vec; 239 240 241 vec.x = SCALED( point->x ); 242 vec.y = SCALED( point->y ); 243 244 FT_TRACE5(( " cubic to (%.2f, %.2f)" 245 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", 246 vec.x / 64.0, vec.y / 64.0, 247 vec1.x / 64.0, vec1.y / 64.0, 248 vec2.x / 64.0, vec2.y / 64.0 )); 249 error = func_interface->cubic_to( &vec1, &vec2, &vec, user ); 250 if ( error ) 251 goto Exit; 252 continue; 253 } 254 255 FT_TRACE5(( " cubic to (%.2f, %.2f)" 256 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", 257 v_start.x / 64.0, v_start.y / 64.0, 258 vec1.x / 64.0, vec1.y / 64.0, 259 vec2.x / 64.0, vec2.y / 64.0 )); 260 error = func_interface->cubic_to( &vec1, &vec2, &v_start, user ); 261 goto Close; 262 } 263 } 264 } 265 266 /* close the contour with a line segment */ 267 FT_TRACE5(( " line to (%.2f, %.2f)\n", 268 v_start.x / 64.0, v_start.y / 64.0 )); 269 error = func_interface->line_to( &v_start, user ); 270 271 Close: 272 if ( error ) 273 goto Exit; 274 275 first = (FT_UInt)last + 1; 276 } 277 278 FT_TRACE5(( "FT_Outline_Decompose: Done\n", n )); 279 return FT_Err_Ok; 280 281 Invalid_Outline: 282 error = FT_THROW( Invalid_Outline ); 283 /* fall through */ 284 285 Exit: 286 FT_TRACE5(( "FT_Outline_Decompose: Error 0x%x\n", error )); 287 return error; 288 } 289 290 291 /* documentation is in ftoutln.h */ 292 293 FT_EXPORT_DEF( FT_Error ) FT_Outline_New(FT_Library library,FT_UInt numPoints,FT_Int numContours,FT_Outline * anoutline)294 FT_Outline_New( FT_Library library, 295 FT_UInt numPoints, 296 FT_Int numContours, 297 FT_Outline *anoutline ) 298 { 299 FT_Error error; 300 FT_Memory memory; 301 302 303 if ( !library ) 304 return FT_THROW( Invalid_Library_Handle ); 305 306 memory = library->memory; 307 308 if ( !anoutline || !memory ) 309 return FT_THROW( Invalid_Argument ); 310 311 *anoutline = null_outline; 312 313 if ( numContours < 0 || 314 (FT_UInt)numContours > numPoints ) 315 return FT_THROW( Invalid_Argument ); 316 317 if ( numPoints > FT_OUTLINE_POINTS_MAX ) 318 return FT_THROW( Array_Too_Large ); 319 320 if ( FT_NEW_ARRAY( anoutline->points, numPoints ) || 321 FT_NEW_ARRAY( anoutline->tags, numPoints ) || 322 FT_NEW_ARRAY( anoutline->contours, numContours ) ) 323 goto Fail; 324 325 anoutline->n_points = (FT_Short)numPoints; 326 anoutline->n_contours = (FT_Short)numContours; 327 anoutline->flags |= FT_OUTLINE_OWNER; 328 329 return FT_Err_Ok; 330 331 Fail: 332 anoutline->flags |= FT_OUTLINE_OWNER; 333 FT_Outline_Done( library, anoutline ); 334 335 return error; 336 } 337 338 339 /* documentation is in ftoutln.h */ 340 341 FT_EXPORT_DEF( FT_Error ) FT_Outline_Check(FT_Outline * outline)342 FT_Outline_Check( FT_Outline* outline ) 343 { 344 if ( outline ) 345 { 346 FT_Int n_points = outline->n_points; 347 FT_Int n_contours = outline->n_contours; 348 FT_Int end0, end; 349 FT_Int n; 350 351 352 /* empty glyph? */ 353 if ( n_points == 0 && n_contours == 0 ) 354 return FT_Err_Ok; 355 356 /* check point and contour counts */ 357 if ( n_points <= 0 || n_contours <= 0 ) 358 goto Bad; 359 360 end0 = end = -1; 361 for ( n = 0; n < n_contours; n++ ) 362 { 363 end = outline->contours[n]; 364 365 /* note that we don't accept empty contours */ 366 if ( end <= end0 || end >= n_points ) 367 goto Bad; 368 369 end0 = end; 370 } 371 372 if ( end != n_points - 1 ) 373 goto Bad; 374 375 /* XXX: check the tags array */ 376 return FT_Err_Ok; 377 } 378 379 Bad: 380 return FT_THROW( Invalid_Argument ); 381 } 382 383 384 /* documentation is in ftoutln.h */ 385 386 FT_EXPORT_DEF( FT_Error ) FT_Outline_Copy(const FT_Outline * source,FT_Outline * target)387 FT_Outline_Copy( const FT_Outline* source, 388 FT_Outline *target ) 389 { 390 FT_Int is_owner; 391 392 393 if ( !source || !target ) 394 return FT_THROW( Invalid_Outline ); 395 396 if ( source->n_points != target->n_points || 397 source->n_contours != target->n_contours ) 398 return FT_THROW( Invalid_Argument ); 399 400 if ( source == target ) 401 return FT_Err_Ok; 402 403 if ( source->n_points ) 404 { 405 FT_ARRAY_COPY( target->points, source->points, source->n_points ); 406 FT_ARRAY_COPY( target->tags, source->tags, source->n_points ); 407 } 408 409 if ( source->n_contours ) 410 FT_ARRAY_COPY( target->contours, source->contours, source->n_contours ); 411 412 /* copy all flags, except the `FT_OUTLINE_OWNER' one */ 413 is_owner = target->flags & FT_OUTLINE_OWNER; 414 target->flags = source->flags; 415 416 target->flags &= ~FT_OUTLINE_OWNER; 417 target->flags |= is_owner; 418 419 return FT_Err_Ok; 420 } 421 422 423 /* documentation is in ftoutln.h */ 424 425 FT_EXPORT_DEF( FT_Error ) FT_Outline_Done(FT_Library library,FT_Outline * outline)426 FT_Outline_Done( FT_Library library, 427 FT_Outline* outline ) 428 { 429 FT_Memory memory; 430 431 432 if ( !library ) 433 return FT_THROW( Invalid_Library_Handle ); 434 435 if ( !outline ) 436 return FT_THROW( Invalid_Outline ); 437 438 memory = library->memory; 439 440 if ( !memory ) 441 return FT_THROW( Invalid_Argument ); 442 443 if ( outline->flags & FT_OUTLINE_OWNER ) 444 { 445 FT_FREE( outline->points ); 446 FT_FREE( outline->tags ); 447 FT_FREE( outline->contours ); 448 } 449 *outline = null_outline; 450 451 return FT_Err_Ok; 452 } 453 454 455 /* documentation is in ftoutln.h */ 456 457 FT_EXPORT_DEF( void ) FT_Outline_Get_CBox(const FT_Outline * outline,FT_BBox * acbox)458 FT_Outline_Get_CBox( const FT_Outline* outline, 459 FT_BBox *acbox ) 460 { 461 FT_Pos xMin, yMin, xMax, yMax; 462 463 464 if ( outline && acbox ) 465 { 466 if ( outline->n_points == 0 ) 467 { 468 xMin = 0; 469 yMin = 0; 470 xMax = 0; 471 yMax = 0; 472 } 473 else 474 { 475 FT_Vector* vec = outline->points; 476 FT_Vector* limit = vec + outline->n_points; 477 478 479 xMin = xMax = vec->x; 480 yMin = yMax = vec->y; 481 vec++; 482 483 for ( ; vec < limit; vec++ ) 484 { 485 FT_Pos x, y; 486 487 488 x = vec->x; 489 if ( x < xMin ) xMin = x; 490 if ( x > xMax ) xMax = x; 491 492 y = vec->y; 493 if ( y < yMin ) yMin = y; 494 if ( y > yMax ) yMax = y; 495 } 496 } 497 acbox->xMin = xMin; 498 acbox->xMax = xMax; 499 acbox->yMin = yMin; 500 acbox->yMax = yMax; 501 } 502 } 503 504 505 /* documentation is in ftoutln.h */ 506 507 FT_EXPORT_DEF( void ) FT_Outline_Translate(const FT_Outline * outline,FT_Pos xOffset,FT_Pos yOffset)508 FT_Outline_Translate( const FT_Outline* outline, 509 FT_Pos xOffset, 510 FT_Pos yOffset ) 511 { 512 FT_UShort n; 513 FT_Vector* vec; 514 515 516 if ( !outline ) 517 return; 518 519 vec = outline->points; 520 521 for ( n = 0; n < outline->n_points; n++ ) 522 { 523 vec->x = ADD_LONG( vec->x, xOffset ); 524 vec->y = ADD_LONG( vec->y, yOffset ); 525 vec++; 526 } 527 } 528 529 530 /* documentation is in ftoutln.h */ 531 532 FT_EXPORT_DEF( void ) FT_Outline_Reverse(FT_Outline * outline)533 FT_Outline_Reverse( FT_Outline* outline ) 534 { 535 FT_UShort n; 536 FT_Int first, last; 537 538 539 if ( !outline ) 540 return; 541 542 first = 0; 543 544 for ( n = 0; n < outline->n_contours; n++ ) 545 { 546 last = outline->contours[n]; 547 548 /* reverse point table */ 549 { 550 FT_Vector* p = outline->points + first; 551 FT_Vector* q = outline->points + last; 552 FT_Vector swap; 553 554 555 while ( p < q ) 556 { 557 swap = *p; 558 *p = *q; 559 *q = swap; 560 p++; 561 q--; 562 } 563 } 564 565 /* reverse tags table */ 566 { 567 char* p = outline->tags + first; 568 char* q = outline->tags + last; 569 570 571 while ( p < q ) 572 { 573 char swap; 574 575 576 swap = *p; 577 *p = *q; 578 *q = swap; 579 p++; 580 q--; 581 } 582 } 583 584 first = last + 1; 585 } 586 587 outline->flags ^= FT_OUTLINE_REVERSE_FILL; 588 } 589 590 591 /* documentation is in ftoutln.h */ 592 593 FT_EXPORT_DEF( FT_Error ) FT_Outline_Render(FT_Library library,FT_Outline * outline,FT_Raster_Params * params)594 FT_Outline_Render( FT_Library library, 595 FT_Outline* outline, 596 FT_Raster_Params* params ) 597 { 598 FT_Error error; 599 FT_Renderer renderer; 600 FT_ListNode node; 601 FT_BBox cbox; 602 603 604 if ( !library ) 605 return FT_THROW( Invalid_Library_Handle ); 606 607 if ( !outline ) 608 return FT_THROW( Invalid_Outline ); 609 610 if ( !params ) 611 return FT_THROW( Invalid_Argument ); 612 613 FT_Outline_Get_CBox( outline, &cbox ); 614 if ( cbox.xMin < -0x1000000L || cbox.yMin < -0x1000000L || 615 cbox.xMax > 0x1000000L || cbox.yMax > 0x1000000L ) 616 return FT_THROW( Invalid_Outline ); 617 618 renderer = library->cur_renderer; 619 node = library->renderers.head; 620 621 params->source = (void*)outline; 622 623 /* preset clip_box for direct mode */ 624 if ( params->flags & FT_RASTER_FLAG_DIRECT && 625 !( params->flags & FT_RASTER_FLAG_CLIP ) ) 626 { 627 params->clip_box.xMin = cbox.xMin >> 6; 628 params->clip_box.yMin = cbox.yMin >> 6; 629 params->clip_box.xMax = ( cbox.xMax + 63 ) >> 6; 630 params->clip_box.yMax = ( cbox.yMax + 63 ) >> 6; 631 } 632 633 error = FT_ERR( Cannot_Render_Glyph ); 634 while ( renderer ) 635 { 636 error = renderer->raster_render( renderer->raster, params ); 637 if ( !error || FT_ERR_NEQ( error, Cannot_Render_Glyph ) ) 638 break; 639 640 /* FT_Err_Cannot_Render_Glyph is returned if the render mode */ 641 /* is unsupported by the current renderer for this glyph image */ 642 /* format */ 643 644 /* now, look for another renderer that supports the same */ 645 /* format */ 646 renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE, 647 &node ); 648 } 649 650 return error; 651 } 652 653 654 /* documentation is in ftoutln.h */ 655 656 FT_EXPORT_DEF( FT_Error ) FT_Outline_Get_Bitmap(FT_Library library,FT_Outline * outline,const FT_Bitmap * abitmap)657 FT_Outline_Get_Bitmap( FT_Library library, 658 FT_Outline* outline, 659 const FT_Bitmap *abitmap ) 660 { 661 FT_Raster_Params params; 662 663 664 if ( !abitmap ) 665 return FT_THROW( Invalid_Argument ); 666 667 /* other checks are delayed to `FT_Outline_Render' */ 668 669 params.target = abitmap; 670 params.flags = 0; 671 672 if ( abitmap->pixel_mode == FT_PIXEL_MODE_GRAY || 673 abitmap->pixel_mode == FT_PIXEL_MODE_LCD || 674 abitmap->pixel_mode == FT_PIXEL_MODE_LCD_V ) 675 params.flags |= FT_RASTER_FLAG_AA; 676 677 return FT_Outline_Render( library, outline, ¶ms ); 678 } 679 680 681 /* documentation is in freetype.h */ 682 683 FT_EXPORT_DEF( void ) FT_Vector_Transform(FT_Vector * vector,const FT_Matrix * matrix)684 FT_Vector_Transform( FT_Vector* vector, 685 const FT_Matrix* matrix ) 686 { 687 FT_Pos xz, yz; 688 689 690 if ( !vector || !matrix ) 691 return; 692 693 xz = FT_MulFix( vector->x, matrix->xx ) + 694 FT_MulFix( vector->y, matrix->xy ); 695 696 yz = FT_MulFix( vector->x, matrix->yx ) + 697 FT_MulFix( vector->y, matrix->yy ); 698 699 vector->x = xz; 700 vector->y = yz; 701 } 702 703 704 /* documentation is in ftoutln.h */ 705 706 FT_EXPORT_DEF( void ) FT_Outline_Transform(const FT_Outline * outline,const FT_Matrix * matrix)707 FT_Outline_Transform( const FT_Outline* outline, 708 const FT_Matrix* matrix ) 709 { 710 FT_Vector* vec; 711 FT_Vector* limit; 712 713 714 if ( !outline || !matrix ) 715 return; 716 717 vec = outline->points; 718 limit = vec + outline->n_points; 719 720 for ( ; vec < limit; vec++ ) 721 FT_Vector_Transform( vec, matrix ); 722 } 723 724 725 #if 0 726 727 #define FT_OUTLINE_GET_CONTOUR( outline, c, first, last ) \ 728 do \ 729 { \ 730 (first) = ( c > 0 ) ? (outline)->points + \ 731 (outline)->contours[c - 1] + 1 \ 732 : (outline)->points; \ 733 (last) = (outline)->points + (outline)->contours[c]; \ 734 } while ( 0 ) 735 736 737 /* Is a point in some contour? */ 738 /* */ 739 /* We treat every point of the contour as if it */ 740 /* it were ON. That is, we allow false positives, */ 741 /* but disallow false negatives. (XXX really?) */ 742 static FT_Bool 743 ft_contour_has( FT_Outline* outline, 744 FT_Short c, 745 FT_Vector* point ) 746 { 747 FT_Vector* first; 748 FT_Vector* last; 749 FT_Vector* a; 750 FT_Vector* b; 751 FT_UInt n = 0; 752 753 754 FT_OUTLINE_GET_CONTOUR( outline, c, first, last ); 755 756 for ( a = first; a <= last; a++ ) 757 { 758 FT_Pos x; 759 FT_Int intersect; 760 761 762 b = ( a == last ) ? first : a + 1; 763 764 intersect = ( a->y - point->y ) ^ ( b->y - point->y ); 765 766 /* a and b are on the same side */ 767 if ( intersect >= 0 ) 768 { 769 if ( intersect == 0 && a->y == point->y ) 770 { 771 if ( ( a->x <= point->x && b->x >= point->x ) || 772 ( a->x >= point->x && b->x <= point->x ) ) 773 return 1; 774 } 775 776 continue; 777 } 778 779 x = a->x + ( b->x - a->x ) * (point->y - a->y ) / ( b->y - a->y ); 780 781 if ( x < point->x ) 782 n++; 783 else if ( x == point->x ) 784 return 1; 785 } 786 787 return n & 1; 788 } 789 790 791 static FT_Bool 792 ft_contour_enclosed( FT_Outline* outline, 793 FT_UShort c ) 794 { 795 FT_Vector* first; 796 FT_Vector* last; 797 FT_Short i; 798 799 800 FT_OUTLINE_GET_CONTOUR( outline, c, first, last ); 801 802 for ( i = 0; i < outline->n_contours; i++ ) 803 { 804 if ( i != c && ft_contour_has( outline, i, first ) ) 805 { 806 FT_Vector* pt; 807 808 809 for ( pt = first + 1; pt <= last; pt++ ) 810 if ( !ft_contour_has( outline, i, pt ) ) 811 return 0; 812 813 return 1; 814 } 815 } 816 817 return 0; 818 } 819 820 821 /* This version differs from the public one in that each */ 822 /* part (contour not enclosed in another contour) of the */ 823 /* outline is checked for orientation. This is */ 824 /* necessary for some buggy CJK fonts. */ 825 static FT_Orientation 826 ft_outline_get_orientation( FT_Outline* outline ) 827 { 828 FT_Short i; 829 FT_Vector* first; 830 FT_Vector* last; 831 FT_Orientation orient = FT_ORIENTATION_NONE; 832 833 834 first = outline->points; 835 for ( i = 0; i < outline->n_contours; i++, first = last + 1 ) 836 { 837 FT_Vector* point; 838 FT_Vector* xmin_point; 839 FT_Pos xmin; 840 841 842 last = outline->points + outline->contours[i]; 843 844 /* skip degenerate contours */ 845 if ( last < first + 2 ) 846 continue; 847 848 if ( ft_contour_enclosed( outline, i ) ) 849 continue; 850 851 xmin = first->x; 852 xmin_point = first; 853 854 for ( point = first + 1; point <= last; point++ ) 855 { 856 if ( point->x < xmin ) 857 { 858 xmin = point->x; 859 xmin_point = point; 860 } 861 } 862 863 /* check the orientation of the contour */ 864 { 865 FT_Vector* prev; 866 FT_Vector* next; 867 FT_Orientation o; 868 869 870 prev = ( xmin_point == first ) ? last : xmin_point - 1; 871 next = ( xmin_point == last ) ? first : xmin_point + 1; 872 873 if ( FT_Atan2( prev->x - xmin_point->x, prev->y - xmin_point->y ) > 874 FT_Atan2( next->x - xmin_point->x, next->y - xmin_point->y ) ) 875 o = FT_ORIENTATION_POSTSCRIPT; 876 else 877 o = FT_ORIENTATION_TRUETYPE; 878 879 if ( orient == FT_ORIENTATION_NONE ) 880 orient = o; 881 else if ( orient != o ) 882 return FT_ORIENTATION_NONE; 883 } 884 } 885 886 return orient; 887 } 888 889 #endif /* 0 */ 890 891 892 /* documentation is in ftoutln.h */ 893 894 FT_EXPORT_DEF( FT_Error ) FT_Outline_Embolden(FT_Outline * outline,FT_Pos strength)895 FT_Outline_Embolden( FT_Outline* outline, 896 FT_Pos strength ) 897 { 898 return FT_Outline_EmboldenXY( outline, strength, strength ); 899 } 900 901 902 /* documentation is in ftoutln.h */ 903 904 FT_EXPORT_DEF( FT_Error ) FT_Outline_EmboldenXY(FT_Outline * outline,FT_Pos xstrength,FT_Pos ystrength)905 FT_Outline_EmboldenXY( FT_Outline* outline, 906 FT_Pos xstrength, 907 FT_Pos ystrength ) 908 { 909 FT_Vector* points; 910 FT_Int c, first, last; 911 FT_Orientation orientation; 912 913 914 if ( !outline ) 915 return FT_THROW( Invalid_Outline ); 916 917 xstrength /= 2; 918 ystrength /= 2; 919 if ( xstrength == 0 && ystrength == 0 ) 920 return FT_Err_Ok; 921 922 orientation = FT_Outline_Get_Orientation( outline ); 923 if ( orientation == FT_ORIENTATION_NONE ) 924 { 925 if ( outline->n_contours ) 926 return FT_THROW( Invalid_Argument ); 927 else 928 return FT_Err_Ok; 929 } 930 931 points = outline->points; 932 933 first = 0; 934 for ( c = 0; c < outline->n_contours; c++ ) 935 { 936 FT_Vector in, out, anchor, shift; 937 FT_Fixed l_in, l_out, l_anchor = 0, l, q, d; 938 FT_Int i, j, k; 939 940 941 l_in = 0; 942 last = outline->contours[c]; 943 944 /* pacify compiler */ 945 in.x = in.y = anchor.x = anchor.y = 0; 946 947 /* Counter j cycles though the points; counter i advances only */ 948 /* when points are moved; anchor k marks the first moved point. */ 949 for ( i = last, j = first, k = -1; 950 j != i && i != k; 951 j = j < last ? j + 1 : first ) 952 { 953 if ( j != k ) 954 { 955 out.x = points[j].x - points[i].x; 956 out.y = points[j].y - points[i].y; 957 l_out = (FT_Fixed)FT_Vector_NormLen( &out ); 958 959 if ( l_out == 0 ) 960 continue; 961 } 962 else 963 { 964 out = anchor; 965 l_out = l_anchor; 966 } 967 968 if ( l_in != 0 ) 969 { 970 if ( k < 0 ) 971 { 972 k = i; 973 anchor = in; 974 l_anchor = l_in; 975 } 976 977 d = FT_MulFix( in.x, out.x ) + FT_MulFix( in.y, out.y ); 978 979 /* shift only if turn is less than ~160 degrees */ 980 if ( d > -0xF000L ) 981 { 982 d = d + 0x10000L; 983 984 /* shift components along lateral bisector in proper orientation */ 985 shift.x = in.y + out.y; 986 shift.y = in.x + out.x; 987 988 if ( orientation == FT_ORIENTATION_TRUETYPE ) 989 shift.x = -shift.x; 990 else 991 shift.y = -shift.y; 992 993 /* restrict shift magnitude to better handle collapsing segments */ 994 q = FT_MulFix( out.x, in.y ) - FT_MulFix( out.y, in.x ); 995 if ( orientation == FT_ORIENTATION_TRUETYPE ) 996 q = -q; 997 998 l = FT_MIN( l_in, l_out ); 999 1000 /* non-strict inequalities avoid divide-by-zero when q == l == 0 */ 1001 if ( FT_MulFix( xstrength, q ) <= FT_MulFix( l, d ) ) 1002 shift.x = FT_MulDiv( shift.x, xstrength, d ); 1003 else 1004 shift.x = FT_MulDiv( shift.x, l, q ); 1005 1006 1007 if ( FT_MulFix( ystrength, q ) <= FT_MulFix( l, d ) ) 1008 shift.y = FT_MulDiv( shift.y, ystrength, d ); 1009 else 1010 shift.y = FT_MulDiv( shift.y, l, q ); 1011 } 1012 else 1013 shift.x = shift.y = 0; 1014 1015 for ( ; 1016 i != j; 1017 i = i < last ? i + 1 : first ) 1018 { 1019 points[i].x += xstrength + shift.x; 1020 points[i].y += ystrength + shift.y; 1021 } 1022 } 1023 else 1024 i = j; 1025 1026 in = out; 1027 l_in = l_out; 1028 } 1029 1030 first = last + 1; 1031 } 1032 1033 return FT_Err_Ok; 1034 } 1035 1036 1037 /* documentation is in ftoutln.h */ 1038 1039 FT_EXPORT_DEF( FT_Orientation ) FT_Outline_Get_Orientation(FT_Outline * outline)1040 FT_Outline_Get_Orientation( FT_Outline* outline ) 1041 { 1042 FT_BBox cbox = { 0, 0, 0, 0 }; 1043 FT_Int xshift, yshift; 1044 FT_Vector* points; 1045 FT_Vector v_prev, v_cur; 1046 FT_Int c, n, first; 1047 FT_Pos area = 0; 1048 1049 1050 if ( !outline || outline->n_points <= 0 ) 1051 return FT_ORIENTATION_TRUETYPE; 1052 1053 /* We use the nonzero winding rule to find the orientation. */ 1054 /* Since glyph outlines behave much more `regular' than arbitrary */ 1055 /* cubic or quadratic curves, this test deals with the polygon */ 1056 /* only that is spanned up by the control points. */ 1057 1058 FT_Outline_Get_CBox( outline, &cbox ); 1059 1060 /* Handle collapsed outlines to avoid undefined FT_MSB. */ 1061 if ( cbox.xMin == cbox.xMax || cbox.yMin == cbox.yMax ) 1062 return FT_ORIENTATION_NONE; 1063 1064 xshift = FT_MSB( (FT_UInt32)( FT_ABS( cbox.xMax ) | 1065 FT_ABS( cbox.xMin ) ) ) - 14; 1066 xshift = FT_MAX( xshift, 0 ); 1067 1068 yshift = FT_MSB( (FT_UInt32)( cbox.yMax - cbox.yMin ) ) - 14; 1069 yshift = FT_MAX( yshift, 0 ); 1070 1071 points = outline->points; 1072 1073 first = 0; 1074 for ( c = 0; c < outline->n_contours; c++ ) 1075 { 1076 FT_Int last = outline->contours[c]; 1077 1078 1079 v_prev.x = points[last].x >> xshift; 1080 v_prev.y = points[last].y >> yshift; 1081 1082 for ( n = first; n <= last; n++ ) 1083 { 1084 v_cur.x = points[n].x >> xshift; 1085 v_cur.y = points[n].y >> yshift; 1086 1087 area = ADD_LONG( area, 1088 MUL_LONG( v_cur.y - v_prev.y, 1089 v_cur.x + v_prev.x ) ); 1090 1091 v_prev = v_cur; 1092 } 1093 1094 first = last + 1; 1095 } 1096 1097 if ( area > 0 ) 1098 return FT_ORIENTATION_POSTSCRIPT; 1099 else if ( area < 0 ) 1100 return FT_ORIENTATION_TRUETYPE; 1101 else 1102 return FT_ORIENTATION_NONE; 1103 } 1104 1105 1106 /* END */ 1107