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