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