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