1 /***************************************************************************/
5 /* A new `perfect' anti-aliasing renderer (body). */
7 /* Copyright 2000 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 /***************************************************************************/
18 /*************************************************************************/
20 /* This file can be compiled without the rest of the FreeType engine, */
21 /* by defining the _STANDALONE_ macro when compiling it. You also need */
22 /* to put the files `ftgrays.h' and `ftimage.h' into the current */
23 /* compilation directory. Typically, you could do something like */
25 /* - copy `src/base/ftgrays.c' to your current directory */
27 /* - copy `include/freetype/ftimage.h' and */
28 /* `include/freetype/ftgrays.h' to the same directory */
30 /* - compile `ftgrays' with the _STANDALONE_ macro defined, as in */
32 /* cc -c -D_STANDALONE_ ftgrays.c */
34 /* The renderer can be initialized with a call to */
35 /* `ft_grays_raster.grays_raster_new'; an anti-aliased bitmap can be */
36 /* generated with a call to `ft_grays_raster.grays_raster_render'. */
38 /* See the comments and documentation in the file `ftimage.h' for */
39 /* more details on how the raster works. */
41 /*************************************************************************/
43 /*************************************************************************/
45 /* This is a new anti-aliasing scan-converter for FreeType 2. The */
46 /* algorithm used here is _very_ different from the one in the standard */
47 /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */
48 /* coverage of the outline on each pixel cell. */
50 /* It is based on ideas that I initially found in Raph Levien's */
51 /* excellent LibArt graphics library (see http://www.levien.com/libart */
52 /* for more information, though the web pages do not tell anything */
53 /* about the renderer; you'll have to dive into the source code to */
54 /* understand how it works). */
56 /* Note, however, that this is a _very_ different implementation */
57 /* compared to Raph's. Coverage information is stored in a very */
58 /* different way, and I don't use sorted vector paths. Also, it */
59 /* doesn't use floating point values. */
61 /* This renderer has the following advantages: */
63 /* - It doesn't need an intermediate bitmap. Instead, one can supply */
64 /* a callback function that will be called by the renderer to draw */
65 /* gray spans on any target surface. You can thus do direct */
66 /* composition on any kind of bitmap, provided that you give the */
67 /* renderer the right callback. */
69 /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */
72 /* - It performs a single pass on the outline (the `standard' FT2 */
73 /* renderer makes two passes). */
75 /* - It can easily be modified to render to _any_ number of gray levels */
78 /* - For small (< 20) pixel sizes, it is faster than the standard */
81 /*************************************************************************/
84 #include <string.h> /* for memcpy() */
87 /*************************************************************************/
89 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
90 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
91 /* messages during execution. */
94 #define FT_COMPONENT trace_aaraster
100 #define ErrRaster_Invalid_Mode -2
101 #define ErrRaster_Invalid_Outline -1
106 /* This macro is used to indicate that a function parameter is unused. */
107 /* Its purpose is simply to reduce compiler warnings. Note also that */
108 /* simply defining it as `(void)x' doesn't avoid warnings with certain */
109 /* ANSI compilers (e.g. LCC). */
110 #define FT_UNUSED( x ) (x) = (x)
112 /* Disable the tracing mechanism for simplicity -- developers can */
113 /* activate it easily by redefining these two macros. */
115 #define FT_ERROR( x ) do ; while ( 0 ) /* nothing */
119 #define FT_TRACE( x ) do ; while ( 0 ) /* nothing */
123 #else /* _STANDALONE_ */
126 #ifdef FT_FLAT_COMPILE
132 #include <smooth/ftgrays.h>
137 #include <freetype/internal/ftobjs.h> /* for FT_UNUSED() */
138 #include <freetype/internal/ftdebug.h> /* for FT_TRACE() and FT_ERROR() */
139 #include <freetype/ftoutln.h> /* for FT_Outline_Decompose() */
141 #define ErrRaster_Invalid_Mode FT_Err_Cannot_Render_Glyph
142 #define ErrRaster_Invalid_Outline FT_Err_Invalid_Outline
145 #endif /* _STANDALONE_ */
148 /* define this to dump debugging information */
149 #define xxxDEBUG_GRAYS
151 /* as usual, for the speed hungry :-) */
153 #ifndef FT_STATIC_RASTER
156 #define RAS_ARG PRaster raster
157 #define RAS_ARG_ PRaster raster,
159 #define RAS_VAR raster
160 #define RAS_VAR_ raster,
162 #define ras (*raster)
165 #else /* FT_STATIC_RASTER */
168 #define RAS_ARG /* empty */
169 #define RAS_ARG_ /* empty */
170 #define RAS_VAR /* empty */
171 #define RAS_VAR_ /* empty */
176 #endif /* FT_STATIC_RASTER */
179 /* must be at least 6 bits! */
182 #define ONE_PIXEL ( 1L << PIXEL_BITS )
183 #define PIXEL_MASK ( -1L << PIXEL_BITS )
184 #define TRUNC( x ) ( (x) >> PIXEL_BITS )
185 #define SUBPIXELS( x ) ( (x) << PIXEL_BITS )
186 #define FLOOR( x ) ( (x) & -ONE_PIXEL )
187 #define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )
188 #define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )
191 #define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) )
192 #define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )
194 #define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )
195 #define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) )
198 /* Define this if you want to use a more compact storage scheme. This */
199 /* increases the number of cells available in the render pool but slows */
200 /* down the rendering a bit. It is useful if you have a really tiny */
202 #define xxxGRAYS_COMPACT
205 /*************************************************************************/
207 /* TYPE DEFINITIONS */
209 typedef int TScan
; /* integer scanline/pixel coordinate */
210 typedef long TPos
; /* sub-pixel coordinate */
212 /* maximal number of gray spans in a call to the span callback */
213 #define FT_MAX_GRAY_SPANS 32
218 typedef struct TCell_
222 int cover
: PIXEL_BITS
+ 2;
223 int area
: PIXEL_BITS
* 2 + 2;
227 #else /* GRAYS_COMPACT */
229 typedef struct TCell_
238 #endif /* GRAYS_COMPACT */
241 typedef struct TRaster_
247 TScan min_ex
, max_ex
;
248 TScan min_ey
, max_ey
;
260 FT_Vector bez_stack
[32 * 3];
266 FT_Span gray_spans
[FT_MAX_GRAY_SPANS
];
269 FT_Raster_Span_Func render_span
;
270 void* render_span_data
;
283 /*************************************************************************/
285 /* Initialize the cells table. */
288 void init_cells( RAS_ARG_
void* buffer
,
291 ras
.cells
= (PCell
)buffer
;
292 ras
.max_cells
= byte_size
/ sizeof ( TCell
);
300 /*************************************************************************/
302 /* Compute the outline bounding box. */
305 void compute_cbox( RAS_ARG_ FT_Outline
* outline
)
307 FT_Vector
* vec
= outline
->points
;
308 FT_Vector
* limit
= vec
+ outline
->n_points
;
311 if ( outline
->n_points
<= 0 )
313 ras
.min_ex
= ras
.max_ex
= 0;
314 ras
.min_ey
= ras
.max_ey
= 0;
318 ras
.min_ex
= ras
.max_ex
= vec
->x
;
319 ras
.min_ey
= ras
.max_ey
= vec
->y
;
323 for ( ; vec
< limit
; vec
++ )
329 if ( x
< ras
.min_ex
) ras
.min_ex
= x
;
330 if ( x
> ras
.max_ex
) ras
.max_ex
= x
;
331 if ( y
< ras
.min_ey
) ras
.min_ey
= y
;
332 if ( y
> ras
.max_ey
) ras
.max_ey
= y
;
335 /* truncate the bounding box to integer pixels */
336 ras
.min_ex
= ras
.min_ex
>> 6;
337 ras
.min_ey
= ras
.min_ey
>> 6;
338 ras
.max_ex
= ( ras
.max_ex
+ 63 ) >> 6;
339 ras
.max_ey
= ( ras
.max_ey
+ 63 ) >> 6;
343 /*************************************************************************/
345 /* Record the current cell in the table. */
348 int record_cell( RAS_ARG
)
353 if ( !ras
.invalid
&& ( ras
.area
| ras
.cover
) )
355 if ( ras
.num_cells
>= ras
.max_cells
)
358 cell
= ras
.cells
+ ras
.num_cells
++;
359 cell
->x
= ras
.ex
- ras
.min_ex
;
360 cell
->y
= ras
.ey
- ras
.min_ey
;
361 cell
->area
= ras
.area
;
362 cell
->cover
= ras
.cover
;
369 /*************************************************************************/
371 /* Set the current cell to a new position. */
374 int set_cell( RAS_ARG_ TScan ex
,
377 int invalid
, record
, clean
;
380 /* Move the cell pointer to a new position. We set the `invalid' */
381 /* flag to indicate that the cell isn't part of those we're interested */
382 /* in during the render phase. This means that: */
384 /* . the new vertical position must be within min_ey..max_ey-1. */
385 /* . the new horizontal position must be strictly less than max_ex */
387 /* Note that if a cell is to the left of the clipping region, it is */
388 /* actually set to the (min_ex-1) horizontal position. */
393 invalid
= ( ey
< ras
.min_ey
|| ey
>= ras
.max_ey
|| ex
>= ras
.max_ex
);
396 /* All cells that are on the left of the clipping region go to the */
397 /* min_ex - 1 horizontal position. */
398 if ( ex
< ras
.min_ex
)
401 /* if our position is new, then record the previous cell */
402 if ( ex
!= ras
.ex
|| ey
!= ras
.ey
)
405 clean
= ras
.invalid
; /* do not clean if we didn't move from */
409 /* record the previous cell if needed (i.e., if we changed the cell */
410 /* position, of changed the `invalid' flag) */
411 if ( ( ras
.invalid
!= invalid
|| record
) && record_cell( RAS_VAR
) )
420 ras
.invalid
= invalid
;
427 /*************************************************************************/
429 /* Start a new contour at a given cell. */
432 void start_cell( RAS_ARG_ TScan ex
,
435 if ( ex
< ras
.min_ex
)
442 ras
.last_ey
= SUBPIXELS( ey
);
445 (void)set_cell( RAS_VAR_ ex
, ey
);
449 /*************************************************************************/
451 /* Render a scanline as one or more cells. */
454 int render_scanline( RAS_ARG_ TScan ey
,
460 TScan ex1
, ex2
, fx1
, fx2
, delta
;
462 int incr
, lift
, mod
, rem
;
467 ex1
= TRUNC( x1
); /* if (ex1 >= ras.max_ex) ex1 = ras.max_ex-1; */
468 ex2
= TRUNC( x2
); /* if (ex2 >= ras.max_ex) ex2 = ras.max_ex-1; */
469 fx1
= x1
- SUBPIXELS( ex1
);
470 fx2
= x2
- SUBPIXELS( ex2
);
472 /* trivial case. Happens often */
474 return set_cell( RAS_VAR_ ex2
, ey
);
476 /* everything is located in a single cell. That is easy! */
481 ras
.area
+= ( fx1
+ fx2
) * delta
;
486 /* ok, we'll have to render a run of adjacent cells on the same */
489 p
= ( ONE_PIXEL
- fx1
) * ( y2
- y1
);
495 p
= fx1
* ( y2
- y1
);
509 ras
.area
+= ( fx1
+ first
) * delta
;
513 if ( set_cell( RAS_VAR_ ex1
, ey
) )
519 p
= ONE_PIXEL
* ( y2
- y1
);
540 ras
.area
+= ONE_PIXEL
* delta
;
544 if ( set_cell( RAS_VAR_ ex1
, ey
) )
550 ras
.area
+= ( fx2
+ ONE_PIXEL
- first
) * delta
;
560 /*************************************************************************/
562 /* Render a given line as a series of scanlines. */
565 int render_line( RAS_ARG_ TPos to_x
,
568 TScan ey1
, ey2
, fy1
, fy2
;
570 int p
, rem
, mod
, lift
, delta
, first
, incr
;
573 ey1
= TRUNC( ras
.last_ey
);
574 ey2
= TRUNC( to_y
); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
575 fy1
= ras
.y
- ras
.last_ey
;
576 fy2
= to_y
- SUBPIXELS( ey2
);
581 /* XXX: we should do something about the trivial case where dx == 0, */
582 /* as it happens very often! */
584 /* perform vertical clipping */
596 if ( min
>= ras
.max_ey
|| max
< ras
.min_ey
)
600 /* everything is on a single scanline */
603 if ( render_scanline( RAS_VAR_ ey1
, ras
.x
, fy1
, to_x
, fy2
) )
608 /* ok, we have to render several scanlines */
609 p
= ( ONE_PIXEL
- fy1
) * dx
;
630 if ( render_scanline( RAS_VAR_ ey1
, ras
.x
, fy1
, x
, first
) )
634 if ( set_cell( RAS_VAR_
TRUNC( x
), ey1
) )
660 if ( render_scanline( RAS_VAR_ ey1
,
661 x
, ONE_PIXEL
- first
, x2
, first
) )
665 if ( set_cell( RAS_VAR_
TRUNC( x
), ey1
) )
670 if ( render_scanline( RAS_VAR_ ey1
,
671 x
, ONE_PIXEL
- first
, to_x
, fy2
) )
677 ras
.last_ey
= SUBPIXELS( ey2
);
687 void split_conic( FT_Vector
* base
)
692 base
[4].x
= base
[2].x
;
694 a
= base
[3].x
= ( base
[2].x
+ b
) / 2;
695 b
= base
[1].x
= ( base
[0].x
+ b
) / 2;
696 base
[2].x
= ( a
+ b
) / 2;
698 base
[4].y
= base
[2].y
;
700 a
= base
[3].y
= ( base
[2].y
+ b
) / 2;
701 b
= base
[1].y
= ( base
[0].y
+ b
) / 2;
702 base
[2].y
= ( a
+ b
) / 2;
707 int render_conic( RAS_ARG_ FT_Vector
* control
,
716 dx
= DOWNSCALE( ras
.x
) + to
->x
- ( control
->x
<< 1 );
719 dy
= DOWNSCALE( ras
.y
) + to
->y
- ( control
->y
<< 1 );
726 dx
= dx
/ ras
.conic_level
;
733 /* a shortcut to speed things up */
736 /* we compute the mid-point directly in order to avoid */
737 /* calling split_conic() */
738 TPos to_x
, to_y
, mid_x
, mid_y
;
741 to_x
= UPSCALE( to
->x
);
742 to_y
= UPSCALE( to
->y
);
743 mid_x
= ( ras
.x
+ to_x
+ 2 * UPSCALE( control
->x
) ) / 4;
744 mid_y
= ( ras
.y
+ to_y
+ 2 * UPSCALE( control
->y
) ) / 4;
746 return render_line( RAS_VAR_ mid_x
, mid_y
) ||
747 render_line( RAS_VAR_ to_x
, to_y
);
751 levels
= ras
.lev_stack
;
755 arc
[0].x
= UPSCALE( to
->x
);
756 arc
[0].y
= UPSCALE( to
->y
);
757 arc
[1].x
= UPSCALE( control
->x
);
758 arc
[1].y
= UPSCALE( control
->y
);
767 /* check that the arc crosses the current band */
771 min
= max
= arc
[0].y
;
774 if ( y
< min
) min
= y
;
775 if ( y
> max
) max
= y
;
778 if ( y
< min
) min
= y
;
779 if ( y
> max
) max
= y
;
781 if ( TRUNC( min
) >= ras
.max_ey
|| TRUNC( max
) < 0 )
787 levels
[top
] = levels
[top
- 1] = level
- 1;
793 TPos to_x
, to_y
, mid_x
, mid_y
;
798 mid_x
= ( ras
.x
+ to_x
+ 2 * arc
[1].x
) / 4;
799 mid_y
= ( ras
.y
+ to_y
+ 2 * arc
[1].y
) / 4;
801 if ( render_line( RAS_VAR_ mid_x
, mid_y
) ||
802 render_line( RAS_VAR_ to_x
, to_y
) )
814 void split_cubic( FT_Vector
* base
)
819 base
[6].x
= base
[3].x
;
822 base
[1].x
= a
= ( base
[0].x
+ c
) / 2;
823 base
[5].x
= b
= ( base
[3].x
+ d
) / 2;
825 base
[2].x
= a
= ( a
+ c
) / 2;
826 base
[4].x
= b
= ( b
+ c
) / 2;
827 base
[3].x
= ( a
+ b
) / 2;
829 base
[6].y
= base
[3].y
;
832 base
[1].y
= a
= ( base
[0].y
+ c
) / 2;
833 base
[5].y
= b
= ( base
[3].y
+ d
) / 2;
835 base
[2].y
= a
= ( a
+ c
) / 2;
836 base
[4].y
= b
= ( b
+ c
) / 2;
837 base
[3].y
= ( a
+ b
) / 2;
842 int render_cubic( RAS_ARG_ FT_Vector
* control1
,
852 dx
= DOWNSCALE( ras
.x
) + to
->x
- ( control1
->x
<< 1 );
855 dy
= DOWNSCALE( ras
.y
) + to
->y
- ( control1
->y
<< 1 );
862 dx
= DOWNSCALE( ras
.x
) + to
->x
- 3 * ( control1
->x
+ control2
->x
);
865 dy
= DOWNSCALE( ras
.y
) + to
->y
- 3 * ( control1
->x
+ control2
->y
);
873 da
= da
/ ras
.cubic_level
;
874 db
= db
/ ras
.conic_level
;
875 while ( da
> 0 || db
> 0 )
884 TPos to_x
, to_y
, mid_x
, mid_y
;
887 to_x
= UPSCALE( to
->x
);
888 to_y
= UPSCALE( to
->y
);
889 mid_x
= ( ras
.x
+ to_x
+
890 3 * UPSCALE( control1
->x
+ control2
->x
) ) / 8;
891 mid_y
= ( ras
.y
+ to_y
+
892 3 * UPSCALE( control1
->y
+ control2
->y
) ) / 8;
894 return render_line( RAS_VAR_ mid_x
, mid_y
) ||
895 render_line( RAS_VAR_ to_x
, to_y
);
899 arc
[0].x
= UPSCALE( to
->x
);
900 arc
[0].y
= UPSCALE( to
->y
);
901 arc
[1].x
= UPSCALE( control2
->x
);
902 arc
[1].y
= UPSCALE( control2
->y
);
903 arc
[2].x
= UPSCALE( control1
->x
);
904 arc
[2].y
= UPSCALE( control1
->y
);
908 levels
= ras
.lev_stack
;
917 /* check that the arc crosses the current band */
921 min
= max
= arc
[0].y
;
923 if ( y
< min
) min
= y
;
924 if ( y
> max
) max
= y
;
926 if ( y
< min
) min
= y
;
927 if ( y
> max
) max
= y
;
929 if ( y
< min
) min
= y
;
930 if ( y
> max
) max
= y
;
931 if ( TRUNC( min
) >= ras
.max_ey
|| TRUNC( max
) < 0 )
936 levels
[top
] = levels
[top
- 1] = level
- 1;
942 TPos to_x
, to_y
, mid_x
, mid_y
;
947 mid_x
= ( ras
.x
+ to_x
+ 3 * ( arc
[1].x
+ arc
[2].x
) ) / 8;
948 mid_y
= ( ras
.y
+ to_y
+ 3 * ( arc
[1].y
+ arc
[2].y
) ) / 8;
950 if ( render_line( RAS_VAR_ mid_x
, mid_y
) ||
951 render_line( RAS_VAR_ to_x
, to_y
) )
961 /* a macro comparing two cell pointers. Returns true if a <= b. */
964 #define PACK( a ) ( ( (long)(a)->y << 16 ) + (a)->x )
965 #define LESS_THAN( a, b ) ( PACK( a ) < PACK( b ) )
969 #define LESS_THAN( a, b ) ( (a)->y < (b)->y || \
970 ( (a)->y == (b)->y && (a)->x < (b)->x ) )
974 #define SWAP_CELLS( a, b, temp ) do \
985 /* a simple shell sort algorithm that works directly on our */
988 void shell_sort ( PCell cells
,
991 PCell i
, j
, limit
= cells
+ count
;
996 /* compute initial gap */
997 for ( gap
= 0; ++gap
< count
; gap
*= 3 )
1002 for ( i
= cells
+ gap
; i
< limit
; i
++ )
1004 for ( j
= i
- gap
; ; j
-= gap
)
1009 if ( LESS_THAN( j
, k
) )
1012 SWAP_CELLS( j
, k
, temp
);
1014 if ( j
< cells
+ gap
)
1021 #endif /* SHELL_SORT */
1026 /* This is a non-recursive quicksort that directly process our cells */
1027 /* array. It should be faster than calling the stdlib qsort(), and we */
1028 /* can even tailor our insertion threshold... */
1030 #define QSORT_THRESHOLD 9 /* below this size, a sub-array will be sorted */
1031 /* through a normal insertion sort */
1034 void quick_sort( PCell cells
,
1037 PCell stack
[40]; /* should be enough ;-) */
1038 PCell
* top
; /* top of stack */
1043 limit
= cells
+ count
;
1049 int len
= limit
- base
;
1053 if ( len
> QSORT_THRESHOLD
)
1055 /* we use base + len/2 as the pivot */
1056 pivot
= base
+ len
/ 2;
1057 SWAP_CELLS( base
, pivot
, temp
);
1062 /* now ensure that *i <= *base <= *j */
1063 if ( LESS_THAN( j
, i
) )
1064 SWAP_CELLS( i
, j
, temp
);
1066 if ( LESS_THAN( base
, i
) )
1067 SWAP_CELLS( base
, i
, temp
);
1069 if ( LESS_THAN( j
, base
) )
1070 SWAP_CELLS( base
, j
, temp
);
1074 do i
++; while ( LESS_THAN( i
, base
) );
1075 do j
--; while ( LESS_THAN( base
, j
) );
1080 SWAP_CELLS( i
, j
, temp
);
1083 SWAP_CELLS( base
, j
, temp
);
1085 /* now, push the largest sub-array */
1086 if ( j
- base
> limit
- i
)
1102 /* the sub-array is small, perform insertion sort */
1106 for ( ; i
< limit
; j
= i
, i
++ )
1108 for ( ; LESS_THAN( j
+ 1, j
); j
-- )
1110 SWAP_CELLS( j
+ 1, j
, temp
);
1127 #endif /* QUICK_SORT */
1134 int check_sort( PCell cells
,
1140 for ( p
= cells
+ count
- 2; p
>= cells
; p
-- )
1143 if ( !LESS_THAN( p
, q
) )
1149 #endif /* DEBUG_SORT */
1150 #endif /* DEBUG_GRAYS */
1154 int Move_To( FT_Vector
* to
,
1160 /* record current cell, if any */
1161 record_cell( (PRaster
)raster
);
1163 /* start to a new position */
1164 x
= UPSCALE( to
->x
);
1165 y
= UPSCALE( to
->y
);
1166 start_cell( (PRaster
)raster
, TRUNC( x
), TRUNC( y
) );
1167 ((PRaster
)raster
)->x
= x
;
1168 ((PRaster
)raster
)->y
= y
;
1174 int Line_To( FT_Vector
* to
,
1177 return render_line( (PRaster
)raster
,
1178 UPSCALE( to
->x
), UPSCALE( to
->y
) );
1183 int Conic_To( FT_Vector
* control
,
1187 return render_conic( (PRaster
)raster
, control
, to
);
1192 int Cubic_To( FT_Vector
* control1
,
1193 FT_Vector
* control2
,
1197 return render_cubic( (PRaster
)raster
, control1
, control2
, to
);
1202 void grays_render_span( int y
,
1208 FT_Bitmap
* map
= &raster
->target
;
1211 /* first of all, compute the scanline offset */
1212 p
= (unsigned char*)map
->buffer
- y
* map
->pitch
;
1213 if ( map
->pitch
>= 0 )
1214 p
+= ( map
->rows
- 1 ) * map
->pitch
;
1216 for ( ; count
> 0; count
--, spans
++ )
1218 if ( spans
->coverage
)
1220 memset( p
+ spans
->x
, (unsigned char)spans
->coverage
, spans
->len
);
1224 limit
= q
+ spans
->len
;
1225 for ( ; q
< limit
; q
++ )
1226 q
[0] = (unsigned char)spans
->coverage
;
1238 void dump_cells( RAS_ARG
)
1245 limit
= cell
+ ras
.num_cells
;
1247 for ( ; cell
< limit
; cell
++ )
1251 fprintf( stderr
, "\n%2d: ", cell
->y
);
1254 fprintf( stderr
, "[%d %d %d]",
1255 cell
->x
, cell
->area
, cell
->cover
);
1257 fprintf(stderr
, "\n" );
1260 #endif /* DEBUG_GRAYS */
1264 void grays_hline( RAS_ARG_ TScan x
,
1274 /* compute the coverage line's coverage, depending on the */
1275 /* outline fill rule */
1277 /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
1279 coverage
= area
>> ( PIXEL_BITS
* 2 + 1 - 8); /* use range 0..256 */
1281 if ( ras
.outline
.flags
& ft_outline_even_odd_fill
)
1284 coverage
= -coverage
;
1286 while ( coverage
>= 512 )
1289 if ( coverage
> 256 )
1290 coverage
= 512 - coverage
;
1291 else if ( coverage
== 256 )
1296 /* normal non-zero winding rule */
1298 coverage
= -coverage
;
1300 if ( coverage
>= 256 )
1309 /* see if we can add this span to the current list */
1310 count
= ras
.num_gray_spans
;
1311 span
= ras
.gray_spans
+ count
- 1;
1314 (int)span
->x
+ span
->len
== (int)x
&&
1315 span
->coverage
== coverage
)
1317 span
->len
+= acount
;
1321 if ( ras
.span_y
!= y
|| count
>= FT_MAX_GRAY_SPANS
)
1323 if ( ras
.render_span
)
1324 ras
.render_span( ras
.span_y
, count
, ras
.gray_spans
,
1325 ras
.render_span_data
);
1326 /* ras.render_span( span->y, ras.gray_spans, count ); */
1330 if ( ras
.span_y
>= 0 )
1335 fprintf( stderr
, "y=%3d ", ras
.span_y
);
1336 span
= ras
.gray_spans
;
1337 for ( n
= 0; n
< count
; n
++, span
++ )
1338 fprintf( stderr
, "[%d..%d]:%02x ",
1339 span
->x
, span
->x
+ span
->len
- 1, span
->coverage
);
1340 fprintf( stderr
, "\n" );
1343 #endif /* DEBUG_GRAYS */
1345 ras
.num_gray_spans
= 0;
1349 span
= ras
.gray_spans
;
1354 /* add a gray span to the current list */
1356 span
->len
= (unsigned short)acount
;
1357 span
->coverage
= (unsigned char)coverage
;
1358 ras
.num_gray_spans
++;
1364 void grays_sweep( RAS_ARG_ FT_Bitmap
* target
)
1366 TScan x
, y
, cover
, area
;
1367 PCell start
, cur
, limit
;
1369 FT_UNUSED( target
);
1373 limit
= cur
+ ras
.num_cells
;
1377 ras
.num_gray_spans
= 0;
1386 cover
+= start
->cover
;
1388 /* accumulate all start cells */
1392 if ( cur
>= limit
|| cur
->y
!= start
->y
|| cur
->x
!= start
->x
)
1396 cover
+= cur
->cover
;
1399 /* if the start cell has a non-null area, we must draw an */
1400 /* individual gray pixel there */
1401 if ( area
&& x
>= 0 )
1403 grays_hline( RAS_VAR_ x
, y
, cover
* ( ONE_PIXEL
* 2 ) - area
, 1 );
1410 if ( cur
< limit
&& start
->y
== cur
->y
)
1412 /* draw a gray span between the start cell and the current one */
1414 grays_hline( RAS_VAR_ x
, y
,
1415 cover
* ( ONE_PIXEL
* 2 ), cur
->x
- x
);
1419 /* draw a gray span until the end of the clipping region */
1420 if ( cover
&& x
< ras
.max_ex
- ras
.min_ex
)
1421 grays_hline( RAS_VAR_ x
, y
,
1422 cover
* ( ONE_PIXEL
* 2 ),
1423 ras
.max_ex
- x
- ras
.min_ex
);
1431 if ( ras
.render_span
&& ras
.num_gray_spans
> 0 )
1432 ras
.render_span( ras
.span_y
, ras
.num_gray_spans
,
1433 ras
.gray_spans
, ras
.render_span_data
);
1442 fprintf( stderr
, "y=%3d ", ras
.span_y
);
1443 span
= ras
.gray_spans
;
1444 for ( n
= 0; n
< ras
.num_gray_spans
; n
++, span
++ )
1445 fprintf( stderr
, "[%d..%d]:%02x ",
1446 span
->x
, span
->x
+ span
->len
- 1, span
->coverage
);
1447 fprintf( stderr
, "\n" );
1450 #endif /* DEBUG_GRAYS */
1457 /*************************************************************************/
1459 /* The following function should only compile in stand_alone mode, */
1460 /* i.e., when building this component without the rest of FreeType. */
1462 /*************************************************************************/
1464 /*************************************************************************/
1467 /* FT_Outline_Decompose */
1470 /* Walks over an outline's structure to decompose it into individual */
1471 /* segments and Bezier arcs. This function is also able to emit */
1472 /* `move to' and `close to' operations to indicate the start and end */
1473 /* of new contours in the outline. */
1476 /* outline :: A pointer to the source target. */
1478 /* interface :: A table of `emitters', i.e,. function pointers called */
1479 /* during decomposition to indicate path operations. */
1481 /* user :: A typeless pointer which is passed to each emitter */
1482 /* during the decomposition. It can be used to store */
1483 /* the state during the decomposition. */
1486 /* Error code. 0 means sucess. */
1489 int FT_Outline_Decompose( FT_Outline
* outline
,
1490 FT_Outline_Funcs
* interface
,
1494 #define SCALED( x ) ( ( (x) << shift ) - delta )
1497 FT_Vector v_control
;
1504 int n
; /* index of contour in outline */
1505 int first
; /* index of first point in contour */
1507 char tag
; /* current point's state */
1509 int shift
= interface
->shift
;
1510 FT_Pos delta
= interface
->delta
;
1515 for ( n
= 0; n
< outline
->n_contours
; n
++ )
1517 int last
; /* index of last point in contour */
1520 last
= outline
->contours
[n
];
1521 limit
= outline
->points
+ last
;
1523 v_start
= outline
->points
[first
];
1524 v_last
= outline
->points
[last
];
1526 v_start
.x
= SCALED( v_start
.x
); v_start
.y
= SCALED( v_start
.y
);
1527 v_last
.x
= SCALED( v_last
.x
); v_last
.y
= SCALED( v_last
.y
);
1529 v_control
= v_start
;
1531 point
= outline
->points
+ first
;
1532 tags
= outline
->tags
+ first
;
1533 tag
= FT_CURVE_TAG( tags
[0] );
1535 /* A contour cannot start with a cubic control point! */
1536 if ( tag
== FT_Curve_Tag_Cubic
)
1537 goto Invalid_Outline
;
1539 /* check first point to determine origin */
1540 if ( tag
== FT_Curve_Tag_Conic
)
1542 /* first point is conic control. Yes, this happens. */
1543 if ( FT_CURVE_TAG( outline
->tags
[last
] ) == FT_Curve_Tag_On
)
1545 /* start at last point if it is on the curve */
1551 /* if both first and last points are conic, */
1552 /* start at their middle and record its position */
1554 v_start
.x
= ( v_start
.x
+ v_last
.x
) / 2;
1555 v_start
.y
= ( v_start
.y
+ v_last
.y
) / 2;
1563 error
= interface
->move_to( &v_start
, user
);
1567 while ( point
< limit
)
1572 tag
= FT_CURVE_TAG( tags
[0] );
1575 case FT_Curve_Tag_On
: /* emit a single line_to */
1580 vec
.x
= SCALED( point
->x
);
1581 vec
.y
= SCALED( point
->y
);
1583 error
= interface
->line_to( &vec
, user
);
1589 case FT_Curve_Tag_Conic
: /* consume conic arcs */
1591 v_control
.x
= SCALED( point
->x
);
1592 v_control
.y
= SCALED( point
->y
);
1595 if ( point
< limit
)
1603 tag
= FT_CURVE_TAG( tags
[0] );
1605 vec
.x
= SCALED( point
->x
);
1606 vec
.y
= SCALED( point
->y
);
1608 if ( tag
== FT_Curve_Tag_On
)
1610 error
= interface
->conic_to( &v_control
, &vec
, user
);
1616 if ( tag
!= FT_Curve_Tag_Conic
)
1617 goto Invalid_Outline
;
1619 v_middle
.x
= ( v_control
.x
+ vec
.x
) / 2;
1620 v_middle
.y
= ( v_control
.y
+ vec
.y
) / 2;
1622 error
= interface
->conic_to( &v_control
, &v_middle
, user
);
1630 error
= interface
->conic_to( &v_control
, &v_start
, user
);
1634 default: /* FT_Curve_Tag_Cubic */
1636 FT_Vector vec1
, vec2
;
1639 if ( point
+ 1 > limit
||
1640 FT_CURVE_TAG( tags
[1] ) != FT_Curve_Tag_Cubic
)
1641 goto Invalid_Outline
;
1646 vec1
.x
= SCALED( point
[-2].x
); vec1
.y
= SCALED( point
[-2].y
);
1647 vec2
.x
= SCALED( point
[-1].x
); vec2
.y
= SCALED( point
[-1].y
);
1649 if ( point
<= limit
)
1654 vec
.x
= SCALED( point
->x
);
1655 vec
.y
= SCALED( point
->y
);
1657 error
= interface
->cubic_to( &vec1
, &vec2
, &vec
, user
);
1663 error
= interface
->cubic_to( &vec1
, &vec2
, &v_start
, user
);
1669 /* close the contour with a line segment */
1670 error
= interface
->line_to( &v_start
, user
);
1685 return ErrRaster_Invalid_Outline
;
1688 #endif /* _STANDALONE_ */
1691 typedef struct TBand_
1699 int grays_convert_glyph( RAS_ARG_ FT_Outline
* outline
)
1702 FT_Outline_Funcs interface
=
1704 (FT_Outline_MoveTo_Func
) Move_To
,
1705 (FT_Outline_LineTo_Func
) Line_To
,
1706 (FT_Outline_ConicTo_Func
)Conic_To
,
1707 (FT_Outline_CubicTo_Func
)Cubic_To
,
1712 TBand bands
[40], *band
;
1714 TPos min
, max
, max_y
;
1717 /* Set up state in the raster object */
1718 compute_cbox( RAS_VAR_ outline
);
1720 /* clip to target bitmap, exit if nothing to do */
1721 if ( ras
.max_ex
<= 0 || ras
.min_ex
>= ras
.target
.width
||
1722 ras
.max_ey
<= 0 || ras
.min_ey
>= ras
.target
.rows
)
1725 if ( ras
.min_ex
< 0 ) ras
.min_ex
= 0;
1726 if ( ras
.min_ey
< 0 ) ras
.min_ey
= 0;
1728 if ( ras
.max_ex
> ras
.target
.width
) ras
.max_ex
= ras
.target
.width
;
1729 if ( ras
.max_ey
> ras
.target
.rows
) ras
.max_ey
= ras
.target
.rows
;
1731 /* simple heuristic used to speed-up the bezier decomposition -- see */
1732 /* the code in render_conic() and render_cubic() for more details */
1733 ras
.conic_level
= 32;
1734 ras
.cubic_level
= 16;
1740 if ( ras
.max_ex
> 24 || ras
.max_ey
> 24 )
1742 if ( ras
.max_ex
> 120 || ras
.max_ey
> 120 )
1745 ras
.conic_level
<<= level
;
1746 ras
.cubic_level
<<= level
;
1749 /* setup vertical bands */
1750 num_bands
= ( ras
.max_ey
- ras
.min_ey
) / ras
.band_size
;
1751 if ( num_bands
== 0 ) num_bands
= 1;
1752 if ( num_bands
>= 39 ) num_bands
= 39;
1759 for ( n
= 0; n
< num_bands
; n
++, min
= max
)
1761 max
= min
+ ras
.band_size
;
1762 if ( n
== num_bands
- 1 || max
> max_y
)
1769 while ( band
>= bands
)
1771 FT_Pos bottom
, top
, middle
;
1777 ras
.min_ey
= band
->min
;
1778 ras
.max_ey
= band
->max
;
1780 error
= FT_Outline_Decompose( outline
, &interface
, &ras
) ||
1781 record_cell( RAS_VAR
);
1786 shell_sort( ras
.cells
, ras
.num_cells
);
1788 quick_sort( ras
.cells
, ras
.num_cells
);
1792 check_sort( ras
.cells
, ras
.num_cells
);
1793 dump_cells( RAS_VAR
);
1796 grays_sweep( RAS_VAR_
&ras
.target
);
1801 /* render pool overflow, we will reduce the render band by half */
1804 middle
= bottom
+ ( ( top
- bottom
) >> 1 );
1806 /* waoow! This is too complex for a single scanline, something */
1807 /* must be really rotten here! */
1808 if ( middle
== bottom
)
1811 fprintf( stderr
, "Rotten glyph!\n" );
1816 if ( bottom
-top
>= ras
.band_size
)
1819 band
[1].min
= bottom
;
1820 band
[1].max
= middle
;
1821 band
[0].min
= middle
;
1827 if ( ras
.band_shoot
> 8 && ras
.band_size
> 16 )
1828 ras
.band_size
= ras
.band_size
/ 2;
1835 int grays_raster_render( PRaster raster
,
1836 FT_Raster_Params
* params
)
1838 FT_Outline
* outline
= (FT_Outline
*)params
->source
;
1839 FT_Bitmap
* target_map
= params
->target
;
1842 if ( !raster
|| !raster
->cells
|| !raster
->max_cells
)
1845 /* return immediately if the outline is empty */
1846 if ( outline
->n_points
== 0 || outline
->n_contours
<= 0 )
1849 if ( !outline
|| !outline
->contours
|| !outline
->points
)
1850 return ErrRaster_Invalid_Outline
;
1852 if ( outline
->n_points
!=
1853 outline
->contours
[outline
->n_contours
- 1] + 1 )
1854 return ErrRaster_Invalid_Outline
;
1856 if ( !target_map
|| !target_map
->buffer
)
1859 /* XXX: this version does not support monochrome rendering yet! */
1860 if ( !(params
->flags
& ft_raster_flag_aa
) )
1861 return ErrRaster_Invalid_Mode
;
1863 ras
.outline
= *outline
;
1864 ras
.target
= *target_map
;
1868 ras
.render_span
= (FT_Raster_Span_Func
)grays_render_span
;
1869 ras
.render_span_data
= &ras
;
1871 if ( params
->flags
& ft_raster_flag_direct
)
1873 ras
.render_span
= (FT_Raster_Span_Func
)params
->gray_spans
;
1874 ras
.render_span_data
= params
->user
;
1877 return grays_convert_glyph( (PRaster
)raster
, outline
);
1881 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
1882 /**** a static object. *****/
1887 int grays_raster_new( void* memory
,
1888 FT_Raster
* araster
)
1890 static TRaster the_raster
;
1892 FT_UNUSED( memory
);
1895 *araster
= (FT_Raster
)&the_raster
;
1896 memset( &the_raster
, 0, sizeof ( the_raster
) );
1903 void grays_raster_done( FT_Raster raster
)
1906 FT_UNUSED( raster
);
1909 #else /* _STANDALONE_ */
1912 int grays_raster_new( FT_Memory memory
,
1913 FT_Raster
* araster
)
1920 if ( !ALLOC( raster
, sizeof ( TRaster
) ) )
1922 raster
->memory
= memory
;
1923 *araster
= (FT_Raster
)raster
;
1931 void grays_raster_done( FT_Raster raster
)
1933 FT_Memory memory
= (FT_Memory
)((PRaster
)raster
)->memory
;
1939 #endif /* _STANDALONE_ */
1943 void grays_raster_reset( FT_Raster raster
,
1944 const char* pool_base
,
1947 PRaster rast
= (PRaster
)raster
;
1950 if ( raster
&& pool_base
&& pool_size
>= 4096 )
1951 init_cells( rast
, (char*)pool_base
, pool_size
);
1953 rast
->band_size
= ( pool_size
/ sizeof ( TCell
) ) / 8;
1957 FT_Raster_Funcs ft_grays_raster
=
1959 ft_glyph_format_outline
,
1961 (FT_Raster_New_Func
) grays_raster_new
,
1962 (FT_Raster_Reset_Func
) grays_raster_reset
,
1963 (FT_Raster_Set_Mode_Func
)0,
1964 (FT_Raster_Render_Func
) grays_raster_render
,
1965 (FT_Raster_Done_Func
) grays_raster_done