1 /***************************************************************************/ 2 /* */ 3 /* ftstroke.c */ 4 /* */ 5 /* FreeType path stroker (body). */ 6 /* */ 7 /* Copyright 2002-2006, 2008-2011, 2013 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_STROKER_H 21 #include FT_TRIGONOMETRY_H 22 #include FT_OUTLINE_H 23 #include FT_INTERNAL_MEMORY_H 24 #include FT_INTERNAL_DEBUG_H 25 #include FT_INTERNAL_OBJECTS_H 26 27 28 /* documentation is in ftstroke.h */ 29 30 FT_EXPORT_DEF( FT_StrokerBorder ) FT_Outline_GetInsideBorder(FT_Outline * outline)31 FT_Outline_GetInsideBorder( FT_Outline* outline ) 32 { 33 FT_Orientation o = FT_Outline_Get_Orientation( outline ); 34 35 36 return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT 37 : FT_STROKER_BORDER_LEFT; 38 } 39 40 41 /* documentation is in ftstroke.h */ 42 43 FT_EXPORT_DEF( FT_StrokerBorder ) FT_Outline_GetOutsideBorder(FT_Outline * outline)44 FT_Outline_GetOutsideBorder( FT_Outline* outline ) 45 { 46 FT_Orientation o = FT_Outline_Get_Orientation( outline ); 47 48 49 return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT 50 : FT_STROKER_BORDER_RIGHT; 51 } 52 53 54 /*************************************************************************/ 55 /*************************************************************************/ 56 /***** *****/ 57 /***** BEZIER COMPUTATIONS *****/ 58 /***** *****/ 59 /*************************************************************************/ 60 /*************************************************************************/ 61 62 #define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 ) 63 #define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 8 ) 64 65 #define FT_EPSILON 2 66 67 #define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON ) 68 69 70 static FT_Pos ft_pos_abs(FT_Pos x)71 ft_pos_abs( FT_Pos x ) 72 { 73 return x >= 0 ? x : -x; 74 } 75 76 77 static void ft_conic_split(FT_Vector * base)78 ft_conic_split( FT_Vector* base ) 79 { 80 FT_Pos a, b; 81 82 83 base[4].x = base[2].x; 84 b = base[1].x; 85 a = base[3].x = ( base[2].x + b ) / 2; 86 b = base[1].x = ( base[0].x + b ) / 2; 87 base[2].x = ( a + b ) / 2; 88 89 base[4].y = base[2].y; 90 b = base[1].y; 91 a = base[3].y = ( base[2].y + b ) / 2; 92 b = base[1].y = ( base[0].y + b ) / 2; 93 base[2].y = ( a + b ) / 2; 94 } 95 96 97 static FT_Bool ft_conic_is_small_enough(FT_Vector * base,FT_Angle * angle_in,FT_Angle * angle_out)98 ft_conic_is_small_enough( FT_Vector* base, 99 FT_Angle *angle_in, 100 FT_Angle *angle_out ) 101 { 102 FT_Vector d1, d2; 103 FT_Angle theta; 104 FT_Int close1, close2; 105 106 107 d1.x = base[1].x - base[2].x; 108 d1.y = base[1].y - base[2].y; 109 d2.x = base[0].x - base[1].x; 110 d2.y = base[0].y - base[1].y; 111 112 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); 113 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); 114 115 if ( close1 ) 116 { 117 if ( close2 ) 118 { 119 /* basically a point; */ 120 /* do nothing to retain original direction */ 121 } 122 else 123 { 124 *angle_in = 125 *angle_out = FT_Atan2( d2.x, d2.y ); 126 } 127 } 128 else /* !close1 */ 129 { 130 if ( close2 ) 131 { 132 *angle_in = 133 *angle_out = FT_Atan2( d1.x, d1.y ); 134 } 135 else 136 { 137 *angle_in = FT_Atan2( d1.x, d1.y ); 138 *angle_out = FT_Atan2( d2.x, d2.y ); 139 } 140 } 141 142 theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) ); 143 144 return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD ); 145 } 146 147 148 static void ft_cubic_split(FT_Vector * base)149 ft_cubic_split( FT_Vector* base ) 150 { 151 FT_Pos a, b, c, d; 152 153 154 base[6].x = base[3].x; 155 c = base[1].x; 156 d = base[2].x; 157 base[1].x = a = ( base[0].x + c ) / 2; 158 base[5].x = b = ( base[3].x + d ) / 2; 159 c = ( c + d ) / 2; 160 base[2].x = a = ( a + c ) / 2; 161 base[4].x = b = ( b + c ) / 2; 162 base[3].x = ( a + b ) / 2; 163 164 base[6].y = base[3].y; 165 c = base[1].y; 166 d = base[2].y; 167 base[1].y = a = ( base[0].y + c ) / 2; 168 base[5].y = b = ( base[3].y + d ) / 2; 169 c = ( c + d ) / 2; 170 base[2].y = a = ( a + c ) / 2; 171 base[4].y = b = ( b + c ) / 2; 172 base[3].y = ( a + b ) / 2; 173 } 174 175 176 /* Return the average of `angle1' and `angle2'. */ 177 /* This gives correct result even if `angle1' and `angle2' */ 178 /* have opposite signs. */ 179 static FT_Angle ft_angle_mean(FT_Angle angle1,FT_Angle angle2)180 ft_angle_mean( FT_Angle angle1, 181 FT_Angle angle2 ) 182 { 183 return angle1 + FT_Angle_Diff( angle1, angle2 ) / 2; 184 } 185 186 187 static FT_Bool ft_cubic_is_small_enough(FT_Vector * base,FT_Angle * angle_in,FT_Angle * angle_mid,FT_Angle * angle_out)188 ft_cubic_is_small_enough( FT_Vector* base, 189 FT_Angle *angle_in, 190 FT_Angle *angle_mid, 191 FT_Angle *angle_out ) 192 { 193 FT_Vector d1, d2, d3; 194 FT_Angle theta1, theta2; 195 FT_Int close1, close2, close3; 196 197 198 d1.x = base[2].x - base[3].x; 199 d1.y = base[2].y - base[3].y; 200 d2.x = base[1].x - base[2].x; 201 d2.y = base[1].y - base[2].y; 202 d3.x = base[0].x - base[1].x; 203 d3.y = base[0].y - base[1].y; 204 205 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); 206 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); 207 close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y ); 208 209 if ( close1 ) 210 { 211 if ( close2 ) 212 { 213 if ( close3 ) 214 { 215 /* basically a point; */ 216 /* do nothing to retain original direction */ 217 } 218 else /* !close3 */ 219 { 220 *angle_in = 221 *angle_mid = 222 *angle_out = FT_Atan2( d3.x, d3.y ); 223 } 224 } 225 else /* !close2 */ 226 { 227 if ( close3 ) 228 { 229 *angle_in = 230 *angle_mid = 231 *angle_out = FT_Atan2( d2.x, d2.y ); 232 } 233 else /* !close3 */ 234 { 235 *angle_in = 236 *angle_mid = FT_Atan2( d2.x, d2.y ); 237 *angle_out = FT_Atan2( d3.x, d3.y ); 238 } 239 } 240 } 241 else /* !close1 */ 242 { 243 if ( close2 ) 244 { 245 if ( close3 ) 246 { 247 *angle_in = 248 *angle_mid = 249 *angle_out = FT_Atan2( d1.x, d1.y ); 250 } 251 else /* !close3 */ 252 { 253 *angle_in = FT_Atan2( d1.x, d1.y ); 254 *angle_out = FT_Atan2( d3.x, d3.y ); 255 *angle_mid = ft_angle_mean( *angle_in, *angle_out ); 256 } 257 } 258 else /* !close2 */ 259 { 260 if ( close3 ) 261 { 262 *angle_in = FT_Atan2( d1.x, d1.y ); 263 *angle_mid = 264 *angle_out = FT_Atan2( d2.x, d2.y ); 265 } 266 else /* !close3 */ 267 { 268 *angle_in = FT_Atan2( d1.x, d1.y ); 269 *angle_mid = FT_Atan2( d2.x, d2.y ); 270 *angle_out = FT_Atan2( d3.x, d3.y ); 271 } 272 } 273 } 274 275 theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) ); 276 theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) ); 277 278 return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD && 279 theta2 < FT_SMALL_CUBIC_THRESHOLD ); 280 } 281 282 283 /*************************************************************************/ 284 /*************************************************************************/ 285 /***** *****/ 286 /***** STROKE BORDERS *****/ 287 /***** *****/ 288 /*************************************************************************/ 289 /*************************************************************************/ 290 291 typedef enum FT_StrokeTags_ 292 { 293 FT_STROKE_TAG_ON = 1, /* on-curve point */ 294 FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ 295 FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ 296 FT_STROKE_TAG_END = 8 /* sub-path end */ 297 298 } FT_StrokeTags; 299 300 #define FT_STROKE_TAG_BEGIN_END ( FT_STROKE_TAG_BEGIN | FT_STROKE_TAG_END ) 301 302 typedef struct FT_StrokeBorderRec_ 303 { 304 FT_UInt num_points; 305 FT_UInt max_points; 306 FT_Vector* points; 307 FT_Byte* tags; 308 FT_Bool movable; /* TRUE for ends of lineto borders */ 309 FT_Int start; /* index of current sub-path start point */ 310 FT_Memory memory; 311 FT_Bool valid; 312 313 } FT_StrokeBorderRec, *FT_StrokeBorder; 314 315 316 static FT_Error ft_stroke_border_grow(FT_StrokeBorder border,FT_UInt new_points)317 ft_stroke_border_grow( FT_StrokeBorder border, 318 FT_UInt new_points ) 319 { 320 FT_UInt old_max = border->max_points; 321 FT_UInt new_max = border->num_points + new_points; 322 FT_Error error = FT_Err_Ok; 323 324 325 if ( new_max > old_max ) 326 { 327 FT_UInt cur_max = old_max; 328 FT_Memory memory = border->memory; 329 330 331 while ( cur_max < new_max ) 332 cur_max += ( cur_max >> 1 ) + 16; 333 334 if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) || 335 FT_RENEW_ARRAY( border->tags, old_max, cur_max ) ) 336 goto Exit; 337 338 border->max_points = cur_max; 339 } 340 341 Exit: 342 return error; 343 } 344 345 346 static void ft_stroke_border_close(FT_StrokeBorder border,FT_Bool reverse)347 ft_stroke_border_close( FT_StrokeBorder border, 348 FT_Bool reverse ) 349 { 350 FT_UInt start = border->start; 351 FT_UInt count = border->num_points; 352 353 354 FT_ASSERT( border->start >= 0 ); 355 356 /* don't record empty paths! */ 357 if ( count <= start + 1U ) 358 border->num_points = start; 359 else 360 { 361 /* copy the last point to the start of this sub-path, since */ 362 /* it contains the `adjusted' starting coordinates */ 363 border->num_points = --count; 364 border->points[start] = border->points[count]; 365 366 if ( reverse ) 367 { 368 /* reverse the points */ 369 { 370 FT_Vector* vec1 = border->points + start + 1; 371 FT_Vector* vec2 = border->points + count - 1; 372 373 374 for ( ; vec1 < vec2; vec1++, vec2-- ) 375 { 376 FT_Vector tmp; 377 378 379 tmp = *vec1; 380 *vec1 = *vec2; 381 *vec2 = tmp; 382 } 383 } 384 385 /* then the tags */ 386 { 387 FT_Byte* tag1 = border->tags + start + 1; 388 FT_Byte* tag2 = border->tags + count - 1; 389 390 391 for ( ; tag1 < tag2; tag1++, tag2-- ) 392 { 393 FT_Byte tmp; 394 395 396 tmp = *tag1; 397 *tag1 = *tag2; 398 *tag2 = tmp; 399 } 400 } 401 } 402 403 border->tags[start ] |= FT_STROKE_TAG_BEGIN; 404 border->tags[count - 1] |= FT_STROKE_TAG_END; 405 } 406 407 border->start = -1; 408 border->movable = FALSE; 409 } 410 411 412 static FT_Error ft_stroke_border_lineto(FT_StrokeBorder border,FT_Vector * to,FT_Bool movable)413 ft_stroke_border_lineto( FT_StrokeBorder border, 414 FT_Vector* to, 415 FT_Bool movable ) 416 { 417 FT_Error error = FT_Err_Ok; 418 419 420 FT_ASSERT( border->start >= 0 ); 421 422 if ( border->movable ) 423 { 424 /* move last point */ 425 border->points[border->num_points - 1] = *to; 426 } 427 else 428 { 429 /* don't add zero-length lineto */ 430 if ( border->num_points > 0 && 431 FT_IS_SMALL( border->points[border->num_points - 1].x - to->x ) && 432 FT_IS_SMALL( border->points[border->num_points - 1].y - to->y ) ) 433 return error; 434 435 /* add one point */ 436 error = ft_stroke_border_grow( border, 1 ); 437 if ( !error ) 438 { 439 FT_Vector* vec = border->points + border->num_points; 440 FT_Byte* tag = border->tags + border->num_points; 441 442 443 vec[0] = *to; 444 tag[0] = FT_STROKE_TAG_ON; 445 446 border->num_points += 1; 447 } 448 } 449 border->movable = movable; 450 return error; 451 } 452 453 454 static FT_Error ft_stroke_border_conicto(FT_StrokeBorder border,FT_Vector * control,FT_Vector * to)455 ft_stroke_border_conicto( FT_StrokeBorder border, 456 FT_Vector* control, 457 FT_Vector* to ) 458 { 459 FT_Error error; 460 461 462 FT_ASSERT( border->start >= 0 ); 463 464 error = ft_stroke_border_grow( border, 2 ); 465 if ( !error ) 466 { 467 FT_Vector* vec = border->points + border->num_points; 468 FT_Byte* tag = border->tags + border->num_points; 469 470 471 vec[0] = *control; 472 vec[1] = *to; 473 474 tag[0] = 0; 475 tag[1] = FT_STROKE_TAG_ON; 476 477 border->num_points += 2; 478 } 479 480 border->movable = FALSE; 481 482 return error; 483 } 484 485 486 static FT_Error ft_stroke_border_cubicto(FT_StrokeBorder border,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)487 ft_stroke_border_cubicto( FT_StrokeBorder border, 488 FT_Vector* control1, 489 FT_Vector* control2, 490 FT_Vector* to ) 491 { 492 FT_Error error; 493 494 495 FT_ASSERT( border->start >= 0 ); 496 497 error = ft_stroke_border_grow( border, 3 ); 498 if ( !error ) 499 { 500 FT_Vector* vec = border->points + border->num_points; 501 FT_Byte* tag = border->tags + border->num_points; 502 503 504 vec[0] = *control1; 505 vec[1] = *control2; 506 vec[2] = *to; 507 508 tag[0] = FT_STROKE_TAG_CUBIC; 509 tag[1] = FT_STROKE_TAG_CUBIC; 510 tag[2] = FT_STROKE_TAG_ON; 511 512 border->num_points += 3; 513 } 514 515 border->movable = FALSE; 516 517 return error; 518 } 519 520 521 #define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 ) 522 523 524 static FT_Error ft_stroke_border_arcto(FT_StrokeBorder border,FT_Vector * center,FT_Fixed radius,FT_Angle angle_start,FT_Angle angle_diff)525 ft_stroke_border_arcto( FT_StrokeBorder border, 526 FT_Vector* center, 527 FT_Fixed radius, 528 FT_Angle angle_start, 529 FT_Angle angle_diff ) 530 { 531 FT_Angle total, angle, step, rotate, next, theta; 532 FT_Vector a, b, a2, b2; 533 FT_Fixed length; 534 FT_Error error = FT_Err_Ok; 535 536 537 /* compute start point */ 538 FT_Vector_From_Polar( &a, radius, angle_start ); 539 a.x += center->x; 540 a.y += center->y; 541 542 total = angle_diff; 543 angle = angle_start; 544 rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2; 545 546 while ( total != 0 ) 547 { 548 step = total; 549 if ( step > FT_ARC_CUBIC_ANGLE ) 550 step = FT_ARC_CUBIC_ANGLE; 551 552 else if ( step < -FT_ARC_CUBIC_ANGLE ) 553 step = -FT_ARC_CUBIC_ANGLE; 554 555 next = angle + step; 556 theta = step; 557 if ( theta < 0 ) 558 theta = -theta; 559 560 theta >>= 1; 561 562 /* compute end point */ 563 FT_Vector_From_Polar( &b, radius, next ); 564 b.x += center->x; 565 b.y += center->y; 566 567 /* compute first and second control points */ 568 length = FT_MulDiv( radius, FT_Sin( theta ) * 4, 569 ( 0x10000L + FT_Cos( theta ) ) * 3 ); 570 571 FT_Vector_From_Polar( &a2, length, angle + rotate ); 572 a2.x += a.x; 573 a2.y += a.y; 574 575 FT_Vector_From_Polar( &b2, length, next - rotate ); 576 b2.x += b.x; 577 b2.y += b.y; 578 579 /* add cubic arc */ 580 error = ft_stroke_border_cubicto( border, &a2, &b2, &b ); 581 if ( error ) 582 break; 583 584 /* process the rest of the arc ?? */ 585 a = b; 586 total -= step; 587 angle = next; 588 } 589 590 return error; 591 } 592 593 594 static FT_Error ft_stroke_border_moveto(FT_StrokeBorder border,FT_Vector * to)595 ft_stroke_border_moveto( FT_StrokeBorder border, 596 FT_Vector* to ) 597 { 598 /* close current open path if any ? */ 599 if ( border->start >= 0 ) 600 ft_stroke_border_close( border, FALSE ); 601 602 border->start = border->num_points; 603 border->movable = FALSE; 604 605 return ft_stroke_border_lineto( border, to, FALSE ); 606 } 607 608 609 static void ft_stroke_border_init(FT_StrokeBorder border,FT_Memory memory)610 ft_stroke_border_init( FT_StrokeBorder border, 611 FT_Memory memory ) 612 { 613 border->memory = memory; 614 border->points = NULL; 615 border->tags = NULL; 616 617 border->num_points = 0; 618 border->max_points = 0; 619 border->start = -1; 620 border->valid = FALSE; 621 } 622 623 624 static void ft_stroke_border_reset(FT_StrokeBorder border)625 ft_stroke_border_reset( FT_StrokeBorder border ) 626 { 627 border->num_points = 0; 628 border->start = -1; 629 border->valid = FALSE; 630 } 631 632 633 static void ft_stroke_border_done(FT_StrokeBorder border)634 ft_stroke_border_done( FT_StrokeBorder border ) 635 { 636 FT_Memory memory = border->memory; 637 638 639 FT_FREE( border->points ); 640 FT_FREE( border->tags ); 641 642 border->num_points = 0; 643 border->max_points = 0; 644 border->start = -1; 645 border->valid = FALSE; 646 } 647 648 649 static FT_Error ft_stroke_border_get_counts(FT_StrokeBorder border,FT_UInt * anum_points,FT_UInt * anum_contours)650 ft_stroke_border_get_counts( FT_StrokeBorder border, 651 FT_UInt *anum_points, 652 FT_UInt *anum_contours ) 653 { 654 FT_Error error = FT_Err_Ok; 655 FT_UInt num_points = 0; 656 FT_UInt num_contours = 0; 657 658 FT_UInt count = border->num_points; 659 FT_Vector* point = border->points; 660 FT_Byte* tags = border->tags; 661 FT_Int in_contour = 0; 662 663 664 for ( ; count > 0; count--, num_points++, point++, tags++ ) 665 { 666 if ( tags[0] & FT_STROKE_TAG_BEGIN ) 667 { 668 if ( in_contour != 0 ) 669 goto Fail; 670 671 in_contour = 1; 672 } 673 else if ( in_contour == 0 ) 674 goto Fail; 675 676 if ( tags[0] & FT_STROKE_TAG_END ) 677 { 678 in_contour = 0; 679 num_contours++; 680 } 681 } 682 683 if ( in_contour != 0 ) 684 goto Fail; 685 686 border->valid = TRUE; 687 688 Exit: 689 *anum_points = num_points; 690 *anum_contours = num_contours; 691 return error; 692 693 Fail: 694 num_points = 0; 695 num_contours = 0; 696 goto Exit; 697 } 698 699 700 static void ft_stroke_border_export(FT_StrokeBorder border,FT_Outline * outline)701 ft_stroke_border_export( FT_StrokeBorder border, 702 FT_Outline* outline ) 703 { 704 /* copy point locations */ 705 FT_ARRAY_COPY( outline->points + outline->n_points, 706 border->points, 707 border->num_points ); 708 709 /* copy tags */ 710 { 711 FT_UInt count = border->num_points; 712 FT_Byte* read = border->tags; 713 FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points; 714 715 716 for ( ; count > 0; count--, read++, write++ ) 717 { 718 if ( *read & FT_STROKE_TAG_ON ) 719 *write = FT_CURVE_TAG_ON; 720 else if ( *read & FT_STROKE_TAG_CUBIC ) 721 *write = FT_CURVE_TAG_CUBIC; 722 else 723 *write = FT_CURVE_TAG_CONIC; 724 } 725 } 726 727 /* copy contours */ 728 { 729 FT_UInt count = border->num_points; 730 FT_Byte* tags = border->tags; 731 FT_Short* write = outline->contours + outline->n_contours; 732 FT_Short idx = (FT_Short)outline->n_points; 733 734 735 for ( ; count > 0; count--, tags++, idx++ ) 736 { 737 if ( *tags & FT_STROKE_TAG_END ) 738 { 739 *write++ = idx; 740 outline->n_contours++; 741 } 742 } 743 } 744 745 outline->n_points = (short)( outline->n_points + border->num_points ); 746 747 FT_ASSERT( FT_Outline_Check( outline ) == 0 ); 748 } 749 750 751 /*************************************************************************/ 752 /*************************************************************************/ 753 /***** *****/ 754 /***** STROKER *****/ 755 /***** *****/ 756 /*************************************************************************/ 757 /*************************************************************************/ 758 759 #define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI ) 760 761 typedef struct FT_StrokerRec_ 762 { 763 FT_Angle angle_in; /* direction into curr join */ 764 FT_Angle angle_out; /* direction out of join */ 765 FT_Vector center; /* current position */ 766 FT_Fixed line_length; /* length of last lineto */ 767 FT_Bool first_point; /* is this the start? */ 768 FT_Bool subpath_open; /* is the subpath open? */ 769 FT_Angle subpath_angle; /* subpath start direction */ 770 FT_Vector subpath_start; /* subpath start position */ 771 FT_Fixed subpath_line_length; /* subpath start lineto len */ 772 FT_Bool handle_wide_strokes; /* use wide strokes logic? */ 773 774 FT_Stroker_LineCap line_cap; 775 FT_Stroker_LineJoin line_join; 776 FT_Stroker_LineJoin line_join_saved; 777 FT_Fixed miter_limit; 778 FT_Fixed radius; 779 780 FT_StrokeBorderRec borders[2]; 781 FT_Library library; 782 783 } FT_StrokerRec; 784 785 786 /* documentation is in ftstroke.h */ 787 788 FT_EXPORT_DEF( FT_Error ) FT_Stroker_New(FT_Library library,FT_Stroker * astroker)789 FT_Stroker_New( FT_Library library, 790 FT_Stroker *astroker ) 791 { 792 FT_Error error; /* assigned in FT_NEW */ 793 FT_Memory memory; 794 FT_Stroker stroker = NULL; 795 796 797 if ( !library ) 798 return FT_THROW( Invalid_Argument ); 799 800 memory = library->memory; 801 802 if ( !FT_NEW( stroker ) ) 803 { 804 stroker->library = library; 805 806 ft_stroke_border_init( &stroker->borders[0], memory ); 807 ft_stroke_border_init( &stroker->borders[1], memory ); 808 } 809 810 *astroker = stroker; 811 812 return error; 813 } 814 815 816 /* documentation is in ftstroke.h */ 817 818 FT_EXPORT_DEF( void ) FT_Stroker_Set(FT_Stroker stroker,FT_Fixed radius,FT_Stroker_LineCap line_cap,FT_Stroker_LineJoin line_join,FT_Fixed miter_limit)819 FT_Stroker_Set( FT_Stroker stroker, 820 FT_Fixed radius, 821 FT_Stroker_LineCap line_cap, 822 FT_Stroker_LineJoin line_join, 823 FT_Fixed miter_limit ) 824 { 825 stroker->radius = radius; 826 stroker->line_cap = line_cap; 827 stroker->line_join = line_join; 828 stroker->miter_limit = miter_limit; 829 830 /* ensure miter limit has sensible value */ 831 if ( stroker->miter_limit < 0x10000 ) 832 stroker->miter_limit = 0x10000; 833 834 /* save line join style: */ 835 /* line join style can be temporarily changed when stroking curves */ 836 stroker->line_join_saved = line_join; 837 838 FT_Stroker_Rewind( stroker ); 839 } 840 841 842 /* documentation is in ftstroke.h */ 843 844 FT_EXPORT_DEF( void ) FT_Stroker_Rewind(FT_Stroker stroker)845 FT_Stroker_Rewind( FT_Stroker stroker ) 846 { 847 if ( stroker ) 848 { 849 ft_stroke_border_reset( &stroker->borders[0] ); 850 ft_stroke_border_reset( &stroker->borders[1] ); 851 } 852 } 853 854 855 /* documentation is in ftstroke.h */ 856 857 FT_EXPORT_DEF( void ) FT_Stroker_Done(FT_Stroker stroker)858 FT_Stroker_Done( FT_Stroker stroker ) 859 { 860 if ( stroker ) 861 { 862 FT_Memory memory = stroker->library->memory; 863 864 865 ft_stroke_border_done( &stroker->borders[0] ); 866 ft_stroke_border_done( &stroker->borders[1] ); 867 868 stroker->library = NULL; 869 FT_FREE( stroker ); 870 } 871 } 872 873 874 /* create a circular arc at a corner or cap */ 875 static FT_Error ft_stroker_arcto(FT_Stroker stroker,FT_Int side)876 ft_stroker_arcto( FT_Stroker stroker, 877 FT_Int side ) 878 { 879 FT_Angle total, rotate; 880 FT_Fixed radius = stroker->radius; 881 FT_Error error = FT_Err_Ok; 882 FT_StrokeBorder border = stroker->borders + side; 883 884 885 rotate = FT_SIDE_TO_ROTATE( side ); 886 887 total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 888 if ( total == FT_ANGLE_PI ) 889 total = -rotate * 2; 890 891 error = ft_stroke_border_arcto( border, 892 &stroker->center, 893 radius, 894 stroker->angle_in + rotate, 895 total ); 896 border->movable = FALSE; 897 return error; 898 } 899 900 901 /* add a cap at the end of an opened path */ 902 static FT_Error ft_stroker_cap(FT_Stroker stroker,FT_Angle angle,FT_Int side)903 ft_stroker_cap( FT_Stroker stroker, 904 FT_Angle angle, 905 FT_Int side ) 906 { 907 FT_Error error = FT_Err_Ok; 908 909 910 if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND ) 911 { 912 /* add a round cap */ 913 stroker->angle_in = angle; 914 stroker->angle_out = angle + FT_ANGLE_PI; 915 916 error = ft_stroker_arcto( stroker, side ); 917 } 918 else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE ) 919 { 920 /* add a square cap */ 921 FT_Vector delta, delta2; 922 FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); 923 FT_Fixed radius = stroker->radius; 924 FT_StrokeBorder border = stroker->borders + side; 925 926 927 FT_Vector_From_Polar( &delta2, radius, angle + rotate ); 928 FT_Vector_From_Polar( &delta, radius, angle ); 929 930 delta.x += stroker->center.x + delta2.x; 931 delta.y += stroker->center.y + delta2.y; 932 933 error = ft_stroke_border_lineto( border, &delta, FALSE ); 934 if ( error ) 935 goto Exit; 936 937 FT_Vector_From_Polar( &delta2, radius, angle - rotate ); 938 FT_Vector_From_Polar( &delta, radius, angle ); 939 940 delta.x += delta2.x + stroker->center.x; 941 delta.y += delta2.y + stroker->center.y; 942 943 error = ft_stroke_border_lineto( border, &delta, FALSE ); 944 } 945 else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT ) 946 { 947 /* add a butt ending */ 948 FT_Vector delta; 949 FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); 950 FT_Fixed radius = stroker->radius; 951 FT_StrokeBorder border = stroker->borders + side; 952 953 954 FT_Vector_From_Polar( &delta, radius, angle + rotate ); 955 956 delta.x += stroker->center.x; 957 delta.y += stroker->center.y; 958 959 error = ft_stroke_border_lineto( border, &delta, FALSE ); 960 if ( error ) 961 goto Exit; 962 963 FT_Vector_From_Polar( &delta, radius, angle - rotate ); 964 965 delta.x += stroker->center.x; 966 delta.y += stroker->center.y; 967 968 error = ft_stroke_border_lineto( border, &delta, FALSE ); 969 } 970 971 Exit: 972 return error; 973 } 974 975 976 /* process an inside corner, i.e. compute intersection */ 977 static FT_Error ft_stroker_inside(FT_Stroker stroker,FT_Int side,FT_Fixed line_length)978 ft_stroker_inside( FT_Stroker stroker, 979 FT_Int side, 980 FT_Fixed line_length ) 981 { 982 FT_StrokeBorder border = stroker->borders + side; 983 FT_Angle phi, theta, rotate; 984 FT_Fixed length, thcos; 985 FT_Vector delta; 986 FT_Error error = FT_Err_Ok; 987 FT_Bool intersect; /* use intersection of lines? */ 988 989 990 rotate = FT_SIDE_TO_ROTATE( side ); 991 992 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2; 993 994 /* Only intersect borders if between two lineto's and both */ 995 /* lines are long enough (line_length is zero for curves). */ 996 if ( !border->movable || line_length == 0 ) 997 intersect = FALSE; 998 else 999 { 1000 /* compute minimum required length of lines */ 1001 FT_Fixed min_length = ft_pos_abs( FT_MulFix( stroker->radius, 1002 FT_Tan( theta ) ) ); 1003 1004 1005 intersect = FT_BOOL( stroker->line_length >= min_length && 1006 line_length >= min_length ); 1007 } 1008 1009 if ( !intersect ) 1010 { 1011 FT_Vector_From_Polar( &delta, stroker->radius, 1012 stroker->angle_out + rotate ); 1013 delta.x += stroker->center.x; 1014 delta.y += stroker->center.y; 1015 1016 border->movable = FALSE; 1017 } 1018 else 1019 { 1020 /* compute median angle */ 1021 phi = stroker->angle_in + theta; 1022 1023 thcos = FT_Cos( theta ); 1024 1025 length = FT_DivFix( stroker->radius, thcos ); 1026 1027 FT_Vector_From_Polar( &delta, length, phi + rotate ); 1028 delta.x += stroker->center.x; 1029 delta.y += stroker->center.y; 1030 } 1031 1032 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1033 1034 return error; 1035 } 1036 1037 1038 /* process an outside corner, i.e. compute bevel/miter/round */ 1039 static FT_Error ft_stroker_outside(FT_Stroker stroker,FT_Int side,FT_Fixed line_length)1040 ft_stroker_outside( FT_Stroker stroker, 1041 FT_Int side, 1042 FT_Fixed line_length ) 1043 { 1044 FT_StrokeBorder border = stroker->borders + side; 1045 FT_Error error; 1046 FT_Angle rotate; 1047 1048 1049 if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND ) 1050 error = ft_stroker_arcto( stroker, side ); 1051 else 1052 { 1053 /* this is a mitered (pointed) or beveled (truncated) corner */ 1054 FT_Fixed sigma = 0, radius = stroker->radius; 1055 FT_Angle theta = 0, phi = 0; 1056 FT_Fixed thcos = 0; 1057 FT_Bool bevel, fixed_bevel; 1058 1059 1060 rotate = FT_SIDE_TO_ROTATE( side ); 1061 1062 bevel = 1063 FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_BEVEL ); 1064 1065 fixed_bevel = 1066 FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_MITER_VARIABLE ); 1067 1068 if ( !bevel ) 1069 { 1070 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 1071 1072 if ( theta == FT_ANGLE_PI ) 1073 { 1074 theta = rotate; 1075 phi = stroker->angle_in; 1076 } 1077 else 1078 { 1079 theta /= 2; 1080 phi = stroker->angle_in + theta + rotate; 1081 } 1082 1083 thcos = FT_Cos( theta ); 1084 sigma = FT_MulFix( stroker->miter_limit, thcos ); 1085 1086 /* is miter limit exceeded? */ 1087 if ( sigma < 0x10000L ) 1088 { 1089 /* don't create variable bevels for very small deviations; */ 1090 /* FT_Sin(x) = 0 for x <= 57 */ 1091 if ( fixed_bevel || ft_pos_abs( theta ) > 57 ) 1092 bevel = TRUE; 1093 } 1094 } 1095 1096 if ( bevel ) /* this is a bevel (broken angle) */ 1097 { 1098 if ( fixed_bevel ) 1099 { 1100 /* the outer corners are simply joined together */ 1101 FT_Vector delta; 1102 1103 1104 /* add bevel */ 1105 FT_Vector_From_Polar( &delta, 1106 radius, 1107 stroker->angle_out + rotate ); 1108 delta.x += stroker->center.x; 1109 delta.y += stroker->center.y; 1110 1111 border->movable = FALSE; 1112 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1113 } 1114 else /* variable bevel */ 1115 { 1116 /* the miter is truncated */ 1117 FT_Vector middle, delta; 1118 FT_Fixed length; 1119 1120 1121 /* compute middle point */ 1122 FT_Vector_From_Polar( &middle, 1123 FT_MulFix( radius, stroker->miter_limit ), 1124 phi ); 1125 middle.x += stroker->center.x; 1126 middle.y += stroker->center.y; 1127 1128 /* compute first angle point */ 1129 length = FT_MulDiv( radius, 0x10000L - sigma, 1130 ft_pos_abs( FT_Sin( theta ) ) ); 1131 1132 FT_Vector_From_Polar( &delta, length, phi + rotate ); 1133 delta.x += middle.x; 1134 delta.y += middle.y; 1135 1136 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1137 if ( error ) 1138 goto Exit; 1139 1140 /* compute second angle point */ 1141 FT_Vector_From_Polar( &delta, length, phi - rotate ); 1142 delta.x += middle.x; 1143 delta.y += middle.y; 1144 1145 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1146 if ( error ) 1147 goto Exit; 1148 1149 /* finally, add an end point; only needed if not lineto */ 1150 /* (line_length is zero for curves) */ 1151 if ( line_length == 0 ) 1152 { 1153 FT_Vector_From_Polar( &delta, 1154 radius, 1155 stroker->angle_out + rotate ); 1156 1157 delta.x += stroker->center.x; 1158 delta.y += stroker->center.y; 1159 1160 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1161 } 1162 } 1163 } 1164 else /* this is a miter (intersection) */ 1165 { 1166 FT_Fixed length; 1167 FT_Vector delta; 1168 1169 1170 length = FT_DivFix( stroker->radius, thcos ); 1171 1172 FT_Vector_From_Polar( &delta, length, phi ); 1173 delta.x += stroker->center.x; 1174 delta.y += stroker->center.y; 1175 1176 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1177 if ( error ) 1178 goto Exit; 1179 1180 /* now add an end point; only needed if not lineto */ 1181 /* (line_length is zero for curves) */ 1182 if ( line_length == 0 ) 1183 { 1184 FT_Vector_From_Polar( &delta, 1185 stroker->radius, 1186 stroker->angle_out + rotate ); 1187 delta.x += stroker->center.x; 1188 delta.y += stroker->center.y; 1189 1190 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1191 } 1192 } 1193 } 1194 1195 Exit: 1196 return error; 1197 } 1198 1199 1200 static FT_Error ft_stroker_process_corner(FT_Stroker stroker,FT_Fixed line_length)1201 ft_stroker_process_corner( FT_Stroker stroker, 1202 FT_Fixed line_length ) 1203 { 1204 FT_Error error = FT_Err_Ok; 1205 FT_Angle turn; 1206 FT_Int inside_side; 1207 1208 1209 turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 1210 1211 /* no specific corner processing is required if the turn is 0 */ 1212 if ( turn == 0 ) 1213 goto Exit; 1214 1215 /* when we turn to the right, the inside side is 0 */ 1216 inside_side = 0; 1217 1218 /* otherwise, the inside side is 1 */ 1219 if ( turn < 0 ) 1220 inside_side = 1; 1221 1222 /* process the inside side */ 1223 error = ft_stroker_inside( stroker, inside_side, line_length ); 1224 if ( error ) 1225 goto Exit; 1226 1227 /* process the outside side */ 1228 error = ft_stroker_outside( stroker, 1 - inside_side, line_length ); 1229 1230 Exit: 1231 return error; 1232 } 1233 1234 1235 /* add two points to the left and right borders corresponding to the */ 1236 /* start of the subpath */ 1237 static FT_Error ft_stroker_subpath_start(FT_Stroker stroker,FT_Angle start_angle,FT_Fixed line_length)1238 ft_stroker_subpath_start( FT_Stroker stroker, 1239 FT_Angle start_angle, 1240 FT_Fixed line_length ) 1241 { 1242 FT_Vector delta; 1243 FT_Vector point; 1244 FT_Error error; 1245 FT_StrokeBorder border; 1246 1247 1248 FT_Vector_From_Polar( &delta, stroker->radius, 1249 start_angle + FT_ANGLE_PI2 ); 1250 1251 point.x = stroker->center.x + delta.x; 1252 point.y = stroker->center.y + delta.y; 1253 1254 border = stroker->borders; 1255 error = ft_stroke_border_moveto( border, &point ); 1256 if ( error ) 1257 goto Exit; 1258 1259 point.x = stroker->center.x - delta.x; 1260 point.y = stroker->center.y - delta.y; 1261 1262 border++; 1263 error = ft_stroke_border_moveto( border, &point ); 1264 1265 /* save angle, position, and line length for last join */ 1266 /* (line_length is zero for curves) */ 1267 stroker->subpath_angle = start_angle; 1268 stroker->first_point = FALSE; 1269 stroker->subpath_line_length = line_length; 1270 1271 Exit: 1272 return error; 1273 } 1274 1275 1276 /* documentation is in ftstroke.h */ 1277 1278 FT_EXPORT_DEF( FT_Error ) FT_Stroker_LineTo(FT_Stroker stroker,FT_Vector * to)1279 FT_Stroker_LineTo( FT_Stroker stroker, 1280 FT_Vector* to ) 1281 { 1282 FT_Error error = FT_Err_Ok; 1283 FT_StrokeBorder border; 1284 FT_Vector delta; 1285 FT_Angle angle; 1286 FT_Int side; 1287 FT_Fixed line_length; 1288 1289 1290 delta.x = to->x - stroker->center.x; 1291 delta.y = to->y - stroker->center.y; 1292 1293 /* a zero-length lineto is a no-op; avoid creating a spurious corner */ 1294 if ( delta.x == 0 && delta.y == 0 ) 1295 goto Exit; 1296 1297 /* compute length of line */ 1298 line_length = FT_Vector_Length( &delta ); 1299 1300 angle = FT_Atan2( delta.x, delta.y ); 1301 FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 ); 1302 1303 /* process corner if necessary */ 1304 if ( stroker->first_point ) 1305 { 1306 /* This is the first segment of a subpath. We need to */ 1307 /* add a point to each border at their respective starting */ 1308 /* point locations. */ 1309 error = ft_stroker_subpath_start( stroker, angle, line_length ); 1310 if ( error ) 1311 goto Exit; 1312 } 1313 else 1314 { 1315 /* process the current corner */ 1316 stroker->angle_out = angle; 1317 error = ft_stroker_process_corner( stroker, line_length ); 1318 if ( error ) 1319 goto Exit; 1320 } 1321 1322 /* now add a line segment to both the `inside' and `outside' paths */ 1323 for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) 1324 { 1325 FT_Vector point; 1326 1327 1328 point.x = to->x + delta.x; 1329 point.y = to->y + delta.y; 1330 1331 /* the ends of lineto borders are movable */ 1332 error = ft_stroke_border_lineto( border, &point, TRUE ); 1333 if ( error ) 1334 goto Exit; 1335 1336 delta.x = -delta.x; 1337 delta.y = -delta.y; 1338 } 1339 1340 stroker->angle_in = angle; 1341 stroker->center = *to; 1342 stroker->line_length = line_length; 1343 1344 Exit: 1345 return error; 1346 } 1347 1348 1349 /* documentation is in ftstroke.h */ 1350 1351 FT_EXPORT_DEF( FT_Error ) FT_Stroker_ConicTo(FT_Stroker stroker,FT_Vector * control,FT_Vector * to)1352 FT_Stroker_ConicTo( FT_Stroker stroker, 1353 FT_Vector* control, 1354 FT_Vector* to ) 1355 { 1356 FT_Error error = FT_Err_Ok; 1357 FT_Vector bez_stack[34]; 1358 FT_Vector* arc; 1359 FT_Vector* limit = bez_stack + 30; 1360 FT_Bool first_arc = TRUE; 1361 1362 1363 /* if all control points are coincident, this is a no-op; */ 1364 /* avoid creating a spurious corner */ 1365 if ( FT_IS_SMALL( stroker->center.x - control->x ) && 1366 FT_IS_SMALL( stroker->center.y - control->y ) && 1367 FT_IS_SMALL( control->x - to->x ) && 1368 FT_IS_SMALL( control->y - to->y ) ) 1369 { 1370 stroker->center = *to; 1371 goto Exit; 1372 } 1373 1374 arc = bez_stack; 1375 arc[0] = *to; 1376 arc[1] = *control; 1377 arc[2] = stroker->center; 1378 1379 while ( arc >= bez_stack ) 1380 { 1381 FT_Angle angle_in, angle_out; 1382 1383 1384 /* initialize with current direction */ 1385 angle_in = angle_out = stroker->angle_in; 1386 1387 if ( arc < limit && 1388 !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) ) 1389 { 1390 if ( stroker->first_point ) 1391 stroker->angle_in = angle_in; 1392 1393 ft_conic_split( arc ); 1394 arc += 2; 1395 continue; 1396 } 1397 1398 if ( first_arc ) 1399 { 1400 first_arc = FALSE; 1401 1402 /* process corner if necessary */ 1403 if ( stroker->first_point ) 1404 error = ft_stroker_subpath_start( stroker, angle_in, 0 ); 1405 else 1406 { 1407 stroker->angle_out = angle_in; 1408 error = ft_stroker_process_corner( stroker, 0 ); 1409 } 1410 } 1411 else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) > 1412 FT_SMALL_CONIC_THRESHOLD / 4 ) 1413 { 1414 /* if the deviation from one arc to the next is too great, */ 1415 /* add a round corner */ 1416 stroker->center = arc[2]; 1417 stroker->angle_out = angle_in; 1418 stroker->line_join = FT_STROKER_LINEJOIN_ROUND; 1419 1420 error = ft_stroker_process_corner( stroker, 0 ); 1421 1422 /* reinstate line join style */ 1423 stroker->line_join = stroker->line_join_saved; 1424 } 1425 1426 if ( error ) 1427 goto Exit; 1428 1429 /* the arc's angle is small enough; we can add it directly to each */ 1430 /* border */ 1431 { 1432 FT_Vector ctrl, end; 1433 FT_Angle theta, phi, rotate, alpha0 = 0; 1434 FT_Fixed length; 1435 FT_StrokeBorder border; 1436 FT_Int side; 1437 1438 1439 theta = FT_Angle_Diff( angle_in, angle_out ) / 2; 1440 phi = angle_in + theta; 1441 length = FT_DivFix( stroker->radius, FT_Cos( theta ) ); 1442 1443 /* compute direction of original arc */ 1444 if ( stroker->handle_wide_strokes ) 1445 alpha0 = FT_Atan2( arc[0].x - arc[2].x, arc[0].y - arc[2].y ); 1446 1447 for ( border = stroker->borders, side = 0; 1448 side <= 1; 1449 side++, border++ ) 1450 { 1451 rotate = FT_SIDE_TO_ROTATE( side ); 1452 1453 /* compute control point */ 1454 FT_Vector_From_Polar( &ctrl, length, phi + rotate ); 1455 ctrl.x += arc[1].x; 1456 ctrl.y += arc[1].y; 1457 1458 /* compute end point */ 1459 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1460 end.x += arc[0].x; 1461 end.y += arc[0].y; 1462 1463 if ( stroker->handle_wide_strokes ) 1464 { 1465 FT_Vector start; 1466 FT_Angle alpha1; 1467 1468 1469 /* determine whether the border radius is greater than the */ 1470 /* radius of curvature of the original arc */ 1471 start = border->points[border->num_points - 1]; 1472 1473 alpha1 = FT_Atan2( end.x - start.x, end.y - start.y ); 1474 1475 /* is the direction of the border arc opposite to */ 1476 /* that of the original arc? */ 1477 if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) > 1478 FT_ANGLE_PI / 2 ) 1479 { 1480 FT_Angle beta, gamma; 1481 FT_Vector bvec, delta; 1482 FT_Fixed blen, sinA, sinB, alen; 1483 1484 1485 /* use the sine rule to find the intersection point */ 1486 beta = FT_Atan2( arc[2].x - start.x, arc[2].y - start.y ); 1487 gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y ); 1488 1489 bvec.x = end.x - start.x; 1490 bvec.y = end.y - start.y; 1491 1492 blen = FT_Vector_Length( &bvec ); 1493 1494 sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) ); 1495 sinB = ft_pos_abs( FT_Sin( beta - gamma ) ); 1496 1497 alen = FT_MulDiv( blen, sinA, sinB ); 1498 1499 FT_Vector_From_Polar( &delta, alen, beta ); 1500 delta.x += start.x; 1501 delta.y += start.y; 1502 1503 /* circumnavigate the negative sector backwards */ 1504 border->movable = FALSE; 1505 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1506 if ( error ) 1507 goto Exit; 1508 error = ft_stroke_border_lineto( border, &end, FALSE ); 1509 if ( error ) 1510 goto Exit; 1511 error = ft_stroke_border_conicto( border, &ctrl, &start ); 1512 if ( error ) 1513 goto Exit; 1514 /* and then move to the endpoint */ 1515 error = ft_stroke_border_lineto( border, &end, FALSE ); 1516 if ( error ) 1517 goto Exit; 1518 1519 continue; 1520 } 1521 1522 /* else fall through */ 1523 } 1524 1525 /* simply add an arc */ 1526 error = ft_stroke_border_conicto( border, &ctrl, &end ); 1527 if ( error ) 1528 goto Exit; 1529 } 1530 } 1531 1532 arc -= 2; 1533 1534 stroker->angle_in = angle_out; 1535 } 1536 1537 stroker->center = *to; 1538 1539 Exit: 1540 return error; 1541 } 1542 1543 1544 /* documentation is in ftstroke.h */ 1545 1546 FT_EXPORT_DEF( FT_Error ) FT_Stroker_CubicTo(FT_Stroker stroker,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)1547 FT_Stroker_CubicTo( FT_Stroker stroker, 1548 FT_Vector* control1, 1549 FT_Vector* control2, 1550 FT_Vector* to ) 1551 { 1552 FT_Error error = FT_Err_Ok; 1553 FT_Vector bez_stack[37]; 1554 FT_Vector* arc; 1555 FT_Vector* limit = bez_stack + 32; 1556 FT_Bool first_arc = TRUE; 1557 1558 1559 /* if all control points are coincident, this is a no-op; */ 1560 /* avoid creating a spurious corner */ 1561 if ( FT_IS_SMALL( stroker->center.x - control1->x ) && 1562 FT_IS_SMALL( stroker->center.y - control1->y ) && 1563 FT_IS_SMALL( control1->x - control2->x ) && 1564 FT_IS_SMALL( control1->y - control2->y ) && 1565 FT_IS_SMALL( control2->x - to->x ) && 1566 FT_IS_SMALL( control2->y - to->y ) ) 1567 { 1568 stroker->center = *to; 1569 goto Exit; 1570 } 1571 1572 arc = bez_stack; 1573 arc[0] = *to; 1574 arc[1] = *control2; 1575 arc[2] = *control1; 1576 arc[3] = stroker->center; 1577 1578 while ( arc >= bez_stack ) 1579 { 1580 FT_Angle angle_in, angle_mid, angle_out; 1581 1582 1583 /* initialize with current direction */ 1584 angle_in = angle_out = angle_mid = stroker->angle_in; 1585 1586 if ( arc < limit && 1587 !ft_cubic_is_small_enough( arc, &angle_in, 1588 &angle_mid, &angle_out ) ) 1589 { 1590 if ( stroker->first_point ) 1591 stroker->angle_in = angle_in; 1592 1593 ft_cubic_split( arc ); 1594 arc += 3; 1595 continue; 1596 } 1597 1598 if ( first_arc ) 1599 { 1600 first_arc = FALSE; 1601 1602 /* process corner if necessary */ 1603 if ( stroker->first_point ) 1604 error = ft_stroker_subpath_start( stroker, angle_in, 0 ); 1605 else 1606 { 1607 stroker->angle_out = angle_in; 1608 error = ft_stroker_process_corner( stroker, 0 ); 1609 } 1610 } 1611 else if ( ft_pos_abs( FT_Angle_Diff( stroker->angle_in, angle_in ) ) > 1612 FT_SMALL_CUBIC_THRESHOLD / 4 ) 1613 { 1614 /* if the deviation from one arc to the next is too great, */ 1615 /* add a round corner */ 1616 stroker->center = arc[3]; 1617 stroker->angle_out = angle_in; 1618 stroker->line_join = FT_STROKER_LINEJOIN_ROUND; 1619 1620 error = ft_stroker_process_corner( stroker, 0 ); 1621 1622 /* reinstate line join style */ 1623 stroker->line_join = stroker->line_join_saved; 1624 } 1625 1626 if ( error ) 1627 goto Exit; 1628 1629 /* the arc's angle is small enough; we can add it directly to each */ 1630 /* border */ 1631 { 1632 FT_Vector ctrl1, ctrl2, end; 1633 FT_Angle theta1, phi1, theta2, phi2, rotate, alpha0 = 0; 1634 FT_Fixed length1, length2; 1635 FT_StrokeBorder border; 1636 FT_Int side; 1637 1638 1639 theta1 = FT_Angle_Diff( angle_in, angle_mid ) / 2; 1640 theta2 = FT_Angle_Diff( angle_mid, angle_out ) / 2; 1641 phi1 = ft_angle_mean( angle_in, angle_mid ); 1642 phi2 = ft_angle_mean( angle_mid, angle_out ); 1643 length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) ); 1644 length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) ); 1645 1646 /* compute direction of original arc */ 1647 if ( stroker->handle_wide_strokes ) 1648 alpha0 = FT_Atan2( arc[0].x - arc[3].x, arc[0].y - arc[3].y ); 1649 1650 for ( border = stroker->borders, side = 0; 1651 side <= 1; 1652 side++, border++ ) 1653 { 1654 rotate = FT_SIDE_TO_ROTATE( side ); 1655 1656 /* compute control points */ 1657 FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate ); 1658 ctrl1.x += arc[2].x; 1659 ctrl1.y += arc[2].y; 1660 1661 FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate ); 1662 ctrl2.x += arc[1].x; 1663 ctrl2.y += arc[1].y; 1664 1665 /* compute end point */ 1666 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1667 end.x += arc[0].x; 1668 end.y += arc[0].y; 1669 1670 if ( stroker->handle_wide_strokes ) 1671 { 1672 FT_Vector start; 1673 FT_Angle alpha1; 1674 1675 1676 /* determine whether the border radius is greater than the */ 1677 /* radius of curvature of the original arc */ 1678 start = border->points[border->num_points - 1]; 1679 1680 alpha1 = FT_Atan2( end.x - start.x, end.y - start.y ); 1681 1682 /* is the direction of the border arc opposite to */ 1683 /* that of the original arc? */ 1684 if ( ft_pos_abs( FT_Angle_Diff( alpha0, alpha1 ) ) > 1685 FT_ANGLE_PI / 2 ) 1686 { 1687 FT_Angle beta, gamma; 1688 FT_Vector bvec, delta; 1689 FT_Fixed blen, sinA, sinB, alen; 1690 1691 1692 /* use the sine rule to find the intersection point */ 1693 beta = FT_Atan2( arc[3].x - start.x, arc[3].y - start.y ); 1694 gamma = FT_Atan2( arc[0].x - end.x, arc[0].y - end.y ); 1695 1696 bvec.x = end.x - start.x; 1697 bvec.y = end.y - start.y; 1698 1699 blen = FT_Vector_Length( &bvec ); 1700 1701 sinA = ft_pos_abs( FT_Sin( alpha1 - gamma ) ); 1702 sinB = ft_pos_abs( FT_Sin( beta - gamma ) ); 1703 1704 alen = FT_MulDiv( blen, sinA, sinB ); 1705 1706 FT_Vector_From_Polar( &delta, alen, beta ); 1707 delta.x += start.x; 1708 delta.y += start.y; 1709 1710 /* circumnavigate the negative sector backwards */ 1711 border->movable = FALSE; 1712 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1713 if ( error ) 1714 goto Exit; 1715 error = ft_stroke_border_lineto( border, &end, FALSE ); 1716 if ( error ) 1717 goto Exit; 1718 error = ft_stroke_border_cubicto( border, 1719 &ctrl2, 1720 &ctrl1, 1721 &start ); 1722 if ( error ) 1723 goto Exit; 1724 /* and then move to the endpoint */ 1725 error = ft_stroke_border_lineto( border, &end, FALSE ); 1726 if ( error ) 1727 goto Exit; 1728 1729 continue; 1730 } 1731 1732 /* else fall through */ 1733 } 1734 1735 /* simply add an arc */ 1736 error = ft_stroke_border_cubicto( border, &ctrl1, &ctrl2, &end ); 1737 if ( error ) 1738 goto Exit; 1739 } 1740 } 1741 1742 arc -= 3; 1743 1744 stroker->angle_in = angle_out; 1745 } 1746 1747 stroker->center = *to; 1748 1749 Exit: 1750 return error; 1751 } 1752 1753 1754 /* documentation is in ftstroke.h */ 1755 1756 FT_EXPORT_DEF( FT_Error ) FT_Stroker_BeginSubPath(FT_Stroker stroker,FT_Vector * to,FT_Bool open)1757 FT_Stroker_BeginSubPath( FT_Stroker stroker, 1758 FT_Vector* to, 1759 FT_Bool open ) 1760 { 1761 /* We cannot process the first point, because there is not enough */ 1762 /* information regarding its corner/cap. The latter will be processed */ 1763 /* in the `FT_Stroker_EndSubPath' routine. */ 1764 /* */ 1765 stroker->first_point = TRUE; 1766 stroker->center = *to; 1767 stroker->subpath_open = open; 1768 1769 /* Determine if we need to check whether the border radius is greater */ 1770 /* than the radius of curvature of a curve, to handle this case */ 1771 /* specially. This is only required if bevel joins or butt caps may */ 1772 /* be created, because round & miter joins and round & square caps */ 1773 /* cover the negative sector created with wide strokes. */ 1774 stroker->handle_wide_strokes = 1775 FT_BOOL( stroker->line_join != FT_STROKER_LINEJOIN_ROUND || 1776 ( stroker->subpath_open && 1777 stroker->line_cap == FT_STROKER_LINECAP_BUTT ) ); 1778 1779 /* record the subpath start point for each border */ 1780 stroker->subpath_start = *to; 1781 1782 stroker->angle_in = 0; 1783 1784 return FT_Err_Ok; 1785 } 1786 1787 1788 static FT_Error ft_stroker_add_reverse_left(FT_Stroker stroker,FT_Bool open)1789 ft_stroker_add_reverse_left( FT_Stroker stroker, 1790 FT_Bool open ) 1791 { 1792 FT_StrokeBorder right = stroker->borders + 0; 1793 FT_StrokeBorder left = stroker->borders + 1; 1794 FT_Int new_points; 1795 FT_Error error = FT_Err_Ok; 1796 1797 1798 FT_ASSERT( left->start >= 0 ); 1799 1800 new_points = left->num_points - left->start; 1801 if ( new_points > 0 ) 1802 { 1803 error = ft_stroke_border_grow( right, (FT_UInt)new_points ); 1804 if ( error ) 1805 goto Exit; 1806 1807 { 1808 FT_Vector* dst_point = right->points + right->num_points; 1809 FT_Byte* dst_tag = right->tags + right->num_points; 1810 FT_Vector* src_point = left->points + left->num_points - 1; 1811 FT_Byte* src_tag = left->tags + left->num_points - 1; 1812 1813 1814 while ( src_point >= left->points + left->start ) 1815 { 1816 *dst_point = *src_point; 1817 *dst_tag = *src_tag; 1818 1819 if ( open ) 1820 dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END; 1821 else 1822 { 1823 FT_Byte ttag = 1824 (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END ); 1825 1826 1827 /* switch begin/end tags if necessary */ 1828 if ( ttag == FT_STROKE_TAG_BEGIN || 1829 ttag == FT_STROKE_TAG_END ) 1830 dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END; 1831 } 1832 1833 src_point--; 1834 src_tag--; 1835 dst_point++; 1836 dst_tag++; 1837 } 1838 } 1839 1840 left->num_points = left->start; 1841 right->num_points += new_points; 1842 1843 right->movable = FALSE; 1844 left->movable = FALSE; 1845 } 1846 1847 Exit: 1848 return error; 1849 } 1850 1851 1852 /* documentation is in ftstroke.h */ 1853 1854 /* there's a lot of magic in this function! */ 1855 FT_EXPORT_DEF( FT_Error ) FT_Stroker_EndSubPath(FT_Stroker stroker)1856 FT_Stroker_EndSubPath( FT_Stroker stroker ) 1857 { 1858 FT_Error error = FT_Err_Ok; 1859 1860 1861 if ( stroker->subpath_open ) 1862 { 1863 FT_StrokeBorder right = stroker->borders; 1864 1865 1866 /* All right, this is an opened path, we need to add a cap between */ 1867 /* right & left, add the reverse of left, then add a final cap */ 1868 /* between left & right. */ 1869 error = ft_stroker_cap( stroker, stroker->angle_in, 0 ); 1870 if ( error ) 1871 goto Exit; 1872 1873 /* add reversed points from `left' to `right' */ 1874 error = ft_stroker_add_reverse_left( stroker, TRUE ); 1875 if ( error ) 1876 goto Exit; 1877 1878 /* now add the final cap */ 1879 stroker->center = stroker->subpath_start; 1880 error = ft_stroker_cap( stroker, 1881 stroker->subpath_angle + FT_ANGLE_PI, 0 ); 1882 if ( error ) 1883 goto Exit; 1884 1885 /* Now end the right subpath accordingly. The left one is */ 1886 /* rewind and doesn't need further processing. */ 1887 ft_stroke_border_close( right, FALSE ); 1888 } 1889 else 1890 { 1891 FT_Angle turn; 1892 FT_Int inside_side; 1893 1894 1895 /* close the path if needed */ 1896 if ( stroker->center.x != stroker->subpath_start.x || 1897 stroker->center.y != stroker->subpath_start.y ) 1898 { 1899 error = FT_Stroker_LineTo( stroker, &stroker->subpath_start ); 1900 if ( error ) 1901 goto Exit; 1902 } 1903 1904 /* process the corner */ 1905 stroker->angle_out = stroker->subpath_angle; 1906 turn = FT_Angle_Diff( stroker->angle_in, 1907 stroker->angle_out ); 1908 1909 /* no specific corner processing is required if the turn is 0 */ 1910 if ( turn != 0 ) 1911 { 1912 /* when we turn to the right, the inside side is 0 */ 1913 inside_side = 0; 1914 1915 /* otherwise, the inside side is 1 */ 1916 if ( turn < 0 ) 1917 inside_side = 1; 1918 1919 error = ft_stroker_inside( stroker, 1920 inside_side, 1921 stroker->subpath_line_length ); 1922 if ( error ) 1923 goto Exit; 1924 1925 /* process the outside side */ 1926 error = ft_stroker_outside( stroker, 1927 1 - inside_side, 1928 stroker->subpath_line_length ); 1929 if ( error ) 1930 goto Exit; 1931 } 1932 1933 /* then end our two subpaths */ 1934 ft_stroke_border_close( stroker->borders + 0, FALSE ); 1935 ft_stroke_border_close( stroker->borders + 1, TRUE ); 1936 } 1937 1938 Exit: 1939 return error; 1940 } 1941 1942 1943 /* documentation is in ftstroke.h */ 1944 1945 FT_EXPORT_DEF( FT_Error ) FT_Stroker_GetBorderCounts(FT_Stroker stroker,FT_StrokerBorder border,FT_UInt * anum_points,FT_UInt * anum_contours)1946 FT_Stroker_GetBorderCounts( FT_Stroker stroker, 1947 FT_StrokerBorder border, 1948 FT_UInt *anum_points, 1949 FT_UInt *anum_contours ) 1950 { 1951 FT_UInt num_points = 0, num_contours = 0; 1952 FT_Error error; 1953 1954 1955 if ( !stroker || border > 1 ) 1956 { 1957 error = FT_THROW( Invalid_Argument ); 1958 goto Exit; 1959 } 1960 1961 error = ft_stroke_border_get_counts( stroker->borders + border, 1962 &num_points, &num_contours ); 1963 Exit: 1964 if ( anum_points ) 1965 *anum_points = num_points; 1966 1967 if ( anum_contours ) 1968 *anum_contours = num_contours; 1969 1970 return error; 1971 } 1972 1973 1974 /* documentation is in ftstroke.h */ 1975 1976 FT_EXPORT_DEF( FT_Error ) FT_Stroker_GetCounts(FT_Stroker stroker,FT_UInt * anum_points,FT_UInt * anum_contours)1977 FT_Stroker_GetCounts( FT_Stroker stroker, 1978 FT_UInt *anum_points, 1979 FT_UInt *anum_contours ) 1980 { 1981 FT_UInt count1, count2, num_points = 0; 1982 FT_UInt count3, count4, num_contours = 0; 1983 FT_Error error; 1984 1985 1986 error = ft_stroke_border_get_counts( stroker->borders + 0, 1987 &count1, &count2 ); 1988 if ( error ) 1989 goto Exit; 1990 1991 error = ft_stroke_border_get_counts( stroker->borders + 1, 1992 &count3, &count4 ); 1993 if ( error ) 1994 goto Exit; 1995 1996 num_points = count1 + count3; 1997 num_contours = count2 + count4; 1998 1999 Exit: 2000 *anum_points = num_points; 2001 *anum_contours = num_contours; 2002 return error; 2003 } 2004 2005 2006 /* documentation is in ftstroke.h */ 2007 2008 FT_EXPORT_DEF( void ) FT_Stroker_ExportBorder(FT_Stroker stroker,FT_StrokerBorder border,FT_Outline * outline)2009 FT_Stroker_ExportBorder( FT_Stroker stroker, 2010 FT_StrokerBorder border, 2011 FT_Outline* outline ) 2012 { 2013 if ( border == FT_STROKER_BORDER_LEFT || 2014 border == FT_STROKER_BORDER_RIGHT ) 2015 { 2016 FT_StrokeBorder sborder = & stroker->borders[border]; 2017 2018 2019 if ( sborder->valid ) 2020 ft_stroke_border_export( sborder, outline ); 2021 } 2022 } 2023 2024 2025 /* documentation is in ftstroke.h */ 2026 2027 FT_EXPORT_DEF( void ) FT_Stroker_Export(FT_Stroker stroker,FT_Outline * outline)2028 FT_Stroker_Export( FT_Stroker stroker, 2029 FT_Outline* outline ) 2030 { 2031 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline ); 2032 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline ); 2033 } 2034 2035 2036 /* documentation is in ftstroke.h */ 2037 2038 /* 2039 * The following is very similar to FT_Outline_Decompose, except 2040 * that we do support opened paths, and do not scale the outline. 2041 */ 2042 FT_EXPORT_DEF( FT_Error ) FT_Stroker_ParseOutline(FT_Stroker stroker,FT_Outline * outline,FT_Bool opened)2043 FT_Stroker_ParseOutline( FT_Stroker stroker, 2044 FT_Outline* outline, 2045 FT_Bool opened ) 2046 { 2047 FT_Vector v_last; 2048 FT_Vector v_control; 2049 FT_Vector v_start; 2050 2051 FT_Vector* point; 2052 FT_Vector* limit; 2053 char* tags; 2054 2055 FT_Error error; 2056 2057 FT_Int n; /* index of contour in outline */ 2058 FT_UInt first; /* index of first point in contour */ 2059 FT_Int tag; /* current point's state */ 2060 2061 2062 if ( !outline || !stroker ) 2063 return FT_THROW( Invalid_Argument ); 2064 2065 FT_Stroker_Rewind( stroker ); 2066 2067 first = 0; 2068 2069 for ( n = 0; n < outline->n_contours; n++ ) 2070 { 2071 FT_UInt last; /* index of last point in contour */ 2072 2073 2074 last = outline->contours[n]; 2075 limit = outline->points + last; 2076 2077 /* skip empty points; we don't stroke these */ 2078 if ( last <= first ) 2079 { 2080 first = last + 1; 2081 continue; 2082 } 2083 2084 v_start = outline->points[first]; 2085 v_last = outline->points[last]; 2086 2087 v_control = v_start; 2088 2089 point = outline->points + first; 2090 tags = outline->tags + first; 2091 tag = FT_CURVE_TAG( tags[0] ); 2092 2093 /* A contour cannot start with a cubic control point! */ 2094 if ( tag == FT_CURVE_TAG_CUBIC ) 2095 goto Invalid_Outline; 2096 2097 /* check first point to determine origin */ 2098 if ( tag == FT_CURVE_TAG_CONIC ) 2099 { 2100 /* First point is conic control. Yes, this happens. */ 2101 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) 2102 { 2103 /* start at last point if it is on the curve */ 2104 v_start = v_last; 2105 limit--; 2106 } 2107 else 2108 { 2109 /* if both first and last points are conic, */ 2110 /* start at their middle */ 2111 v_start.x = ( v_start.x + v_last.x ) / 2; 2112 v_start.y = ( v_start.y + v_last.y ) / 2; 2113 } 2114 point--; 2115 tags--; 2116 } 2117 2118 error = FT_Stroker_BeginSubPath( stroker, &v_start, opened ); 2119 if ( error ) 2120 goto Exit; 2121 2122 while ( point < limit ) 2123 { 2124 point++; 2125 tags++; 2126 2127 tag = FT_CURVE_TAG( tags[0] ); 2128 switch ( tag ) 2129 { 2130 case FT_CURVE_TAG_ON: /* emit a single line_to */ 2131 { 2132 FT_Vector vec; 2133 2134 2135 vec.x = point->x; 2136 vec.y = point->y; 2137 2138 error = FT_Stroker_LineTo( stroker, &vec ); 2139 if ( error ) 2140 goto Exit; 2141 continue; 2142 } 2143 2144 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ 2145 v_control.x = point->x; 2146 v_control.y = point->y; 2147 2148 Do_Conic: 2149 if ( point < limit ) 2150 { 2151 FT_Vector vec; 2152 FT_Vector v_middle; 2153 2154 2155 point++; 2156 tags++; 2157 tag = FT_CURVE_TAG( tags[0] ); 2158 2159 vec = point[0]; 2160 2161 if ( tag == FT_CURVE_TAG_ON ) 2162 { 2163 error = FT_Stroker_ConicTo( stroker, &v_control, &vec ); 2164 if ( error ) 2165 goto Exit; 2166 continue; 2167 } 2168 2169 if ( tag != FT_CURVE_TAG_CONIC ) 2170 goto Invalid_Outline; 2171 2172 v_middle.x = ( v_control.x + vec.x ) / 2; 2173 v_middle.y = ( v_control.y + vec.y ) / 2; 2174 2175 error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle ); 2176 if ( error ) 2177 goto Exit; 2178 2179 v_control = vec; 2180 goto Do_Conic; 2181 } 2182 2183 error = FT_Stroker_ConicTo( stroker, &v_control, &v_start ); 2184 goto Close; 2185 2186 default: /* FT_CURVE_TAG_CUBIC */ 2187 { 2188 FT_Vector vec1, vec2; 2189 2190 2191 if ( point + 1 > limit || 2192 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) 2193 goto Invalid_Outline; 2194 2195 point += 2; 2196 tags += 2; 2197 2198 vec1 = point[-2]; 2199 vec2 = point[-1]; 2200 2201 if ( point <= limit ) 2202 { 2203 FT_Vector vec; 2204 2205 2206 vec = point[0]; 2207 2208 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec ); 2209 if ( error ) 2210 goto Exit; 2211 continue; 2212 } 2213 2214 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start ); 2215 goto Close; 2216 } 2217 } 2218 } 2219 2220 Close: 2221 if ( error ) 2222 goto Exit; 2223 2224 /* don't try to end the path if no segments have been generated */ 2225 if ( !stroker->first_point ) 2226 { 2227 error = FT_Stroker_EndSubPath( stroker ); 2228 if ( error ) 2229 goto Exit; 2230 } 2231 2232 first = last + 1; 2233 } 2234 2235 return FT_Err_Ok; 2236 2237 Exit: 2238 return error; 2239 2240 Invalid_Outline: 2241 return FT_THROW( Invalid_Outline ); 2242 } 2243 2244 2245 /* declare an extern to access `ft_outline_glyph_class' globally */ 2246 /* allocated in `ftglyph.c', and use the FT_OUTLINE_GLYPH_CLASS_GET */ 2247 /* macro to access it when FT_CONFIG_OPTION_PIC is defined */ 2248 #ifndef FT_CONFIG_OPTION_PIC 2249 extern const FT_Glyph_Class ft_outline_glyph_class; 2250 #endif 2251 #include "basepic.h" 2252 2253 2254 /* documentation is in ftstroke.h */ 2255 2256 FT_EXPORT_DEF( FT_Error ) FT_Glyph_Stroke(FT_Glyph * pglyph,FT_Stroker stroker,FT_Bool destroy)2257 FT_Glyph_Stroke( FT_Glyph *pglyph, 2258 FT_Stroker stroker, 2259 FT_Bool destroy ) 2260 { 2261 FT_Error error = FT_ERR( Invalid_Argument ); 2262 FT_Glyph glyph = NULL; 2263 FT_Library library = stroker->library; 2264 2265 FT_UNUSED( library ); 2266 2267 2268 if ( pglyph == NULL ) 2269 goto Exit; 2270 2271 glyph = *pglyph; 2272 if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET ) 2273 goto Exit; 2274 2275 { 2276 FT_Glyph copy; 2277 2278 2279 error = FT_Glyph_Copy( glyph, © ); 2280 if ( error ) 2281 goto Exit; 2282 2283 glyph = copy; 2284 } 2285 2286 { 2287 FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph; 2288 FT_Outline* outline = &oglyph->outline; 2289 FT_UInt num_points, num_contours; 2290 2291 2292 error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); 2293 if ( error ) 2294 goto Fail; 2295 2296 (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours ); 2297 2298 FT_Outline_Done( glyph->library, outline ); 2299 2300 error = FT_Outline_New( glyph->library, 2301 num_points, num_contours, outline ); 2302 if ( error ) 2303 goto Fail; 2304 2305 outline->n_points = 0; 2306 outline->n_contours = 0; 2307 2308 FT_Stroker_Export( stroker, outline ); 2309 } 2310 2311 if ( destroy ) 2312 FT_Done_Glyph( *pglyph ); 2313 2314 *pglyph = glyph; 2315 goto Exit; 2316 2317 Fail: 2318 FT_Done_Glyph( glyph ); 2319 glyph = NULL; 2320 2321 if ( !destroy ) 2322 *pglyph = NULL; 2323 2324 Exit: 2325 return error; 2326 } 2327 2328 2329 /* documentation is in ftstroke.h */ 2330 2331 FT_EXPORT_DEF( FT_Error ) FT_Glyph_StrokeBorder(FT_Glyph * pglyph,FT_Stroker stroker,FT_Bool inside,FT_Bool destroy)2332 FT_Glyph_StrokeBorder( FT_Glyph *pglyph, 2333 FT_Stroker stroker, 2334 FT_Bool inside, 2335 FT_Bool destroy ) 2336 { 2337 FT_Error error = FT_ERR( Invalid_Argument ); 2338 FT_Glyph glyph = NULL; 2339 FT_Library library = stroker->library; 2340 2341 FT_UNUSED( library ); 2342 2343 2344 if ( pglyph == NULL ) 2345 goto Exit; 2346 2347 glyph = *pglyph; 2348 if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET ) 2349 goto Exit; 2350 2351 { 2352 FT_Glyph copy; 2353 2354 2355 error = FT_Glyph_Copy( glyph, © ); 2356 if ( error ) 2357 goto Exit; 2358 2359 glyph = copy; 2360 } 2361 2362 { 2363 FT_OutlineGlyph oglyph = (FT_OutlineGlyph)glyph; 2364 FT_StrokerBorder border; 2365 FT_Outline* outline = &oglyph->outline; 2366 FT_UInt num_points, num_contours; 2367 2368 2369 border = FT_Outline_GetOutsideBorder( outline ); 2370 if ( inside ) 2371 { 2372 if ( border == FT_STROKER_BORDER_LEFT ) 2373 border = FT_STROKER_BORDER_RIGHT; 2374 else 2375 border = FT_STROKER_BORDER_LEFT; 2376 } 2377 2378 error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); 2379 if ( error ) 2380 goto Fail; 2381 2382 (void)FT_Stroker_GetBorderCounts( stroker, border, 2383 &num_points, &num_contours ); 2384 2385 FT_Outline_Done( glyph->library, outline ); 2386 2387 error = FT_Outline_New( glyph->library, 2388 num_points, 2389 num_contours, 2390 outline ); 2391 if ( error ) 2392 goto Fail; 2393 2394 outline->n_points = 0; 2395 outline->n_contours = 0; 2396 2397 FT_Stroker_ExportBorder( stroker, border, outline ); 2398 } 2399 2400 if ( destroy ) 2401 FT_Done_Glyph( *pglyph ); 2402 2403 *pglyph = glyph; 2404 goto Exit; 2405 2406 Fail: 2407 FT_Done_Glyph( glyph ); 2408 glyph = NULL; 2409 2410 if ( !destroy ) 2411 *pglyph = NULL; 2412 2413 Exit: 2414 return error; 2415 } 2416 2417 2418 /* END */ 2419