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 )
FT_Outline_Decompose(FT_Outline * outline,const FT_Outline_Funcs * func_interface,void * user)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 )
FT_Outline_New_Internal(FT_Memory memory,FT_UInt numPoints,FT_Int numContours,FT_Outline * anoutline)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 )
FT_Outline_New(FT_Library library,FT_UInt numPoints,FT_Int numContours,FT_Outline * anoutline)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 )
FT_Outline_Check(FT_Outline * outline)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 )
FT_Outline_Copy(const FT_Outline * source,FT_Outline * target)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 )
FT_Outline_Done_Internal(FT_Memory memory,FT_Outline * outline)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 )
FT_Outline_Done(FT_Library library,FT_Outline * outline)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 )
FT_Outline_Get_CBox(const FT_Outline * outline,FT_BBox * acbox)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 )
FT_Outline_Translate(const FT_Outline * outline,FT_Pos xOffset,FT_Pos yOffset)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 )
FT_Outline_Reverse(FT_Outline * outline)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 )
FT_Outline_Render(FT_Library library,FT_Outline * outline,FT_Raster_Params * params)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 )
FT_Outline_Get_Bitmap(FT_Library library,FT_Outline * outline,const FT_Bitmap * abitmap)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 )
FT_Vector_Transform(FT_Vector * vector,const FT_Matrix * matrix)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 )
FT_Outline_Transform(const FT_Outline * outline,const FT_Matrix * matrix)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 )
FT_Outline_Embolden(FT_Outline * outline,FT_Pos strength)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 )
FT_Outline_Get_Orientation(FT_Outline * outline)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 */
1129