1 /***************************************************************************/ 2 /* */ 3 /* ftstroke.c */ 4 /* */ 5 /* FreeType path stroker (body). */ 6 /* */ 7 /* Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009 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 / 6 ) 64 #define FT_EPSILON 2 65 66 #define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON ) 67 68 69 static FT_Pos ft_pos_abs(FT_Pos x)70 ft_pos_abs( FT_Pos x ) 71 { 72 return x >= 0 ? x : -x ; 73 } 74 75 76 static void ft_conic_split(FT_Vector * base)77 ft_conic_split( FT_Vector* base ) 78 { 79 FT_Pos a, b; 80 81 82 base[4].x = base[2].x; 83 b = base[1].x; 84 a = base[3].x = ( base[2].x + b ) / 2; 85 b = base[1].x = ( base[0].x + b ) / 2; 86 base[2].x = ( a + b ) / 2; 87 88 base[4].y = base[2].y; 89 b = base[1].y; 90 a = base[3].y = ( base[2].y + b ) / 2; 91 b = base[1].y = ( base[0].y + b ) / 2; 92 base[2].y = ( a + b ) / 2; 93 } 94 95 96 static FT_Bool ft_conic_is_small_enough(FT_Vector * base,FT_Angle * angle_in,FT_Angle * angle_out)97 ft_conic_is_small_enough( FT_Vector* base, 98 FT_Angle *angle_in, 99 FT_Angle *angle_out ) 100 { 101 FT_Vector d1, d2; 102 FT_Angle theta; 103 FT_Int close1, close2; 104 105 106 d1.x = base[1].x - base[2].x; 107 d1.y = base[1].y - base[2].y; 108 d2.x = base[0].x - base[1].x; 109 d2.y = base[0].y - base[1].y; 110 111 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); 112 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); 113 114 if ( close1 ) 115 { 116 if ( close2 ) 117 *angle_in = *angle_out = 0; 118 else 119 *angle_in = *angle_out = FT_Atan2( d2.x, d2.y ); 120 } 121 else if ( close2 ) 122 { 123 *angle_in = *angle_out = FT_Atan2( d1.x, d1.y ); 124 } 125 else 126 { 127 *angle_in = FT_Atan2( d1.x, d1.y ); 128 *angle_out = FT_Atan2( d2.x, d2.y ); 129 } 130 131 theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) ); 132 133 return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD ); 134 } 135 136 137 static void ft_cubic_split(FT_Vector * base)138 ft_cubic_split( FT_Vector* base ) 139 { 140 FT_Pos a, b, c, d; 141 142 143 base[6].x = base[3].x; 144 c = base[1].x; 145 d = base[2].x; 146 base[1].x = a = ( base[0].x + c ) / 2; 147 base[5].x = b = ( base[3].x + d ) / 2; 148 c = ( c + d ) / 2; 149 base[2].x = a = ( a + c ) / 2; 150 base[4].x = b = ( b + c ) / 2; 151 base[3].x = ( a + b ) / 2; 152 153 base[6].y = base[3].y; 154 c = base[1].y; 155 d = base[2].y; 156 base[1].y = a = ( base[0].y + c ) / 2; 157 base[5].y = b = ( base[3].y + d ) / 2; 158 c = ( c + d ) / 2; 159 base[2].y = a = ( a + c ) / 2; 160 base[4].y = b = ( b + c ) / 2; 161 base[3].y = ( a + b ) / 2; 162 } 163 164 165 static FT_Bool ft_cubic_is_small_enough(FT_Vector * base,FT_Angle * angle_in,FT_Angle * angle_mid,FT_Angle * angle_out)166 ft_cubic_is_small_enough( FT_Vector* base, 167 FT_Angle *angle_in, 168 FT_Angle *angle_mid, 169 FT_Angle *angle_out ) 170 { 171 FT_Vector d1, d2, d3; 172 FT_Angle theta1, theta2; 173 FT_Int close1, close2, close3; 174 175 176 d1.x = base[2].x - base[3].x; 177 d1.y = base[2].y - base[3].y; 178 d2.x = base[1].x - base[2].x; 179 d2.y = base[1].y - base[2].y; 180 d3.x = base[0].x - base[1].x; 181 d3.y = base[0].y - base[1].y; 182 183 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y ); 184 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y ); 185 close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y ); 186 187 if ( close1 || close3 ) 188 { 189 if ( close2 ) 190 { 191 /* basically a point */ 192 *angle_in = *angle_out = *angle_mid = 0; 193 } 194 else if ( close1 ) 195 { 196 *angle_in = *angle_mid = FT_Atan2( d2.x, d2.y ); 197 *angle_out = FT_Atan2( d3.x, d3.y ); 198 } 199 else /* close2 */ 200 { 201 *angle_in = FT_Atan2( d1.x, d1.y ); 202 *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y ); 203 } 204 } 205 else if ( close2 ) 206 { 207 *angle_in = *angle_mid = FT_Atan2( d1.x, d1.y ); 208 *angle_out = FT_Atan2( d3.x, d3.y ); 209 } 210 else 211 { 212 *angle_in = FT_Atan2( d1.x, d1.y ); 213 *angle_mid = FT_Atan2( d2.x, d2.y ); 214 *angle_out = FT_Atan2( d3.x, d3.y ); 215 } 216 217 theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) ); 218 theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) ); 219 220 return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD && 221 theta2 < FT_SMALL_CUBIC_THRESHOLD ); 222 } 223 224 225 /***************************************************************************/ 226 /***************************************************************************/ 227 /***** *****/ 228 /***** STROKE BORDERS *****/ 229 /***** *****/ 230 /***************************************************************************/ 231 /***************************************************************************/ 232 233 typedef enum FT_StrokeTags_ 234 { 235 FT_STROKE_TAG_ON = 1, /* on-curve point */ 236 FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */ 237 FT_STROKE_TAG_BEGIN = 4, /* sub-path start */ 238 FT_STROKE_TAG_END = 8 /* sub-path end */ 239 240 } FT_StrokeTags; 241 242 #define FT_STROKE_TAG_BEGIN_END (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END) 243 244 typedef struct FT_StrokeBorderRec_ 245 { 246 FT_UInt num_points; 247 FT_UInt max_points; 248 FT_Vector* points; 249 FT_Byte* tags; 250 FT_Bool movable; 251 FT_Int start; /* index of current sub-path start point */ 252 FT_Memory memory; 253 FT_Bool valid; 254 255 } FT_StrokeBorderRec, *FT_StrokeBorder; 256 257 258 static FT_Error ft_stroke_border_grow(FT_StrokeBorder border,FT_UInt new_points)259 ft_stroke_border_grow( FT_StrokeBorder border, 260 FT_UInt new_points ) 261 { 262 FT_UInt old_max = border->max_points; 263 FT_UInt new_max = border->num_points + new_points; 264 FT_Error error = FT_Err_Ok; 265 266 267 if ( new_max > old_max ) 268 { 269 FT_UInt cur_max = old_max; 270 FT_Memory memory = border->memory; 271 272 273 while ( cur_max < new_max ) 274 cur_max += ( cur_max >> 1 ) + 16; 275 276 if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) || 277 FT_RENEW_ARRAY( border->tags, old_max, cur_max ) ) 278 goto Exit; 279 280 border->max_points = cur_max; 281 } 282 283 Exit: 284 return error; 285 } 286 287 288 static void ft_stroke_border_close(FT_StrokeBorder border,FT_Bool reverse)289 ft_stroke_border_close( FT_StrokeBorder border, 290 FT_Bool reverse ) 291 { 292 FT_UInt start = border->start; 293 FT_UInt count = border->num_points; 294 295 296 FT_ASSERT( border->start >= 0 ); 297 298 /* don't record empty paths! */ 299 if ( count <= start + 1U ) 300 border->num_points = start; 301 else 302 { 303 /* copy the last point to the start of this sub-path, since */ 304 /* it contains the `adjusted' starting coordinates */ 305 border->num_points = --count; 306 border->points[start] = border->points[count]; 307 308 if ( reverse ) 309 { 310 /* reverse the points */ 311 { 312 FT_Vector* vec1 = border->points + start + 1; 313 FT_Vector* vec2 = border->points + count - 1; 314 315 316 for ( ; vec1 < vec2; vec1++, vec2-- ) 317 { 318 FT_Vector tmp; 319 320 321 tmp = *vec1; 322 *vec1 = *vec2; 323 *vec2 = tmp; 324 } 325 } 326 327 /* then the tags */ 328 { 329 FT_Byte* tag1 = border->tags + start + 1; 330 FT_Byte* tag2 = border->tags + count - 1; 331 332 333 for ( ; tag1 < tag2; tag1++, tag2-- ) 334 { 335 FT_Byte tmp; 336 337 338 tmp = *tag1; 339 *tag1 = *tag2; 340 *tag2 = tmp; 341 } 342 } 343 } 344 345 border->tags[start ] |= FT_STROKE_TAG_BEGIN; 346 border->tags[count - 1] |= FT_STROKE_TAG_END; 347 } 348 349 border->start = -1; 350 border->movable = FALSE; 351 } 352 353 354 static FT_Error ft_stroke_border_lineto(FT_StrokeBorder border,FT_Vector * to,FT_Bool movable)355 ft_stroke_border_lineto( FT_StrokeBorder border, 356 FT_Vector* to, 357 FT_Bool movable ) 358 { 359 FT_Error error = FT_Err_Ok; 360 361 362 FT_ASSERT( border->start >= 0 ); 363 364 if ( border->movable ) 365 { 366 /* move last point */ 367 border->points[border->num_points - 1] = *to; 368 } 369 else 370 { 371 /* add one point */ 372 error = ft_stroke_border_grow( border, 1 ); 373 if ( !error ) 374 { 375 FT_Vector* vec = border->points + border->num_points; 376 FT_Byte* tag = border->tags + border->num_points; 377 378 379 vec[0] = *to; 380 tag[0] = FT_STROKE_TAG_ON; 381 382 border->num_points += 1; 383 } 384 } 385 border->movable = movable; 386 return error; 387 } 388 389 390 static FT_Error ft_stroke_border_conicto(FT_StrokeBorder border,FT_Vector * control,FT_Vector * to)391 ft_stroke_border_conicto( FT_StrokeBorder border, 392 FT_Vector* control, 393 FT_Vector* to ) 394 { 395 FT_Error error; 396 397 398 FT_ASSERT( border->start >= 0 ); 399 400 error = ft_stroke_border_grow( border, 2 ); 401 if ( !error ) 402 { 403 FT_Vector* vec = border->points + border->num_points; 404 FT_Byte* tag = border->tags + border->num_points; 405 406 vec[0] = *control; 407 vec[1] = *to; 408 409 tag[0] = 0; 410 tag[1] = FT_STROKE_TAG_ON; 411 412 border->num_points += 2; 413 } 414 border->movable = FALSE; 415 return error; 416 } 417 418 419 static FT_Error ft_stroke_border_cubicto(FT_StrokeBorder border,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)420 ft_stroke_border_cubicto( FT_StrokeBorder border, 421 FT_Vector* control1, 422 FT_Vector* control2, 423 FT_Vector* to ) 424 { 425 FT_Error error; 426 427 428 FT_ASSERT( border->start >= 0 ); 429 430 error = ft_stroke_border_grow( border, 3 ); 431 if ( !error ) 432 { 433 FT_Vector* vec = border->points + border->num_points; 434 FT_Byte* tag = border->tags + border->num_points; 435 436 437 vec[0] = *control1; 438 vec[1] = *control2; 439 vec[2] = *to; 440 441 tag[0] = FT_STROKE_TAG_CUBIC; 442 tag[1] = FT_STROKE_TAG_CUBIC; 443 tag[2] = FT_STROKE_TAG_ON; 444 445 border->num_points += 3; 446 } 447 border->movable = FALSE; 448 return error; 449 } 450 451 452 #define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 ) 453 454 455 static FT_Error ft_stroke_border_arcto(FT_StrokeBorder border,FT_Vector * center,FT_Fixed radius,FT_Angle angle_start,FT_Angle angle_diff)456 ft_stroke_border_arcto( FT_StrokeBorder border, 457 FT_Vector* center, 458 FT_Fixed radius, 459 FT_Angle angle_start, 460 FT_Angle angle_diff ) 461 { 462 FT_Angle total, angle, step, rotate, next, theta; 463 FT_Vector a, b, a2, b2; 464 FT_Fixed length; 465 FT_Error error = FT_Err_Ok; 466 467 468 /* compute start point */ 469 FT_Vector_From_Polar( &a, radius, angle_start ); 470 a.x += center->x; 471 a.y += center->y; 472 473 total = angle_diff; 474 angle = angle_start; 475 rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2; 476 477 while ( total != 0 ) 478 { 479 step = total; 480 if ( step > FT_ARC_CUBIC_ANGLE ) 481 step = FT_ARC_CUBIC_ANGLE; 482 483 else if ( step < -FT_ARC_CUBIC_ANGLE ) 484 step = -FT_ARC_CUBIC_ANGLE; 485 486 next = angle + step; 487 theta = step; 488 if ( theta < 0 ) 489 theta = -theta; 490 491 theta >>= 1; 492 493 /* compute end point */ 494 FT_Vector_From_Polar( &b, radius, next ); 495 b.x += center->x; 496 b.y += center->y; 497 498 /* compute first and second control points */ 499 length = FT_MulDiv( radius, FT_Sin( theta ) * 4, 500 ( 0x10000L + FT_Cos( theta ) ) * 3 ); 501 502 FT_Vector_From_Polar( &a2, length, angle + rotate ); 503 a2.x += a.x; 504 a2.y += a.y; 505 506 FT_Vector_From_Polar( &b2, length, next - rotate ); 507 b2.x += b.x; 508 b2.y += b.y; 509 510 /* add cubic arc */ 511 error = ft_stroke_border_cubicto( border, &a2, &b2, &b ); 512 if ( error ) 513 break; 514 515 /* process the rest of the arc ?? */ 516 a = b; 517 total -= step; 518 angle = next; 519 } 520 521 return error; 522 } 523 524 525 static FT_Error ft_stroke_border_moveto(FT_StrokeBorder border,FT_Vector * to)526 ft_stroke_border_moveto( FT_StrokeBorder border, 527 FT_Vector* to ) 528 { 529 /* close current open path if any ? */ 530 if ( border->start >= 0 ) 531 ft_stroke_border_close( border, FALSE ); 532 533 border->start = border->num_points; 534 border->movable = FALSE; 535 536 return ft_stroke_border_lineto( border, to, FALSE ); 537 } 538 539 540 static void ft_stroke_border_init(FT_StrokeBorder border,FT_Memory memory)541 ft_stroke_border_init( FT_StrokeBorder border, 542 FT_Memory memory ) 543 { 544 border->memory = memory; 545 border->points = NULL; 546 border->tags = NULL; 547 548 border->num_points = 0; 549 border->max_points = 0; 550 border->start = -1; 551 border->valid = FALSE; 552 } 553 554 555 static void ft_stroke_border_reset(FT_StrokeBorder border)556 ft_stroke_border_reset( FT_StrokeBorder border ) 557 { 558 border->num_points = 0; 559 border->start = -1; 560 border->valid = FALSE; 561 } 562 563 564 static void ft_stroke_border_done(FT_StrokeBorder border)565 ft_stroke_border_done( FT_StrokeBorder border ) 566 { 567 FT_Memory memory = border->memory; 568 569 570 FT_FREE( border->points ); 571 FT_FREE( border->tags ); 572 573 border->num_points = 0; 574 border->max_points = 0; 575 border->start = -1; 576 border->valid = FALSE; 577 } 578 579 580 static FT_Error ft_stroke_border_get_counts(FT_StrokeBorder border,FT_UInt * anum_points,FT_UInt * anum_contours)581 ft_stroke_border_get_counts( FT_StrokeBorder border, 582 FT_UInt *anum_points, 583 FT_UInt *anum_contours ) 584 { 585 FT_Error error = FT_Err_Ok; 586 FT_UInt num_points = 0; 587 FT_UInt num_contours = 0; 588 589 FT_UInt count = border->num_points; 590 FT_Vector* point = border->points; 591 FT_Byte* tags = border->tags; 592 FT_Int in_contour = 0; 593 594 595 for ( ; count > 0; count--, num_points++, point++, tags++ ) 596 { 597 if ( tags[0] & FT_STROKE_TAG_BEGIN ) 598 { 599 if ( in_contour != 0 ) 600 goto Fail; 601 602 in_contour = 1; 603 } 604 else if ( in_contour == 0 ) 605 goto Fail; 606 607 if ( tags[0] & FT_STROKE_TAG_END ) 608 { 609 in_contour = 0; 610 num_contours++; 611 } 612 } 613 614 if ( in_contour != 0 ) 615 goto Fail; 616 617 border->valid = TRUE; 618 619 Exit: 620 *anum_points = num_points; 621 *anum_contours = num_contours; 622 return error; 623 624 Fail: 625 num_points = 0; 626 num_contours = 0; 627 goto Exit; 628 } 629 630 631 static void ft_stroke_border_export(FT_StrokeBorder border,FT_Outline * outline)632 ft_stroke_border_export( FT_StrokeBorder border, 633 FT_Outline* outline ) 634 { 635 /* copy point locations */ 636 FT_ARRAY_COPY( outline->points + outline->n_points, 637 border->points, 638 border->num_points ); 639 640 /* copy tags */ 641 { 642 FT_UInt count = border->num_points; 643 FT_Byte* read = border->tags; 644 FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points; 645 646 647 for ( ; count > 0; count--, read++, write++ ) 648 { 649 if ( *read & FT_STROKE_TAG_ON ) 650 *write = FT_CURVE_TAG_ON; 651 else if ( *read & FT_STROKE_TAG_CUBIC ) 652 *write = FT_CURVE_TAG_CUBIC; 653 else 654 *write = FT_CURVE_TAG_CONIC; 655 } 656 } 657 658 /* copy contours */ 659 { 660 FT_UInt count = border->num_points; 661 FT_Byte* tags = border->tags; 662 FT_Short* write = outline->contours + outline->n_contours; 663 FT_Short idx = (FT_Short)outline->n_points; 664 665 666 for ( ; count > 0; count--, tags++, idx++ ) 667 { 668 if ( *tags & FT_STROKE_TAG_END ) 669 { 670 *write++ = idx; 671 outline->n_contours++; 672 } 673 } 674 } 675 676 outline->n_points = (short)( outline->n_points + border->num_points ); 677 678 FT_ASSERT( FT_Outline_Check( outline ) == 0 ); 679 } 680 681 682 /***************************************************************************/ 683 /***************************************************************************/ 684 /***** *****/ 685 /***** STROKER *****/ 686 /***** *****/ 687 /***************************************************************************/ 688 /***************************************************************************/ 689 690 #define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI ) 691 692 typedef struct FT_StrokerRec_ 693 { 694 FT_Angle angle_in; 695 FT_Angle angle_out; 696 FT_Vector center; 697 FT_Bool first_point; 698 FT_Bool subpath_open; 699 FT_Angle subpath_angle; 700 FT_Vector subpath_start; 701 702 FT_Stroker_LineCap line_cap; 703 FT_Stroker_LineJoin line_join; 704 FT_Fixed miter_limit; 705 FT_Fixed radius; 706 707 FT_Bool valid; 708 FT_StrokeBorderRec borders[2]; 709 FT_Memory memory; 710 711 } FT_StrokerRec; 712 713 714 /* documentation is in ftstroke.h */ 715 716 FT_EXPORT_DEF( FT_Error ) FT_Stroker_New(FT_Library library,FT_Stroker * astroker)717 FT_Stroker_New( FT_Library library, 718 FT_Stroker *astroker ) 719 { 720 FT_Error error; 721 FT_Memory memory; 722 FT_Stroker stroker; 723 724 725 if ( !library ) 726 return FT_Err_Invalid_Argument; 727 728 memory = library->memory; 729 730 if ( !FT_NEW( stroker ) ) 731 { 732 stroker->memory = memory; 733 734 ft_stroke_border_init( &stroker->borders[0], memory ); 735 ft_stroke_border_init( &stroker->borders[1], memory ); 736 } 737 *astroker = stroker; 738 return error; 739 } 740 741 742 /* documentation is in ftstroke.h */ 743 744 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)745 FT_Stroker_Set( FT_Stroker stroker, 746 FT_Fixed radius, 747 FT_Stroker_LineCap line_cap, 748 FT_Stroker_LineJoin line_join, 749 FT_Fixed miter_limit ) 750 { 751 stroker->radius = radius; 752 stroker->line_cap = line_cap; 753 stroker->line_join = line_join; 754 stroker->miter_limit = miter_limit; 755 756 FT_Stroker_Rewind( stroker ); 757 } 758 759 760 /* documentation is in ftstroke.h */ 761 762 FT_EXPORT_DEF( void ) FT_Stroker_Rewind(FT_Stroker stroker)763 FT_Stroker_Rewind( FT_Stroker stroker ) 764 { 765 if ( stroker ) 766 { 767 ft_stroke_border_reset( &stroker->borders[0] ); 768 ft_stroke_border_reset( &stroker->borders[1] ); 769 } 770 } 771 772 773 /* documentation is in ftstroke.h */ 774 775 FT_EXPORT_DEF( void ) FT_Stroker_Done(FT_Stroker stroker)776 FT_Stroker_Done( FT_Stroker stroker ) 777 { 778 if ( stroker ) 779 { 780 FT_Memory memory = stroker->memory; 781 782 783 ft_stroke_border_done( &stroker->borders[0] ); 784 ft_stroke_border_done( &stroker->borders[1] ); 785 786 stroker->memory = NULL; 787 FT_FREE( stroker ); 788 } 789 } 790 791 792 /* creates a circular arc at a corner or cap */ 793 static FT_Error ft_stroker_arcto(FT_Stroker stroker,FT_Int side)794 ft_stroker_arcto( FT_Stroker stroker, 795 FT_Int side ) 796 { 797 FT_Angle total, rotate; 798 FT_Fixed radius = stroker->radius; 799 FT_Error error = FT_Err_Ok; 800 FT_StrokeBorder border = stroker->borders + side; 801 802 803 rotate = FT_SIDE_TO_ROTATE( side ); 804 805 total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 806 if ( total == FT_ANGLE_PI ) 807 total = -rotate * 2; 808 809 error = ft_stroke_border_arcto( border, 810 &stroker->center, 811 radius, 812 stroker->angle_in + rotate, 813 total ); 814 border->movable = FALSE; 815 return error; 816 } 817 818 819 /* adds a cap at the end of an opened path */ 820 static FT_Error ft_stroker_cap(FT_Stroker stroker,FT_Angle angle,FT_Int side)821 ft_stroker_cap( FT_Stroker stroker, 822 FT_Angle angle, 823 FT_Int side ) 824 { 825 FT_Error error = FT_Err_Ok; 826 827 828 if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND ) 829 { 830 /* add a round cap */ 831 stroker->angle_in = angle; 832 stroker->angle_out = angle + FT_ANGLE_PI; 833 error = ft_stroker_arcto( stroker, side ); 834 } 835 else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE ) 836 { 837 /* add a square cap */ 838 FT_Vector delta, delta2; 839 FT_Angle rotate = FT_SIDE_TO_ROTATE( side ); 840 FT_Fixed radius = stroker->radius; 841 FT_StrokeBorder border = stroker->borders + side; 842 843 844 FT_Vector_From_Polar( &delta2, radius, angle + rotate ); 845 FT_Vector_From_Polar( &delta, radius, angle ); 846 847 delta.x += stroker->center.x + delta2.x; 848 delta.y += stroker->center.y + delta2.y; 849 850 error = ft_stroke_border_lineto( border, &delta, FALSE ); 851 if ( error ) 852 goto Exit; 853 854 FT_Vector_From_Polar( &delta2, radius, angle - rotate ); 855 FT_Vector_From_Polar( &delta, radius, angle ); 856 857 delta.x += delta2.x + stroker->center.x; 858 delta.y += delta2.y + stroker->center.y; 859 860 error = ft_stroke_border_lineto( border, &delta, FALSE ); 861 } 862 863 Exit: 864 return error; 865 } 866 867 868 /* process an inside corner, i.e. compute intersection */ 869 static FT_Error ft_stroker_inside(FT_Stroker stroker,FT_Int side)870 ft_stroker_inside( FT_Stroker stroker, 871 FT_Int side) 872 { 873 FT_StrokeBorder border = stroker->borders + side; 874 FT_Angle phi, theta, rotate; 875 FT_Fixed length, thcos, sigma; 876 FT_Vector delta; 877 FT_Error error = FT_Err_Ok; 878 879 880 rotate = FT_SIDE_TO_ROTATE( side ); 881 882 /* compute median angle */ 883 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 884 if ( theta == FT_ANGLE_PI ) 885 theta = rotate; 886 else 887 theta = theta / 2; 888 889 phi = stroker->angle_in + theta; 890 891 thcos = FT_Cos( theta ); 892 sigma = FT_MulFix( stroker->miter_limit, thcos ); 893 894 /* TODO: find better criterion to switch off the optimization */ 895 if ( sigma < 0x10000L ) 896 { 897 FT_Vector_From_Polar( &delta, stroker->radius, 898 stroker->angle_out + rotate ); 899 delta.x += stroker->center.x; 900 delta.y += stroker->center.y; 901 border->movable = FALSE; 902 } 903 else 904 { 905 length = FT_DivFix( stroker->radius, thcos ); 906 907 FT_Vector_From_Polar( &delta, length, phi + rotate ); 908 delta.x += stroker->center.x; 909 delta.y += stroker->center.y; 910 } 911 912 error = ft_stroke_border_lineto( border, &delta, FALSE ); 913 914 return error; 915 } 916 917 918 /* process an outside corner, i.e. compute bevel/miter/round */ 919 static FT_Error ft_stroker_outside(FT_Stroker stroker,FT_Int side)920 ft_stroker_outside( FT_Stroker stroker, 921 FT_Int side ) 922 { 923 FT_StrokeBorder border = stroker->borders + side; 924 FT_Error error; 925 FT_Angle rotate; 926 927 928 if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND ) 929 error = ft_stroker_arcto( stroker, side ); 930 else 931 { 932 /* this is a mitered or beveled corner */ 933 FT_Fixed sigma, radius = stroker->radius; 934 FT_Angle theta, phi; 935 FT_Fixed thcos; 936 FT_Bool miter; 937 938 939 rotate = FT_SIDE_TO_ROTATE( side ); 940 miter = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER ); 941 942 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 943 if ( theta == FT_ANGLE_PI ) 944 { 945 theta = rotate; 946 phi = stroker->angle_in; 947 } 948 else 949 { 950 theta = theta / 2; 951 phi = stroker->angle_in + theta + rotate; 952 } 953 954 thcos = FT_Cos( theta ); 955 sigma = FT_MulFix( stroker->miter_limit, thcos ); 956 957 if ( sigma >= 0x10000L ) 958 miter = FALSE; 959 960 if ( miter ) /* this is a miter (broken angle) */ 961 { 962 FT_Vector middle, delta; 963 FT_Fixed length; 964 965 966 /* compute middle point */ 967 FT_Vector_From_Polar( &middle, 968 FT_MulFix( radius, stroker->miter_limit ), 969 phi ); 970 middle.x += stroker->center.x; 971 middle.y += stroker->center.y; 972 973 /* compute first angle point */ 974 length = FT_MulFix( radius, 975 FT_DivFix( 0x10000L - sigma, 976 ft_pos_abs( FT_Sin( theta ) ) ) ); 977 978 FT_Vector_From_Polar( &delta, length, phi + rotate ); 979 delta.x += middle.x; 980 delta.y += middle.y; 981 982 error = ft_stroke_border_lineto( border, &delta, FALSE ); 983 if ( error ) 984 goto Exit; 985 986 /* compute second angle point */ 987 FT_Vector_From_Polar( &delta, length, phi - rotate ); 988 delta.x += middle.x; 989 delta.y += middle.y; 990 991 error = ft_stroke_border_lineto( border, &delta, FALSE ); 992 if ( error ) 993 goto Exit; 994 995 /* finally, add a movable end point */ 996 FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate ); 997 delta.x += stroker->center.x; 998 delta.y += stroker->center.y; 999 1000 error = ft_stroke_border_lineto( border, &delta, TRUE ); 1001 } 1002 1003 else /* this is a bevel (intersection) */ 1004 { 1005 FT_Fixed length; 1006 FT_Vector delta; 1007 1008 1009 length = FT_DivFix( stroker->radius, thcos ); 1010 1011 FT_Vector_From_Polar( &delta, length, phi ); 1012 delta.x += stroker->center.x; 1013 delta.y += stroker->center.y; 1014 1015 error = ft_stroke_border_lineto( border, &delta, FALSE ); 1016 if ( error ) 1017 goto Exit; 1018 1019 /* now add end point */ 1020 FT_Vector_From_Polar( &delta, stroker->radius, 1021 stroker->angle_out + rotate ); 1022 delta.x += stroker->center.x; 1023 delta.y += stroker->center.y; 1024 1025 error = ft_stroke_border_lineto( border, &delta, TRUE ); 1026 } 1027 } 1028 1029 Exit: 1030 return error; 1031 } 1032 1033 1034 static FT_Error ft_stroker_process_corner(FT_Stroker stroker)1035 ft_stroker_process_corner( FT_Stroker stroker ) 1036 { 1037 FT_Error error = FT_Err_Ok; 1038 FT_Angle turn; 1039 FT_Int inside_side; 1040 1041 1042 turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out ); 1043 1044 /* no specific corner processing is required if the turn is 0 */ 1045 if ( turn == 0 ) 1046 goto Exit; 1047 1048 /* when we turn to the right, the inside side is 0 */ 1049 inside_side = 0; 1050 1051 /* otherwise, the inside side is 1 */ 1052 if ( turn < 0 ) 1053 inside_side = 1; 1054 1055 /* process the inside side */ 1056 error = ft_stroker_inside( stroker, inside_side ); 1057 if ( error ) 1058 goto Exit; 1059 1060 /* process the outside side */ 1061 error = ft_stroker_outside( stroker, 1 - inside_side ); 1062 1063 Exit: 1064 return error; 1065 } 1066 1067 1068 /* add two points to the left and right borders corresponding to the */ 1069 /* start of the subpath */ 1070 static FT_Error ft_stroker_subpath_start(FT_Stroker stroker,FT_Angle start_angle)1071 ft_stroker_subpath_start( FT_Stroker stroker, 1072 FT_Angle start_angle ) 1073 { 1074 FT_Vector delta; 1075 FT_Vector point; 1076 FT_Error error; 1077 FT_StrokeBorder border; 1078 1079 1080 FT_Vector_From_Polar( &delta, stroker->radius, 1081 start_angle + FT_ANGLE_PI2 ); 1082 1083 point.x = stroker->center.x + delta.x; 1084 point.y = stroker->center.y + delta.y; 1085 1086 border = stroker->borders; 1087 error = ft_stroke_border_moveto( border, &point ); 1088 if ( error ) 1089 goto Exit; 1090 1091 point.x = stroker->center.x - delta.x; 1092 point.y = stroker->center.y - delta.y; 1093 1094 border++; 1095 error = ft_stroke_border_moveto( border, &point ); 1096 1097 /* save angle for last cap */ 1098 stroker->subpath_angle = start_angle; 1099 stroker->first_point = FALSE; 1100 1101 Exit: 1102 return error; 1103 } 1104 1105 1106 /* documentation is in ftstroke.h */ 1107 1108 FT_EXPORT_DEF( FT_Error ) FT_Stroker_LineTo(FT_Stroker stroker,FT_Vector * to)1109 FT_Stroker_LineTo( FT_Stroker stroker, 1110 FT_Vector* to ) 1111 { 1112 FT_Error error = FT_Err_Ok; 1113 FT_StrokeBorder border; 1114 FT_Vector delta; 1115 FT_Angle angle; 1116 FT_Int side; 1117 1118 delta.x = to->x - stroker->center.x; 1119 delta.y = to->y - stroker->center.y; 1120 1121 angle = FT_Atan2( delta.x, delta.y ); 1122 FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 ); 1123 1124 /* process corner if necessary */ 1125 if ( stroker->first_point ) 1126 { 1127 /* This is the first segment of a subpath. We need to */ 1128 /* add a point to each border at their respective starting */ 1129 /* point locations. */ 1130 error = ft_stroker_subpath_start( stroker, angle ); 1131 if ( error ) 1132 goto Exit; 1133 } 1134 else 1135 { 1136 /* process the current corner */ 1137 stroker->angle_out = angle; 1138 error = ft_stroker_process_corner( stroker ); 1139 if ( error ) 1140 goto Exit; 1141 } 1142 1143 /* now add a line segment to both the `inside' and `outside' paths */ 1144 1145 for ( border = stroker->borders, side = 1; side >= 0; side--, border++ ) 1146 { 1147 FT_Vector point; 1148 1149 1150 point.x = to->x + delta.x; 1151 point.y = to->y + delta.y; 1152 1153 error = ft_stroke_border_lineto( border, &point, TRUE ); 1154 if ( error ) 1155 goto Exit; 1156 1157 delta.x = -delta.x; 1158 delta.y = -delta.y; 1159 } 1160 1161 stroker->angle_in = angle; 1162 stroker->center = *to; 1163 1164 Exit: 1165 return error; 1166 } 1167 1168 1169 /* documentation is in ftstroke.h */ 1170 1171 FT_EXPORT_DEF( FT_Error ) FT_Stroker_ConicTo(FT_Stroker stroker,FT_Vector * control,FT_Vector * to)1172 FT_Stroker_ConicTo( FT_Stroker stroker, 1173 FT_Vector* control, 1174 FT_Vector* to ) 1175 { 1176 FT_Error error = FT_Err_Ok; 1177 FT_Vector bez_stack[34]; 1178 FT_Vector* arc; 1179 FT_Vector* limit = bez_stack + 30; 1180 FT_Angle start_angle; 1181 FT_Bool first_arc = TRUE; 1182 1183 1184 arc = bez_stack; 1185 arc[0] = *to; 1186 arc[1] = *control; 1187 arc[2] = stroker->center; 1188 1189 while ( arc >= bez_stack ) 1190 { 1191 FT_Angle angle_in, angle_out; 1192 1193 1194 angle_in = angle_out = 0; /* remove compiler warnings */ 1195 1196 if ( arc < limit && 1197 !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) ) 1198 { 1199 ft_conic_split( arc ); 1200 arc += 2; 1201 continue; 1202 } 1203 1204 if ( first_arc ) 1205 { 1206 first_arc = FALSE; 1207 1208 start_angle = angle_in; 1209 1210 /* process corner if necessary */ 1211 if ( stroker->first_point ) 1212 error = ft_stroker_subpath_start( stroker, start_angle ); 1213 else 1214 { 1215 stroker->angle_out = start_angle; 1216 error = ft_stroker_process_corner( stroker ); 1217 } 1218 } 1219 1220 /* the arc's angle is small enough; we can add it directly to each */ 1221 /* border */ 1222 { 1223 FT_Vector ctrl, end; 1224 FT_Angle theta, phi, rotate; 1225 FT_Fixed length; 1226 FT_Int side; 1227 1228 1229 theta = FT_Angle_Diff( angle_in, angle_out ) / 2; 1230 phi = angle_in + theta; 1231 length = FT_DivFix( stroker->radius, FT_Cos( theta ) ); 1232 1233 for ( side = 0; side <= 1; side++ ) 1234 { 1235 rotate = FT_SIDE_TO_ROTATE( side ); 1236 1237 /* compute control point */ 1238 FT_Vector_From_Polar( &ctrl, length, phi + rotate ); 1239 ctrl.x += arc[1].x; 1240 ctrl.y += arc[1].y; 1241 1242 /* compute end point */ 1243 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1244 end.x += arc[0].x; 1245 end.y += arc[0].y; 1246 1247 error = ft_stroke_border_conicto( stroker->borders + side, 1248 &ctrl, &end ); 1249 if ( error ) 1250 goto Exit; 1251 } 1252 } 1253 1254 arc -= 2; 1255 1256 if ( arc < bez_stack ) 1257 stroker->angle_in = angle_out; 1258 } 1259 1260 stroker->center = *to; 1261 1262 Exit: 1263 return error; 1264 } 1265 1266 1267 /* documentation is in ftstroke.h */ 1268 1269 FT_EXPORT_DEF( FT_Error ) FT_Stroker_CubicTo(FT_Stroker stroker,FT_Vector * control1,FT_Vector * control2,FT_Vector * to)1270 FT_Stroker_CubicTo( FT_Stroker stroker, 1271 FT_Vector* control1, 1272 FT_Vector* control2, 1273 FT_Vector* to ) 1274 { 1275 FT_Error error = FT_Err_Ok; 1276 FT_Vector bez_stack[37]; 1277 FT_Vector* arc; 1278 FT_Vector* limit = bez_stack + 32; 1279 FT_Angle start_angle; 1280 FT_Bool first_arc = TRUE; 1281 1282 1283 arc = bez_stack; 1284 arc[0] = *to; 1285 arc[1] = *control2; 1286 arc[2] = *control1; 1287 arc[3] = stroker->center; 1288 1289 while ( arc >= bez_stack ) 1290 { 1291 FT_Angle angle_in, angle_mid, angle_out; 1292 1293 1294 /* remove compiler warnings */ 1295 angle_in = angle_out = angle_mid = 0; 1296 1297 if ( arc < limit && 1298 !ft_cubic_is_small_enough( arc, &angle_in, 1299 &angle_mid, &angle_out ) ) 1300 { 1301 ft_cubic_split( arc ); 1302 arc += 3; 1303 continue; 1304 } 1305 1306 if ( first_arc ) 1307 { 1308 first_arc = FALSE; 1309 1310 /* process corner if necessary */ 1311 start_angle = angle_in; 1312 1313 if ( stroker->first_point ) 1314 error = ft_stroker_subpath_start( stroker, start_angle ); 1315 else 1316 { 1317 stroker->angle_out = start_angle; 1318 error = ft_stroker_process_corner( stroker ); 1319 } 1320 if ( error ) 1321 goto Exit; 1322 } 1323 1324 /* the arc's angle is small enough; we can add it directly to each */ 1325 /* border */ 1326 { 1327 FT_Vector ctrl1, ctrl2, end; 1328 FT_Angle theta1, phi1, theta2, phi2, rotate; 1329 FT_Fixed length1, length2; 1330 FT_Int side; 1331 1332 1333 theta1 = ft_pos_abs( angle_mid - angle_in ) / 2; 1334 theta2 = ft_pos_abs( angle_out - angle_mid ) / 2; 1335 phi1 = (angle_mid + angle_in ) / 2; 1336 phi2 = (angle_mid + angle_out ) / 2; 1337 length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) ); 1338 length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) ); 1339 1340 for ( side = 0; side <= 1; side++ ) 1341 { 1342 rotate = FT_SIDE_TO_ROTATE( side ); 1343 1344 /* compute control points */ 1345 FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate ); 1346 ctrl1.x += arc[2].x; 1347 ctrl1.y += arc[2].y; 1348 1349 FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate ); 1350 ctrl2.x += arc[1].x; 1351 ctrl2.y += arc[1].y; 1352 1353 /* compute end point */ 1354 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate ); 1355 end.x += arc[0].x; 1356 end.y += arc[0].y; 1357 1358 error = ft_stroke_border_cubicto( stroker->borders + side, 1359 &ctrl1, &ctrl2, &end ); 1360 if ( error ) 1361 goto Exit; 1362 } 1363 } 1364 1365 arc -= 3; 1366 if ( arc < bez_stack ) 1367 stroker->angle_in = angle_out; 1368 } 1369 1370 stroker->center = *to; 1371 1372 Exit: 1373 return error; 1374 } 1375 1376 1377 /* documentation is in ftstroke.h */ 1378 1379 FT_EXPORT_DEF( FT_Error ) FT_Stroker_BeginSubPath(FT_Stroker stroker,FT_Vector * to,FT_Bool open)1380 FT_Stroker_BeginSubPath( FT_Stroker stroker, 1381 FT_Vector* to, 1382 FT_Bool open ) 1383 { 1384 /* We cannot process the first point, because there is not enough */ 1385 /* information regarding its corner/cap. The latter will be processed */ 1386 /* in the `FT_Stroker_EndSubPath' routine. */ 1387 /* */ 1388 stroker->first_point = TRUE; 1389 stroker->center = *to; 1390 stroker->subpath_open = open; 1391 1392 /* record the subpath start point for each border */ 1393 stroker->subpath_start = *to; 1394 1395 return FT_Err_Ok; 1396 } 1397 1398 1399 static FT_Error ft_stroker_add_reverse_left(FT_Stroker stroker,FT_Bool open)1400 ft_stroker_add_reverse_left( FT_Stroker stroker, 1401 FT_Bool open ) 1402 { 1403 FT_StrokeBorder right = stroker->borders + 0; 1404 FT_StrokeBorder left = stroker->borders + 1; 1405 FT_Int new_points; 1406 FT_Error error = FT_Err_Ok; 1407 1408 1409 FT_ASSERT( left->start >= 0 ); 1410 1411 new_points = left->num_points - left->start; 1412 if ( new_points > 0 ) 1413 { 1414 error = ft_stroke_border_grow( right, (FT_UInt)new_points ); 1415 if ( error ) 1416 goto Exit; 1417 1418 { 1419 FT_Vector* dst_point = right->points + right->num_points; 1420 FT_Byte* dst_tag = right->tags + right->num_points; 1421 FT_Vector* src_point = left->points + left->num_points - 1; 1422 FT_Byte* src_tag = left->tags + left->num_points - 1; 1423 1424 while ( src_point >= left->points + left->start ) 1425 { 1426 *dst_point = *src_point; 1427 *dst_tag = *src_tag; 1428 1429 if ( open ) 1430 dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END; 1431 else 1432 { 1433 FT_Byte ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END ); 1434 1435 1436 /* switch begin/end tags if necessary */ 1437 if ( ttag == FT_STROKE_TAG_BEGIN || 1438 ttag == FT_STROKE_TAG_END ) 1439 dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END; 1440 1441 } 1442 1443 src_point--; 1444 src_tag--; 1445 dst_point++; 1446 dst_tag++; 1447 } 1448 } 1449 1450 left->num_points = left->start; 1451 right->num_points += new_points; 1452 1453 right->movable = FALSE; 1454 left->movable = FALSE; 1455 } 1456 1457 Exit: 1458 return error; 1459 } 1460 1461 1462 /* documentation is in ftstroke.h */ 1463 1464 /* there's a lot of magic in this function! */ 1465 FT_EXPORT_DEF( FT_Error ) FT_Stroker_EndSubPath(FT_Stroker stroker)1466 FT_Stroker_EndSubPath( FT_Stroker stroker ) 1467 { 1468 FT_Error error = FT_Err_Ok; 1469 1470 1471 if ( stroker->subpath_open ) 1472 { 1473 FT_StrokeBorder right = stroker->borders; 1474 1475 /* All right, this is an opened path, we need to add a cap between */ 1476 /* right & left, add the reverse of left, then add a final cap */ 1477 /* between left & right. */ 1478 error = ft_stroker_cap( stroker, stroker->angle_in, 0 ); 1479 if ( error ) 1480 goto Exit; 1481 1482 /* add reversed points from `left' to `right' */ 1483 error = ft_stroker_add_reverse_left( stroker, TRUE ); 1484 if ( error ) 1485 goto Exit; 1486 1487 /* now add the final cap */ 1488 stroker->center = stroker->subpath_start; 1489 error = ft_stroker_cap( stroker, 1490 stroker->subpath_angle + FT_ANGLE_PI, 0 ); 1491 if ( error ) 1492 goto Exit; 1493 1494 /* Now end the right subpath accordingly. The left one is */ 1495 /* rewind and doesn't need further processing. */ 1496 ft_stroke_border_close( right, FALSE ); 1497 } 1498 else 1499 { 1500 FT_Angle turn; 1501 FT_Int inside_side; 1502 1503 /* close the path if needed */ 1504 if ( stroker->center.x != stroker->subpath_start.x || 1505 stroker->center.y != stroker->subpath_start.y ) 1506 { 1507 error = FT_Stroker_LineTo( stroker, &stroker->subpath_start ); 1508 if ( error ) 1509 goto Exit; 1510 } 1511 1512 /* process the corner */ 1513 stroker->angle_out = stroker->subpath_angle; 1514 turn = FT_Angle_Diff( stroker->angle_in, 1515 stroker->angle_out ); 1516 1517 /* no specific corner processing is required if the turn is 0 */ 1518 if ( turn != 0 ) 1519 { 1520 /* when we turn to the right, the inside side is 0 */ 1521 inside_side = 0; 1522 1523 /* otherwise, the inside side is 1 */ 1524 if ( turn < 0 ) 1525 inside_side = 1; 1526 1527 error = ft_stroker_inside( stroker, inside_side ); 1528 if ( error ) 1529 goto Exit; 1530 1531 /* process the outside side */ 1532 error = ft_stroker_outside( stroker, 1 - inside_side ); 1533 if ( error ) 1534 goto Exit; 1535 } 1536 1537 /* then end our two subpaths */ 1538 ft_stroke_border_close( stroker->borders + 0, TRUE ); 1539 ft_stroke_border_close( stroker->borders + 1, FALSE ); 1540 } 1541 1542 Exit: 1543 return error; 1544 } 1545 1546 1547 /* documentation is in ftstroke.h */ 1548 1549 FT_EXPORT_DEF( FT_Error ) FT_Stroker_GetBorderCounts(FT_Stroker stroker,FT_StrokerBorder border,FT_UInt * anum_points,FT_UInt * anum_contours)1550 FT_Stroker_GetBorderCounts( FT_Stroker stroker, 1551 FT_StrokerBorder border, 1552 FT_UInt *anum_points, 1553 FT_UInt *anum_contours ) 1554 { 1555 FT_UInt num_points = 0, num_contours = 0; 1556 FT_Error error; 1557 1558 1559 if ( !stroker || border > 1 ) 1560 { 1561 error = FT_Err_Invalid_Argument; 1562 goto Exit; 1563 } 1564 1565 error = ft_stroke_border_get_counts( stroker->borders + border, 1566 &num_points, &num_contours ); 1567 Exit: 1568 if ( anum_points ) 1569 *anum_points = num_points; 1570 1571 if ( anum_contours ) 1572 *anum_contours = num_contours; 1573 1574 return error; 1575 } 1576 1577 1578 /* documentation is in ftstroke.h */ 1579 1580 FT_EXPORT_DEF( FT_Error ) FT_Stroker_GetCounts(FT_Stroker stroker,FT_UInt * anum_points,FT_UInt * anum_contours)1581 FT_Stroker_GetCounts( FT_Stroker stroker, 1582 FT_UInt *anum_points, 1583 FT_UInt *anum_contours ) 1584 { 1585 FT_UInt count1, count2, num_points = 0; 1586 FT_UInt count3, count4, num_contours = 0; 1587 FT_Error error; 1588 1589 1590 error = ft_stroke_border_get_counts( stroker->borders + 0, 1591 &count1, &count2 ); 1592 if ( error ) 1593 goto Exit; 1594 1595 error = ft_stroke_border_get_counts( stroker->borders + 1, 1596 &count3, &count4 ); 1597 if ( error ) 1598 goto Exit; 1599 1600 num_points = count1 + count3; 1601 num_contours = count2 + count4; 1602 1603 Exit: 1604 *anum_points = num_points; 1605 *anum_contours = num_contours; 1606 return error; 1607 } 1608 1609 1610 /* documentation is in ftstroke.h */ 1611 1612 FT_EXPORT_DEF( void ) FT_Stroker_ExportBorder(FT_Stroker stroker,FT_StrokerBorder border,FT_Outline * outline)1613 FT_Stroker_ExportBorder( FT_Stroker stroker, 1614 FT_StrokerBorder border, 1615 FT_Outline* outline ) 1616 { 1617 if ( border == FT_STROKER_BORDER_LEFT || 1618 border == FT_STROKER_BORDER_RIGHT ) 1619 { 1620 FT_StrokeBorder sborder = & stroker->borders[border]; 1621 1622 1623 if ( sborder->valid ) 1624 ft_stroke_border_export( sborder, outline ); 1625 } 1626 } 1627 1628 1629 /* documentation is in ftstroke.h */ 1630 1631 FT_EXPORT_DEF( void ) FT_Stroker_Export(FT_Stroker stroker,FT_Outline * outline)1632 FT_Stroker_Export( FT_Stroker stroker, 1633 FT_Outline* outline ) 1634 { 1635 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline ); 1636 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline ); 1637 } 1638 1639 1640 /* documentation is in ftstroke.h */ 1641 1642 /* 1643 * The following is very similar to FT_Outline_Decompose, except 1644 * that we do support opened paths, and do not scale the outline. 1645 */ 1646 FT_EXPORT_DEF( FT_Error ) FT_Stroker_ParseOutline(FT_Stroker stroker,FT_Outline * outline,FT_Bool opened)1647 FT_Stroker_ParseOutline( FT_Stroker stroker, 1648 FT_Outline* outline, 1649 FT_Bool opened ) 1650 { 1651 FT_Vector v_last; 1652 FT_Vector v_control; 1653 FT_Vector v_start; 1654 1655 FT_Vector* point; 1656 FT_Vector* limit; 1657 char* tags; 1658 1659 FT_Error error; 1660 1661 FT_Int n; /* index of contour in outline */ 1662 FT_UInt first; /* index of first point in contour */ 1663 FT_Int tag; /* current point's state */ 1664 1665 1666 if ( !outline || !stroker ) 1667 return FT_Err_Invalid_Argument; 1668 1669 FT_Stroker_Rewind( stroker ); 1670 1671 first = 0; 1672 1673 for ( n = 0; n < outline->n_contours; n++ ) 1674 { 1675 FT_UInt last; /* index of last point in contour */ 1676 1677 1678 last = outline->contours[n]; 1679 limit = outline->points + last; 1680 1681 /* skip empty points; we don't stroke these */ 1682 if ( last <= first ) 1683 { 1684 first = last + 1; 1685 continue; 1686 } 1687 1688 v_start = outline->points[first]; 1689 v_last = outline->points[last]; 1690 1691 v_control = v_start; 1692 1693 point = outline->points + first; 1694 tags = outline->tags + first; 1695 tag = FT_CURVE_TAG( tags[0] ); 1696 1697 /* A contour cannot start with a cubic control point! */ 1698 if ( tag == FT_CURVE_TAG_CUBIC ) 1699 goto Invalid_Outline; 1700 1701 /* check first point to determine origin */ 1702 if ( tag == FT_CURVE_TAG_CONIC ) 1703 { 1704 /* First point is conic control. Yes, this happens. */ 1705 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) 1706 { 1707 /* start at last point if it is on the curve */ 1708 v_start = v_last; 1709 limit--; 1710 } 1711 else 1712 { 1713 /* if both first and last points are conic, */ 1714 /* start at their middle and record its position */ 1715 /* for closure */ 1716 v_start.x = ( v_start.x + v_last.x ) / 2; 1717 v_start.y = ( v_start.y + v_last.y ) / 2; 1718 1719 v_last = v_start; 1720 } 1721 point--; 1722 tags--; 1723 } 1724 1725 error = FT_Stroker_BeginSubPath( stroker, &v_start, opened ); 1726 if ( error ) 1727 goto Exit; 1728 1729 while ( point < limit ) 1730 { 1731 point++; 1732 tags++; 1733 1734 tag = FT_CURVE_TAG( tags[0] ); 1735 switch ( tag ) 1736 { 1737 case FT_CURVE_TAG_ON: /* emit a single line_to */ 1738 { 1739 FT_Vector vec; 1740 1741 1742 vec.x = point->x; 1743 vec.y = point->y; 1744 1745 error = FT_Stroker_LineTo( stroker, &vec ); 1746 if ( error ) 1747 goto Exit; 1748 continue; 1749 } 1750 1751 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ 1752 v_control.x = point->x; 1753 v_control.y = point->y; 1754 1755 Do_Conic: 1756 if ( point < limit ) 1757 { 1758 FT_Vector vec; 1759 FT_Vector v_middle; 1760 1761 1762 point++; 1763 tags++; 1764 tag = FT_CURVE_TAG( tags[0] ); 1765 1766 vec = point[0]; 1767 1768 if ( tag == FT_CURVE_TAG_ON ) 1769 { 1770 error = FT_Stroker_ConicTo( stroker, &v_control, &vec ); 1771 if ( error ) 1772 goto Exit; 1773 continue; 1774 } 1775 1776 if ( tag != FT_CURVE_TAG_CONIC ) 1777 goto Invalid_Outline; 1778 1779 v_middle.x = ( v_control.x + vec.x ) / 2; 1780 v_middle.y = ( v_control.y + vec.y ) / 2; 1781 1782 error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle ); 1783 if ( error ) 1784 goto Exit; 1785 1786 v_control = vec; 1787 goto Do_Conic; 1788 } 1789 1790 error = FT_Stroker_ConicTo( stroker, &v_control, &v_start ); 1791 goto Close; 1792 1793 default: /* FT_CURVE_TAG_CUBIC */ 1794 { 1795 FT_Vector vec1, vec2; 1796 1797 1798 if ( point + 1 > limit || 1799 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) 1800 goto Invalid_Outline; 1801 1802 point += 2; 1803 tags += 2; 1804 1805 vec1 = point[-2]; 1806 vec2 = point[-1]; 1807 1808 if ( point <= limit ) 1809 { 1810 FT_Vector vec; 1811 1812 1813 vec = point[0]; 1814 1815 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec ); 1816 if ( error ) 1817 goto Exit; 1818 continue; 1819 } 1820 1821 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start ); 1822 goto Close; 1823 } 1824 } 1825 } 1826 1827 Close: 1828 if ( error ) 1829 goto Exit; 1830 1831 error = FT_Stroker_EndSubPath( stroker ); 1832 if ( error ) 1833 goto Exit; 1834 1835 first = last + 1; 1836 } 1837 1838 return FT_Err_Ok; 1839 1840 Exit: 1841 return error; 1842 1843 Invalid_Outline: 1844 return FT_Err_Invalid_Outline; 1845 } 1846 1847 1848 extern const FT_Glyph_Class ft_outline_glyph_class; 1849 1850 1851 /* documentation is in ftstroke.h */ 1852 1853 FT_EXPORT_DEF( FT_Error ) FT_Glyph_Stroke(FT_Glyph * pglyph,FT_Stroker stroker,FT_Bool destroy)1854 FT_Glyph_Stroke( FT_Glyph *pglyph, 1855 FT_Stroker stroker, 1856 FT_Bool destroy ) 1857 { 1858 FT_Error error = FT_Err_Invalid_Argument; 1859 FT_Glyph glyph = NULL; 1860 1861 1862 if ( pglyph == NULL ) 1863 goto Exit; 1864 1865 glyph = *pglyph; 1866 if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class ) 1867 goto Exit; 1868 1869 { 1870 FT_Glyph copy; 1871 1872 1873 error = FT_Glyph_Copy( glyph, © ); 1874 if ( error ) 1875 goto Exit; 1876 1877 glyph = copy; 1878 } 1879 1880 { 1881 FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph; 1882 FT_Outline* outline = &oglyph->outline; 1883 FT_UInt num_points, num_contours; 1884 1885 1886 error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); 1887 if ( error ) 1888 goto Fail; 1889 1890 (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours ); 1891 1892 FT_Outline_Done( glyph->library, outline ); 1893 1894 error = FT_Outline_New( glyph->library, 1895 num_points, num_contours, outline ); 1896 if ( error ) 1897 goto Fail; 1898 1899 outline->n_points = 0; 1900 outline->n_contours = 0; 1901 1902 FT_Stroker_Export( stroker, outline ); 1903 } 1904 1905 if ( destroy ) 1906 FT_Done_Glyph( *pglyph ); 1907 1908 *pglyph = glyph; 1909 goto Exit; 1910 1911 Fail: 1912 FT_Done_Glyph( glyph ); 1913 glyph = NULL; 1914 1915 if ( !destroy ) 1916 *pglyph = NULL; 1917 1918 Exit: 1919 return error; 1920 } 1921 1922 1923 /* documentation is in ftstroke.h */ 1924 1925 FT_EXPORT_DEF( FT_Error ) FT_Glyph_StrokeBorder(FT_Glyph * pglyph,FT_Stroker stroker,FT_Bool inside,FT_Bool destroy)1926 FT_Glyph_StrokeBorder( FT_Glyph *pglyph, 1927 FT_Stroker stroker, 1928 FT_Bool inside, 1929 FT_Bool destroy ) 1930 { 1931 FT_Error error = FT_Err_Invalid_Argument; 1932 FT_Glyph glyph = NULL; 1933 1934 1935 if ( pglyph == NULL ) 1936 goto Exit; 1937 1938 glyph = *pglyph; 1939 if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class ) 1940 goto Exit; 1941 1942 { 1943 FT_Glyph copy; 1944 1945 1946 error = FT_Glyph_Copy( glyph, © ); 1947 if ( error ) 1948 goto Exit; 1949 1950 glyph = copy; 1951 } 1952 1953 { 1954 FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph; 1955 FT_StrokerBorder border; 1956 FT_Outline* outline = &oglyph->outline; 1957 FT_UInt num_points, num_contours; 1958 1959 1960 border = FT_Outline_GetOutsideBorder( outline ); 1961 if ( inside ) 1962 { 1963 if ( border == FT_STROKER_BORDER_LEFT ) 1964 border = FT_STROKER_BORDER_RIGHT; 1965 else 1966 border = FT_STROKER_BORDER_LEFT; 1967 } 1968 1969 error = FT_Stroker_ParseOutline( stroker, outline, FALSE ); 1970 if ( error ) 1971 goto Fail; 1972 1973 (void)FT_Stroker_GetBorderCounts( stroker, border, 1974 &num_points, &num_contours ); 1975 1976 FT_Outline_Done( glyph->library, outline ); 1977 1978 error = FT_Outline_New( glyph->library, 1979 num_points, 1980 num_contours, 1981 outline ); 1982 if ( error ) 1983 goto Fail; 1984 1985 outline->n_points = 0; 1986 outline->n_contours = 0; 1987 1988 FT_Stroker_ExportBorder( stroker, border, outline ); 1989 } 1990 1991 if ( destroy ) 1992 FT_Done_Glyph( *pglyph ); 1993 1994 *pglyph = glyph; 1995 goto Exit; 1996 1997 Fail: 1998 FT_Done_Glyph( glyph ); 1999 glyph = NULL; 2000 2001 if ( !destroy ) 2002 *pglyph = NULL; 2003 2004 Exit: 2005 return error; 2006 } 2007 2008 2009 /* END */ 2010