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