00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <ft2build.h>
00020 #include FT_STROKER_H
00021 #include FT_TRIGONOMETRY_H
00022 #include FT_OUTLINE_H
00023 #include FT_INTERNAL_MEMORY_H
00024 #include FT_INTERNAL_DEBUG_H
00025 #include FT_INTERNAL_OBJECTS_H
00026
00027
00028
00029
00030 FT_EXPORT_DEF( FT_StrokerBorder )
00031 FT_Outline_GetInsideBorder( FT_Outline* outline )
00032 {
00033 FT_Orientation o = FT_Outline_Get_Orientation( outline );
00034
00035
00036 return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
00037 : FT_STROKER_BORDER_LEFT ;
00038 }
00039
00040
00041
00042
00043 FT_EXPORT_DEF( FT_StrokerBorder )
00044 FT_Outline_GetOutsideBorder( FT_Outline* outline )
00045 {
00046 FT_Orientation o = FT_Outline_Get_Orientation( outline );
00047
00048
00049 return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
00050 : FT_STROKER_BORDER_RIGHT ;
00051 }
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062 #define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 )
00063 #define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 6 )
00064 #define FT_EPSILON 2
00065
00066 #define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
00067
00068
00069 static FT_Pos
00070 ft_pos_abs( FT_Pos x )
00071 {
00072 return x >= 0 ? x : -x ;
00073 }
00074
00075
00076 static void
00077 ft_conic_split( FT_Vector* base )
00078 {
00079 FT_Pos a, b;
00080
00081
00082 base[4].x = base[2].x;
00083 b = base[1].x;
00084 a = base[3].x = ( base[2].x + b ) / 2;
00085 b = base[1].x = ( base[0].x + b ) / 2;
00086 base[2].x = ( a + b ) / 2;
00087
00088 base[4].y = base[2].y;
00089 b = base[1].y;
00090 a = base[3].y = ( base[2].y + b ) / 2;
00091 b = base[1].y = ( base[0].y + b ) / 2;
00092 base[2].y = ( a + b ) / 2;
00093 }
00094
00095
00096 static FT_Bool
00097 ft_conic_is_small_enough( FT_Vector* base,
00098 FT_Angle *angle_in,
00099 FT_Angle *angle_out )
00100 {
00101 FT_Vector d1, d2;
00102 FT_Angle theta;
00103 FT_Int close1, close2;
00104
00105
00106 d1.x = base[1].x - base[2].x;
00107 d1.y = base[1].y - base[2].y;
00108 d2.x = base[0].x - base[1].x;
00109 d2.y = base[0].y - base[1].y;
00110
00111 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
00112 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
00113
00114 if ( close1 )
00115 {
00116 if ( close2 )
00117 *angle_in = *angle_out = 0;
00118 else
00119 *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
00120 }
00121 else if ( close2 )
00122 {
00123 *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
00124 }
00125 else
00126 {
00127 *angle_in = FT_Atan2( d1.x, d1.y );
00128 *angle_out = FT_Atan2( d2.x, d2.y );
00129 }
00130
00131 theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
00132
00133 return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
00134 }
00135
00136
00137 static void
00138 ft_cubic_split( FT_Vector* base )
00139 {
00140 FT_Pos a, b, c, d;
00141
00142
00143 base[6].x = base[3].x;
00144 c = base[1].x;
00145 d = base[2].x;
00146 base[1].x = a = ( base[0].x + c ) / 2;
00147 base[5].x = b = ( base[3].x + d ) / 2;
00148 c = ( c + d ) / 2;
00149 base[2].x = a = ( a + c ) / 2;
00150 base[4].x = b = ( b + c ) / 2;
00151 base[3].x = ( a + b ) / 2;
00152
00153 base[6].y = base[3].y;
00154 c = base[1].y;
00155 d = base[2].y;
00156 base[1].y = a = ( base[0].y + c ) / 2;
00157 base[5].y = b = ( base[3].y + d ) / 2;
00158 c = ( c + d ) / 2;
00159 base[2].y = a = ( a + c ) / 2;
00160 base[4].y = b = ( b + c ) / 2;
00161 base[3].y = ( a + b ) / 2;
00162 }
00163
00164
00165 static FT_Bool
00166 ft_cubic_is_small_enough( FT_Vector* base,
00167 FT_Angle *angle_in,
00168 FT_Angle *angle_mid,
00169 FT_Angle *angle_out )
00170 {
00171 FT_Vector d1, d2, d3;
00172 FT_Angle theta1, theta2;
00173 FT_Int close1, close2, close3;
00174
00175
00176 d1.x = base[2].x - base[3].x;
00177 d1.y = base[2].y - base[3].y;
00178 d2.x = base[1].x - base[2].x;
00179 d2.y = base[1].y - base[2].y;
00180 d3.x = base[0].x - base[1].x;
00181 d3.y = base[0].y - base[1].y;
00182
00183 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
00184 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
00185 close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
00186
00187 if ( close1 || close3 )
00188 {
00189 if ( close2 )
00190 {
00191
00192 *angle_in = *angle_out = *angle_mid = 0;
00193 }
00194 else if ( close1 )
00195 {
00196 *angle_in = *angle_mid = FT_Atan2( d2.x, d2.y );
00197 *angle_out = FT_Atan2( d3.x, d3.y );
00198 }
00199 else
00200 {
00201 *angle_in = FT_Atan2( d1.x, d1.y );
00202 *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
00203 }
00204 }
00205 else if ( close2 )
00206 {
00207 *angle_in = *angle_mid = FT_Atan2( d1.x, d1.y );
00208 *angle_out = FT_Atan2( d3.x, d3.y );
00209 }
00210 else
00211 {
00212 *angle_in = FT_Atan2( d1.x, d1.y );
00213 *angle_mid = FT_Atan2( d2.x, d2.y );
00214 *angle_out = FT_Atan2( d3.x, d3.y );
00215 }
00216
00217 theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) );
00218 theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
00219
00220 return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
00221 theta2 < FT_SMALL_CUBIC_THRESHOLD );
00222 }
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233 typedef enum FT_StrokeTags_
00234 {
00235 FT_STROKE_TAG_ON = 1,
00236 FT_STROKE_TAG_CUBIC = 2,
00237 FT_STROKE_TAG_BEGIN = 4,
00238 FT_STROKE_TAG_END = 8
00239
00240 } FT_StrokeTags;
00241
00242 #define FT_STROKE_TAG_BEGIN_END (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END)
00243
00244 typedef struct FT_StrokeBorderRec_
00245 {
00246 FT_UInt num_points;
00247 FT_UInt max_points;
00248 FT_Vector* points;
00249 FT_Byte* tags;
00250 FT_Bool movable;
00251 FT_Int start;
00252 FT_Memory memory;
00253 FT_Bool valid;
00254
00255 } FT_StrokeBorderRec, *FT_StrokeBorder;
00256
00257
00258 static FT_Error
00259 ft_stroke_border_grow( FT_StrokeBorder border,
00260 FT_UInt new_points )
00261 {
00262 FT_UInt old_max = border->max_points;
00263 FT_UInt new_max = border->num_points + new_points;
00264 FT_Error error = FT_Err_Ok;
00265
00266
00267 if ( new_max > old_max )
00268 {
00269 FT_UInt cur_max = old_max;
00270 FT_Memory memory = border->memory;
00271
00272
00273 while ( cur_max < new_max )
00274 cur_max += ( cur_max >> 1 ) + 16;
00275
00276 if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
00277 FT_RENEW_ARRAY( border->tags, old_max, cur_max ) )
00278 goto Exit;
00279
00280 border->max_points = cur_max;
00281 }
00282
00283 Exit:
00284 return error;
00285 }
00286
00287
00288 static void
00289 ft_stroke_border_close( FT_StrokeBorder border,
00290 FT_Bool reverse )
00291 {
00292 FT_UInt start = border->start;
00293 FT_UInt count = border->num_points;
00294
00295
00296 FT_ASSERT( border->start >= 0 );
00297
00298
00299 if ( count <= start + 1U )
00300 border->num_points = start;
00301 else
00302 {
00303
00304
00305 border->num_points = --count;
00306 border->points[start] = border->points[count];
00307
00308 if ( reverse )
00309 {
00310
00311 {
00312 FT_Vector* vec1 = border->points + start + 1;
00313 FT_Vector* vec2 = border->points + count - 1;
00314
00315
00316 for ( ; vec1 < vec2; vec1++, vec2-- )
00317 {
00318 FT_Vector tmp;
00319
00320
00321 tmp = *vec1;
00322 *vec1 = *vec2;
00323 *vec2 = tmp;
00324 }
00325 }
00326
00327
00328 {
00329 FT_Byte* tag1 = border->tags + start + 1;
00330 FT_Byte* tag2 = border->tags + count - 1;
00331
00332
00333 for ( ; tag1 < tag2; tag1++, tag2-- )
00334 {
00335 FT_Byte tmp;
00336
00337
00338 tmp = *tag1;
00339 *tag1 = *tag2;
00340 *tag2 = tmp;
00341 }
00342 }
00343 }
00344
00345 border->tags[start ] |= FT_STROKE_TAG_BEGIN;
00346 border->tags[count - 1] |= FT_STROKE_TAG_END;
00347 }
00348
00349 border->start = -1;
00350 border->movable = FALSE;
00351 }
00352
00353
00354 static FT_Error
00355 ft_stroke_border_lineto( FT_StrokeBorder border,
00356 FT_Vector* to,
00357 FT_Bool movable )
00358 {
00359 FT_Error error = FT_Err_Ok;
00360
00361
00362 FT_ASSERT( border->start >= 0 );
00363
00364 if ( border->movable )
00365 {
00366
00367 border->points[border->num_points - 1] = *to;
00368 }
00369 else
00370 {
00371
00372 error = ft_stroke_border_grow( border, 1 );
00373 if ( !error )
00374 {
00375 FT_Vector* vec = border->points + border->num_points;
00376 FT_Byte* tag = border->tags + border->num_points;
00377
00378
00379 vec[0] = *to;
00380 tag[0] = FT_STROKE_TAG_ON;
00381
00382 border->num_points += 1;
00383 }
00384 }
00385 border->movable = movable;
00386 return error;
00387 }
00388
00389
00390 static FT_Error
00391 ft_stroke_border_conicto( FT_StrokeBorder border,
00392 FT_Vector* control,
00393 FT_Vector* to )
00394 {
00395 FT_Error error;
00396
00397
00398 FT_ASSERT( border->start >= 0 );
00399
00400 error = ft_stroke_border_grow( border, 2 );
00401 if ( !error )
00402 {
00403 FT_Vector* vec = border->points + border->num_points;
00404 FT_Byte* tag = border->tags + border->num_points;
00405
00406 vec[0] = *control;
00407 vec[1] = *to;
00408
00409 tag[0] = 0;
00410 tag[1] = FT_STROKE_TAG_ON;
00411
00412 border->num_points += 2;
00413 }
00414 border->movable = FALSE;
00415 return error;
00416 }
00417
00418
00419 static FT_Error
00420 ft_stroke_border_cubicto( FT_StrokeBorder border,
00421 FT_Vector* control1,
00422 FT_Vector* control2,
00423 FT_Vector* to )
00424 {
00425 FT_Error error;
00426
00427
00428 FT_ASSERT( border->start >= 0 );
00429
00430 error = ft_stroke_border_grow( border, 3 );
00431 if ( !error )
00432 {
00433 FT_Vector* vec = border->points + border->num_points;
00434 FT_Byte* tag = border->tags + border->num_points;
00435
00436
00437 vec[0] = *control1;
00438 vec[1] = *control2;
00439 vec[2] = *to;
00440
00441 tag[0] = FT_STROKE_TAG_CUBIC;
00442 tag[1] = FT_STROKE_TAG_CUBIC;
00443 tag[2] = FT_STROKE_TAG_ON;
00444
00445 border->num_points += 3;
00446 }
00447 border->movable = FALSE;
00448 return error;
00449 }
00450
00451
00452 #define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 )
00453
00454
00455 static FT_Error
00456 ft_stroke_border_arcto( FT_StrokeBorder border,
00457 FT_Vector* center,
00458 FT_Fixed radius,
00459 FT_Angle angle_start,
00460 FT_Angle angle_diff )
00461 {
00462 FT_Angle total, angle, step, rotate, next, theta;
00463 FT_Vector a, b, a2, b2;
00464 FT_Fixed length;
00465 FT_Error error = FT_Err_Ok;
00466
00467
00468
00469 FT_Vector_From_Polar( &a, radius, angle_start );
00470 a.x += center->x;
00471 a.y += center->y;
00472
00473 total = angle_diff;
00474 angle = angle_start;
00475 rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
00476
00477 while ( total != 0 )
00478 {
00479 step = total;
00480 if ( step > FT_ARC_CUBIC_ANGLE )
00481 step = FT_ARC_CUBIC_ANGLE;
00482
00483 else if ( step < -FT_ARC_CUBIC_ANGLE )
00484 step = -FT_ARC_CUBIC_ANGLE;
00485
00486 next = angle + step;
00487 theta = step;
00488 if ( theta < 0 )
00489 theta = -theta;
00490
00491 theta >>= 1;
00492
00493
00494 FT_Vector_From_Polar( &b, radius, next );
00495 b.x += center->x;
00496 b.y += center->y;
00497
00498
00499 length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
00500 ( 0x10000L + FT_Cos( theta ) ) * 3 );
00501
00502 FT_Vector_From_Polar( &a2, length, angle + rotate );
00503 a2.x += a.x;
00504 a2.y += a.y;
00505
00506 FT_Vector_From_Polar( &b2, length, next - rotate );
00507 b2.x += b.x;
00508 b2.y += b.y;
00509
00510
00511 error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
00512 if ( error )
00513 break;
00514
00515
00516 a = b;
00517 total -= step;
00518 angle = next;
00519 }
00520
00521 return error;
00522 }
00523
00524
00525 static FT_Error
00526 ft_stroke_border_moveto( FT_StrokeBorder border,
00527 FT_Vector* to )
00528 {
00529
00530 if ( border->start >= 0 )
00531 ft_stroke_border_close( border, FALSE );
00532
00533 border->start = border->num_points;
00534 border->movable = FALSE;
00535
00536 return ft_stroke_border_lineto( border, to, FALSE );
00537 }
00538
00539
00540 static void
00541 ft_stroke_border_init( FT_StrokeBorder border,
00542 FT_Memory memory )
00543 {
00544 border->memory = memory;
00545 border->points = NULL;
00546 border->tags = NULL;
00547
00548 border->num_points = 0;
00549 border->max_points = 0;
00550 border->start = -1;
00551 border->valid = FALSE;
00552 }
00553
00554
00555 static void
00556 ft_stroke_border_reset( FT_StrokeBorder border )
00557 {
00558 border->num_points = 0;
00559 border->start = -1;
00560 border->valid = FALSE;
00561 }
00562
00563
00564 static void
00565 ft_stroke_border_done( FT_StrokeBorder border )
00566 {
00567 FT_Memory memory = border->memory;
00568
00569
00570 FT_FREE( border->points );
00571 FT_FREE( border->tags );
00572
00573 border->num_points = 0;
00574 border->max_points = 0;
00575 border->start = -1;
00576 border->valid = FALSE;
00577 }
00578
00579
00580 static FT_Error
00581 ft_stroke_border_get_counts( FT_StrokeBorder border,
00582 FT_UInt *anum_points,
00583 FT_UInt *anum_contours )
00584 {
00585 FT_Error error = FT_Err_Ok;
00586 FT_UInt num_points = 0;
00587 FT_UInt num_contours = 0;
00588
00589 FT_UInt count = border->num_points;
00590 FT_Vector* point = border->points;
00591 FT_Byte* tags = border->tags;
00592 FT_Int in_contour = 0;
00593
00594
00595 for ( ; count > 0; count--, num_points++, point++, tags++ )
00596 {
00597 if ( tags[0] & FT_STROKE_TAG_BEGIN )
00598 {
00599 if ( in_contour != 0 )
00600 goto Fail;
00601
00602 in_contour = 1;
00603 }
00604 else if ( in_contour == 0 )
00605 goto Fail;
00606
00607 if ( tags[0] & FT_STROKE_TAG_END )
00608 {
00609 in_contour = 0;
00610 num_contours++;
00611 }
00612 }
00613
00614 if ( in_contour != 0 )
00615 goto Fail;
00616
00617 border->valid = TRUE;
00618
00619 Exit:
00620 *anum_points = num_points;
00621 *anum_contours = num_contours;
00622 return error;
00623
00624 Fail:
00625 num_points = 0;
00626 num_contours = 0;
00627 goto Exit;
00628 }
00629
00630
00631 static void
00632 ft_stroke_border_export( FT_StrokeBorder border,
00633 FT_Outline* outline )
00634 {
00635
00636 FT_ARRAY_COPY( outline->points + outline->n_points,
00637 border->points,
00638 border->num_points );
00639
00640
00641 {
00642 FT_UInt count = border->num_points;
00643 FT_Byte* read = border->tags;
00644 FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points;
00645
00646
00647 for ( ; count > 0; count--, read++, write++ )
00648 {
00649 if ( *read & FT_STROKE_TAG_ON )
00650 *write = FT_CURVE_TAG_ON;
00651 else if ( *read & FT_STROKE_TAG_CUBIC )
00652 *write = FT_CURVE_TAG_CUBIC;
00653 else
00654 *write = FT_CURVE_TAG_CONIC;
00655 }
00656 }
00657
00658
00659 {
00660 FT_UInt count = border->num_points;
00661 FT_Byte* tags = border->tags;
00662 FT_Short* write = outline->contours + outline->n_contours;
00663 FT_Short idx = (FT_Short)outline->n_points;
00664
00665
00666 for ( ; count > 0; count--, tags++, idx++ )
00667 {
00668 if ( *tags & FT_STROKE_TAG_END )
00669 {
00670 *write++ = idx;
00671 outline->n_contours++;
00672 }
00673 }
00674 }
00675
00676 outline->n_points = (short)( outline->n_points + border->num_points );
00677
00678 FT_ASSERT( FT_Outline_Check( outline ) == 0 );
00679 }
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690 #define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
00691
00692 typedef struct FT_StrokerRec_
00693 {
00694 FT_Angle angle_in;
00695 FT_Angle angle_out;
00696 FT_Vector center;
00697 FT_Bool first_point;
00698 FT_Bool subpath_open;
00699 FT_Angle subpath_angle;
00700 FT_Vector subpath_start;
00701
00702 FT_Stroker_LineCap line_cap;
00703 FT_Stroker_LineJoin line_join;
00704 FT_Fixed miter_limit;
00705 FT_Fixed radius;
00706
00707 FT_Bool valid;
00708 FT_StrokeBorderRec borders[2];
00709 FT_Library library;
00710
00711 } FT_StrokerRec;
00712
00713
00714
00715
00716 FT_EXPORT_DEF( FT_Error )
00717 FT_Stroker_New( FT_Library library,
00718 FT_Stroker *astroker )
00719 {
00720 FT_Error error;
00721 FT_Memory memory;
00722 FT_Stroker stroker;
00723
00724
00725 if ( !library )
00726 return FT_Err_Invalid_Argument;
00727
00728 memory = library->memory;
00729
00730 if ( !FT_NEW( stroker ) )
00731 {
00732 stroker->library = library;
00733
00734 ft_stroke_border_init( &stroker->borders[0], memory );
00735 ft_stroke_border_init( &stroker->borders[1], memory );
00736 }
00737 *astroker = stroker;
00738 return error;
00739 }
00740
00741
00742
00743
00744 FT_EXPORT_DEF( void )
00745 FT_Stroker_Set( FT_Stroker stroker,
00746 FT_Fixed radius,
00747 FT_Stroker_LineCap line_cap,
00748 FT_Stroker_LineJoin line_join,
00749 FT_Fixed miter_limit )
00750 {
00751 stroker->radius = radius;
00752 stroker->line_cap = line_cap;
00753 stroker->line_join = line_join;
00754 stroker->miter_limit = miter_limit;
00755
00756 FT_Stroker_Rewind( stroker );
00757 }
00758
00759
00760
00761
00762 FT_EXPORT_DEF( void )
00763 FT_Stroker_Rewind( FT_Stroker stroker )
00764 {
00765 if ( stroker )
00766 {
00767 ft_stroke_border_reset( &stroker->borders[0] );
00768 ft_stroke_border_reset( &stroker->borders[1] );
00769 }
00770 }
00771
00772
00773
00774
00775 FT_EXPORT_DEF( void )
00776 FT_Stroker_Done( FT_Stroker stroker )
00777 {
00778 if ( stroker )
00779 {
00780 FT_Memory memory = stroker->library->memory;
00781
00782
00783 ft_stroke_border_done( &stroker->borders[0] );
00784 ft_stroke_border_done( &stroker->borders[1] );
00785
00786 stroker->library = NULL;
00787 FT_FREE( stroker );
00788 }
00789 }
00790
00791
00792
00793 static FT_Error
00794 ft_stroker_arcto( FT_Stroker stroker,
00795 FT_Int side )
00796 {
00797 FT_Angle total, rotate;
00798 FT_Fixed radius = stroker->radius;
00799 FT_Error error = FT_Err_Ok;
00800 FT_StrokeBorder border = stroker->borders + side;
00801
00802
00803 rotate = FT_SIDE_TO_ROTATE( side );
00804
00805 total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
00806 if ( total == FT_ANGLE_PI )
00807 total = -rotate * 2;
00808
00809 error = ft_stroke_border_arcto( border,
00810 &stroker->center,
00811 radius,
00812 stroker->angle_in + rotate,
00813 total );
00814 border->movable = FALSE;
00815 return error;
00816 }
00817
00818
00819
00820 static FT_Error
00821 ft_stroker_cap( FT_Stroker stroker,
00822 FT_Angle angle,
00823 FT_Int side )
00824 {
00825 FT_Error error = FT_Err_Ok;
00826
00827
00828 if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
00829 {
00830
00831 stroker->angle_in = angle;
00832 stroker->angle_out = angle + FT_ANGLE_PI;
00833 error = ft_stroker_arcto( stroker, side );
00834 }
00835 else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
00836 {
00837
00838 FT_Vector delta, delta2;
00839 FT_Angle rotate = FT_SIDE_TO_ROTATE( side );
00840 FT_Fixed radius = stroker->radius;
00841 FT_StrokeBorder border = stroker->borders + side;
00842
00843
00844 FT_Vector_From_Polar( &delta2, radius, angle + rotate );
00845 FT_Vector_From_Polar( &delta, radius, angle );
00846
00847 delta.x += stroker->center.x + delta2.x;
00848 delta.y += stroker->center.y + delta2.y;
00849
00850 error = ft_stroke_border_lineto( border, &delta, FALSE );
00851 if ( error )
00852 goto Exit;
00853
00854 FT_Vector_From_Polar( &delta2, radius, angle - rotate );
00855 FT_Vector_From_Polar( &delta, radius, angle );
00856
00857 delta.x += delta2.x + stroker->center.x;
00858 delta.y += delta2.y + stroker->center.y;
00859
00860 error = ft_stroke_border_lineto( border, &delta, FALSE );
00861 }
00862 else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT )
00863 {
00864
00865 FT_Vector delta;
00866 FT_Angle rotate = FT_SIDE_TO_ROTATE( side );
00867 FT_Fixed radius = stroker->radius;
00868 FT_StrokeBorder border = stroker->borders + side;
00869
00870
00871 FT_Vector_From_Polar( &delta, radius, angle + rotate );
00872
00873 delta.x += stroker->center.x;
00874 delta.y += stroker->center.y;
00875
00876 error = ft_stroke_border_lineto( border, &delta, FALSE );
00877 if ( error )
00878 goto Exit;
00879
00880 FT_Vector_From_Polar( &delta, radius, angle - rotate );
00881
00882 delta.x += stroker->center.x;
00883 delta.y += stroker->center.y;
00884
00885 error = ft_stroke_border_lineto( border, &delta, FALSE );
00886 }
00887
00888 Exit:
00889 return error;
00890 }
00891
00892
00893
00894 static FT_Error
00895 ft_stroker_inside( FT_Stroker stroker,
00896 FT_Int side)
00897 {
00898 FT_StrokeBorder border = stroker->borders + side;
00899 FT_Angle phi, theta, rotate;
00900 FT_Fixed length, thcos, sigma;
00901 FT_Vector delta;
00902 FT_Error error = FT_Err_Ok;
00903
00904
00905 rotate = FT_SIDE_TO_ROTATE( side );
00906
00907
00908 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
00909 if ( theta == FT_ANGLE_PI )
00910 theta = rotate;
00911 else
00912 theta = theta / 2;
00913
00914 phi = stroker->angle_in + theta;
00915
00916 thcos = FT_Cos( theta );
00917 sigma = FT_MulFix( stroker->miter_limit, thcos );
00918
00919
00920 if ( sigma < 0x10000L )
00921 {
00922 FT_Vector_From_Polar( &delta, stroker->radius,
00923 stroker->angle_out + rotate );
00924 delta.x += stroker->center.x;
00925 delta.y += stroker->center.y;
00926 border->movable = FALSE;
00927 }
00928 else
00929 {
00930 length = FT_DivFix( stroker->radius, thcos );
00931
00932 FT_Vector_From_Polar( &delta, length, phi + rotate );
00933 delta.x += stroker->center.x;
00934 delta.y += stroker->center.y;
00935 }
00936
00937 error = ft_stroke_border_lineto( border, &delta, FALSE );
00938
00939 return error;
00940 }
00941
00942
00943
00944 static FT_Error
00945 ft_stroker_outside( FT_Stroker stroker,
00946 FT_Int side )
00947 {
00948 FT_StrokeBorder border = stroker->borders + side;
00949 FT_Error error;
00950 FT_Angle rotate;
00951
00952
00953 if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
00954 error = ft_stroker_arcto( stroker, side );
00955 else
00956 {
00957
00958 FT_Fixed sigma, radius = stroker->radius;
00959 FT_Angle theta, phi;
00960 FT_Fixed thcos;
00961 FT_Bool miter;
00962
00963
00964 rotate = FT_SIDE_TO_ROTATE( side );
00965 miter = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
00966
00967 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
00968 if ( theta == FT_ANGLE_PI )
00969 {
00970 theta = rotate;
00971 phi = stroker->angle_in;
00972 }
00973 else
00974 {
00975 theta = theta / 2;
00976 phi = stroker->angle_in + theta + rotate;
00977 }
00978
00979 thcos = FT_Cos( theta );
00980 sigma = FT_MulFix( stroker->miter_limit, thcos );
00981
00982
00983 if ( sigma >= 0x10000L || ft_pos_abs( theta ) <= 57 )
00984 miter = FALSE;
00985
00986 if ( miter )
00987 {
00988 FT_Vector middle, delta;
00989 FT_Fixed length;
00990
00991
00992
00993 FT_Vector_From_Polar( &middle,
00994 FT_MulFix( radius, stroker->miter_limit ),
00995 phi );
00996 middle.x += stroker->center.x;
00997 middle.y += stroker->center.y;
00998
00999
01000 length = FT_MulFix( radius,
01001 FT_DivFix( 0x10000L - sigma,
01002 ft_pos_abs( FT_Sin( theta ) ) ) );
01003
01004 FT_Vector_From_Polar( &delta, length, phi + rotate );
01005 delta.x += middle.x;
01006 delta.y += middle.y;
01007
01008 error = ft_stroke_border_lineto( border, &delta, FALSE );
01009 if ( error )
01010 goto Exit;
01011
01012
01013 FT_Vector_From_Polar( &delta, length, phi - rotate );
01014 delta.x += middle.x;
01015 delta.y += middle.y;
01016
01017 error = ft_stroke_border_lineto( border, &delta, FALSE );
01018 if ( error )
01019 goto Exit;
01020
01021
01022 FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
01023 delta.x += stroker->center.x;
01024 delta.y += stroker->center.y;
01025
01026 error = ft_stroke_border_lineto( border, &delta, TRUE );
01027 }
01028
01029 else
01030 {
01031 FT_Fixed length;
01032 FT_Vector delta;
01033
01034
01035 length = FT_DivFix( stroker->radius, thcos );
01036
01037 FT_Vector_From_Polar( &delta, length, phi );
01038 delta.x += stroker->center.x;
01039 delta.y += stroker->center.y;
01040
01041 error = ft_stroke_border_lineto( border, &delta, FALSE );
01042 if ( error )
01043 goto Exit;
01044
01045
01046 FT_Vector_From_Polar( &delta, stroker->radius,
01047 stroker->angle_out + rotate );
01048 delta.x += stroker->center.x;
01049 delta.y += stroker->center.y;
01050
01051 error = ft_stroke_border_lineto( border, &delta, TRUE );
01052 }
01053 }
01054
01055 Exit:
01056 return error;
01057 }
01058
01059
01060 static FT_Error
01061 ft_stroker_process_corner( FT_Stroker stroker )
01062 {
01063 FT_Error error = FT_Err_Ok;
01064 FT_Angle turn;
01065 FT_Int inside_side;
01066
01067
01068 turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
01069
01070
01071 if ( turn == 0 )
01072 goto Exit;
01073
01074
01075 inside_side = 0;
01076
01077
01078 if ( turn < 0 )
01079 inside_side = 1;
01080
01081
01082 error = ft_stroker_inside( stroker, inside_side );
01083 if ( error )
01084 goto Exit;
01085
01086
01087 error = ft_stroker_outside( stroker, 1 - inside_side );
01088
01089 Exit:
01090 return error;
01091 }
01092
01093
01094
01095
01096 static FT_Error
01097 ft_stroker_subpath_start( FT_Stroker stroker,
01098 FT_Angle start_angle )
01099 {
01100 FT_Vector delta;
01101 FT_Vector point;
01102 FT_Error error;
01103 FT_StrokeBorder border;
01104
01105
01106 FT_Vector_From_Polar( &delta, stroker->radius,
01107 start_angle + FT_ANGLE_PI2 );
01108
01109 point.x = stroker->center.x + delta.x;
01110 point.y = stroker->center.y + delta.y;
01111
01112 border = stroker->borders;
01113 error = ft_stroke_border_moveto( border, &point );
01114 if ( error )
01115 goto Exit;
01116
01117 point.x = stroker->center.x - delta.x;
01118 point.y = stroker->center.y - delta.y;
01119
01120 border++;
01121 error = ft_stroke_border_moveto( border, &point );
01122
01123
01124 stroker->subpath_angle = start_angle;
01125 stroker->first_point = FALSE;
01126
01127 Exit:
01128 return error;
01129 }
01130
01131
01132
01133
01134 FT_EXPORT_DEF( FT_Error )
01135 FT_Stroker_LineTo( FT_Stroker stroker,
01136 FT_Vector* to )
01137 {
01138 FT_Error error = FT_Err_Ok;
01139 FT_StrokeBorder border;
01140 FT_Vector delta;
01141 FT_Angle angle;
01142 FT_Int side;
01143
01144 delta.x = to->x - stroker->center.x;
01145 delta.y = to->y - stroker->center.y;
01146
01147 angle = FT_Atan2( delta.x, delta.y );
01148 FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
01149
01150
01151 if ( stroker->first_point )
01152 {
01153
01154
01155
01156 error = ft_stroker_subpath_start( stroker, angle );
01157 if ( error )
01158 goto Exit;
01159 }
01160 else
01161 {
01162
01163 stroker->angle_out = angle;
01164 error = ft_stroker_process_corner( stroker );
01165 if ( error )
01166 goto Exit;
01167 }
01168
01169
01170
01171 for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
01172 {
01173 FT_Vector point;
01174
01175
01176 point.x = to->x + delta.x;
01177 point.y = to->y + delta.y;
01178
01179 error = ft_stroke_border_lineto( border, &point, TRUE );
01180 if ( error )
01181 goto Exit;
01182
01183 delta.x = -delta.x;
01184 delta.y = -delta.y;
01185 }
01186
01187 stroker->angle_in = angle;
01188 stroker->center = *to;
01189
01190 Exit:
01191 return error;
01192 }
01193
01194
01195
01196
01197 FT_EXPORT_DEF( FT_Error )
01198 FT_Stroker_ConicTo( FT_Stroker stroker,
01199 FT_Vector* control,
01200 FT_Vector* to )
01201 {
01202 FT_Error error = FT_Err_Ok;
01203 FT_Vector bez_stack[34];
01204 FT_Vector* arc;
01205 FT_Vector* limit = bez_stack + 30;
01206 FT_Angle start_angle;
01207 FT_Bool first_arc = TRUE;
01208
01209
01210 arc = bez_stack;
01211 arc[0] = *to;
01212 arc[1] = *control;
01213 arc[2] = stroker->center;
01214
01215 while ( arc >= bez_stack )
01216 {
01217 FT_Angle angle_in, angle_out;
01218
01219
01220 angle_in = angle_out = 0;
01221
01222 if ( arc < limit &&
01223 !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
01224 {
01225 ft_conic_split( arc );
01226 arc += 2;
01227 continue;
01228 }
01229
01230 if ( first_arc )
01231 {
01232 first_arc = FALSE;
01233
01234 start_angle = angle_in;
01235
01236
01237 if ( stroker->first_point )
01238 error = ft_stroker_subpath_start( stroker, start_angle );
01239 else
01240 {
01241 stroker->angle_out = start_angle;
01242 error = ft_stroker_process_corner( stroker );
01243 }
01244 }
01245
01246
01247
01248 {
01249 FT_Vector ctrl, end;
01250 FT_Angle theta, phi, rotate;
01251 FT_Fixed length;
01252 FT_Int side;
01253
01254
01255 theta = FT_Angle_Diff( angle_in, angle_out ) / 2;
01256 phi = angle_in + theta;
01257 length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
01258
01259 for ( side = 0; side <= 1; side++ )
01260 {
01261 rotate = FT_SIDE_TO_ROTATE( side );
01262
01263
01264 FT_Vector_From_Polar( &ctrl, length, phi + rotate );
01265 ctrl.x += arc[1].x;
01266 ctrl.y += arc[1].y;
01267
01268
01269 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
01270 end.x += arc[0].x;
01271 end.y += arc[0].y;
01272
01273 error = ft_stroke_border_conicto( stroker->borders + side,
01274 &ctrl, &end );
01275 if ( error )
01276 goto Exit;
01277 }
01278 }
01279
01280 arc -= 2;
01281
01282 if ( arc < bez_stack )
01283 stroker->angle_in = angle_out;
01284 }
01285
01286 stroker->center = *to;
01287
01288 Exit:
01289 return error;
01290 }
01291
01292
01293
01294
01295 FT_EXPORT_DEF( FT_Error )
01296 FT_Stroker_CubicTo( FT_Stroker stroker,
01297 FT_Vector* control1,
01298 FT_Vector* control2,
01299 FT_Vector* to )
01300 {
01301 FT_Error error = FT_Err_Ok;
01302 FT_Vector bez_stack[37];
01303 FT_Vector* arc;
01304 FT_Vector* limit = bez_stack + 32;
01305 FT_Angle start_angle;
01306 FT_Bool first_arc = TRUE;
01307
01308
01309 arc = bez_stack;
01310 arc[0] = *to;
01311 arc[1] = *control2;
01312 arc[2] = *control1;
01313 arc[3] = stroker->center;
01314
01315 while ( arc >= bez_stack )
01316 {
01317 FT_Angle angle_in, angle_mid, angle_out;
01318
01319
01320
01321 angle_in = angle_out = angle_mid = 0;
01322
01323 if ( arc < limit &&
01324 !ft_cubic_is_small_enough( arc, &angle_in,
01325 &angle_mid, &angle_out ) )
01326 {
01327 ft_cubic_split( arc );
01328 arc += 3;
01329 continue;
01330 }
01331
01332 if ( first_arc )
01333 {
01334 first_arc = FALSE;
01335
01336
01337 start_angle = angle_in;
01338
01339 if ( stroker->first_point )
01340 error = ft_stroker_subpath_start( stroker, start_angle );
01341 else
01342 {
01343 stroker->angle_out = start_angle;
01344 error = ft_stroker_process_corner( stroker );
01345 }
01346 if ( error )
01347 goto Exit;
01348 }
01349
01350
01351
01352 {
01353 FT_Vector ctrl1, ctrl2, end;
01354 FT_Angle theta1, phi1, theta2, phi2, rotate;
01355 FT_Fixed length1, length2;
01356 FT_Int side;
01357
01358
01359 theta1 = ft_pos_abs( angle_mid - angle_in ) / 2;
01360 theta2 = ft_pos_abs( angle_out - angle_mid ) / 2;
01361 phi1 = (angle_mid + angle_in ) / 2;
01362 phi2 = (angle_mid + angle_out ) / 2;
01363 length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
01364 length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) );
01365
01366 for ( side = 0; side <= 1; side++ )
01367 {
01368 rotate = FT_SIDE_TO_ROTATE( side );
01369
01370
01371 FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
01372 ctrl1.x += arc[2].x;
01373 ctrl1.y += arc[2].y;
01374
01375 FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
01376 ctrl2.x += arc[1].x;
01377 ctrl2.y += arc[1].y;
01378
01379
01380 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
01381 end.x += arc[0].x;
01382 end.y += arc[0].y;
01383
01384 error = ft_stroke_border_cubicto( stroker->borders + side,
01385 &ctrl1, &ctrl2, &end );
01386 if ( error )
01387 goto Exit;
01388 }
01389 }
01390
01391 arc -= 3;
01392 if ( arc < bez_stack )
01393 stroker->angle_in = angle_out;
01394 }
01395
01396 stroker->center = *to;
01397
01398 Exit:
01399 return error;
01400 }
01401
01402
01403
01404
01405 FT_EXPORT_DEF( FT_Error )
01406 FT_Stroker_BeginSubPath( FT_Stroker stroker,
01407 FT_Vector* to,
01408 FT_Bool open )
01409 {
01410
01411
01412
01413
01414 stroker->first_point = TRUE;
01415 stroker->center = *to;
01416 stroker->subpath_open = open;
01417
01418
01419 stroker->subpath_start = *to;
01420
01421 return FT_Err_Ok;
01422 }
01423
01424
01425 static FT_Error
01426 ft_stroker_add_reverse_left( FT_Stroker stroker,
01427 FT_Bool open )
01428 {
01429 FT_StrokeBorder right = stroker->borders + 0;
01430 FT_StrokeBorder left = stroker->borders + 1;
01431 FT_Int new_points;
01432 FT_Error error = FT_Err_Ok;
01433
01434
01435 FT_ASSERT( left->start >= 0 );
01436
01437 new_points = left->num_points - left->start;
01438 if ( new_points > 0 )
01439 {
01440 error = ft_stroke_border_grow( right, (FT_UInt)new_points );
01441 if ( error )
01442 goto Exit;
01443
01444 {
01445 FT_Vector* dst_point = right->points + right->num_points;
01446 FT_Byte* dst_tag = right->tags + right->num_points;
01447 FT_Vector* src_point = left->points + left->num_points - 1;
01448 FT_Byte* src_tag = left->tags + left->num_points - 1;
01449
01450 while ( src_point >= left->points + left->start )
01451 {
01452 *dst_point = *src_point;
01453 *dst_tag = *src_tag;
01454
01455 if ( open )
01456 dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
01457 else
01458 {
01459 FT_Byte ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
01460
01461
01462
01463 if ( ttag == FT_STROKE_TAG_BEGIN ||
01464 ttag == FT_STROKE_TAG_END )
01465 dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
01466
01467 }
01468
01469 src_point--;
01470 src_tag--;
01471 dst_point++;
01472 dst_tag++;
01473 }
01474 }
01475
01476 left->num_points = left->start;
01477 right->num_points += new_points;
01478
01479 right->movable = FALSE;
01480 left->movable = FALSE;
01481 }
01482
01483 Exit:
01484 return error;
01485 }
01486
01487
01488
01489
01490
01491 FT_EXPORT_DEF( FT_Error )
01492 FT_Stroker_EndSubPath( FT_Stroker stroker )
01493 {
01494 FT_Error error = FT_Err_Ok;
01495
01496
01497 if ( stroker->subpath_open )
01498 {
01499 FT_StrokeBorder right = stroker->borders;
01500
01501
01502
01503
01504 error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
01505 if ( error )
01506 goto Exit;
01507
01508
01509 error = ft_stroker_add_reverse_left( stroker, TRUE );
01510 if ( error )
01511 goto Exit;
01512
01513
01514 stroker->center = stroker->subpath_start;
01515 error = ft_stroker_cap( stroker,
01516 stroker->subpath_angle + FT_ANGLE_PI, 0 );
01517 if ( error )
01518 goto Exit;
01519
01520
01521
01522 ft_stroke_border_close( right, FALSE );
01523 }
01524 else
01525 {
01526 FT_Angle turn;
01527 FT_Int inside_side;
01528
01529
01530 if ( stroker->center.x != stroker->subpath_start.x ||
01531 stroker->center.y != stroker->subpath_start.y )
01532 {
01533 error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
01534 if ( error )
01535 goto Exit;
01536 }
01537
01538
01539 stroker->angle_out = stroker->subpath_angle;
01540 turn = FT_Angle_Diff( stroker->angle_in,
01541 stroker->angle_out );
01542
01543
01544 if ( turn != 0 )
01545 {
01546
01547 inside_side = 0;
01548
01549
01550 if ( turn < 0 )
01551 inside_side = 1;
01552
01553 error = ft_stroker_inside( stroker, inside_side );
01554 if ( error )
01555 goto Exit;
01556
01557
01558 error = ft_stroker_outside( stroker, 1 - inside_side );
01559 if ( error )
01560 goto Exit;
01561 }
01562
01563
01564 ft_stroke_border_close( stroker->borders + 0, TRUE );
01565 ft_stroke_border_close( stroker->borders + 1, FALSE );
01566 }
01567
01568 Exit:
01569 return error;
01570 }
01571
01572
01573
01574
01575 FT_EXPORT_DEF( FT_Error )
01576 FT_Stroker_GetBorderCounts( FT_Stroker stroker,
01577 FT_StrokerBorder border,
01578 FT_UInt *anum_points,
01579 FT_UInt *anum_contours )
01580 {
01581 FT_UInt num_points = 0, num_contours = 0;
01582 FT_Error error;
01583
01584
01585 if ( !stroker || border > 1 )
01586 {
01587 error = FT_Err_Invalid_Argument;
01588 goto Exit;
01589 }
01590
01591 error = ft_stroke_border_get_counts( stroker->borders + border,
01592 &num_points, &num_contours );
01593 Exit:
01594 if ( anum_points )
01595 *anum_points = num_points;
01596
01597 if ( anum_contours )
01598 *anum_contours = num_contours;
01599
01600 return error;
01601 }
01602
01603
01604
01605
01606 FT_EXPORT_DEF( FT_Error )
01607 FT_Stroker_GetCounts( FT_Stroker stroker,
01608 FT_UInt *anum_points,
01609 FT_UInt *anum_contours )
01610 {
01611 FT_UInt count1, count2, num_points = 0;
01612 FT_UInt count3, count4, num_contours = 0;
01613 FT_Error error;
01614
01615
01616 error = ft_stroke_border_get_counts( stroker->borders + 0,
01617 &count1, &count2 );
01618 if ( error )
01619 goto Exit;
01620
01621 error = ft_stroke_border_get_counts( stroker->borders + 1,
01622 &count3, &count4 );
01623 if ( error )
01624 goto Exit;
01625
01626 num_points = count1 + count3;
01627 num_contours = count2 + count4;
01628
01629 Exit:
01630 *anum_points = num_points;
01631 *anum_contours = num_contours;
01632 return error;
01633 }
01634
01635
01636
01637
01638 FT_EXPORT_DEF( void )
01639 FT_Stroker_ExportBorder( FT_Stroker stroker,
01640 FT_StrokerBorder border,
01641 FT_Outline* outline )
01642 {
01643 if ( border == FT_STROKER_BORDER_LEFT ||
01644 border == FT_STROKER_BORDER_RIGHT )
01645 {
01646 FT_StrokeBorder sborder = & stroker->borders[border];
01647
01648
01649 if ( sborder->valid )
01650 ft_stroke_border_export( sborder, outline );
01651 }
01652 }
01653
01654
01655
01656
01657 FT_EXPORT_DEF( void )
01658 FT_Stroker_Export( FT_Stroker stroker,
01659 FT_Outline* outline )
01660 {
01661 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
01662 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
01663 }
01664
01665
01666
01667
01668
01669
01670
01671
01672 FT_EXPORT_DEF( FT_Error )
01673 FT_Stroker_ParseOutline( FT_Stroker stroker,
01674 FT_Outline* outline,
01675 FT_Bool opened )
01676 {
01677 FT_Vector v_last;
01678 FT_Vector v_control;
01679 FT_Vector v_start;
01680
01681 FT_Vector* point;
01682 FT_Vector* limit;
01683 char* tags;
01684
01685 FT_Error error;
01686
01687 FT_Int n;
01688 FT_UInt first;
01689 FT_Int tag;
01690
01691
01692 if ( !outline || !stroker )
01693 return FT_Err_Invalid_Argument;
01694
01695 FT_Stroker_Rewind( stroker );
01696
01697 first = 0;
01698
01699 for ( n = 0; n < outline->n_contours; n++ )
01700 {
01701 FT_UInt last;
01702
01703
01704 last = outline->contours[n];
01705 limit = outline->points + last;
01706
01707
01708 if ( last <= first )
01709 {
01710 first = last + 1;
01711 continue;
01712 }
01713
01714 v_start = outline->points[first];
01715 v_last = outline->points[last];
01716
01717 v_control = v_start;
01718
01719 point = outline->points + first;
01720 tags = outline->tags + first;
01721 tag = FT_CURVE_TAG( tags[0] );
01722
01723
01724 if ( tag == FT_CURVE_TAG_CUBIC )
01725 goto Invalid_Outline;
01726
01727
01728 if ( tag == FT_CURVE_TAG_CONIC )
01729 {
01730
01731 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
01732 {
01733
01734 v_start = v_last;
01735 limit--;
01736 }
01737 else
01738 {
01739
01740
01741 v_start.x = ( v_start.x + v_last.x ) / 2;
01742 v_start.y = ( v_start.y + v_last.y ) / 2;
01743 }
01744 point--;
01745 tags--;
01746 }
01747
01748 error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
01749 if ( error )
01750 goto Exit;
01751
01752 while ( point < limit )
01753 {
01754 point++;
01755 tags++;
01756
01757 tag = FT_CURVE_TAG( tags[0] );
01758 switch ( tag )
01759 {
01760 case FT_CURVE_TAG_ON:
01761 {
01762 FT_Vector vec;
01763
01764
01765 vec.x = point->x;
01766 vec.y = point->y;
01767
01768 error = FT_Stroker_LineTo( stroker, &vec );
01769 if ( error )
01770 goto Exit;
01771 continue;
01772 }
01773
01774 case FT_CURVE_TAG_CONIC:
01775 v_control.x = point->x;
01776 v_control.y = point->y;
01777
01778 Do_Conic:
01779 if ( point < limit )
01780 {
01781 FT_Vector vec;
01782 FT_Vector v_middle;
01783
01784
01785 point++;
01786 tags++;
01787 tag = FT_CURVE_TAG( tags[0] );
01788
01789 vec = point[0];
01790
01791 if ( tag == FT_CURVE_TAG_ON )
01792 {
01793 error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
01794 if ( error )
01795 goto Exit;
01796 continue;
01797 }
01798
01799 if ( tag != FT_CURVE_TAG_CONIC )
01800 goto Invalid_Outline;
01801
01802 v_middle.x = ( v_control.x + vec.x ) / 2;
01803 v_middle.y = ( v_control.y + vec.y ) / 2;
01804
01805 error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
01806 if ( error )
01807 goto Exit;
01808
01809 v_control = vec;
01810 goto Do_Conic;
01811 }
01812
01813 error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
01814 goto Close;
01815
01816 default:
01817 {
01818 FT_Vector vec1, vec2;
01819
01820
01821 if ( point + 1 > limit ||
01822 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
01823 goto Invalid_Outline;
01824
01825 point += 2;
01826 tags += 2;
01827
01828 vec1 = point[-2];
01829 vec2 = point[-1];
01830
01831 if ( point <= limit )
01832 {
01833 FT_Vector vec;
01834
01835
01836 vec = point[0];
01837
01838 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
01839 if ( error )
01840 goto Exit;
01841 continue;
01842 }
01843
01844 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
01845 goto Close;
01846 }
01847 }
01848 }
01849
01850 Close:
01851 if ( error )
01852 goto Exit;
01853
01854 error = FT_Stroker_EndSubPath( stroker );
01855 if ( error )
01856 goto Exit;
01857
01858 first = last + 1;
01859 }
01860
01861 return FT_Err_Ok;
01862
01863 Exit:
01864 return error;
01865
01866 Invalid_Outline:
01867 return FT_Err_Invalid_Outline;
01868 }
01869
01870
01871
01872
01873 #ifndef FT_CONFIG_OPTION_PIC
01874 extern const FT_Glyph_Class ft_outline_glyph_class;
01875 #endif
01876 #include "basepic.h"
01877
01878
01879
01880
01881 FT_EXPORT_DEF( FT_Error )
01882 FT_Glyph_Stroke( FT_Glyph *pglyph,
01883 FT_Stroker stroker,
01884 FT_Bool destroy )
01885 {
01886 FT_Error error = FT_Err_Invalid_Argument;
01887 FT_Glyph glyph = NULL;
01888 FT_Library library = stroker->library;
01889 FT_UNUSED(library);
01890
01891 if ( pglyph == NULL )
01892 goto Exit;
01893
01894 glyph = *pglyph;
01895 if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
01896 goto Exit;
01897
01898 {
01899 FT_Glyph copy;
01900
01901
01902 error = FT_Glyph_Copy( glyph, © );
01903 if ( error )
01904 goto Exit;
01905
01906 glyph = copy;
01907 }
01908
01909 {
01910 FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph;
01911 FT_Outline* outline = &oglyph->outline;
01912 FT_UInt num_points, num_contours;
01913
01914
01915 error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
01916 if ( error )
01917 goto Fail;
01918
01919 (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
01920
01921 FT_Outline_Done( glyph->library, outline );
01922
01923 error = FT_Outline_New( glyph->library,
01924 num_points, num_contours, outline );
01925 if ( error )
01926 goto Fail;
01927
01928 outline->n_points = 0;
01929 outline->n_contours = 0;
01930
01931 FT_Stroker_Export( stroker, outline );
01932 }
01933
01934 if ( destroy )
01935 FT_Done_Glyph( *pglyph );
01936
01937 *pglyph = glyph;
01938 goto Exit;
01939
01940 Fail:
01941 FT_Done_Glyph( glyph );
01942 glyph = NULL;
01943
01944 if ( !destroy )
01945 *pglyph = NULL;
01946
01947 Exit:
01948 return error;
01949 }
01950
01951
01952
01953
01954 FT_EXPORT_DEF( FT_Error )
01955 FT_Glyph_StrokeBorder( FT_Glyph *pglyph,
01956 FT_Stroker stroker,
01957 FT_Bool inside,
01958 FT_Bool destroy )
01959 {
01960 FT_Error error = FT_Err_Invalid_Argument;
01961 FT_Glyph glyph = NULL;
01962 FT_Library library = stroker->library;
01963 FT_UNUSED(library);
01964
01965 if ( pglyph == NULL )
01966 goto Exit;
01967
01968 glyph = *pglyph;
01969 if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
01970 goto Exit;
01971
01972 {
01973 FT_Glyph copy;
01974
01975
01976 error = FT_Glyph_Copy( glyph, © );
01977 if ( error )
01978 goto Exit;
01979
01980 glyph = copy;
01981 }
01982
01983 {
01984 FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph;
01985 FT_StrokerBorder border;
01986 FT_Outline* outline = &oglyph->outline;
01987 FT_UInt num_points, num_contours;
01988
01989
01990 border = FT_Outline_GetOutsideBorder( outline );
01991 if ( inside )
01992 {
01993 if ( border == FT_STROKER_BORDER_LEFT )
01994 border = FT_STROKER_BORDER_RIGHT;
01995 else
01996 border = FT_STROKER_BORDER_LEFT;
01997 }
01998
01999 error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
02000 if ( error )
02001 goto Fail;
02002
02003 (void)FT_Stroker_GetBorderCounts( stroker, border,
02004 &num_points, &num_contours );
02005
02006 FT_Outline_Done( glyph->library, outline );
02007
02008 error = FT_Outline_New( glyph->library,
02009 num_points,
02010 num_contours,
02011 outline );
02012 if ( error )
02013 goto Fail;
02014
02015 outline->n_points = 0;
02016 outline->n_contours = 0;
02017
02018 FT_Stroker_ExportBorder( stroker, border, outline );
02019 }
02020
02021 if ( destroy )
02022 FT_Done_Glyph( *pglyph );
02023
02024 *pglyph = glyph;
02025 goto Exit;
02026
02027 Fail:
02028 FT_Done_Glyph( glyph );
02029 glyph = NULL;
02030
02031 if ( !destroy )
02032 *pglyph = NULL;
02033
02034 Exit:
02035 return error;
02036 }
02037
02038
02039