1 /***************************************************************************/
5 /* FreeType outline management (body). */
7 /* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
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. */
16 /***************************************************************************/
19 /*************************************************************************/
21 /* All functions are declared in freetype.h. */
23 /*************************************************************************/
28 #include FT_INTERNAL_OBJECTS_H
29 #include FT_TRIGONOMETRY_H
32 /*************************************************************************/
34 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
35 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
36 /* messages during execution. */
39 #define FT_COMPONENT trace_outline
43 const FT_Outline null_outline
= { 0, 0, 0, 0, 0, 0 };
46 /* documentation is in ftoutln.h */
48 FT_EXPORT_DEF( FT_Error
)
49 FT_Outline_Decompose( FT_Outline
* outline
,
50 const FT_Outline_Funcs
* func_interface
,
54 #define SCALED( x ) ( ( (x) << shift ) - delta )
66 FT_Int n
; /* index of contour in outline */
67 FT_UInt first
; /* index of first point in contour */
68 FT_Int tag
; /* current point's state */
74 if ( !outline
|| !func_interface
)
75 return FT_Err_Invalid_Argument
;
77 shift
= func_interface
->shift
;
78 delta
= func_interface
->delta
;
81 for ( n
= 0; n
< outline
->n_contours
; n
++ )
83 FT_Int last
; /* index of last point in contour */
86 last
= outline
->contours
[n
];
89 limit
= outline
->points
+ last
;
91 v_start
= outline
->points
[first
];
92 v_last
= outline
->points
[last
];
94 v_start
.x
= SCALED( v_start
.x
); v_start
.y
= SCALED( v_start
.y
);
95 v_last
.x
= SCALED( v_last
.x
); v_last
.y
= SCALED( v_last
.y
);
99 point
= outline
->points
+ first
;
100 tags
= outline
->tags
+ first
;
101 tag
= FT_CURVE_TAG( tags
[0] );
103 /* A contour cannot start with a cubic control point! */
104 if ( tag
== FT_CURVE_TAG_CUBIC
)
105 goto Invalid_Outline
;
107 /* check first point to determine origin */
108 if ( tag
== FT_CURVE_TAG_CONIC
)
110 /* first point is conic control. Yes, this happens. */
111 if ( FT_CURVE_TAG( outline
->tags
[last
] ) == FT_CURVE_TAG_ON
)
113 /* start at last point if it is on the curve */
119 /* if both first and last points are conic, */
120 /* start at their middle and record its position */
122 v_start
.x
= ( v_start
.x
+ v_last
.x
) / 2;
123 v_start
.y
= ( v_start
.y
+ v_last
.y
) / 2;
131 error
= func_interface
->move_to( &v_start
, user
);
135 while ( point
< limit
)
140 tag
= FT_CURVE_TAG( tags
[0] );
143 case FT_CURVE_TAG_ON
: /* emit a single line_to */
148 vec
.x
= SCALED( point
->x
);
149 vec
.y
= SCALED( point
->y
);
151 error
= func_interface
->line_to( &vec
, user
);
157 case FT_CURVE_TAG_CONIC
: /* consume conic arcs */
158 v_control
.x
= SCALED( point
->x
);
159 v_control
.y
= SCALED( point
->y
);
170 tag
= FT_CURVE_TAG( tags
[0] );
172 vec
.x
= SCALED( point
->x
);
173 vec
.y
= SCALED( point
->y
);
175 if ( tag
== FT_CURVE_TAG_ON
)
177 error
= func_interface
->conic_to( &v_control
, &vec
, user
);
183 if ( tag
!= FT_CURVE_TAG_CONIC
)
184 goto Invalid_Outline
;
186 v_middle
.x
= ( v_control
.x
+ vec
.x
) / 2;
187 v_middle
.y
= ( v_control
.y
+ vec
.y
) / 2;
189 error
= func_interface
->conic_to( &v_control
, &v_middle
, user
);
197 error
= func_interface
->conic_to( &v_control
, &v_start
, user
);
200 default: /* FT_CURVE_TAG_CUBIC */
202 FT_Vector vec1
, vec2
;
205 if ( point
+ 1 > limit
||
206 FT_CURVE_TAG( tags
[1] ) != FT_CURVE_TAG_CUBIC
)
207 goto Invalid_Outline
;
212 vec1
.x
= SCALED( point
[-2].x
); vec1
.y
= SCALED( point
[-2].y
);
213 vec2
.x
= SCALED( point
[-1].x
); vec2
.y
= SCALED( point
[-1].y
);
215 if ( point
<= limit
)
220 vec
.x
= SCALED( point
->x
);
221 vec
.y
= SCALED( point
->y
);
223 error
= func_interface
->cubic_to( &vec1
, &vec2
, &vec
, user
);
229 error
= func_interface
->cubic_to( &vec1
, &vec2
, &v_start
, user
);
235 /* close the contour with a line segment */
236 error
= func_interface
->line_to( &v_start
, user
);
251 return FT_Err_Invalid_Outline
;
255 FT_EXPORT_DEF( FT_Error
)
256 FT_Outline_New_Internal( FT_Memory memory
,
259 FT_Outline
*anoutline
)
264 if ( !anoutline
|| !memory
)
265 return FT_Err_Invalid_Argument
;
267 *anoutline
= null_outline
;
269 if ( FT_NEW_ARRAY( anoutline
->points
, numPoints
* 2L ) ||
270 FT_NEW_ARRAY( anoutline
->tags
, numPoints
) ||
271 FT_NEW_ARRAY( anoutline
->contours
, numContours
) )
274 anoutline
->n_points
= (FT_UShort
)numPoints
;
275 anoutline
->n_contours
= (FT_Short
)numContours
;
276 anoutline
->flags
|= FT_OUTLINE_OWNER
;
281 anoutline
->flags
|= FT_OUTLINE_OWNER
;
282 FT_Outline_Done_Internal( memory
, anoutline
);
288 /* documentation is in ftoutln.h */
290 FT_EXPORT_DEF( FT_Error
)
291 FT_Outline_New( FT_Library library
,
294 FT_Outline
*anoutline
)
297 return FT_Err_Invalid_Library_Handle
;
299 return FT_Outline_New_Internal( library
->memory
, numPoints
,
300 numContours
, anoutline
);
304 /* documentation is in ftoutln.h */
306 FT_EXPORT_DEF( FT_Error
)
307 FT_Outline_Check( FT_Outline
* outline
)
311 FT_Int n_points
= outline
->n_points
;
312 FT_Int n_contours
= outline
->n_contours
;
318 if ( n_points
== 0 && n_contours
== 0 )
321 /* check point and contour counts */
322 if ( n_points
<= 0 || n_contours
<= 0 )
326 for ( n
= 0; n
< n_contours
; n
++ )
328 end
= outline
->contours
[n
];
330 /* note that we don't accept empty contours */
331 if ( end
<= end0
|| end
>= n_points
)
337 if ( end
!= n_points
- 1 )
340 /* XXX: check the tags array */
345 return FT_Err_Invalid_Argument
;
349 /* documentation is in ftoutln.h */
351 FT_EXPORT_DEF( FT_Error
)
352 FT_Outline_Copy( const FT_Outline
* source
,
358 if ( !source
|| !target
||
359 source
->n_points
!= target
->n_points
||
360 source
->n_contours
!= target
->n_contours
)
361 return FT_Err_Invalid_Argument
;
363 if ( source
== target
)
366 FT_ARRAY_COPY( target
->points
, source
->points
, source
->n_points
);
368 FT_ARRAY_COPY( target
->tags
, source
->tags
, source
->n_points
);
370 FT_ARRAY_COPY( target
->contours
, source
->contours
, source
->n_contours
);
372 /* copy all flags, except the `FT_OUTLINE_OWNER' one */
373 is_owner
= target
->flags
& FT_OUTLINE_OWNER
;
374 target
->flags
= source
->flags
;
376 target
->flags
&= ~FT_OUTLINE_OWNER
;
377 target
->flags
|= is_owner
;
383 FT_EXPORT_DEF( FT_Error
)
384 FT_Outline_Done_Internal( FT_Memory memory
,
385 FT_Outline
* outline
)
387 if ( memory
&& outline
)
389 if ( outline
->flags
& FT_OUTLINE_OWNER
)
391 FT_FREE( outline
->points
);
392 FT_FREE( outline
->tags
);
393 FT_FREE( outline
->contours
);
395 *outline
= null_outline
;
400 return FT_Err_Invalid_Argument
;
404 /* documentation is in ftoutln.h */
406 FT_EXPORT_DEF( FT_Error
)
407 FT_Outline_Done( FT_Library library
,
408 FT_Outline
* outline
)
410 /* check for valid `outline' in FT_Outline_Done_Internal() */
413 return FT_Err_Invalid_Library_Handle
;
415 return FT_Outline_Done_Internal( library
->memory
, outline
);
419 /* documentation is in ftoutln.h */
421 FT_EXPORT_DEF( void )
422 FT_Outline_Get_CBox( const FT_Outline
* outline
,
425 FT_Pos xMin
, yMin
, xMax
, yMax
;
428 if ( outline
&& acbox
)
430 if ( outline
->n_points
== 0 )
439 FT_Vector
* vec
= outline
->points
;
440 FT_Vector
* limit
= vec
+ outline
->n_points
;
443 xMin
= xMax
= vec
->x
;
444 yMin
= yMax
= vec
->y
;
447 for ( ; vec
< limit
; vec
++ )
453 if ( x
< xMin
) xMin
= x
;
454 if ( x
> xMax
) xMax
= x
;
457 if ( y
< yMin
) yMin
= y
;
458 if ( y
> yMax
) yMax
= y
;
469 /* documentation is in ftoutln.h */
471 FT_EXPORT_DEF( void )
472 FT_Outline_Translate( const FT_Outline
* outline
,
483 vec
= outline
->points
;
485 for ( n
= 0; n
< outline
->n_points
; n
++ )
494 /* documentation is in ftoutln.h */
496 FT_EXPORT_DEF( void )
497 FT_Outline_Reverse( FT_Outline
* outline
)
508 for ( n
= 0; n
< outline
->n_contours
; n
++ )
510 last
= outline
->contours
[n
];
512 /* reverse point table */
514 FT_Vector
* p
= outline
->points
+ first
;
515 FT_Vector
* q
= outline
->points
+ last
;
529 /* reverse tags table */
531 char* p
= outline
->tags
+ first
;
532 char* q
= outline
->tags
+ last
;
549 outline
->flags
^= FT_OUTLINE_REVERSE_FILL
;
553 /* documentation is in ftoutln.h */
555 FT_EXPORT_DEF( FT_Error
)
556 FT_Outline_Render( FT_Library library
,
558 FT_Raster_Params
* params
)
562 FT_Renderer renderer
;
567 return FT_Err_Invalid_Library_Handle
;
569 if ( !outline
|| !params
)
570 return FT_Err_Invalid_Argument
;
572 renderer
= library
->cur_renderer
;
573 node
= library
->renderers
.head
;
575 params
->source
= (void*)outline
;
577 error
= FT_Err_Cannot_Render_Glyph
;
580 error
= renderer
->raster_render( renderer
->raster
, params
);
581 if ( !error
|| FT_ERROR_BASE( error
) != FT_Err_Cannot_Render_Glyph
)
584 /* FT_Err_Cannot_Render_Glyph is returned if the render mode */
585 /* is unsupported by the current renderer for this glyph image */
588 /* now, look for another renderer that supports the same */
590 renderer
= FT_Lookup_Renderer( library
, FT_GLYPH_FORMAT_OUTLINE
,
595 /* if we changed the current renderer for the glyph image format */
596 /* we need to select it as the next current one */
597 if ( !error
&& update
&& renderer
)
598 FT_Set_Renderer( library
, renderer
, 0, 0 );
604 /* documentation is in ftoutln.h */
606 FT_EXPORT_DEF( FT_Error
)
607 FT_Outline_Get_Bitmap( FT_Library library
,
609 const FT_Bitmap
*abitmap
)
611 FT_Raster_Params params
;
615 return FT_Err_Invalid_Argument
;
617 /* other checks are delayed to FT_Outline_Render() */
619 params
.target
= abitmap
;
622 if ( abitmap
->pixel_mode
== FT_PIXEL_MODE_GRAY
||
623 abitmap
->pixel_mode
== FT_PIXEL_MODE_LCD
||
624 abitmap
->pixel_mode
== FT_PIXEL_MODE_LCD_V
)
625 params
.flags
|= FT_RASTER_FLAG_AA
;
627 return FT_Outline_Render( library
, outline
, ¶ms
);
631 /* documentation is in freetype.h */
633 FT_EXPORT_DEF( void )
634 FT_Vector_Transform( FT_Vector
* vector
,
635 const FT_Matrix
* matrix
)
640 if ( !vector
|| !matrix
)
643 xz
= FT_MulFix( vector
->x
, matrix
->xx
) +
644 FT_MulFix( vector
->y
, matrix
->xy
);
646 yz
= FT_MulFix( vector
->x
, matrix
->yx
) +
647 FT_MulFix( vector
->y
, matrix
->yy
);
654 /* documentation is in ftoutln.h */
656 FT_EXPORT_DEF( void )
657 FT_Outline_Transform( const FT_Outline
* outline
,
658 const FT_Matrix
* matrix
)
664 if ( !outline
|| !matrix
)
667 vec
= outline
->points
;
668 limit
= vec
+ outline
->n_points
;
670 for ( ; vec
< limit
; vec
++ )
671 FT_Vector_Transform( vec
, matrix
);
677 #define FT_OUTLINE_GET_CONTOUR( outline, c, first, last ) \
679 (first) = ( c > 0 ) ? (outline)->points + \
680 (outline)->contours[c - 1] + 1 \
681 : (outline)->points; \
682 (last) = (outline)->points + (outline)->contours[c]; \
686 /* Is a point in some contour? */
688 /* We treat every point of the contour as if it */
689 /* it were ON. That is, we allow false positives, */
690 /* but disallow false negatives. (XXX really?) */
692 ft_contour_has( FT_Outline
* outline
,
703 FT_OUTLINE_GET_CONTOUR( outline
, c
, first
, last
);
705 for ( a
= first
; a
<= last
; a
++ )
711 b
= ( a
== last
) ? first
: a
+ 1;
713 intersect
= ( a
->y
- point
->y
) ^ ( b
->y
- point
->y
);
715 /* a and b are on the same side */
716 if ( intersect
>= 0 )
718 if ( intersect
== 0 && a
->y
== point
->y
)
720 if ( ( a
->x
<= point
->x
&& b
->x
>= point
->x
) ||
721 ( a
->x
>= point
->x
&& b
->x
<= point
->x
) )
728 x
= a
->x
+ ( b
->x
- a
->x
) * (point
->y
- a
->y
) / ( b
->y
- a
->y
);
732 else if ( x
== point
->x
)
741 ft_contour_enclosed( FT_Outline
* outline
,
749 FT_OUTLINE_GET_CONTOUR( outline
, c
, first
, last
);
751 for ( i
= 0; i
< outline
->n_contours
; i
++ )
753 if ( i
!= c
&& ft_contour_has( outline
, i
, first
) )
758 for ( pt
= first
+ 1; pt
<= last
; pt
++ )
759 if ( !ft_contour_has( outline
, i
, pt
) )
770 /* This version differs from the public one in that each */
771 /* part (contour not enclosed in another contour) of the */
772 /* outline is checked for orientation. This is */
773 /* necessary for some buggy CJK fonts. */
774 static FT_Orientation
775 ft_outline_get_orientation( FT_Outline
* outline
)
780 FT_Orientation orient
= FT_ORIENTATION_NONE
;
783 first
= outline
->points
;
784 for ( i
= 0; i
< outline
->n_contours
; i
++, first
= last
+ 1 )
787 FT_Vector
* xmin_point
;
791 last
= outline
->points
+ outline
->contours
[i
];
793 /* skip degenerate contours */
794 if ( last
< first
+ 2 )
797 if ( ft_contour_enclosed( outline
, i
) )
803 for ( point
= first
+ 1; point
<= last
; point
++ )
805 if ( point
->x
< xmin
)
812 /* check the orientation of the contour */
819 prev
= ( xmin_point
== first
) ? last
: xmin_point
- 1;
820 next
= ( xmin_point
== last
) ? first
: xmin_point
+ 1;
822 if ( FT_Atan2( prev
->x
- xmin_point
->x
, prev
->y
- xmin_point
->y
) >
823 FT_Atan2( next
->x
- xmin_point
->x
, next
->y
- xmin_point
->y
) )
824 o
= FT_ORIENTATION_POSTSCRIPT
;
826 o
= FT_ORIENTATION_TRUETYPE
;
828 if ( orient
== FT_ORIENTATION_NONE
)
830 else if ( orient
!= o
)
831 return FT_ORIENTATION_NONE
;
841 /* documentation is in ftoutln.h */
843 FT_EXPORT_DEF( FT_Error
)
844 FT_Outline_Embolden( FT_Outline
* outline
,
848 FT_Vector v_prev
, v_first
, v_next
, v_cur
;
849 FT_Angle rotate
, angle_in
, angle_out
;
855 return FT_Err_Invalid_Argument
;
861 orientation
= FT_Outline_Get_Orientation( outline
);
862 if ( orientation
== FT_ORIENTATION_NONE
)
864 if ( outline
->n_contours
)
865 return FT_Err_Invalid_Argument
;
870 if ( orientation
== FT_ORIENTATION_TRUETYPE
)
871 rotate
= -FT_ANGLE_PI2
;
873 rotate
= FT_ANGLE_PI2
;
875 points
= outline
->points
;
878 for ( c
= 0; c
< outline
->n_contours
; c
++ )
880 int last
= outline
->contours
[c
];
883 v_first
= points
[first
];
884 v_prev
= points
[last
];
887 for ( n
= first
; n
<= last
; n
++ )
896 v_next
= points
[n
+ 1];
900 /* compute the in and out vectors */
901 in
.x
= v_cur
.x
- v_prev
.x
;
902 in
.y
= v_cur
.y
- v_prev
.y
;
904 out
.x
= v_next
.x
- v_cur
.x
;
905 out
.y
= v_next
.y
- v_cur
.y
;
907 angle_in
= FT_Atan2( in
.x
, in
.y
);
908 angle_out
= FT_Atan2( out
.x
, out
.y
);
909 angle_diff
= FT_Angle_Diff( angle_in
, angle_out
);
910 scale
= FT_Cos( angle_diff
/ 2 );
912 if ( scale
< 0x4000L
&& scale
> -0x4000L
)
916 d
= FT_DivFix( strength
, scale
);
918 FT_Vector_From_Polar( &in
, d
, angle_in
+ angle_diff
/ 2 - rotate
);
921 outline
->points
[n
].x
= v_cur
.x
+ strength
+ in
.x
;
922 outline
->points
[n
].y
= v_cur
.y
+ strength
+ in
.y
;
935 /* documentation is in ftoutln.h */
937 FT_EXPORT_DEF( FT_Orientation
)
938 FT_Outline_Get_Orientation( FT_Outline
* outline
)
940 FT_Pos xmin
= 32768L;
941 FT_Pos xmin_ymin
= 32768L;
942 FT_Pos xmin_ymax
= -32768L;
943 FT_Vector
* xmin_first
= NULL
;
944 FT_Vector
* xmin_last
= NULL
;
955 FT_Orientation result
[3];
958 if ( !outline
|| outline
->n_points
<= 0 )
959 return FT_ORIENTATION_TRUETYPE
;
961 /* We use the nonzero winding rule to find the orientation. */
962 /* Since glyph outlines behave much more `regular' than arbitrary */
963 /* cubic or quadratic curves, this test deals with the polygon */
964 /* only which is spanned up by the control points. */
966 first
= outline
->points
;
967 for ( contour
= outline
->contours
;
968 contour
< outline
->contours
+ outline
->n_contours
;
969 contour
++, first
= last
+ 1 )
971 FT_Pos contour_xmin
= 32768L;
972 FT_Pos contour_xmax
= -32768L;
973 FT_Pos contour_ymin
= 32768L;
974 FT_Pos contour_ymax
= -32768L;
977 last
= outline
->points
+ *contour
;
979 /* skip degenerate contours */
980 if ( last
< first
+ 2 )
983 for ( point
= first
; point
<= last
; ++point
)
985 if ( point
->x
< contour_xmin
)
986 contour_xmin
= point
->x
;
988 if ( point
->x
> contour_xmax
)
989 contour_xmax
= point
->x
;
991 if ( point
->y
< contour_ymin
)
992 contour_ymin
= point
->y
;
994 if ( point
->y
> contour_ymax
)
995 contour_ymax
= point
->y
;
998 if ( contour_xmin
< xmin
&&
999 contour_xmin
!= contour_xmax
&&
1000 contour_ymin
!= contour_ymax
)
1002 xmin
= contour_xmin
;
1003 xmin_ymin
= contour_ymin
;
1004 xmin_ymax
= contour_ymax
;
1010 if ( xmin
== 32768 )
1011 return FT_ORIENTATION_TRUETYPE
;
1013 ray_y
[0] = ( xmin_ymin
* 3 + xmin_ymax
) >> 2;
1014 ray_y
[1] = ( xmin_ymin
+ xmin_ymax
) >> 1;
1015 ray_y
[2] = ( xmin_ymin
+ xmin_ymax
* 3 ) >> 2;
1017 for ( i
= 0; i
< 3; i
++ )
1031 left1
= left2
= right1
= right2
= NULL
;
1034 for ( point
= xmin_first
; point
<= xmin_last
; prev
= point
, ++point
)
1039 if ( point
->y
== ray_y
[i
] || prev
->y
== ray_y
[i
] )
1045 if ( ( point
->y
< ray_y
[i
] && prev
->y
< ray_y
[i
] ) ||
1046 ( point
->y
> ray_y
[i
] && prev
->y
> ray_y
[i
] ) )
1049 tmp_x
= FT_MulDiv( point
->x
- prev
->x
,
1051 point
->y
- prev
->y
) + prev
->x
;
1053 if ( tmp_x
< left_x
)
1060 if ( tmp_x
> right_x
)
1068 if ( left1
&& right1
)
1070 if ( left1
->y
< left2
->y
&& right1
->y
> right2
->y
)
1071 result
[i
] = FT_ORIENTATION_TRUETYPE
;
1072 else if ( left1
->y
> left2
->y
&& right1
->y
< right2
->y
)
1073 result
[i
] = FT_ORIENTATION_POSTSCRIPT
;
1075 result
[i
] = FT_ORIENTATION_NONE
;
1079 if ( result
[0] != FT_ORIENTATION_NONE
&&
1080 ( result
[0] == result
[1] || result
[0] == result
[2] ) )
1083 if ( result
[1] != FT_ORIENTATION_NONE
&& result
[1] == result
[2] )
1086 return FT_ORIENTATION_TRUETYPE
;