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