Disabling auto-refresh of game list by default, as it is causing bugs sometimes
[open-ps2-loader.git] / thirdparty / freetype-2.3.12 / src / base / ftstroke.c
blob75bcbded6a3d38a233a42084fb1280b195b34d1e
1 /***************************************************************************/
2 /* */
3 /* ftstroke.c */
4 /* */
5 /* FreeType path stroker (body). */
6 /* */
7 /* Copyright 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 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 /***************************************************************************/
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
28 /* documentation is in ftstroke.h */
30 FT_EXPORT_DEF( FT_StrokerBorder )
31 FT_Outline_GetInsideBorder( FT_Outline* outline )
33 FT_Orientation o = FT_Outline_Get_Orientation( outline );
36 return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
37 : FT_STROKER_BORDER_LEFT ;
41 /* documentation is in ftstroke.h */
43 FT_EXPORT_DEF( FT_StrokerBorder )
44 FT_Outline_GetOutsideBorder( FT_Outline* outline )
46 FT_Orientation o = FT_Outline_Get_Orientation( outline );
49 return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
50 : FT_STROKER_BORDER_RIGHT ;
54 /***************************************************************************/
55 /***************************************************************************/
56 /***** *****/
57 /***** BEZIER COMPUTATIONS *****/
58 /***** *****/
59 /***************************************************************************/
60 /***************************************************************************/
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
66 #define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
69 static FT_Pos
70 ft_pos_abs( FT_Pos x )
72 return x >= 0 ? x : -x ;
76 static void
77 ft_conic_split( FT_Vector* base )
79 FT_Pos a, b;
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;
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;
96 static FT_Bool
97 ft_conic_is_small_enough( FT_Vector* base,
98 FT_Angle *angle_in,
99 FT_Angle *angle_out )
101 FT_Vector d1, d2;
102 FT_Angle theta;
103 FT_Int close1, close2;
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;
111 close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
112 close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
114 if ( close1 )
116 if ( close2 )
117 *angle_in = *angle_out = 0;
118 else
119 *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
121 else if ( close2 )
123 *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
125 else
127 *angle_in = FT_Atan2( d1.x, d1.y );
128 *angle_out = FT_Atan2( d2.x, d2.y );
131 theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
133 return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
137 static void
138 ft_cubic_split( FT_Vector* base )
140 FT_Pos a, b, c, d;
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;
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;
165 static FT_Bool
166 ft_cubic_is_small_enough( FT_Vector* base,
167 FT_Angle *angle_in,
168 FT_Angle *angle_mid,
169 FT_Angle *angle_out )
171 FT_Vector d1, d2, d3;
172 FT_Angle theta1, theta2;
173 FT_Int close1, close2, close3;
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;
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 );
187 if ( close1 || close3 )
189 if ( close2 )
191 /* basically a point */
192 *angle_in = *angle_out = *angle_mid = 0;
194 else if ( close1 )
196 *angle_in = *angle_mid = FT_Atan2( d2.x, d2.y );
197 *angle_out = FT_Atan2( d3.x, d3.y );
199 else /* close2 */
201 *angle_in = FT_Atan2( d1.x, d1.y );
202 *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
205 else if ( close2 )
207 *angle_in = *angle_mid = FT_Atan2( d1.x, d1.y );
208 *angle_out = FT_Atan2( d3.x, d3.y );
210 else
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 );
217 theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) );
218 theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
220 return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
221 theta2 < FT_SMALL_CUBIC_THRESHOLD );
225 /***************************************************************************/
226 /***************************************************************************/
227 /***** *****/
228 /***** STROKE BORDERS *****/
229 /***** *****/
230 /***************************************************************************/
231 /***************************************************************************/
233 typedef enum FT_StrokeTags_
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 */
240 } FT_StrokeTags;
242 #define FT_STROKE_TAG_BEGIN_END (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END)
244 typedef struct FT_StrokeBorderRec_
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;
255 } FT_StrokeBorderRec, *FT_StrokeBorder;
258 static FT_Error
259 ft_stroke_border_grow( FT_StrokeBorder border,
260 FT_UInt new_points )
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;
267 if ( new_max > old_max )
269 FT_UInt cur_max = old_max;
270 FT_Memory memory = border->memory;
273 while ( cur_max < new_max )
274 cur_max += ( cur_max >> 1 ) + 16;
276 if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
277 FT_RENEW_ARRAY( border->tags, old_max, cur_max ) )
278 goto Exit;
280 border->max_points = cur_max;
283 Exit:
284 return error;
288 static void
289 ft_stroke_border_close( FT_StrokeBorder border,
290 FT_Bool reverse )
292 FT_UInt start = border->start;
293 FT_UInt count = border->num_points;
296 FT_ASSERT( border->start >= 0 );
298 /* don't record empty paths! */
299 if ( count <= start + 1U )
300 border->num_points = start;
301 else
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];
308 if ( reverse )
310 /* reverse the points */
312 FT_Vector* vec1 = border->points + start + 1;
313 FT_Vector* vec2 = border->points + count - 1;
316 for ( ; vec1 < vec2; vec1++, vec2-- )
318 FT_Vector tmp;
321 tmp = *vec1;
322 *vec1 = *vec2;
323 *vec2 = tmp;
327 /* then the tags */
329 FT_Byte* tag1 = border->tags + start + 1;
330 FT_Byte* tag2 = border->tags + count - 1;
333 for ( ; tag1 < tag2; tag1++, tag2-- )
335 FT_Byte tmp;
338 tmp = *tag1;
339 *tag1 = *tag2;
340 *tag2 = tmp;
345 border->tags[start ] |= FT_STROKE_TAG_BEGIN;
346 border->tags[count - 1] |= FT_STROKE_TAG_END;
349 border->start = -1;
350 border->movable = FALSE;
354 static FT_Error
355 ft_stroke_border_lineto( FT_StrokeBorder border,
356 FT_Vector* to,
357 FT_Bool movable )
359 FT_Error error = FT_Err_Ok;
362 FT_ASSERT( border->start >= 0 );
364 if ( border->movable )
366 /* move last point */
367 border->points[border->num_points - 1] = *to;
369 else
371 /* add one point */
372 error = ft_stroke_border_grow( border, 1 );
373 if ( !error )
375 FT_Vector* vec = border->points + border->num_points;
376 FT_Byte* tag = border->tags + border->num_points;
379 vec[0] = *to;
380 tag[0] = FT_STROKE_TAG_ON;
382 border->num_points += 1;
385 border->movable = movable;
386 return error;
390 static FT_Error
391 ft_stroke_border_conicto( FT_StrokeBorder border,
392 FT_Vector* control,
393 FT_Vector* to )
395 FT_Error error;
398 FT_ASSERT( border->start >= 0 );
400 error = ft_stroke_border_grow( border, 2 );
401 if ( !error )
403 FT_Vector* vec = border->points + border->num_points;
404 FT_Byte* tag = border->tags + border->num_points;
406 vec[0] = *control;
407 vec[1] = *to;
409 tag[0] = 0;
410 tag[1] = FT_STROKE_TAG_ON;
412 border->num_points += 2;
414 border->movable = FALSE;
415 return error;
419 static FT_Error
420 ft_stroke_border_cubicto( FT_StrokeBorder border,
421 FT_Vector* control1,
422 FT_Vector* control2,
423 FT_Vector* to )
425 FT_Error error;
428 FT_ASSERT( border->start >= 0 );
430 error = ft_stroke_border_grow( border, 3 );
431 if ( !error )
433 FT_Vector* vec = border->points + border->num_points;
434 FT_Byte* tag = border->tags + border->num_points;
437 vec[0] = *control1;
438 vec[1] = *control2;
439 vec[2] = *to;
441 tag[0] = FT_STROKE_TAG_CUBIC;
442 tag[1] = FT_STROKE_TAG_CUBIC;
443 tag[2] = FT_STROKE_TAG_ON;
445 border->num_points += 3;
447 border->movable = FALSE;
448 return error;
452 #define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 )
455 static FT_Error
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 )
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;
468 /* compute start point */
469 FT_Vector_From_Polar( &a, radius, angle_start );
470 a.x += center->x;
471 a.y += center->y;
473 total = angle_diff;
474 angle = angle_start;
475 rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
477 while ( total != 0 )
479 step = total;
480 if ( step > FT_ARC_CUBIC_ANGLE )
481 step = FT_ARC_CUBIC_ANGLE;
483 else if ( step < -FT_ARC_CUBIC_ANGLE )
484 step = -FT_ARC_CUBIC_ANGLE;
486 next = angle + step;
487 theta = step;
488 if ( theta < 0 )
489 theta = -theta;
491 theta >>= 1;
493 /* compute end point */
494 FT_Vector_From_Polar( &b, radius, next );
495 b.x += center->x;
496 b.y += center->y;
498 /* compute first and second control points */
499 length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
500 ( 0x10000L + FT_Cos( theta ) ) * 3 );
502 FT_Vector_From_Polar( &a2, length, angle + rotate );
503 a2.x += a.x;
504 a2.y += a.y;
506 FT_Vector_From_Polar( &b2, length, next - rotate );
507 b2.x += b.x;
508 b2.y += b.y;
510 /* add cubic arc */
511 error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
512 if ( error )
513 break;
515 /* process the rest of the arc ?? */
516 a = b;
517 total -= step;
518 angle = next;
521 return error;
525 static FT_Error
526 ft_stroke_border_moveto( FT_StrokeBorder border,
527 FT_Vector* to )
529 /* close current open path if any ? */
530 if ( border->start >= 0 )
531 ft_stroke_border_close( border, FALSE );
533 border->start = border->num_points;
534 border->movable = FALSE;
536 return ft_stroke_border_lineto( border, to, FALSE );
540 static void
541 ft_stroke_border_init( FT_StrokeBorder border,
542 FT_Memory memory )
544 border->memory = memory;
545 border->points = NULL;
546 border->tags = NULL;
548 border->num_points = 0;
549 border->max_points = 0;
550 border->start = -1;
551 border->valid = FALSE;
555 static void
556 ft_stroke_border_reset( FT_StrokeBorder border )
558 border->num_points = 0;
559 border->start = -1;
560 border->valid = FALSE;
564 static void
565 ft_stroke_border_done( FT_StrokeBorder border )
567 FT_Memory memory = border->memory;
570 FT_FREE( border->points );
571 FT_FREE( border->tags );
573 border->num_points = 0;
574 border->max_points = 0;
575 border->start = -1;
576 border->valid = FALSE;
580 static FT_Error
581 ft_stroke_border_get_counts( FT_StrokeBorder border,
582 FT_UInt *anum_points,
583 FT_UInt *anum_contours )
585 FT_Error error = FT_Err_Ok;
586 FT_UInt num_points = 0;
587 FT_UInt num_contours = 0;
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;
595 for ( ; count > 0; count--, num_points++, point++, tags++ )
597 if ( tags[0] & FT_STROKE_TAG_BEGIN )
599 if ( in_contour != 0 )
600 goto Fail;
602 in_contour = 1;
604 else if ( in_contour == 0 )
605 goto Fail;
607 if ( tags[0] & FT_STROKE_TAG_END )
609 in_contour = 0;
610 num_contours++;
614 if ( in_contour != 0 )
615 goto Fail;
617 border->valid = TRUE;
619 Exit:
620 *anum_points = num_points;
621 *anum_contours = num_contours;
622 return error;
624 Fail:
625 num_points = 0;
626 num_contours = 0;
627 goto Exit;
631 static void
632 ft_stroke_border_export( FT_StrokeBorder border,
633 FT_Outline* outline )
635 /* copy point locations */
636 FT_ARRAY_COPY( outline->points + outline->n_points,
637 border->points,
638 border->num_points );
640 /* copy tags */
642 FT_UInt count = border->num_points;
643 FT_Byte* read = border->tags;
644 FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points;
647 for ( ; count > 0; count--, read++, write++ )
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;
658 /* copy contours */
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;
666 for ( ; count > 0; count--, tags++, idx++ )
668 if ( *tags & FT_STROKE_TAG_END )
670 *write++ = idx;
671 outline->n_contours++;
676 outline->n_points = (short)( outline->n_points + border->num_points );
678 FT_ASSERT( FT_Outline_Check( outline ) == 0 );
682 /***************************************************************************/
683 /***************************************************************************/
684 /***** *****/
685 /***** STROKER *****/
686 /***** *****/
687 /***************************************************************************/
688 /***************************************************************************/
690 #define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
692 typedef struct FT_StrokerRec_
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;
702 FT_Stroker_LineCap line_cap;
703 FT_Stroker_LineJoin line_join;
704 FT_Fixed miter_limit;
705 FT_Fixed radius;
707 FT_Bool valid;
708 FT_StrokeBorderRec borders[2];
709 FT_Library library;
711 } FT_StrokerRec;
714 /* documentation is in ftstroke.h */
716 FT_EXPORT_DEF( FT_Error )
717 FT_Stroker_New( FT_Library library,
718 FT_Stroker *astroker )
720 FT_Error error;
721 FT_Memory memory;
722 FT_Stroker stroker;
725 if ( !library )
726 return FT_Err_Invalid_Argument;
728 memory = library->memory;
730 if ( !FT_NEW( stroker ) )
732 stroker->library = library;
734 ft_stroke_border_init( &stroker->borders[0], memory );
735 ft_stroke_border_init( &stroker->borders[1], memory );
737 *astroker = stroker;
738 return error;
742 /* documentation is in ftstroke.h */
744 FT_EXPORT_DEF( void )
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 )
751 stroker->radius = radius;
752 stroker->line_cap = line_cap;
753 stroker->line_join = line_join;
754 stroker->miter_limit = miter_limit;
756 FT_Stroker_Rewind( stroker );
760 /* documentation is in ftstroke.h */
762 FT_EXPORT_DEF( void )
763 FT_Stroker_Rewind( FT_Stroker stroker )
765 if ( stroker )
767 ft_stroke_border_reset( &stroker->borders[0] );
768 ft_stroke_border_reset( &stroker->borders[1] );
773 /* documentation is in ftstroke.h */
775 FT_EXPORT_DEF( void )
776 FT_Stroker_Done( FT_Stroker stroker )
778 if ( stroker )
780 FT_Memory memory = stroker->library->memory;
783 ft_stroke_border_done( &stroker->borders[0] );
784 ft_stroke_border_done( &stroker->borders[1] );
786 stroker->library = NULL;
787 FT_FREE( stroker );
792 /* creates a circular arc at a corner or cap */
793 static FT_Error
794 ft_stroker_arcto( FT_Stroker stroker,
795 FT_Int side )
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;
803 rotate = FT_SIDE_TO_ROTATE( side );
805 total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
806 if ( total == FT_ANGLE_PI )
807 total = -rotate * 2;
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;
819 /* adds a cap at the end of an opened path */
820 static FT_Error
821 ft_stroker_cap( FT_Stroker stroker,
822 FT_Angle angle,
823 FT_Int side )
825 FT_Error error = FT_Err_Ok;
828 if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
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 );
835 else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
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;
844 FT_Vector_From_Polar( &delta2, radius, angle + rotate );
845 FT_Vector_From_Polar( &delta, radius, angle );
847 delta.x += stroker->center.x + delta2.x;
848 delta.y += stroker->center.y + delta2.y;
850 error = ft_stroke_border_lineto( border, &delta, FALSE );
851 if ( error )
852 goto Exit;
854 FT_Vector_From_Polar( &delta2, radius, angle - rotate );
855 FT_Vector_From_Polar( &delta, radius, angle );
857 delta.x += delta2.x + stroker->center.x;
858 delta.y += delta2.y + stroker->center.y;
860 error = ft_stroke_border_lineto( border, &delta, FALSE );
862 else if ( stroker->line_cap == FT_STROKER_LINECAP_BUTT )
864 /* add a butt ending */
865 FT_Vector delta;
866 FT_Angle rotate = FT_SIDE_TO_ROTATE( side );
867 FT_Fixed radius = stroker->radius;
868 FT_StrokeBorder border = stroker->borders + side;
871 FT_Vector_From_Polar( &delta, radius, angle + rotate );
873 delta.x += stroker->center.x;
874 delta.y += stroker->center.y;
876 error = ft_stroke_border_lineto( border, &delta, FALSE );
877 if ( error )
878 goto Exit;
880 FT_Vector_From_Polar( &delta, radius, angle - rotate );
882 delta.x += stroker->center.x;
883 delta.y += stroker->center.y;
885 error = ft_stroke_border_lineto( border, &delta, FALSE );
888 Exit:
889 return error;
893 /* process an inside corner, i.e. compute intersection */
894 static FT_Error
895 ft_stroker_inside( FT_Stroker stroker,
896 FT_Int side)
898 FT_StrokeBorder border = stroker->borders + side;
899 FT_Angle phi, theta, rotate;
900 FT_Fixed length, thcos, sigma;
901 FT_Vector delta;
902 FT_Error error = FT_Err_Ok;
905 rotate = FT_SIDE_TO_ROTATE( side );
907 /* compute median angle */
908 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
909 if ( theta == FT_ANGLE_PI )
910 theta = rotate;
911 else
912 theta = theta / 2;
914 phi = stroker->angle_in + theta;
916 thcos = FT_Cos( theta );
917 sigma = FT_MulFix( stroker->miter_limit, thcos );
919 /* TODO: find better criterion to switch off the optimization */
920 if ( sigma < 0x10000L )
922 FT_Vector_From_Polar( &delta, stroker->radius,
923 stroker->angle_out + rotate );
924 delta.x += stroker->center.x;
925 delta.y += stroker->center.y;
926 border->movable = FALSE;
928 else
930 length = FT_DivFix( stroker->radius, thcos );
932 FT_Vector_From_Polar( &delta, length, phi + rotate );
933 delta.x += stroker->center.x;
934 delta.y += stroker->center.y;
937 error = ft_stroke_border_lineto( border, &delta, FALSE );
939 return error;
943 /* process an outside corner, i.e. compute bevel/miter/round */
944 static FT_Error
945 ft_stroker_outside( FT_Stroker stroker,
946 FT_Int side )
948 FT_StrokeBorder border = stroker->borders + side;
949 FT_Error error;
950 FT_Angle rotate;
953 if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
954 error = ft_stroker_arcto( stroker, side );
955 else
957 /* this is a mitered or beveled corner */
958 FT_Fixed sigma, radius = stroker->radius;
959 FT_Angle theta, phi;
960 FT_Fixed thcos;
961 FT_Bool miter;
964 rotate = FT_SIDE_TO_ROTATE( side );
965 miter = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
967 theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
968 if ( theta == FT_ANGLE_PI )
970 theta = rotate;
971 phi = stroker->angle_in;
973 else
975 theta = theta / 2;
976 phi = stroker->angle_in + theta + rotate;
979 thcos = FT_Cos( theta );
980 sigma = FT_MulFix( stroker->miter_limit, thcos );
982 /* FT_Sin(x) = 0 for x <= 57 */
983 if ( sigma >= 0x10000L || ft_pos_abs( theta ) <= 57 )
984 miter = FALSE;
986 if ( miter ) /* this is a miter (broken angle) */
988 FT_Vector middle, delta;
989 FT_Fixed length;
992 /* compute middle point */
993 FT_Vector_From_Polar( &middle,
994 FT_MulFix( radius, stroker->miter_limit ),
995 phi );
996 middle.x += stroker->center.x;
997 middle.y += stroker->center.y;
999 /* compute first angle point */
1000 length = FT_MulFix( radius,
1001 FT_DivFix( 0x10000L - sigma,
1002 ft_pos_abs( FT_Sin( theta ) ) ) );
1004 FT_Vector_From_Polar( &delta, length, phi + rotate );
1005 delta.x += middle.x;
1006 delta.y += middle.y;
1008 error = ft_stroke_border_lineto( border, &delta, FALSE );
1009 if ( error )
1010 goto Exit;
1012 /* compute second angle point */
1013 FT_Vector_From_Polar( &delta, length, phi - rotate );
1014 delta.x += middle.x;
1015 delta.y += middle.y;
1017 error = ft_stroke_border_lineto( border, &delta, FALSE );
1018 if ( error )
1019 goto Exit;
1021 /* finally, add a movable end point */
1022 FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
1023 delta.x += stroker->center.x;
1024 delta.y += stroker->center.y;
1026 error = ft_stroke_border_lineto( border, &delta, TRUE );
1029 else /* this is a bevel (intersection) */
1031 FT_Fixed length;
1032 FT_Vector delta;
1035 length = FT_DivFix( stroker->radius, thcos );
1037 FT_Vector_From_Polar( &delta, length, phi );
1038 delta.x += stroker->center.x;
1039 delta.y += stroker->center.y;
1041 error = ft_stroke_border_lineto( border, &delta, FALSE );
1042 if ( error )
1043 goto Exit;
1045 /* now add end point */
1046 FT_Vector_From_Polar( &delta, stroker->radius,
1047 stroker->angle_out + rotate );
1048 delta.x += stroker->center.x;
1049 delta.y += stroker->center.y;
1051 error = ft_stroke_border_lineto( border, &delta, TRUE );
1055 Exit:
1056 return error;
1060 static FT_Error
1061 ft_stroker_process_corner( FT_Stroker stroker )
1063 FT_Error error = FT_Err_Ok;
1064 FT_Angle turn;
1065 FT_Int inside_side;
1068 turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
1070 /* no specific corner processing is required if the turn is 0 */
1071 if ( turn == 0 )
1072 goto Exit;
1074 /* when we turn to the right, the inside side is 0 */
1075 inside_side = 0;
1077 /* otherwise, the inside side is 1 */
1078 if ( turn < 0 )
1079 inside_side = 1;
1081 /* process the inside side */
1082 error = ft_stroker_inside( stroker, inside_side );
1083 if ( error )
1084 goto Exit;
1086 /* process the outside side */
1087 error = ft_stroker_outside( stroker, 1 - inside_side );
1089 Exit:
1090 return error;
1094 /* add two points to the left and right borders corresponding to the */
1095 /* start of the subpath */
1096 static FT_Error
1097 ft_stroker_subpath_start( FT_Stroker stroker,
1098 FT_Angle start_angle )
1100 FT_Vector delta;
1101 FT_Vector point;
1102 FT_Error error;
1103 FT_StrokeBorder border;
1106 FT_Vector_From_Polar( &delta, stroker->radius,
1107 start_angle + FT_ANGLE_PI2 );
1109 point.x = stroker->center.x + delta.x;
1110 point.y = stroker->center.y + delta.y;
1112 border = stroker->borders;
1113 error = ft_stroke_border_moveto( border, &point );
1114 if ( error )
1115 goto Exit;
1117 point.x = stroker->center.x - delta.x;
1118 point.y = stroker->center.y - delta.y;
1120 border++;
1121 error = ft_stroke_border_moveto( border, &point );
1123 /* save angle for last cap */
1124 stroker->subpath_angle = start_angle;
1125 stroker->first_point = FALSE;
1127 Exit:
1128 return error;
1132 /* documentation is in ftstroke.h */
1134 FT_EXPORT_DEF( FT_Error )
1135 FT_Stroker_LineTo( FT_Stroker stroker,
1136 FT_Vector* to )
1138 FT_Error error = FT_Err_Ok;
1139 FT_StrokeBorder border;
1140 FT_Vector delta;
1141 FT_Angle angle;
1142 FT_Int side;
1144 delta.x = to->x - stroker->center.x;
1145 delta.y = to->y - stroker->center.y;
1147 angle = FT_Atan2( delta.x, delta.y );
1148 FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
1150 /* process corner if necessary */
1151 if ( stroker->first_point )
1153 /* This is the first segment of a subpath. We need to */
1154 /* add a point to each border at their respective starting */
1155 /* point locations. */
1156 error = ft_stroker_subpath_start( stroker, angle );
1157 if ( error )
1158 goto Exit;
1160 else
1162 /* process the current corner */
1163 stroker->angle_out = angle;
1164 error = ft_stroker_process_corner( stroker );
1165 if ( error )
1166 goto Exit;
1169 /* now add a line segment to both the `inside' and `outside' paths */
1171 for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
1173 FT_Vector point;
1176 point.x = to->x + delta.x;
1177 point.y = to->y + delta.y;
1179 error = ft_stroke_border_lineto( border, &point, TRUE );
1180 if ( error )
1181 goto Exit;
1183 delta.x = -delta.x;
1184 delta.y = -delta.y;
1187 stroker->angle_in = angle;
1188 stroker->center = *to;
1190 Exit:
1191 return error;
1195 /* documentation is in ftstroke.h */
1197 FT_EXPORT_DEF( FT_Error )
1198 FT_Stroker_ConicTo( FT_Stroker stroker,
1199 FT_Vector* control,
1200 FT_Vector* to )
1202 FT_Error error = FT_Err_Ok;
1203 FT_Vector bez_stack[34];
1204 FT_Vector* arc;
1205 FT_Vector* limit = bez_stack + 30;
1206 FT_Angle start_angle;
1207 FT_Bool first_arc = TRUE;
1210 arc = bez_stack;
1211 arc[0] = *to;
1212 arc[1] = *control;
1213 arc[2] = stroker->center;
1215 while ( arc >= bez_stack )
1217 FT_Angle angle_in, angle_out;
1220 angle_in = angle_out = 0; /* remove compiler warnings */
1222 if ( arc < limit &&
1223 !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
1225 ft_conic_split( arc );
1226 arc += 2;
1227 continue;
1230 if ( first_arc )
1232 first_arc = FALSE;
1234 start_angle = angle_in;
1236 /* process corner if necessary */
1237 if ( stroker->first_point )
1238 error = ft_stroker_subpath_start( stroker, start_angle );
1239 else
1241 stroker->angle_out = start_angle;
1242 error = ft_stroker_process_corner( stroker );
1246 /* the arc's angle is small enough; we can add it directly to each */
1247 /* border */
1249 FT_Vector ctrl, end;
1250 FT_Angle theta, phi, rotate;
1251 FT_Fixed length;
1252 FT_Int side;
1255 theta = FT_Angle_Diff( angle_in, angle_out ) / 2;
1256 phi = angle_in + theta;
1257 length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
1259 for ( side = 0; side <= 1; side++ )
1261 rotate = FT_SIDE_TO_ROTATE( side );
1263 /* compute control point */
1264 FT_Vector_From_Polar( &ctrl, length, phi + rotate );
1265 ctrl.x += arc[1].x;
1266 ctrl.y += arc[1].y;
1268 /* compute end point */
1269 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1270 end.x += arc[0].x;
1271 end.y += arc[0].y;
1273 error = ft_stroke_border_conicto( stroker->borders + side,
1274 &ctrl, &end );
1275 if ( error )
1276 goto Exit;
1280 arc -= 2;
1282 if ( arc < bez_stack )
1283 stroker->angle_in = angle_out;
1286 stroker->center = *to;
1288 Exit:
1289 return error;
1293 /* documentation is in ftstroke.h */
1295 FT_EXPORT_DEF( FT_Error )
1296 FT_Stroker_CubicTo( FT_Stroker stroker,
1297 FT_Vector* control1,
1298 FT_Vector* control2,
1299 FT_Vector* to )
1301 FT_Error error = FT_Err_Ok;
1302 FT_Vector bez_stack[37];
1303 FT_Vector* arc;
1304 FT_Vector* limit = bez_stack + 32;
1305 FT_Angle start_angle;
1306 FT_Bool first_arc = TRUE;
1309 arc = bez_stack;
1310 arc[0] = *to;
1311 arc[1] = *control2;
1312 arc[2] = *control1;
1313 arc[3] = stroker->center;
1315 while ( arc >= bez_stack )
1317 FT_Angle angle_in, angle_mid, angle_out;
1320 /* remove compiler warnings */
1321 angle_in = angle_out = angle_mid = 0;
1323 if ( arc < limit &&
1324 !ft_cubic_is_small_enough( arc, &angle_in,
1325 &angle_mid, &angle_out ) )
1327 ft_cubic_split( arc );
1328 arc += 3;
1329 continue;
1332 if ( first_arc )
1334 first_arc = FALSE;
1336 /* process corner if necessary */
1337 start_angle = angle_in;
1339 if ( stroker->first_point )
1340 error = ft_stroker_subpath_start( stroker, start_angle );
1341 else
1343 stroker->angle_out = start_angle;
1344 error = ft_stroker_process_corner( stroker );
1346 if ( error )
1347 goto Exit;
1350 /* the arc's angle is small enough; we can add it directly to each */
1351 /* border */
1353 FT_Vector ctrl1, ctrl2, end;
1354 FT_Angle theta1, phi1, theta2, phi2, rotate;
1355 FT_Fixed length1, length2;
1356 FT_Int side;
1359 theta1 = ft_pos_abs( angle_mid - angle_in ) / 2;
1360 theta2 = ft_pos_abs( angle_out - angle_mid ) / 2;
1361 phi1 = (angle_mid + angle_in ) / 2;
1362 phi2 = (angle_mid + angle_out ) / 2;
1363 length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
1364 length2 = FT_DivFix( stroker->radius, FT_Cos( theta2 ) );
1366 for ( side = 0; side <= 1; side++ )
1368 rotate = FT_SIDE_TO_ROTATE( side );
1370 /* compute control points */
1371 FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
1372 ctrl1.x += arc[2].x;
1373 ctrl1.y += arc[2].y;
1375 FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
1376 ctrl2.x += arc[1].x;
1377 ctrl2.y += arc[1].y;
1379 /* compute end point */
1380 FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
1381 end.x += arc[0].x;
1382 end.y += arc[0].y;
1384 error = ft_stroke_border_cubicto( stroker->borders + side,
1385 &ctrl1, &ctrl2, &end );
1386 if ( error )
1387 goto Exit;
1391 arc -= 3;
1392 if ( arc < bez_stack )
1393 stroker->angle_in = angle_out;
1396 stroker->center = *to;
1398 Exit:
1399 return error;
1403 /* documentation is in ftstroke.h */
1405 FT_EXPORT_DEF( FT_Error )
1406 FT_Stroker_BeginSubPath( FT_Stroker stroker,
1407 FT_Vector* to,
1408 FT_Bool open )
1410 /* We cannot process the first point, because there is not enough */
1411 /* information regarding its corner/cap. The latter will be processed */
1412 /* in the `FT_Stroker_EndSubPath' routine. */
1413 /* */
1414 stroker->first_point = TRUE;
1415 stroker->center = *to;
1416 stroker->subpath_open = open;
1418 /* record the subpath start point for each border */
1419 stroker->subpath_start = *to;
1421 return FT_Err_Ok;
1425 static FT_Error
1426 ft_stroker_add_reverse_left( FT_Stroker stroker,
1427 FT_Bool open )
1429 FT_StrokeBorder right = stroker->borders + 0;
1430 FT_StrokeBorder left = stroker->borders + 1;
1431 FT_Int new_points;
1432 FT_Error error = FT_Err_Ok;
1435 FT_ASSERT( left->start >= 0 );
1437 new_points = left->num_points - left->start;
1438 if ( new_points > 0 )
1440 error = ft_stroke_border_grow( right, (FT_UInt)new_points );
1441 if ( error )
1442 goto Exit;
1445 FT_Vector* dst_point = right->points + right->num_points;
1446 FT_Byte* dst_tag = right->tags + right->num_points;
1447 FT_Vector* src_point = left->points + left->num_points - 1;
1448 FT_Byte* src_tag = left->tags + left->num_points - 1;
1450 while ( src_point >= left->points + left->start )
1452 *dst_point = *src_point;
1453 *dst_tag = *src_tag;
1455 if ( open )
1456 dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
1457 else
1459 FT_Byte ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
1462 /* switch begin/end tags if necessary */
1463 if ( ttag == FT_STROKE_TAG_BEGIN ||
1464 ttag == FT_STROKE_TAG_END )
1465 dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
1469 src_point--;
1470 src_tag--;
1471 dst_point++;
1472 dst_tag++;
1476 left->num_points = left->start;
1477 right->num_points += new_points;
1479 right->movable = FALSE;
1480 left->movable = FALSE;
1483 Exit:
1484 return error;
1488 /* documentation is in ftstroke.h */
1490 /* there's a lot of magic in this function! */
1491 FT_EXPORT_DEF( FT_Error )
1492 FT_Stroker_EndSubPath( FT_Stroker stroker )
1494 FT_Error error = FT_Err_Ok;
1497 if ( stroker->subpath_open )
1499 FT_StrokeBorder right = stroker->borders;
1501 /* All right, this is an opened path, we need to add a cap between */
1502 /* right & left, add the reverse of left, then add a final cap */
1503 /* between left & right. */
1504 error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
1505 if ( error )
1506 goto Exit;
1508 /* add reversed points from `left' to `right' */
1509 error = ft_stroker_add_reverse_left( stroker, TRUE );
1510 if ( error )
1511 goto Exit;
1513 /* now add the final cap */
1514 stroker->center = stroker->subpath_start;
1515 error = ft_stroker_cap( stroker,
1516 stroker->subpath_angle + FT_ANGLE_PI, 0 );
1517 if ( error )
1518 goto Exit;
1520 /* Now end the right subpath accordingly. The left one is */
1521 /* rewind and doesn't need further processing. */
1522 ft_stroke_border_close( right, FALSE );
1524 else
1526 FT_Angle turn;
1527 FT_Int inside_side;
1529 /* close the path if needed */
1530 if ( stroker->center.x != stroker->subpath_start.x ||
1531 stroker->center.y != stroker->subpath_start.y )
1533 error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
1534 if ( error )
1535 goto Exit;
1538 /* process the corner */
1539 stroker->angle_out = stroker->subpath_angle;
1540 turn = FT_Angle_Diff( stroker->angle_in,
1541 stroker->angle_out );
1543 /* no specific corner processing is required if the turn is 0 */
1544 if ( turn != 0 )
1546 /* when we turn to the right, the inside side is 0 */
1547 inside_side = 0;
1549 /* otherwise, the inside side is 1 */
1550 if ( turn < 0 )
1551 inside_side = 1;
1553 error = ft_stroker_inside( stroker, inside_side );
1554 if ( error )
1555 goto Exit;
1557 /* process the outside side */
1558 error = ft_stroker_outside( stroker, 1 - inside_side );
1559 if ( error )
1560 goto Exit;
1563 /* then end our two subpaths */
1564 ft_stroke_border_close( stroker->borders + 0, TRUE );
1565 ft_stroke_border_close( stroker->borders + 1, FALSE );
1568 Exit:
1569 return error;
1573 /* documentation is in ftstroke.h */
1575 FT_EXPORT_DEF( FT_Error )
1576 FT_Stroker_GetBorderCounts( FT_Stroker stroker,
1577 FT_StrokerBorder border,
1578 FT_UInt *anum_points,
1579 FT_UInt *anum_contours )
1581 FT_UInt num_points = 0, num_contours = 0;
1582 FT_Error error;
1585 if ( !stroker || border > 1 )
1587 error = FT_Err_Invalid_Argument;
1588 goto Exit;
1591 error = ft_stroke_border_get_counts( stroker->borders + border,
1592 &num_points, &num_contours );
1593 Exit:
1594 if ( anum_points )
1595 *anum_points = num_points;
1597 if ( anum_contours )
1598 *anum_contours = num_contours;
1600 return error;
1604 /* documentation is in ftstroke.h */
1606 FT_EXPORT_DEF( FT_Error )
1607 FT_Stroker_GetCounts( FT_Stroker stroker,
1608 FT_UInt *anum_points,
1609 FT_UInt *anum_contours )
1611 FT_UInt count1, count2, num_points = 0;
1612 FT_UInt count3, count4, num_contours = 0;
1613 FT_Error error;
1616 error = ft_stroke_border_get_counts( stroker->borders + 0,
1617 &count1, &count2 );
1618 if ( error )
1619 goto Exit;
1621 error = ft_stroke_border_get_counts( stroker->borders + 1,
1622 &count3, &count4 );
1623 if ( error )
1624 goto Exit;
1626 num_points = count1 + count3;
1627 num_contours = count2 + count4;
1629 Exit:
1630 *anum_points = num_points;
1631 *anum_contours = num_contours;
1632 return error;
1636 /* documentation is in ftstroke.h */
1638 FT_EXPORT_DEF( void )
1639 FT_Stroker_ExportBorder( FT_Stroker stroker,
1640 FT_StrokerBorder border,
1641 FT_Outline* outline )
1643 if ( border == FT_STROKER_BORDER_LEFT ||
1644 border == FT_STROKER_BORDER_RIGHT )
1646 FT_StrokeBorder sborder = & stroker->borders[border];
1649 if ( sborder->valid )
1650 ft_stroke_border_export( sborder, outline );
1655 /* documentation is in ftstroke.h */
1657 FT_EXPORT_DEF( void )
1658 FT_Stroker_Export( FT_Stroker stroker,
1659 FT_Outline* outline )
1661 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
1662 FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
1666 /* documentation is in ftstroke.h */
1669 * The following is very similar to FT_Outline_Decompose, except
1670 * that we do support opened paths, and do not scale the outline.
1672 FT_EXPORT_DEF( FT_Error )
1673 FT_Stroker_ParseOutline( FT_Stroker stroker,
1674 FT_Outline* outline,
1675 FT_Bool opened )
1677 FT_Vector v_last;
1678 FT_Vector v_control;
1679 FT_Vector v_start;
1681 FT_Vector* point;
1682 FT_Vector* limit;
1683 char* tags;
1685 FT_Error error;
1687 FT_Int n; /* index of contour in outline */
1688 FT_UInt first; /* index of first point in contour */
1689 FT_Int tag; /* current point's state */
1692 if ( !outline || !stroker )
1693 return FT_Err_Invalid_Argument;
1695 FT_Stroker_Rewind( stroker );
1697 first = 0;
1699 for ( n = 0; n < outline->n_contours; n++ )
1701 FT_UInt last; /* index of last point in contour */
1704 last = outline->contours[n];
1705 limit = outline->points + last;
1707 /* skip empty points; we don't stroke these */
1708 if ( last <= first )
1710 first = last + 1;
1711 continue;
1714 v_start = outline->points[first];
1715 v_last = outline->points[last];
1717 v_control = v_start;
1719 point = outline->points + first;
1720 tags = outline->tags + first;
1721 tag = FT_CURVE_TAG( tags[0] );
1723 /* A contour cannot start with a cubic control point! */
1724 if ( tag == FT_CURVE_TAG_CUBIC )
1725 goto Invalid_Outline;
1727 /* check first point to determine origin */
1728 if ( tag == FT_CURVE_TAG_CONIC )
1730 /* First point is conic control. Yes, this happens. */
1731 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
1733 /* start at last point if it is on the curve */
1734 v_start = v_last;
1735 limit--;
1737 else
1739 /* if both first and last points are conic, */
1740 /* start at their middle */
1741 v_start.x = ( v_start.x + v_last.x ) / 2;
1742 v_start.y = ( v_start.y + v_last.y ) / 2;
1744 point--;
1745 tags--;
1748 error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
1749 if ( error )
1750 goto Exit;
1752 while ( point < limit )
1754 point++;
1755 tags++;
1757 tag = FT_CURVE_TAG( tags[0] );
1758 switch ( tag )
1760 case FT_CURVE_TAG_ON: /* emit a single line_to */
1762 FT_Vector vec;
1765 vec.x = point->x;
1766 vec.y = point->y;
1768 error = FT_Stroker_LineTo( stroker, &vec );
1769 if ( error )
1770 goto Exit;
1771 continue;
1774 case FT_CURVE_TAG_CONIC: /* consume conic arcs */
1775 v_control.x = point->x;
1776 v_control.y = point->y;
1778 Do_Conic:
1779 if ( point < limit )
1781 FT_Vector vec;
1782 FT_Vector v_middle;
1785 point++;
1786 tags++;
1787 tag = FT_CURVE_TAG( tags[0] );
1789 vec = point[0];
1791 if ( tag == FT_CURVE_TAG_ON )
1793 error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
1794 if ( error )
1795 goto Exit;
1796 continue;
1799 if ( tag != FT_CURVE_TAG_CONIC )
1800 goto Invalid_Outline;
1802 v_middle.x = ( v_control.x + vec.x ) / 2;
1803 v_middle.y = ( v_control.y + vec.y ) / 2;
1805 error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
1806 if ( error )
1807 goto Exit;
1809 v_control = vec;
1810 goto Do_Conic;
1813 error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
1814 goto Close;
1816 default: /* FT_CURVE_TAG_CUBIC */
1818 FT_Vector vec1, vec2;
1821 if ( point + 1 > limit ||
1822 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
1823 goto Invalid_Outline;
1825 point += 2;
1826 tags += 2;
1828 vec1 = point[-2];
1829 vec2 = point[-1];
1831 if ( point <= limit )
1833 FT_Vector vec;
1836 vec = point[0];
1838 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
1839 if ( error )
1840 goto Exit;
1841 continue;
1844 error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
1845 goto Close;
1850 Close:
1851 if ( error )
1852 goto Exit;
1854 error = FT_Stroker_EndSubPath( stroker );
1855 if ( error )
1856 goto Exit;
1858 first = last + 1;
1861 return FT_Err_Ok;
1863 Exit:
1864 return error;
1866 Invalid_Outline:
1867 return FT_Err_Invalid_Outline;
1870 /* declare an extern to access ft_outline_glyph_class global allocated
1871 in ftglyph.c, and use the FT_OUTLINE_GLYPH_CLASS_GET macro to access
1872 it when FT_CONFIG_OPTION_PIC is defined */
1873 #ifndef FT_CONFIG_OPTION_PIC
1874 extern const FT_Glyph_Class ft_outline_glyph_class;
1875 #endif
1876 #include "basepic.h"
1879 /* documentation is in ftstroke.h */
1881 FT_EXPORT_DEF( FT_Error )
1882 FT_Glyph_Stroke( FT_Glyph *pglyph,
1883 FT_Stroker stroker,
1884 FT_Bool destroy )
1886 FT_Error error = FT_Err_Invalid_Argument;
1887 FT_Glyph glyph = NULL;
1888 FT_Library library = stroker->library;
1889 FT_UNUSED(library);
1891 if ( pglyph == NULL )
1892 goto Exit;
1894 glyph = *pglyph;
1895 if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
1896 goto Exit;
1899 FT_Glyph copy;
1902 error = FT_Glyph_Copy( glyph, &copy );
1903 if ( error )
1904 goto Exit;
1906 glyph = copy;
1910 FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph;
1911 FT_Outline* outline = &oglyph->outline;
1912 FT_UInt num_points, num_contours;
1915 error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
1916 if ( error )
1917 goto Fail;
1919 (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
1921 FT_Outline_Done( glyph->library, outline );
1923 error = FT_Outline_New( glyph->library,
1924 num_points, num_contours, outline );
1925 if ( error )
1926 goto Fail;
1928 outline->n_points = 0;
1929 outline->n_contours = 0;
1931 FT_Stroker_Export( stroker, outline );
1934 if ( destroy )
1935 FT_Done_Glyph( *pglyph );
1937 *pglyph = glyph;
1938 goto Exit;
1940 Fail:
1941 FT_Done_Glyph( glyph );
1942 glyph = NULL;
1944 if ( !destroy )
1945 *pglyph = NULL;
1947 Exit:
1948 return error;
1952 /* documentation is in ftstroke.h */
1954 FT_EXPORT_DEF( FT_Error )
1955 FT_Glyph_StrokeBorder( FT_Glyph *pglyph,
1956 FT_Stroker stroker,
1957 FT_Bool inside,
1958 FT_Bool destroy )
1960 FT_Error error = FT_Err_Invalid_Argument;
1961 FT_Glyph glyph = NULL;
1962 FT_Library library = stroker->library;
1963 FT_UNUSED(library);
1965 if ( pglyph == NULL )
1966 goto Exit;
1968 glyph = *pglyph;
1969 if ( glyph == NULL || glyph->clazz != FT_OUTLINE_GLYPH_CLASS_GET )
1970 goto Exit;
1973 FT_Glyph copy;
1976 error = FT_Glyph_Copy( glyph, &copy );
1977 if ( error )
1978 goto Exit;
1980 glyph = copy;
1984 FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph;
1985 FT_StrokerBorder border;
1986 FT_Outline* outline = &oglyph->outline;
1987 FT_UInt num_points, num_contours;
1990 border = FT_Outline_GetOutsideBorder( outline );
1991 if ( inside )
1993 if ( border == FT_STROKER_BORDER_LEFT )
1994 border = FT_STROKER_BORDER_RIGHT;
1995 else
1996 border = FT_STROKER_BORDER_LEFT;
1999 error = FT_Stroker_ParseOutline( stroker, outline, FALSE );
2000 if ( error )
2001 goto Fail;
2003 (void)FT_Stroker_GetBorderCounts( stroker, border,
2004 &num_points, &num_contours );
2006 FT_Outline_Done( glyph->library, outline );
2008 error = FT_Outline_New( glyph->library,
2009 num_points,
2010 num_contours,
2011 outline );
2012 if ( error )
2013 goto Fail;
2015 outline->n_points = 0;
2016 outline->n_contours = 0;
2018 FT_Stroker_ExportBorder( stroker, border, outline );
2021 if ( destroy )
2022 FT_Done_Glyph( *pglyph );
2024 *pglyph = glyph;
2025 goto Exit;
2027 Fail:
2028 FT_Done_Glyph( glyph );
2029 glyph = NULL;
2031 if ( !destroy )
2032 *pglyph = NULL;
2034 Exit:
2035 return error;
2039 /* END */