Sync with trunk r47367
[reactos.git] / lib / 3rdparty / freetype / src / base / ftoutln.c
1 /***************************************************************************/
2 /* */
3 /* ftoutln.c */
4 /* */
5 /* FreeType outline management (body). */
6 /* */
7 /* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 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 /*************************************************************************/
20 /* */
21 /* All functions are declared in freetype.h. */
22 /* */
23 /*************************************************************************/
24
25
26 #include <ft2build.h>
27 #include FT_OUTLINE_H
28 #include FT_INTERNAL_OBJECTS_H
29 #include FT_INTERNAL_DEBUG_H
30 #include FT_TRIGONOMETRY_H
31
32
33 /*************************************************************************/
34 /* */
35 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
36 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
37 /* messages during execution. */
38 /* */
39 #undef FT_COMPONENT
40 #define FT_COMPONENT trace_outline
41
42
43 static
44 const FT_Outline null_outline = { 0, 0, 0, 0, 0, 0 };
45
46
47 /* documentation is in ftoutln.h */
48
49 FT_EXPORT_DEF( FT_Error )
50 FT_Outline_Decompose( FT_Outline* outline,
51 const FT_Outline_Funcs* func_interface,
52 void* user )
53 {
54 #undef SCALED
55 #define SCALED( x ) ( ( (x) << shift ) - delta )
56
57 FT_Vector v_last;
58 FT_Vector v_control;
59 FT_Vector v_start;
60
61 FT_Vector* point;
62 FT_Vector* limit;
63 char* tags;
64
65 FT_Error error;
66
67 FT_Int n; /* index of contour in outline */
68 FT_UInt first; /* index of first point in contour */
69 FT_Int tag; /* current point's state */
70
71 FT_Int shift;
72 FT_Pos delta;
73
74
75 if ( !outline || !func_interface )
76 return FT_Err_Invalid_Argument;
77
78 shift = func_interface->shift;
79 delta = func_interface->delta;
80 first = 0;
81
82 for ( n = 0; n < outline->n_contours; n++ )
83 {
84 FT_Int last; /* index of last point in contour */
85
86
87 FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n ));
88
89 last = outline->contours[n];
90 if ( last < 0 )
91 goto Invalid_Outline;
92 limit = outline->points + last;
93
94 v_start = outline->points[first];
95 v_start.x = SCALED( v_start.x );
96 v_start.y = SCALED( v_start.y );
97
98 v_last = outline->points[last];
99 v_last.x = SCALED( v_last.x );
100 v_last.y = SCALED( v_last.y );
101
102 v_control = v_start;
103
104 point = outline->points + first;
105 tags = outline->tags + first;
106 tag = FT_CURVE_TAG( tags[0] );
107
108 /* A contour cannot start with a cubic control point! */
109 if ( tag == FT_CURVE_TAG_CUBIC )
110 goto Invalid_Outline;
111
112 /* check first point to determine origin */
113 if ( tag == FT_CURVE_TAG_CONIC )
114 {
115 /* first point is conic control. Yes, this happens. */
116 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
117 {
118 /* start at last point if it is on the curve */
119 v_start = v_last;
120 limit--;
121 }
122 else
123 {
124 /* if both first and last points are conic, */
125 /* start at their middle and record its position */
126 /* for closure */
127 v_start.x = ( v_start.x + v_last.x ) / 2;
128 v_start.y = ( v_start.y + v_last.y ) / 2;
129
130 v_last = v_start;
131 }
132 point--;
133 tags--;
134 }
135
136 FT_TRACE5(( " move to (%.2f, %.2f)\n",
137 v_start.x / 64.0, v_start.y / 64.0 ));
138 error = func_interface->move_to( &v_start, user );
139 if ( error )
140 goto Exit;
141
142 while ( point < limit )
143 {
144 point++;
145 tags++;
146
147 tag = FT_CURVE_TAG( tags[0] );
148 switch ( tag )
149 {
150 case FT_CURVE_TAG_ON: /* emit a single line_to */
151 {
152 FT_Vector vec;
153
154
155 vec.x = SCALED( point->x );
156 vec.y = SCALED( point->y );
157
158 FT_TRACE5(( " line to (%.2f, %.2f)\n",
159 vec.x / 64.0, vec.y / 64.0 ));
160 error = func_interface->line_to( &vec, user );
161 if ( error )
162 goto Exit;
163 continue;
164 }
165
166 case FT_CURVE_TAG_CONIC: /* consume conic arcs */
167 v_control.x = SCALED( point->x );
168 v_control.y = SCALED( point->y );
169
170 Do_Conic:
171 if ( point < limit )
172 {
173 FT_Vector vec;
174 FT_Vector v_middle;
175
176
177 point++;
178 tags++;
179 tag = FT_CURVE_TAG( tags[0] );
180
181 vec.x = SCALED( point->x );
182 vec.y = SCALED( point->y );
183
184 if ( tag == FT_CURVE_TAG_ON )
185 {
186 FT_TRACE5(( " conic to (%.2f, %.2f)"
187 " with control (%.2f, %.2f)\n",
188 vec.x / 64.0, vec.y / 64.0,
189 v_control.x / 64.0, v_control.y / 64.0 ));
190 error = func_interface->conic_to( &v_control, &vec, user );
191 if ( error )
192 goto Exit;
193 continue;
194 }
195
196 if ( tag != FT_CURVE_TAG_CONIC )
197 goto Invalid_Outline;
198
199 v_middle.x = ( v_control.x + vec.x ) / 2;
200 v_middle.y = ( v_control.y + vec.y ) / 2;
201
202 FT_TRACE5(( " conic to (%.2f, %.2f)"
203 " with control (%.2f, %.2f)\n",
204 v_middle.x / 64.0, v_middle.y / 64.0,
205 v_control.x / 64.0, v_control.y / 64.0 ));
206 error = func_interface->conic_to( &v_control, &v_middle, user );
207 if ( error )
208 goto Exit;
209
210 v_control = vec;
211 goto Do_Conic;
212 }
213
214 FT_TRACE5(( " conic to (%.2f, %.2f)"
215 " with control (%.2f, %.2f)\n",
216 v_start.x / 64.0, v_start.y / 64.0,
217 v_control.x / 64.0, v_control.y / 64.0 ));
218 error = func_interface->conic_to( &v_control, &v_start, user );
219 goto Close;
220
221 default: /* FT_CURVE_TAG_CUBIC */
222 {
223 FT_Vector vec1, vec2;
224
225
226 if ( point + 1 > limit ||
227 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
228 goto Invalid_Outline;
229
230 point += 2;
231 tags += 2;
232
233 vec1.x = SCALED( point[-2].x );
234 vec1.y = SCALED( point[-2].y );
235
236 vec2.x = SCALED( point[-1].x );
237 vec2.y = SCALED( point[-1].y );
238
239 if ( point <= limit )
240 {
241 FT_Vector vec;
242
243
244 vec.x = SCALED( point->x );
245 vec.y = SCALED( point->y );
246
247 FT_TRACE5(( " cubic to (%.2f, %.2f)"
248 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
249 vec.x / 64.0, vec.y / 64.0,
250 vec1.x / 64.0, vec1.y / 64.0,
251 vec2.x / 64.0, vec2.y / 64.0 ));
252 error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
253 if ( error )
254 goto Exit;
255 continue;
256 }
257
258 FT_TRACE5(( " cubic to (%.2f, %.2f)"
259 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
260 v_start.x / 64.0, v_start.y / 64.0,
261 vec1.x / 64.0, vec1.y / 64.0,
262 vec2.x / 64.0, vec2.y / 64.0 ));
263 error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
264 goto Close;
265 }
266 }
267 }
268
269 /* close the contour with a line segment */
270 FT_TRACE5(( " line to (%.2f, %.2f)\n",
271 v_start.x / 64.0, v_start.y / 64.0 ));
272 error = func_interface->line_to( &v_start, user );
273
274 Close:
275 if ( error )
276 goto Exit;
277
278 first = last + 1;
279 }
280
281 FT_TRACE5(( "FT_Outline_Decompose: Done\n", n ));
282 return FT_Err_Ok;
283
284 Exit:
285 FT_TRACE5(( "FT_Outline_Decompose: Error %d\n", error ));
286 return error;
287
288 Invalid_Outline:
289 return FT_Err_Invalid_Outline;
290 }
291
292
293 FT_EXPORT_DEF( FT_Error )
294 FT_Outline_New_Internal( FT_Memory memory,
295 FT_UInt numPoints,
296 FT_Int numContours,
297 FT_Outline *anoutline )
298 {
299 FT_Error error;
300
301
302 if ( !anoutline || !memory )
303 return FT_Err_Invalid_Argument;
304
305 *anoutline = null_outline;
306
307 if ( FT_NEW_ARRAY( anoutline->points, numPoints ) ||
308 FT_NEW_ARRAY( anoutline->tags, numPoints ) ||
309 FT_NEW_ARRAY( anoutline->contours, numContours ) )
310 goto Fail;
311
312 anoutline->n_points = (FT_UShort)numPoints;
313 anoutline->n_contours = (FT_Short)numContours;
314 anoutline->flags |= FT_OUTLINE_OWNER;
315
316 return FT_Err_Ok;
317
318 Fail:
319 anoutline->flags |= FT_OUTLINE_OWNER;
320 FT_Outline_Done_Internal( memory, anoutline );
321
322 return error;
323 }
324
325
326 /* documentation is in ftoutln.h */
327
328 FT_EXPORT_DEF( FT_Error )
329 FT_Outline_New( FT_Library library,
330 FT_UInt numPoints,
331 FT_Int numContours,
332 FT_Outline *anoutline )
333 {
334 if ( !library )
335 return FT_Err_Invalid_Library_Handle;
336
337 return FT_Outline_New_Internal( library->memory, numPoints,
338 numContours, anoutline );
339 }
340
341
342 /* documentation is in ftoutln.h */
343
344 FT_EXPORT_DEF( FT_Error )
345 FT_Outline_Check( FT_Outline* outline )
346 {
347 if ( outline )
348 {
349 FT_Int n_points = outline->n_points;
350 FT_Int n_contours = outline->n_contours;
351 FT_Int end0, end;
352 FT_Int n;
353
354
355 /* empty glyph? */
356 if ( n_points == 0 && n_contours == 0 )
357 return 0;
358
359 /* check point and contour counts */
360 if ( n_points <= 0 || n_contours <= 0 )
361 goto Bad;
362
363 end0 = end = -1;
364 for ( n = 0; n < n_contours; n++ )
365 {
366 end = outline->contours[n];
367
368 /* note that we don't accept empty contours */
369 if ( end <= end0 || end >= n_points )
370 goto Bad;
371
372 end0 = end;
373 }
374
375 if ( end != n_points - 1 )
376 goto Bad;
377
378 /* XXX: check the tags array */
379 return 0;
380 }
381
382 Bad:
383 return FT_Err_Invalid_Argument;
384 }
385
386
387 /* documentation is in ftoutln.h */
388
389 FT_EXPORT_DEF( FT_Error )
390 FT_Outline_Copy( const FT_Outline* source,
391 FT_Outline *target )
392 {
393 FT_Int is_owner;
394
395
396 if ( !source || !target ||
397 source->n_points != target->n_points ||
398 source->n_contours != target->n_contours )
399 return FT_Err_Invalid_Argument;
400
401 if ( source == target )
402 return FT_Err_Ok;
403
404 FT_ARRAY_COPY( target->points, source->points, source->n_points );
405
406 FT_ARRAY_COPY( target->tags, source->tags, source->n_points );
407
408 FT_ARRAY_COPY( target->contours, source->contours, source->n_contours );
409
410 /* copy all flags, except the `FT_OUTLINE_OWNER' one */
411 is_owner = target->flags & FT_OUTLINE_OWNER;
412 target->flags = source->flags;
413
414 target->flags &= ~FT_OUTLINE_OWNER;
415 target->flags |= is_owner;
416
417 return FT_Err_Ok;
418 }
419
420
421 FT_EXPORT_DEF( FT_Error )
422 FT_Outline_Done_Internal( FT_Memory memory,
423 FT_Outline* outline )
424 {
425 if ( memory && outline )
426 {
427 if ( outline->flags & FT_OUTLINE_OWNER )
428 {
429 FT_FREE( outline->points );
430 FT_FREE( outline->tags );
431 FT_FREE( outline->contours );
432 }
433 *outline = null_outline;
434
435 return FT_Err_Ok;
436 }
437 else
438 return FT_Err_Invalid_Argument;
439 }
440
441
442 /* documentation is in ftoutln.h */
443
444 FT_EXPORT_DEF( FT_Error )
445 FT_Outline_Done( FT_Library library,
446 FT_Outline* outline )
447 {
448 /* check for valid `outline' in FT_Outline_Done_Internal() */
449
450 if ( !library )
451 return FT_Err_Invalid_Library_Handle;
452
453 return FT_Outline_Done_Internal( library->memory, outline );
454 }
455
456
457 /* documentation is in ftoutln.h */
458
459 FT_EXPORT_DEF( void )
460 FT_Outline_Get_CBox( const FT_Outline* outline,
461 FT_BBox *acbox )
462 {
463 FT_Pos xMin, yMin, xMax, yMax;
464
465
466 if ( outline && acbox )
467 {
468 if ( outline->n_points == 0 )
469 {
470 xMin = 0;
471 yMin = 0;
472 xMax = 0;
473 yMax = 0;
474 }
475 else
476 {
477 FT_Vector* vec = outline->points;
478 FT_Vector* limit = vec + outline->n_points;
479
480
481 xMin = xMax = vec->x;
482 yMin = yMax = vec->y;
483 vec++;
484
485 for ( ; vec < limit; vec++ )
486 {
487 FT_Pos x, y;
488
489
490 x = vec->x;
491 if ( x < xMin ) xMin = x;
492 if ( x > xMax ) xMax = x;
493
494 y = vec->y;
495 if ( y < yMin ) yMin = y;
496 if ( y > yMax ) yMax = y;
497 }
498 }
499 acbox->xMin = xMin;
500 acbox->xMax = xMax;
501 acbox->yMin = yMin;
502 acbox->yMax = yMax;
503 }
504 }
505
506
507 /* documentation is in ftoutln.h */
508
509 FT_EXPORT_DEF( void )
510 FT_Outline_Translate( const FT_Outline* outline,
511 FT_Pos xOffset,
512 FT_Pos yOffset )
513 {
514 FT_UShort n;
515 FT_Vector* vec;
516
517
518 if ( !outline )
519 return;
520
521 vec = outline->points;
522
523 for ( n = 0; n < outline->n_points; n++ )
524 {
525 vec->x += xOffset;
526 vec->y += yOffset;
527 vec++;
528 }
529 }
530
531
532 /* documentation is in ftoutln.h */
533
534 FT_EXPORT_DEF( void )
535 FT_Outline_Reverse( FT_Outline* outline )
536 {
537 FT_UShort n;
538 FT_Int first, last;
539
540
541 if ( !outline )
542 return;
543
544 first = 0;
545
546 for ( n = 0; n < outline->n_contours; n++ )
547 {
548 last = outline->contours[n];
549
550 /* reverse point table */
551 {
552 FT_Vector* p = outline->points + first;
553 FT_Vector* q = outline->points + last;
554 FT_Vector swap;
555
556
557 while ( p < q )
558 {
559 swap = *p;
560 *p = *q;
561 *q = swap;
562 p++;
563 q--;
564 }
565 }
566
567 /* reverse tags table */
568 {
569 char* p = outline->tags + first;
570 char* q = outline->tags + last;
571 char swap;
572
573
574 while ( p < q )
575 {
576 swap = *p;
577 *p = *q;
578 *q = swap;
579 p++;
580 q--;
581 }
582 }
583
584 first = last + 1;
585 }
586
587 outline->flags ^= FT_OUTLINE_REVERSE_FILL;
588 }
589
590
591 /* documentation is in ftoutln.h */
592
593 FT_EXPORT_DEF( FT_Error )
594 FT_Outline_Render( FT_Library library,
595 FT_Outline* outline,
596 FT_Raster_Params* params )
597 {
598 FT_Error error;
599 FT_Bool update = FALSE;
600 FT_Renderer renderer;
601 FT_ListNode node;
602
603
604 if ( !library )
605 return FT_Err_Invalid_Library_Handle;
606
607 if ( !outline || !params )
608 return FT_Err_Invalid_Argument;
609
610 renderer = library->cur_renderer;
611 node = library->renderers.head;
612
613 params->source = (void*)outline;
614
615 error = FT_Err_Cannot_Render_Glyph;
616 while ( renderer )
617 {
618 error = renderer->raster_render( renderer->raster, params );
619 if ( !error || FT_ERROR_BASE( error ) != FT_Err_Cannot_Render_Glyph )
620 break;
621
622 /* FT_Err_Cannot_Render_Glyph is returned if the render mode */
623 /* is unsupported by the current renderer for this glyph image */
624 /* format */
625
626 /* now, look for another renderer that supports the same */
627 /* format */
628 renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE,
629 &node );
630 update = TRUE;
631 }
632
633 /* if we changed the current renderer for the glyph image format */
634 /* we need to select it as the next current one */
635 if ( !error && update && renderer )
636 FT_Set_Renderer( library, renderer, 0, 0 );
637
638 return error;
639 }
640
641
642 /* documentation is in ftoutln.h */
643
644 FT_EXPORT_DEF( FT_Error )
645 FT_Outline_Get_Bitmap( FT_Library library,
646 FT_Outline* outline,
647 const FT_Bitmap *abitmap )
648 {
649 FT_Raster_Params params;
650
651
652 if ( !abitmap )
653 return FT_Err_Invalid_Argument;
654
655 /* other checks are delayed to FT_Outline_Render() */
656
657 params.target = abitmap;
658 params.flags = 0;
659
660 if ( abitmap->pixel_mode == FT_PIXEL_MODE_GRAY ||
661 abitmap->pixel_mode == FT_PIXEL_MODE_LCD ||
662 abitmap->pixel_mode == FT_PIXEL_MODE_LCD_V )
663 params.flags |= FT_RASTER_FLAG_AA;
664
665 return FT_Outline_Render( library, outline, &params );
666 }
667
668
669 /* documentation is in freetype.h */
670
671 FT_EXPORT_DEF( void )
672 FT_Vector_Transform( FT_Vector* vector,
673 const FT_Matrix* matrix )
674 {
675 FT_Pos xz, yz;
676
677
678 if ( !vector || !matrix )
679 return;
680
681 xz = FT_MulFix( vector->x, matrix->xx ) +
682 FT_MulFix( vector->y, matrix->xy );
683
684 yz = FT_MulFix( vector->x, matrix->yx ) +
685 FT_MulFix( vector->y, matrix->yy );
686
687 vector->x = xz;
688 vector->y = yz;
689 }
690
691
692 /* documentation is in ftoutln.h */
693
694 FT_EXPORT_DEF( void )
695 FT_Outline_Transform( const FT_Outline* outline,
696 const FT_Matrix* matrix )
697 {
698 FT_Vector* vec;
699 FT_Vector* limit;
700
701
702 if ( !outline || !matrix )
703 return;
704
705 vec = outline->points;
706 limit = vec + outline->n_points;
707
708 for ( ; vec < limit; vec++ )
709 FT_Vector_Transform( vec, matrix );
710 }
711
712
713 #if 0
714
715 #define FT_OUTLINE_GET_CONTOUR( outline, c, first, last ) \
716 do { \
717 (first) = ( c > 0 ) ? (outline)->points + \
718 (outline)->contours[c - 1] + 1 \
719 : (outline)->points; \
720 (last) = (outline)->points + (outline)->contours[c]; \
721 } while ( 0 )
722
723
724 /* Is a point in some contour? */
725 /* */
726 /* We treat every point of the contour as if it */
727 /* it were ON. That is, we allow false positives, */
728 /* but disallow false negatives. (XXX really?) */
729 static FT_Bool
730 ft_contour_has( FT_Outline* outline,
731 FT_Short c,
732 FT_Vector* point )
733 {
734 FT_Vector* first;
735 FT_Vector* last;
736 FT_Vector* a;
737 FT_Vector* b;
738 FT_UInt n = 0;
739
740
741 FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
742
743 for ( a = first; a <= last; a++ )
744 {
745 FT_Pos x;
746 FT_Int intersect;
747
748
749 b = ( a == last ) ? first : a + 1;
750
751 intersect = ( a->y - point->y ) ^ ( b->y - point->y );
752
753 /* a and b are on the same side */
754 if ( intersect >= 0 )
755 {
756 if ( intersect == 0 && a->y == point->y )
757 {
758 if ( ( a->x <= point->x && b->x >= point->x ) ||
759 ( a->x >= point->x && b->x <= point->x ) )
760 return 1;
761 }
762
763 continue;
764 }
765
766 x = a->x + ( b->x - a->x ) * (point->y - a->y ) / ( b->y - a->y );
767
768 if ( x < point->x )
769 n++;
770 else if ( x == point->x )
771 return 1;
772 }
773
774 return ( n % 2 );
775 }
776
777
778 static FT_Bool
779 ft_contour_enclosed( FT_Outline* outline,
780 FT_UShort c )
781 {
782 FT_Vector* first;
783 FT_Vector* last;
784 FT_Short i;
785
786
787 FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
788
789 for ( i = 0; i < outline->n_contours; i++ )
790 {
791 if ( i != c && ft_contour_has( outline, i, first ) )
792 {
793 FT_Vector* pt;
794
795
796 for ( pt = first + 1; pt <= last; pt++ )
797 if ( !ft_contour_has( outline, i, pt ) )
798 return 0;
799
800 return 1;
801 }
802 }
803
804 return 0;
805 }
806
807
808 /* This version differs from the public one in that each */
809 /* part (contour not enclosed in another contour) of the */
810 /* outline is checked for orientation. This is */
811 /* necessary for some buggy CJK fonts. */
812 static FT_Orientation
813 ft_outline_get_orientation( FT_Outline* outline )
814 {
815 FT_Short i;
816 FT_Vector* first;
817 FT_Vector* last;
818 FT_Orientation orient = FT_ORIENTATION_NONE;
819
820
821 first = outline->points;
822 for ( i = 0; i < outline->n_contours; i++, first = last + 1 )
823 {
824 FT_Vector* point;
825 FT_Vector* xmin_point;
826 FT_Pos xmin;
827
828
829 last = outline->points + outline->contours[i];
830
831 /* skip degenerate contours */
832 if ( last < first + 2 )
833 continue;
834
835 if ( ft_contour_enclosed( outline, i ) )
836 continue;
837
838 xmin = first->x;
839 xmin_point = first;
840
841 for ( point = first + 1; point <= last; point++ )
842 {
843 if ( point->x < xmin )
844 {
845 xmin = point->x;
846 xmin_point = point;
847 }
848 }
849
850 /* check the orientation of the contour */
851 {
852 FT_Vector* prev;
853 FT_Vector* next;
854 FT_Orientation o;
855
856
857 prev = ( xmin_point == first ) ? last : xmin_point - 1;
858 next = ( xmin_point == last ) ? first : xmin_point + 1;
859
860 if ( FT_Atan2( prev->x - xmin_point->x, prev->y - xmin_point->y ) >
861 FT_Atan2( next->x - xmin_point->x, next->y - xmin_point->y ) )
862 o = FT_ORIENTATION_POSTSCRIPT;
863 else
864 o = FT_ORIENTATION_TRUETYPE;
865
866 if ( orient == FT_ORIENTATION_NONE )
867 orient = o;
868 else if ( orient != o )
869 return FT_ORIENTATION_NONE;
870 }
871 }
872
873 return orient;
874 }
875
876 #endif /* 0 */
877
878
879 /* documentation is in ftoutln.h */
880
881 FT_EXPORT_DEF( FT_Error )
882 FT_Outline_Embolden( FT_Outline* outline,
883 FT_Pos strength )
884 {
885 FT_Vector* points;
886 FT_Vector v_prev, v_first, v_next, v_cur;
887 FT_Angle rotate, angle_in, angle_out;
888 FT_Int c, n, first;
889 FT_Int orientation;
890
891
892 if ( !outline )
893 return FT_Err_Invalid_Argument;
894
895 strength /= 2;
896 if ( strength == 0 )
897 return FT_Err_Ok;
898
899 orientation = FT_Outline_Get_Orientation( outline );
900 if ( orientation == FT_ORIENTATION_NONE )
901 {
902 if ( outline->n_contours )
903 return FT_Err_Invalid_Argument;
904 else
905 return FT_Err_Ok;
906 }
907
908 if ( orientation == FT_ORIENTATION_TRUETYPE )
909 rotate = -FT_ANGLE_PI2;
910 else
911 rotate = FT_ANGLE_PI2;
912
913 points = outline->points;
914
915 first = 0;
916 for ( c = 0; c < outline->n_contours; c++ )
917 {
918 int last = outline->contours[c];
919
920
921 v_first = points[first];
922 v_prev = points[last];
923 v_cur = v_first;
924
925 for ( n = first; n <= last; n++ )
926 {
927 FT_Vector in, out;
928 FT_Angle angle_diff;
929 FT_Pos d;
930 FT_Fixed scale;
931
932
933 if ( n < last )
934 v_next = points[n + 1];
935 else
936 v_next = v_first;
937
938 /* compute the in and out vectors */
939 in.x = v_cur.x - v_prev.x;
940 in.y = v_cur.y - v_prev.y;
941
942 out.x = v_next.x - v_cur.x;
943 out.y = v_next.y - v_cur.y;
944
945 angle_in = FT_Atan2( in.x, in.y );
946 angle_out = FT_Atan2( out.x, out.y );
947 angle_diff = FT_Angle_Diff( angle_in, angle_out );
948 scale = FT_Cos( angle_diff / 2 );
949
950 if ( scale < 0x4000L && scale > -0x4000L )
951 in.x = in.y = 0;
952 else
953 {
954 d = FT_DivFix( strength, scale );
955
956 FT_Vector_From_Polar( &in, d, angle_in + angle_diff / 2 - rotate );
957 }
958
959 outline->points[n].x = v_cur.x + strength + in.x;
960 outline->points[n].y = v_cur.y + strength + in.y;
961
962 v_prev = v_cur;
963 v_cur = v_next;
964 }
965
966 first = last + 1;
967 }
968
969 return FT_Err_Ok;
970 }
971
972
973 /* documentation is in ftoutln.h */
974
975 FT_EXPORT_DEF( FT_Orientation )
976 FT_Outline_Get_Orientation( FT_Outline* outline )
977 {
978 FT_Pos xmin = 32768L;
979 FT_Pos xmin_ymin = 32768L;
980 FT_Pos xmin_ymax = -32768L;
981 FT_Vector* xmin_first = NULL;
982 FT_Vector* xmin_last = NULL;
983
984 short* contour;
985
986 FT_Vector* first;
987 FT_Vector* last;
988 FT_Vector* prev;
989 FT_Vector* point;
990
991 int i;
992 FT_Pos ray_y[3];
993 FT_Orientation result[3];
994
995
996 if ( !outline || outline->n_points <= 0 )
997 return FT_ORIENTATION_TRUETYPE;
998
999 /* We use the nonzero winding rule to find the orientation. */
1000 /* Since glyph outlines behave much more `regular' than arbitrary */
1001 /* cubic or quadratic curves, this test deals with the polygon */
1002 /* only which is spanned up by the control points. */
1003
1004 first = outline->points;
1005 for ( contour = outline->contours;
1006 contour < outline->contours + outline->n_contours;
1007 contour++, first = last + 1 )
1008 {
1009 FT_Pos contour_xmin = 32768L;
1010 FT_Pos contour_xmax = -32768L;
1011 FT_Pos contour_ymin = 32768L;
1012 FT_Pos contour_ymax = -32768L;
1013
1014
1015 last = outline->points + *contour;
1016
1017 /* skip degenerate contours */
1018 if ( last < first + 2 )
1019 continue;
1020
1021 for ( point = first; point <= last; ++point )
1022 {
1023 if ( point->x < contour_xmin )
1024 contour_xmin = point->x;
1025
1026 if ( point->x > contour_xmax )
1027 contour_xmax = point->x;
1028
1029 if ( point->y < contour_ymin )
1030 contour_ymin = point->y;
1031
1032 if ( point->y > contour_ymax )
1033 contour_ymax = point->y;
1034 }
1035
1036 if ( contour_xmin < xmin &&
1037 contour_xmin != contour_xmax &&
1038 contour_ymin != contour_ymax )
1039 {
1040 xmin = contour_xmin;
1041 xmin_ymin = contour_ymin;
1042 xmin_ymax = contour_ymax;
1043 xmin_first = first;
1044 xmin_last = last;
1045 }
1046 }
1047
1048 if ( xmin == 32768L )
1049 return FT_ORIENTATION_TRUETYPE;
1050
1051 ray_y[0] = ( xmin_ymin * 3 + xmin_ymax ) >> 2;
1052 ray_y[1] = ( xmin_ymin + xmin_ymax ) >> 1;
1053 ray_y[2] = ( xmin_ymin + xmin_ymax * 3 ) >> 2;
1054
1055 for ( i = 0; i < 3; i++ )
1056 {
1057 FT_Pos left_x;
1058 FT_Pos right_x;
1059 FT_Vector* left1;
1060 FT_Vector* left2;
1061 FT_Vector* right1;
1062 FT_Vector* right2;
1063
1064
1065 RedoRay:
1066 left_x = 32768L;
1067 right_x = -32768L;
1068
1069 left1 = left2 = right1 = right2 = NULL;
1070
1071 prev = xmin_last;
1072 for ( point = xmin_first; point <= xmin_last; prev = point, ++point )
1073 {
1074 FT_Pos tmp_x;
1075
1076
1077 if ( point->y == ray_y[i] || prev->y == ray_y[i] )
1078 {
1079 ray_y[i]++;
1080 goto RedoRay;
1081 }
1082
1083 if ( ( point->y < ray_y[i] && prev->y < ray_y[i] ) ||
1084 ( point->y > ray_y[i] && prev->y > ray_y[i] ) )
1085 continue;
1086
1087 tmp_x = FT_MulDiv( point->x - prev->x,
1088 ray_y[i] - prev->y,
1089 point->y - prev->y ) + prev->x;
1090
1091 if ( tmp_x < left_x )
1092 {
1093 left_x = tmp_x;
1094 left1 = prev;
1095 left2 = point;
1096 }
1097
1098 if ( tmp_x > right_x )
1099 {
1100 right_x = tmp_x;
1101 right1 = prev;
1102 right2 = point;
1103 }
1104 }
1105
1106 if ( left1 && right1 )
1107 {
1108 if ( left1->y < left2->y && right1->y > right2->y )
1109 result[i] = FT_ORIENTATION_TRUETYPE;
1110 else if ( left1->y > left2->y && right1->y < right2->y )
1111 result[i] = FT_ORIENTATION_POSTSCRIPT;
1112 else
1113 result[i] = FT_ORIENTATION_NONE;
1114 }
1115 }
1116
1117 if ( result[0] != FT_ORIENTATION_NONE &&
1118 ( result[0] == result[1] || result[0] == result[2] ) )
1119 return result[0];
1120
1121 if ( result[1] != FT_ORIENTATION_NONE && result[1] == result[2] )
1122 return result[1];
1123
1124 return FT_ORIENTATION_TRUETYPE;
1125 }
1126
1127
1128 /* END */