1 /***************************************************************************/
5 /* The FreeType glyph rasterizer (body). */
7 /* Copyright 1996-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 is a rewrite of the FreeType 1.x scan-line converter */
22 /*************************************************************************/
26 #include <freetype/internal/ftcalc.h> /* for FT_MulDiv() only */
29 /*************************************************************************/
31 /* A simple technical note on how the raster works */
32 /* ----------------------------------------------- */
34 /* Converting an outline into a bitmap is achieved in several steps: */
36 /* 1 - Decomposing the outline into successive `profiles'. Each */
37 /* profile is simply an array of scanline intersections on a given */
38 /* dimension. A profile's main attributes are */
40 /* o its scanline position boundaries, i.e. `Ymin' and `Ymax'. */
42 /* o an array of intersection coordinates for each scanline */
43 /* between `Ymin' and `Ymax'. */
45 /* o a direction, indicating whether it was built going `up' or */
46 /* `down', as this is very important for filling rules. */
48 /* 2 - Sweeping the target map's scanlines in order to compute segment */
49 /* `spans' which are then filled. Additionally, this pass */
50 /* performs drop-out control. */
52 /* The outline data is parsed during step 1 only. The profiles are */
53 /* built from the bottom of the render pool, used as a stack. The */
54 /* following graphics shows the profile list under construction: */
56 /* ____________________________________________________________ _ _ */
58 /* | profile | coordinates for | profile | coordinates for |--> */
59 /* | 1 | profile 1 | 2 | profile 2 |--> */
60 /* |_________|___________________|_________|_________________|__ _ _ */
64 /* start of render pool top */
66 /* The top of the profile stack is kept in the `top' variable. */
68 /* As you can see, a profile record is pushed on top of the render */
69 /* pool, which is then followed by its coordinates/intersections. If */
70 /* a change of direction is detected in the outline, a new profile is */
71 /* generated until the end of the outline. */
73 /* Note that when all profiles have been generated, the function */
74 /* Finalize_Profile_Table() is used to record, for each profile, its */
75 /* bottom-most scanline as well as the scanline above its upmost */
76 /* boundary. These positions are called `y-turns' because they (sort */
77 /* of) correspond to local extrema. They are stored in a sorted list */
78 /* built from the top of the render pool as a downwards stack: */
80 /* _ _ _______________________________________ */
82 /* <--| sorted list of | */
83 /* <--| extrema scanlines | */
84 /* _ _ __________________|____________________| */
88 /* maxBuff sizeBuff = end of pool */
90 /* This list is later used during the sweep phase in order to */
91 /* optimize performance (see technical note on the sweep below). */
93 /* Of course, the raster detects whether the two stacks collide and */
94 /* handles the situation propertly. */
96 /*************************************************************************/
99 /*************************************************************************/
100 /*************************************************************************/
102 /** CONFIGURATION MACROS **/
104 /*************************************************************************/
105 /*************************************************************************/
107 /* define DEBUG_RASTER if you want to compile a debugging version */
108 #define xxxDEBUG_RASTER
110 /* The default render pool size in bytes */
111 #define RASTER_RENDER_POOL 8192
113 /* undefine FT_RASTER_OPTION_ANTI_ALIASING if you do not want to support */
114 /* 5-levels anti-aliasing */
115 #ifdef FT_CONFIG_OPTION_5_GRAY_LEVELS
116 #define FT_RASTER_OPTION_ANTI_ALIASING
119 /* The size of the two-lines intermediate bitmap used */
120 /* for anti-aliasing, in bytes. */
121 #define RASTER_GRAY_LINES 2048
124 /*************************************************************************/
125 /*************************************************************************/
127 /** OTHER MACROS (do not change) **/
129 /*************************************************************************/
130 /*************************************************************************/
132 /*************************************************************************/
134 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
135 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
136 /* messages during execution. */
139 #define FT_COMPONENT trace_raster
145 /* This macro is used to indicate that a function parameter is unused. */
146 /* Its purpose is simply to reduce compiler warnings. Note also that */
147 /* simply defining it as `(void)x' doesn't avoid warnings with certain */
148 /* ANSI compilers (e.g. LCC). */
149 #define FT_UNUSED( x ) (x) = (x)
151 /* Disable the tracing mechanism for simplicity -- developers can */
152 /* activate it easily by redefining these two macros. */
154 #define FT_ERROR( x ) do ; while ( 0 ) /* nothing */
158 #define FT_TRACE( x ) do ; while ( 0 ) /* nothing */
161 #define Raster_Err_None 0
162 #define Raster_Err_Not_Ini -1
163 #define Raster_Err_Overflow -2
164 #define Raster_Err_Neg_Height -3
165 #define Raster_Err_Invalid -4
166 #define Raster_Err_Unsupported -5
169 #else /* _STANDALONE_ */
172 #include <freetype/internal/ftobjs.h>
173 #include <freetype/internal/ftdebug.h> /* for FT_TRACE() and FT_ERROR() */
175 #define Raster_Err_None FT_Err_Ok
176 #define Raster_Err_Not_Ini FT_Err_Raster_Uninitialized
177 #define Raster_Err_Overflow FT_Err_Raster_Overflow
178 #define Raster_Err_Neg_Height FT_Err_Raster_Negative_Height
179 #define Raster_Err_Invalid FT_Err_Invalid_Outline
180 #define Raster_Err_Unsupported FT_Err_Unimplemented_Feature
183 #endif /* _STANDALONE_ */
186 /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */
187 /* typically a small value and the result of a*b is known to fit into */
189 #define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
191 /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
192 /* for clipping computations. It simply uses the FT_MulDiv() function */
193 /* defined in `ftcalc.h'. */
194 #define SMulDiv FT_MulDiv
196 /* The rasterizer is a very general purpose component; please leave */
197 /* the following redefinitions there (you never know your target */
209 #define NULL (void*)0
221 #define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
222 /* Setting this constant to more than 32 is a */
223 /* pure waste of space. */
225 #define Pixel_Bits 6 /* fractional bits of *input* coordinates */
228 /*************************************************************************/
229 /*************************************************************************/
231 /** SIMPLE TYPE DECLARATIONS **/
233 /*************************************************************************/
234 /*************************************************************************/
237 typedef unsigned int UInt
;
239 typedef unsigned short UShort
, *PUShort
;
240 typedef long Long
, *PLong
;
241 typedef unsigned long ULong
;
243 typedef unsigned char Byte
, *PByte
;
246 typedef struct TPoint_
263 /* States of each line, arc, and profile */
264 typedef enum TStates_
274 typedef struct TProfile_ TProfile
;
275 typedef TProfile
* PProfile
;
279 FT_F26Dot6 X
; /* current coordinate during sweep */
280 PProfile link
; /* link to next profile - various purpose */
281 PLong offset
; /* start of profile's data in render pool */
282 Int flow
; /* Profile orientation: Asc/Descending */
283 Long height
; /* profile's height in scanlines */
284 Long start
; /* profile's starting scanline */
286 UShort countL
; /* number of lines to step before this */
287 /* profile becomes drawable */
289 PProfile next
; /* next profile in same contour, used */
290 /* during drop-out control */
293 typedef PProfile TProfileList
;
294 typedef PProfile
* PProfileList
;
297 /* Simple record used to implement a stack of bands, required */
298 /* by the sub-banding mechanism */
299 typedef struct TBand_
301 Short y_min
; /* band's minimum */
302 Short y_max
; /* band's maximum */
307 #define AlignProfileSize \
308 ( ( sizeof ( TProfile ) + sizeof ( long ) - 1 ) / sizeof ( long ) )
311 #ifdef TT_STATIC_RASTER
314 #define RAS_ARGS /* void */
315 #define RAS_ARG /* void */
317 #define RAS_VARS /* void */
318 #define RAS_VAR /* void */
320 #define FT_UNUSED_RASTER do ; while ( 0 )
323 #else /* TT_STATIC_RASTER */
326 #define RAS_ARGS TRaster_Instance* raster,
327 #define RAS_ARG TRaster_Instance* raster
329 #define RAS_VARS raster,
330 #define RAS_VAR raster
332 #define FT_UNUSED_RASTER FT_UNUSED( raster )
335 #endif /* TT_STATIC_RASTER */
338 typedef struct TRaster_Instance_ TRaster_Instance
;
341 /* prototypes used for sweep function dispatch */
342 typedef void Function_Sweep_Init( RAS_ARGS Short
* min
,
345 typedef void Function_Sweep_Span( RAS_ARGS Short y
,
351 typedef void Function_Sweep_Step( RAS_ARG
);
354 /* NOTE: These operations are only valid on 2's complement processors */
356 #define FLOOR( x ) ( (x) & -ras.precision )
357 #define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
358 #define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )
359 #define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
360 #define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half )
362 /* Note that I have moved the location of some fields in the */
363 /* structure to ensure that the most used variables are used */
364 /* at the top. Thus, their offset can be coded with less */
365 /* opcodes, and it results in a smaller executable. */
367 struct TRaster_Instance_
369 Int precision_bits
; /* precision related variables */
375 Int precision_jitter
;
377 Int scale_shift
; /* == precision_shift for bitmaps */
378 /* == precision_shift+1 for pixmaps */
380 PLong buff
; /* The profiles buffer */
381 PLong sizeBuff
; /* Render pool size */
382 PLong maxBuff
; /* Profiles buffer size */
383 PLong top
; /* Current cursor in buffer */
387 Int numTurns
; /* number of Y-turns in outline */
389 TPoint
* arc
; /* current Bezier arc pointer */
391 UShort bWidth
; /* target bitmap width */
392 PByte bTarget
; /* target bitmap buffer */
393 PByte gTarget
; /* target pixmap buffer */
395 Long lastX
, lastY
, minY
, maxY
;
397 UShort num_Profs
; /* current number of profiles */
399 Bool fresh
; /* signals a fresh new profile which */
400 /* 'start' field must be completed */
401 Bool joint
; /* signals that the last arc ended */
402 /* exactly on a scanline. Allows */
403 /* removal of doublets */
404 PProfile cProfile
; /* current profile */
405 PProfile fProfile
; /* head of linked list of profiles */
406 PProfile gProfile
; /* contour's first profile in case */
409 TStates state
; /* rendering state */
411 FT_Bitmap target
; /* description of target bit/pixmap */
414 Long traceOfs
; /* current offset in target bitmap */
415 Long traceG
; /* current offset in target pixmap */
417 Short traceIncr
; /* sweep's increment in target bitmap */
419 Short gray_min_x
; /* current min x during gray rendering */
420 Short gray_max_x
; /* current max x during gray rendering */
422 /* dispatch variables */
424 Function_Sweep_Init
* Proc_Sweep_Init
;
425 Function_Sweep_Span
* Proc_Sweep_Span
;
426 Function_Sweep_Span
* Proc_Sweep_Drop
;
427 Function_Sweep_Step
* Proc_Sweep_Step
;
429 Byte dropOutControl
; /* current drop_out control method */
431 Bool second_pass
; /* indicates wether a horizontal pass */
432 /* should be performed to control */
433 /* drop-out accurately when calling */
434 /* Render_Glyph. Note that there is */
435 /* no horizontal pass during gray */
438 TPoint arcs
[2 * MaxBezier
+ 1]; /* The Bezier stack */
440 TBand band_stack
[16]; /* band stack used for sub-banding */
441 Int band_top
; /* band stack top */
443 Int count_table
[256]; /* Look-up table used to quickly count */
444 /* set bits in a gray 2x2 cell */
448 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
450 Byte grays
[5]; /* Palette of gray levels used for */
453 Byte gray_lines
[RASTER_GRAY_LINES
];
454 /* Intermediate table used to render the */
455 /* graylevels pixmaps. */
456 /* gray_lines is a buffer holding two */
457 /* monochrome scanlines */
459 Short gray_width
; /* width in bytes of one monochrome */
460 /* intermediate scanline of gray_lines. */
461 /* Each gray pixel takes 2 bits long there */
463 /* The gray_lines must hold 2 lines, thus with size */
464 /* in bytes of at least `gray_width*2'. */
466 #endif /* FT_RASTER_ANTI_ALIASING */
469 PByte flags
; /* current flags table */
470 PUShort outs
; /* current outlines table */
473 UShort nPoints
; /* number of points in current glyph */
474 Short nContours
; /* number of contours in current glyph */
480 #ifdef FT_CONFIG_OPTION_STATIC_RASTER
482 static TRaster_Instance cur_ras
;
487 #define ras (*raster)
489 #endif /* FT_CONFIG_OPTION_STATIC_RASTER */
492 /*************************************************************************/
493 /*************************************************************************/
495 /** PROFILES COMPUTATION **/
497 /*************************************************************************/
498 /*************************************************************************/
501 /*************************************************************************/
504 /* Set_High_Precision */
507 /* Sets precision variables according to param flag. */
510 /* High :: Set to True for high precision (typically for ppem < 18), */
511 /* false otherwise. */
514 void Set_High_Precision( RAS_ARGS Int High
)
518 ras
.precision_bits
= 10;
519 ras
.precision_step
= 128;
520 ras
.precision_jitter
= 24;
524 ras
.precision_bits
= 6;
525 ras
.precision_step
= 32;
526 ras
.precision_jitter
= 2;
529 FT_TRACE6(( "Set_High_Precision(%s)\n", High
? "true" : "false" ));
531 ras
.precision
= 1L << ras
.precision_bits
;
532 ras
.precision_half
= ras
.precision
/ 2;
533 ras
.precision_shift
= ras
.precision_bits
- Pixel_Bits
;
534 ras
.precision_mask
= -ras
.precision
;
538 /*************************************************************************/
544 /* Creates a new profile in the render pool. */
547 /* aState :: The state/orientation of the new profile. */
550 /* SUCCESS on success. FAILURE in case of overflow or of incoherent */
554 Bool
New_Profile( RAS_ARGS TStates aState
)
558 ras
.cProfile
= (PProfile
)ras
.top
;
559 ras
.fProfile
= ras
.cProfile
;
560 ras
.top
+= AlignProfileSize
;
563 if ( ras
.top
>= ras
.maxBuff
)
565 ras
.error
= Raster_Err_Overflow
;
572 ras
.cProfile
->flow
= Flow_Up
;
573 FT_TRACE6(( "New ascending profile = %lx\n", (long)ras
.cProfile
));
577 ras
.cProfile
->flow
= Flow_Down
;
578 FT_TRACE6(( "New descending profile = %lx\n", (long)ras
.cProfile
));
582 FT_ERROR(( "New_Profile: invalid profile direction!\n" ));
583 ras
.error
= Raster_Err_Invalid
;
587 ras
.cProfile
->start
= 0;
588 ras
.cProfile
->height
= 0;
589 ras
.cProfile
->offset
= ras
.top
;
590 ras
.cProfile
->link
= (PProfile
)0;
591 ras
.cProfile
->next
= (PProfile
)0;
594 ras
.gProfile
= ras
.cProfile
;
604 /*************************************************************************/
610 /* Finalizes the current profile. */
613 /* SUCCESS on success. FAILURE in case of overflow or incoherency. */
616 Bool
End_Profile( RAS_ARG
)
622 h
= ras
.top
- ras
.cProfile
->offset
;
626 FT_ERROR(( "End_Profile: negative height encountered!\n" ));
627 ras
.error
= Raster_Err_Neg_Height
;
633 FT_TRACE6(( "Ending profile %lx, start = %ld, height = %ld\n",
634 (long)ras
.cProfile
, ras
.cProfile
->start
, h
));
636 oldProfile
= ras
.cProfile
;
637 ras
.cProfile
->height
= h
;
638 ras
.cProfile
= (PProfile
)ras
.top
;
640 ras
.top
+= AlignProfileSize
;
642 ras
.cProfile
->height
= 0;
643 ras
.cProfile
->offset
= ras
.top
;
644 oldProfile
->next
= ras
.cProfile
;
648 if ( ras
.top
>= ras
.maxBuff
)
650 FT_TRACE1(( "overflow in End_Profile\n" ));
651 ras
.error
= Raster_Err_Overflow
;
661 /*************************************************************************/
667 /* Inserts a salient into the sorted list placed on top of the render */
671 /* New y scanline position. */
674 /* SUCCESS on success. FAILURE in case of overflow. */
677 Bool
Insert_Y_Turn( RAS_ARGS Int y
)
683 n
= ras
.numTurns
- 1;
684 y_turns
= ras
.sizeBuff
- ras
.numTurns
;
686 /* look for first y value that is <= */
687 while ( n
>= 0 && y
< y_turns
[n
] )
690 /* if it is <, simply insert it, ignore if == */
691 if ( n
>= 0 && y
> y_turns
[n
] )
702 if ( ras
.maxBuff
<= ras
.top
)
704 ras
.error
= Raster_Err_Overflow
;
709 ras
.sizeBuff
[-ras
.numTurns
] = y
;
716 /*************************************************************************/
719 /* Finalize_Profile_Table */
722 /* Adjusts all links in the profiles list. */
725 /* SUCCESS on success. FAILURE in case of overflow. */
728 Bool
Finalize_Profile_Table( RAS_ARG
)
743 p
->link
= (PProfile
)( p
->offset
+ p
->height
);
750 bottom
= p
->start
- p
->height
+1;
753 p
->offset
+= p
->height
- 1;
759 top
= p
->start
+ p
->height
- 1;
762 if ( Insert_Y_Turn( RAS_VARS bottom
) ||
763 Insert_Y_Turn( RAS_VARS top
+ 1 ) )
777 /*************************************************************************/
783 /* Subdivides one conic Bezier into two joint sub-arcs in the Bezier */
787 /* None (subdivided Bezier is taken from the top of the stack). */
790 /* This routine is the `beef' of this component. It is _the_ inner */
791 /* loop that should be optimized to hell to get the best performance. */
794 void Split_Conic( TPoint
* base
)
799 base
[4].x
= base
[2].x
;
801 a
= base
[3].x
= ( base
[2].x
+ b
) / 2;
802 b
= base
[1].x
= ( base
[0].x
+ b
) / 2;
803 base
[2].x
= ( a
+ b
) / 2;
805 base
[4].y
= base
[2].y
;
807 a
= base
[3].y
= ( base
[2].y
+ b
) / 2;
808 b
= base
[1].y
= ( base
[0].y
+ b
) / 2;
809 base
[2].y
= ( a
+ b
) / 2;
811 /* hand optimized. gcc doesn't seem to be too good at common */
812 /* expression substitution and instruction scheduling ;-) */
816 /*************************************************************************/
822 /* Subdivides a third-order Bezier arc into two joint sub-arcs in the */
826 /* This routine is the `beef' of the component. It is one of _the_ */
827 /* inner loops that should be optimized like hell to get the best */
831 void Split_Cubic( TPoint
* base
)
836 base
[6].x
= base
[3].x
;
839 base
[1].x
= a
= ( base
[0].x
+ c
+ 1 ) >> 1;
840 base
[5].x
= b
= ( base
[3].x
+ d
+ 1 ) >> 1;
841 c
= ( c
+ d
+ 1 ) >> 1;
842 base
[2].x
= a
= ( a
+ c
+ 1 ) >> 1;
843 base
[4].x
= b
= ( b
+ c
+ 1 ) >> 1;
844 base
[3].x
= ( a
+ b
+ 1 ) >> 1;
846 base
[6].y
= base
[3].y
;
849 base
[1].y
= a
= ( base
[0].y
+ c
+ 1 ) >> 1;
850 base
[5].y
= b
= ( base
[3].y
+ d
+ 1 ) >> 1;
851 c
= ( c
+ d
+ 1 ) >> 1;
852 base
[2].y
= a
= ( a
+ c
+ 1 ) >> 1;
853 base
[4].y
= b
= ( b
+ c
+ 1 ) >> 1;
854 base
[3].y
= ( a
+ b
+ 1 ) >> 1;
858 /*************************************************************************/
864 /* Computes the x-coordinates of an ascending line segment and stores */
865 /* them in the render pool. */
868 /* x1 :: The x-coordinate of the segment's start point. */
870 /* y1 :: The y-coordinate of the segment's start point. */
872 /* x2 :: The x-coordinate of the segment's end point. */
874 /* y2 :: The y-coordinate of the segment's end point. */
876 /* miny :: A lower vertical clipping bound value. */
878 /* maxy :: An upper vertical clipping bound value. */
881 /* SUCCESS on success, FAILURE on render pool overflow. */
884 Bool
Line_Up( RAS_ARGS Long x1
,
892 Int e1
, e2
, f1
, f2
, size
; /* XXX: is `Short' sufficient? */
901 if ( Dy
<= 0 || y2
< miny
|| y1
> maxy
)
906 /* Take care: miny-y1 can be a very large value; we use */
907 /* a slow MulDiv function to avoid clipping bugs */
908 x1
+= SMulDiv( Dx
, miny
- y1
, Dy
);
920 /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
936 x1
+= FMulDiv( Dx
, ras
.precision
- f1
, Dy
);
947 ras
.joint
= ( f2
== 0 );
951 ras
.cProfile
->start
= e1
;
956 if ( ras
.top
+ size
>= ras
.maxBuff
)
958 ras
.error
= Raster_Err_Overflow
;
964 Ix
= ( ras
.precision
* Dx
) / Dy
;
965 Rx
= ( ras
.precision
* Dx
) % Dy
;
970 Ix
= -( ( ras
.precision
* -Dx
) / Dy
);
971 Rx
= ( ras
.precision
* -Dx
) % Dy
;
997 /*************************************************************************/
1003 /* Computes the x-coordinates of an descending line segment and */
1004 /* stores them in the render pool. */
1007 /* x1 :: The x-coordinate of the segment's start point. */
1009 /* y1 :: The y-coordinate of the segment's start point. */
1011 /* x2 :: The x-coordinate of the segment's end point. */
1013 /* y2 :: The y-coordinate of the segment's end point. */
1015 /* miny :: A lower vertical clipping bound value. */
1017 /* maxy :: An upper vertical clipping bound value. */
1020 /* SUCCESS on success, FAILURE on render pool overflow. */
1023 Bool
Line_Down( RAS_ARGS Long x1
,
1035 result
= Line_Up( RAS_VARS x1
, -y1
, x2
, -y2
, -maxy
, -miny
);
1037 if ( fresh
&& !ras
.fresh
)
1038 ras
.cProfile
->start
= -ras
.cProfile
->start
;
1044 /* A function type describing the functions used to split Bezier arcs */
1045 typedef void (*TSplitter
)( TPoint
* base
);
1048 /*************************************************************************/
1054 /* Computes the x-coordinates of an ascending Bezier arc and stores */
1055 /* them in the render pool. */
1058 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1060 /* splitter :: The function to split Bezier arcs. */
1062 /* miny :: A lower vertical clipping bound value. */
1064 /* maxy :: An upper vertical clipping bound value. */
1067 /* SUCCESS on success, FAILURE on render pool overflow. */
1070 Bool
Bezier_Up( RAS_ARGS Int degree
,
1075 Long y1
, y2
, e
, e2
, e0
;
1089 if ( y2
< miny
|| y1
> maxy
)
1115 *top
++ = arc
[degree
].x
;
1123 ras
.cProfile
->start
= TRUNC( e0
);
1130 if ( ( top
+ TRUNC( e2
- e
) + 1 ) >= ras
.maxBuff
)
1133 ras
.error
= Raster_Err_Overflow
;
1139 while ( arc
>= start_arc
&& e
<= e2
)
1148 if ( y2
- y1
>= ras
.precision_step
)
1155 *top
++ = arc
[degree
].x
+ FMulDiv( arc
[0].x
-arc
[degree
].x
,
1181 /*************************************************************************/
1187 /* Computes the x-coordinates of an descending Bezier arc and stores */
1188 /* them in the render pool. */
1191 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1193 /* splitter :: The function to split Bezier arcs. */
1195 /* miny :: A lower vertical clipping bound value. */
1197 /* maxy :: An upper vertical clipping bound value. */
1200 /* SUCCESS on success, FAILURE on render pool overflow. */
1203 Bool
Bezier_Down( RAS_ARGS Int degree
,
1208 TPoint
* arc
= ras
.arc
;
1212 arc
[0].y
= -arc
[0].y
;
1213 arc
[1].y
= -arc
[1].y
;
1214 arc
[2].y
= -arc
[2].y
;
1216 arc
[3].y
= -arc
[3].y
;
1220 result
= Bezier_Up( RAS_VARS degree
, splitter
, -maxy
, -miny
);
1222 if ( fresh
&& !ras
.fresh
)
1223 ras
.cProfile
->start
= -ras
.cProfile
->start
;
1225 arc
[0].y
= -arc
[0].y
;
1230 /*************************************************************************/
1236 /* Injects a new line segment and adjusts Profiles list. */
1239 /* x :: The x-coordinate of the segment's end point (its start point */
1240 /* is stored in `LastX'). */
1242 /* y :: The y-coordinate of the segment's end point (its start point */
1243 /* is stored in `LastY'). */
1246 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1250 Bool
Line_To( RAS_ARGS Long x
,
1253 /* First, detect a change of direction */
1255 switch ( ras
.state
)
1258 if ( y
> ras
.lastY
)
1260 if ( New_Profile( RAS_VARS Ascending
) )
1265 if ( y
< ras
.lastY
)
1266 if ( New_Profile( RAS_VARS Descending
) )
1272 if ( y
< ras
.lastY
)
1274 if ( End_Profile( RAS_VAR
) ||
1275 New_Profile( RAS_VARS Descending
) )
1281 if ( y
> ras
.lastY
)
1283 if ( End_Profile( RAS_VAR
) ||
1284 New_Profile( RAS_VARS Ascending
) )
1293 /* Then compute the lines */
1295 switch ( ras
.state
)
1298 if ( Line_Up( RAS_VARS ras
.lastX
, ras
.lastY
,
1299 x
, y
, ras
.minY
, ras
.maxY
) )
1304 if ( Line_Down( RAS_VARS ras
.lastX
, ras
.lastY
,
1305 x
, y
, ras
.minY
, ras
.maxY
) )
1320 /*************************************************************************/
1326 /* Injects a new conic arc and adjusts the profile list. */
1329 /* cx :: The x-coordinate of the arc's new control point. */
1331 /* cy :: The y-coordinate of the arc's new control point. */
1333 /* x :: The x-coordinate of the arc's end point (its start point is */
1334 /* stored in `LastX'). */
1336 /* y :: The y-coordinate of the arc's end point (its start point is */
1337 /* stored in `LastY'). */
1340 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1344 Bool
Conic_To( RAS_ARGS Long cx
,
1349 Long y1
, y2
, y3
, x3
, ymin
, ymax
;
1354 ras
.arc
[2].x
= ras
.lastX
;
1355 ras
.arc
[2].y
= ras
.lastY
;
1356 ras
.arc
[1].x
= cx
; ras
.arc
[1].y
= cy
;
1357 ras
.arc
[0].x
= x
; ras
.arc
[0].y
= y
;
1366 /* first, categorize the Bezier arc */
1379 if ( y2
< ymin
|| y2
> ymax
)
1381 /* this arc has no given direction, split it! */
1382 Split_Conic( ras
.arc
);
1385 else if ( y1
== y3
)
1387 /* this arc is flat, ignore it and pop it from the Bezier stack */
1392 /* the arc is y-monotonous, either ascending or descending */
1393 /* detect a change of direction */
1394 state_bez
= y1
< y3
? Ascending
: Descending
;
1395 if ( ras
.state
!= state_bez
)
1397 /* finalize current profile if any */
1398 if ( ras
.state
!= Unknown
&&
1399 End_Profile( RAS_VAR
) )
1402 /* create a new profile */
1403 if ( New_Profile( RAS_VARS state_bez
) )
1407 /* now call the appropriate routine */
1408 if ( state_bez
== Ascending
)
1410 if ( Bezier_Up( RAS_VARS
2, Split_Conic
, ras
.minY
, ras
.maxY
) )
1414 if ( Bezier_Down( RAS_VARS
2, Split_Conic
, ras
.minY
, ras
.maxY
) )
1418 } while ( ras
.arc
>= ras
.arcs
);
1430 /*************************************************************************/
1436 /* Injects a new cubic arc and adjusts the profile list. */
1439 /* cx1 :: The x-coordinate of the arc's first new control point. */
1441 /* cy1 :: The y-coordinate of the arc's first new control point. */
1443 /* cx2 :: The x-coordinate of the arc's second new control point. */
1445 /* cy2 :: The y-coordinate of the arc's second new control point. */
1447 /* x :: The x-coordinate of the arc's end point (its start point is */
1448 /* stored in `LastX'). */
1450 /* y :: The y-coordinate of the arc's end point (its start point is */
1451 /* stored in `LastY'). */
1454 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1458 Bool
Cubic_To( RAS_ARGS Long cx1
,
1465 Long y1
, y2
, y3
, y4
, x4
, ymin1
, ymax1
, ymin2
, ymax2
;
1470 ras
.arc
[3].x
= ras
.lastX
;
1471 ras
.arc
[3].y
= ras
.lastY
;
1472 ras
.arc
[2].x
= cx1
; ras
.arc
[2].y
= cy1
;
1473 ras
.arc
[1].x
= cx2
; ras
.arc
[1].y
= cy2
;
1474 ras
.arc
[0].x
= x
; ras
.arc
[0].y
= y
;
1484 /* first, categorize the Bezier arc */
1508 if ( ymin2
< ymin1
|| ymax2
> ymax1
)
1510 /* this arc has no given direction, split it! */
1511 Split_Cubic( ras
.arc
);
1514 else if ( y1
== y4
)
1516 /* this arc is flat, ignore it and pop it from the Bezier stack */
1521 state_bez
= ( y1
<= y4
) ? Ascending
: Descending
;
1523 /* detect a change of direction */
1524 if ( ras
.state
!= state_bez
)
1526 if ( ras
.state
!= Unknown
&&
1527 End_Profile( RAS_VAR
) )
1530 if ( New_Profile( RAS_VARS state_bez
) )
1534 /* compute intersections */
1535 if ( state_bez
== Ascending
)
1537 if ( Bezier_Up( RAS_VARS
3, Split_Cubic
, ras
.minY
, ras
.maxY
) )
1541 if ( Bezier_Down( RAS_VARS
3, Split_Cubic
, ras
.minY
, ras
.maxY
) )
1545 } while ( ras
.arc
>= ras
.arcs
);
1558 #define SWAP_( x, y ) do \
1568 /*************************************************************************/
1571 /* Decompose_Curve */
1574 /* Scans the outline arays in order to emit individual segments and */
1575 /* Beziers by calling Line_To() and Bezier_To(). It handles all */
1576 /* weird cases, like when the first point is off the curve, or when */
1577 /* there are simply no `on' points in the contour! */
1580 /* first :: The index of the first point in the contour. */
1582 /* last :: The index of the last point in the contour. */
1584 /* flipped :: If set, flip the direction of the curve. */
1587 /* SUCCESS on success, FAILURE on error. */
1590 Bool
Decompose_Curve( RAS_ARGS UShort first
,
1595 FT_Vector v_control
;
1603 char tag
; /* current point's state */
1606 points
= ras
.outline
.points
;
1607 limit
= points
+ last
;
1609 v_start
.x
= SCALED( points
[first
].x
);
1610 v_start
.y
= SCALED( points
[first
].y
);
1611 v_last
.x
= SCALED( points
[last
].x
);
1612 v_last
.y
= SCALED( points
[last
].y
);
1616 SWAP_( v_start
.x
, v_start
.y
);
1617 SWAP_( v_last
.x
, v_last
.y
);
1620 v_control
= v_start
;
1622 point
= points
+ first
;
1623 tags
= ras
.outline
.tags
+ first
;
1624 tag
= FT_CURVE_TAG( tags
[0] );
1626 /* A contour cannot start with a cubic control point! */
1627 if ( tag
== FT_Curve_Tag_Cubic
)
1628 goto Invalid_Outline
;
1630 /* check first point to determine origin */
1631 if ( tag
== FT_Curve_Tag_Conic
)
1633 /* first point is conic control. Yes, this happens. */
1634 if ( FT_CURVE_TAG( ras
.outline
.tags
[last
] ) == FT_Curve_Tag_On
)
1636 /* start at last point if it is on the curve */
1642 /* if both first and last points are conic, */
1643 /* start at their middle and record its position */
1645 v_start
.x
= ( v_start
.x
+ v_last
.x
) / 2;
1646 v_start
.y
= ( v_start
.y
+ v_last
.y
) / 2;
1654 ras
.lastX
= v_start
.x
;
1655 ras
.lastY
= v_start
.y
;
1657 while ( point
< limit
)
1662 tag
= FT_CURVE_TAG( tags
[0] );
1666 case FT_Curve_Tag_On
: /* emit a single line_to */
1671 x
= SCALED( point
->x
);
1672 y
= SCALED( point
->y
);
1676 if ( Line_To( RAS_VARS x
, y
) )
1681 case FT_Curve_Tag_Conic
: /* consume conic arcs */
1682 v_control
.x
= SCALED( point
[0].x
);
1683 v_control
.y
= SCALED( point
[0].y
);
1686 SWAP_( v_control
.x
, v_control
.y
);
1689 if ( point
< limit
)
1697 tag
= FT_CURVE_TAG( tags
[0] );
1699 x
= SCALED( point
[0].x
);
1700 y
= SCALED( point
[0].y
);
1705 if ( tag
== FT_Curve_Tag_On
)
1707 if ( Conic_To( RAS_VARS v_control
.x
, v_control
.y
, x
, y
) )
1712 if ( tag
!= FT_Curve_Tag_Conic
)
1713 goto Invalid_Outline
;
1715 v_middle
.x
= ( v_control
.x
+ x
) / 2;
1716 v_middle
.y
= ( v_control
.y
+ y
) / 2;
1718 if ( Conic_To( RAS_VARS v_control
.x
, v_control
.y
,
1719 v_middle
.x
, v_middle
.y
) )
1728 if ( Conic_To( RAS_VARS v_control
.x
, v_control
.y
,
1729 v_start
.x
, v_start
.y
) )
1734 default: /* FT_Curve_Tag_Cubic */
1736 Long x1
, y1
, x2
, y2
, x3
, y3
;
1739 if ( point
+ 1 > limit
||
1740 FT_CURVE_TAG( tags
[1] ) != FT_Curve_Tag_Cubic
)
1741 goto Invalid_Outline
;
1746 x1
= SCALED( point
[-2].x
);
1747 y1
= SCALED( point
[-2].y
);
1748 x2
= SCALED( point
[-1].x
);
1749 y2
= SCALED( point
[-1].y
);
1750 x3
= SCALED( point
[ 0].x
);
1751 y3
= SCALED( point
[ 0].y
);
1760 if ( point
<= limit
)
1762 if ( Cubic_To( RAS_VARS x1
, y1
, x2
, y2
, x3
, y3
) )
1767 if ( Cubic_To( RAS_VARS x1
, y1
, x2
, y2
, v_start
.x
, v_start
.y
) )
1774 /* close the contour with a line segment */
1775 if ( Line_To( RAS_VARS v_start
.x
, v_start
.y
) )
1782 ras
.error
= Raster_Err_Invalid
;
1789 /*************************************************************************/
1795 /* Converts a glyph into a series of segments and arcs and makes a */
1796 /* profiles list with them. */
1799 /* flipped :: If set, flip the direction of curve. */
1802 /* SUCCESS on success, FAILURE if any error was encountered during */
1806 Bool
Convert_Glyph( RAS_ARGS
int flipped
)
1811 PProfile lastProfile
;
1814 ras
.fProfile
= NULL
;
1818 ras
.maxBuff
= ras
.sizeBuff
- AlignProfileSize
;
1822 ras
.cProfile
= (PProfile
)ras
.top
;
1823 ras
.cProfile
->offset
= ras
.top
;
1828 for ( i
= 0; i
< ras
.outline
.n_contours
; i
++ )
1830 ras
.state
= Unknown
;
1831 ras
.gProfile
= NULL
;
1833 if ( Decompose_Curve( RAS_VARS start
, ras
.outline
.contours
[i
], flipped
) )
1836 start
= ras
.outline
.contours
[i
] + 1;
1838 /* We must now see whether the extreme arcs join or not */
1839 if ( FRAC( ras
.lastY
) == 0 &&
1840 ras
.lastY
>= ras
.minY
&&
1841 ras
.lastY
<= ras
.maxY
)
1842 if ( ras
.gProfile
&& ras
.gProfile
->flow
== ras
.cProfile
->flow
)
1844 /* Note that ras.gProfile can be nil if the contour was too small */
1847 lastProfile
= ras
.cProfile
;
1848 if ( End_Profile( RAS_VAR
) )
1851 /* close the `next profile in contour' linked list */
1853 lastProfile
->next
= ras
.gProfile
;
1856 if ( Finalize_Profile_Table( RAS_VAR
) )
1859 return ( ras
.top
< ras
.maxBuff
? SUCCESS
: FAILURE
);
1863 /*************************************************************************/
1864 /*************************************************************************/
1866 /** SCAN-LINE SWEEPS AND DRAWING **/
1868 /*************************************************************************/
1869 /*************************************************************************/
1872 /*************************************************************************/
1876 /* Initializes an empty linked list. */
1879 void Init_Linked( TProfileList
* l
)
1885 /*************************************************************************/
1889 /* Inserts a new profile in a linked list. */
1892 void InsNew( PProfileList list
,
1895 PProfile
*old
, current
;
1905 if ( x
< current
->X
)
1907 old
= ¤t
->link
;
1911 profile
->link
= current
;
1916 /*************************************************************************/
1920 /* Removes an old profile from a linked list. */
1923 void DelOld( PProfileList list
,
1926 PProfile
*old
, current
;
1934 if ( current
== profile
)
1936 *old
= current
->link
;
1940 old
= ¤t
->link
;
1944 /* we should never get there, unless the profile was not part of */
1949 /*************************************************************************/
1953 /* Update all X offsets of a drawing list. */
1956 void Update( PProfile first
)
1958 PProfile current
= first
;
1963 current
->X
= *current
->offset
;
1964 current
->offset
+= current
->flow
;
1966 current
= current
->link
;
1971 /*************************************************************************/
1975 /* Sorts a trace list. In 95%, the list is already sorted. We need */
1976 /* an algorithm which is fast in this case. Bubble sort is enough */
1980 void Sort( PProfileList list
)
1982 PProfile
*old
, current
, next
;
1985 /* First, set the new X coordinate of each profile */
1988 /* Then sort them */
1995 next
= current
->link
;
1999 if ( current
->X
<= next
->X
)
2001 old
= ¤t
->link
;
2010 current
->link
= next
->link
;
2011 next
->link
= current
;
2017 next
= current
->link
;
2022 /*************************************************************************/
2024 /* Vertical Sweep Procedure Set */
2026 /* These four routines are used during the vertical black/white sweep */
2027 /* phase by the generic Draw_Sweep() function. */
2029 /*************************************************************************/
2032 void Vertical_Sweep_Init( RAS_ARGS Short
* min
,
2035 Long pitch
= ras
.target
.pitch
;
2040 ras
.traceIncr
= (Short
)-pitch
;
2041 ras
.traceOfs
= -*min
* pitch
;
2043 ras
.traceOfs
+= ( ras
.target
.rows
- 1 ) * pitch
;
2051 void Vertical_Sweep_Span( RAS_ARGS Short y
,
2067 /* Drop-out control */
2069 e1
= TRUNC( CEILING( x1
) );
2071 if ( x2
- x1
- ras
.precision
<= ras
.precision_jitter
)
2074 e2
= TRUNC( FLOOR( x2
) );
2076 if ( e2
>= 0 && e1
< ras
.bWidth
)
2080 if ( e2
>= ras
.bWidth
)
2081 e2
= ras
.bWidth
- 1;
2083 c1
= (Short
)( e1
>> 3 );
2084 c2
= (Short
)( e2
>> 3 );
2086 f1
= (unsigned char)0xFF >> ( e1
& 7 );
2087 f2
= ~( (unsigned char)0x7F >> ( e2
& 7 ) );
2089 if ( ras
.gray_min_x
> c1
) ras
.gray_min_x
= c1
;
2090 if ( ras
.gray_max_x
< c2
) ras
.gray_max_x
= c2
;
2092 target
= ras
.bTarget
+ ras
.traceOfs
+ c1
;
2099 /* memset() is slower than the following code on many platforms. */
2100 /* This is due to the fact that, in the vast majority of cases, */
2101 /* the span length in bytes is relatively small. */
2111 *target
|= ( f1
& f2
);
2117 void Vertical_Sweep_Drop( RAS_ARGS Short y
,
2127 /* Drop-out control */
2134 if ( e1
== e2
+ ras
.precision
)
2136 switch ( ras
.dropOutControl
)
2143 e1
= CEILING( (x1
+ x2
+ 1) / 2 );
2148 /* Drop-out Control Rule #4 */
2150 /* The spec is not very clear regarding rule #4. It */
2151 /* presents a method that is way too costly to implement */
2152 /* while the general idea seems to get rid of `stubs'. */
2154 /* Here, we only get rid of stubs recognized if: */
2158 /* - P_Left and P_Right are in the same contour */
2159 /* - P_Right is the successor of P_Left in that contour */
2160 /* - y is the top of P_Left and P_Right */
2164 /* - P_Left and P_Right are in the same contour */
2165 /* - P_Left is the successor of P_Right in that contour */
2166 /* - y is the bottom of P_Left */
2169 /* FIXXXME: uncommenting this line solves the disappearing */
2170 /* bit problem in the `7' of verdana 10pts, but */
2171 /* makes a new one in the `C' of arial 14pts */
2174 if ( x2
- x1
< ras
.precision_half
)
2177 /* upper stub test */
2178 if ( left
->next
== right
&& left
->height
<= 0 )
2181 /* lower stub test */
2182 if ( right
->next
== left
&& left
->start
== y
)
2186 /* check that the rightmost pixel isn't set */
2190 c1
= (Short
)( e1
>> 3 );
2193 if ( e1
>= 0 && e1
< ras
.bWidth
&&
2194 ras
.bTarget
[ras
.traceOfs
+ c1
] & ( 0x80 >> f1
) )
2197 if ( ras
.dropOutControl
== 2 )
2200 e1
= CEILING( ( x1
+ x2
+ 1 ) / 2 );
2205 return; /* unsupported mode */
2214 if ( e1
>= 0 && e1
< ras
.bWidth
)
2216 c1
= (Short
)( e1
>> 3 );
2219 if ( ras
.gray_min_x
> c1
) ras
.gray_min_x
= c1
;
2220 if ( ras
.gray_max_x
< c1
) ras
.gray_max_x
= c1
;
2222 ras
.bTarget
[ras
.traceOfs
+ c1
] |= (char)( 0x80 >> f1
);
2228 void Vertical_Sweep_Step( RAS_ARG
)
2230 ras
.traceOfs
+= ras
.traceIncr
;
2234 /***********************************************************************/
2236 /* Horizontal Sweep Procedure Set */
2238 /* These four routines are used during the horizontal black/white */
2239 /* sweep phase by the generic Draw_Sweep() function. */
2241 /***********************************************************************/
2244 void Horizontal_Sweep_Init( RAS_ARGS Short
* min
,
2247 /* nothing, really */
2248 FT_UNUSED( raster
);
2255 void Horizontal_Sweep_Span( RAS_ARGS Short y
,
2269 if ( x2
- x1
< ras
.precision
)
2276 bits
= ras
.bTarget
+ ( y
>> 3 );
2277 f1
= (Byte
)( 0x80 >> ( y
& 7 ) );
2281 if ( e1
>= 0 && e1
< ras
.target
.rows
)
2286 p
= bits
- e1
*ras
.target
.pitch
;
2287 if ( ras
.target
.pitch
> 0 )
2288 p
+= ( ras
.target
.rows
- 1 ) * ras
.target
.pitch
;
2298 void Horizontal_Sweep_Drop( RAS_ARGS Short y
,
2309 /* During the horizontal sweep, we only take care of drop-outs */
2316 if ( e1
== e2
+ ras
.precision
)
2318 switch ( ras
.dropOutControl
)
2325 e1
= CEILING( ( x1
+ x2
+ 1 ) / 2 );
2331 /* Drop-out Control Rule #4 */
2333 /* The spec is not very clear regarding rule #4. It */
2334 /* presents a method that is way too costly to implement */
2335 /* while the general idea seems to get rid of `stubs'. */
2338 /* rightmost stub test */
2339 if ( left
->next
== right
&& left
->height
<= 0 )
2342 /* leftmost stub test */
2343 if ( right
->next
== left
&& left
->start
== y
)
2346 /* check that the rightmost pixel isn't set */
2350 bits
= ras
.bTarget
+ ( y
>> 3 );
2351 f1
= (Byte
)( 0x80 >> ( y
& 7 ) );
2353 bits
-= e1
* ras
.target
.pitch
;
2354 if ( ras
.target
.pitch
> 0 )
2355 bits
+= ( ras
.target
.rows
- 1 ) * ras
.target
.pitch
;
2358 e1
< ras
.target
.rows
&&
2362 if ( ras
.dropOutControl
== 2 )
2365 e1
= CEILING( ( x1
+ x2
+ 1 ) / 2 );
2370 return; /* unsupported mode */
2377 bits
= ras
.bTarget
+ ( y
>> 3 );
2378 f1
= (Byte
)( 0x80 >> ( y
& 7 ) );
2382 if ( e1
>= 0 && e1
< ras
.target
.rows
)
2384 bits
-= e1
* ras
.target
.pitch
;
2385 if ( ras
.target
.pitch
> 0 )
2386 bits
+= ( ras
.target
.rows
- 1 ) * ras
.target
.pitch
;
2394 void Horizontal_Sweep_Step( RAS_ARG
)
2396 /* Nothing, really */
2397 FT_UNUSED( raster
);
2401 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
2404 /*************************************************************************/
2406 /* Vertical Gray Sweep Procedure Set */
2408 /* These two routines are used during the vertical gray-levels sweep */
2409 /* phase by the generic Draw_Sweep() function. */
2413 /* - The target pixmap's width *must* be a multiple of 4. */
2415 /* - You have to use the function Vertical_Sweep_Span() for the gray */
2418 /*************************************************************************/
2421 void Vertical_Gray_Sweep_Init( RAS_ARGS Short
* min
,
2424 Long pitch
, byte_len
;
2428 *max
= ( *max
+ 3 ) & -2;
2431 pitch
= ras
.target
.pitch
;
2433 ras
.traceIncr
= (Short
)byte_len
;
2434 ras
.traceG
= ( *min
/ 2 ) * byte_len
;
2438 ras
.traceG
+= ( ras
.target
.rows
- 1 ) * pitch
;
2439 byte_len
= -byte_len
;
2442 ras
.gray_min_x
= (Short
)byte_len
;
2443 ras
.gray_max_x
= -(Short
)byte_len
;
2448 void Vertical_Gray_Sweep_Step( RAS_ARG
)
2451 PByte pix
, bit
, bit2
;
2452 Int
* count
= ras
.count_table
;
2456 ras
.traceOfs
+= ras
.gray_width
;
2458 if ( ras
.traceOfs
> ras
.gray_width
)
2460 pix
= ras
.gTarget
+ ras
.traceG
+ ras
.gray_min_x
* 4;
2463 if ( ras
.gray_max_x
>= 0 )
2465 Long last_pixel
= ras
.target
.width
- 1;
2466 Int last_cell
= last_pixel
>> 2;
2467 Int last_bit
= last_pixel
& 3;
2471 if ( ras
.gray_max_x
>= last_cell
&& last_bit
!= 3 )
2473 ras
.gray_max_x
= last_cell
- 1;
2477 if ( ras
.gray_min_x
< 0 )
2480 bit
= ras
.bTarget
+ ras
.gray_min_x
;
2481 bit2
= bit
+ ras
.gray_width
;
2483 c1
= ras
.gray_max_x
- ras
.gray_min_x
;
2487 c2
= count
[*bit
] + count
[*bit2
];
2491 pix
[0] = grays
[(c2
>> 12) & 0x000F];
2492 pix
[1] = grays
[(c2
>> 8 ) & 0x000F];
2493 pix
[2] = grays
[(c2
>> 4 ) & 0x000F];
2494 pix
[3] = grays
[ c2
& 0x000F];
2508 c2
= count
[*bit
] + count
[*bit2
];
2514 pix
[2] = grays
[(c2
>> 4 ) & 0x000F];
2516 pix
[1] = grays
[(c2
>> 8 ) & 0x000F];
2518 pix
[0] = grays
[(c2
>> 12) & 0x000F];
2528 ras
.traceG
+= ras
.traceIncr
;
2530 ras
.gray_min_x
= 32000;
2531 ras
.gray_max_x
= -32000;
2537 void Horizontal_Gray_Sweep_Span( RAS_ARGS Short y
,
2543 /* nothing, really */
2544 FT_UNUSED( raster
);
2554 void Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y
,
2565 /* During the horizontal sweep, we only take care of drop-outs */
2571 if ( e1
== e2
+ ras
.precision
)
2573 switch ( ras
.dropOutControl
)
2580 e1
= CEILING( ( x1
+ x2
+ 1 ) / 2 );
2586 /* Drop-out Control Rule #4 */
2588 /* The spec is not very clear regarding rule #4. It */
2589 /* presents a method that is way too costly to implement */
2590 /* while the general idea seems to get rid of `stubs'. */
2593 /* rightmost stub test */
2594 if ( left
->next
== right
&& left
->height
<= 0 )
2597 /* leftmost stub test */
2598 if ( right
->next
== left
&& left
->start
== y
)
2601 if ( ras
.dropOutControl
== 2 )
2604 e1
= CEILING( ( x1
+ x2
+ 1 ) / 2 );
2609 return; /* unsupported mode */
2618 if ( x2
- x1
>= ras
.precision_half
)
2619 color
= ras
.grays
[2];
2621 color
= ras
.grays
[1];
2623 e1
= TRUNC( e1
) / 2;
2624 if ( e1
< ras
.target
.rows
)
2626 pixel
= ras
.gTarget
- e1
* ras
.target
.pitch
+ y
/ 2;
2627 if ( ras
.target
.pitch
> 0 )
2628 pixel
+= ( ras
.target
.rows
- 1 ) * ras
.target
.pitch
;
2630 if ( pixel
[0] == ras
.grays
[0] )
2637 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
2640 /*************************************************************************/
2642 /* Generic Sweep Drawing routine */
2644 /*************************************************************************/
2647 Bool
Draw_Sweep( RAS_ARG
)
2649 Short y
, y_change
, y_height
;
2651 PProfile P
, Q
, P_Left
, P_Right
;
2653 Short min_Y
, max_Y
, top
, bottom
, dropouts
;
2655 Long x1
, x2
, xs
, e1
, e2
;
2658 TProfileList draw_left
, draw_right
;
2661 /* Init empty linked lists */
2663 Init_Linked( &wait
);
2665 Init_Linked( &draw_left
);
2666 Init_Linked( &draw_right
);
2668 /* first, compute min and max Y */
2671 max_Y
= (Short
)TRUNC( ras
.minY
);
2672 min_Y
= (Short
)TRUNC( ras
.maxY
);
2678 bottom
= (Short
)P
->start
;
2679 top
= (Short
)P
->start
+ P
->height
- 1;
2681 if ( min_Y
> bottom
) min_Y
= bottom
;
2682 if ( max_Y
< top
) max_Y
= top
;
2690 /* Check the Y-turns */
2691 if ( ras
.numTurns
== 0 )
2693 ras
.error
= Raster_Err_Invalid
;
2697 /* Now inits the sweep */
2699 ras
.Proc_Sweep_Init( RAS_VARS
&min_Y
, &max_Y
);
2701 /* Then compute the distance of each profile from min_Y */
2707 P
->countL
= P
->start
- min_Y
;
2716 if ( ras
.numTurns
> 0 &&
2717 ras
.sizeBuff
[-ras
.numTurns
] == min_Y
)
2720 while ( ras
.numTurns
> 0 )
2722 /* look in the wait list for new activations */
2729 P
->countL
-= y_height
;
2730 if ( P
->countL
== 0 )
2737 InsNew( &draw_left
, P
);
2741 InsNew( &draw_right
, P
);
2749 /* Sort the drawing lists */
2752 Sort( &draw_right
);
2754 y_change
= (Short
)ras
.sizeBuff
[-ras
.numTurns
--];
2755 y_height
= y_change
- y
;
2757 while ( y
< y_change
)
2764 P_Right
= draw_right
;
2778 if ( x2
- x1
<= ras
.precision
)
2783 if ( ras
.dropOutControl
!= 0 &&
2784 ( e1
> e2
|| e2
== e1
+ ras
.precision
) )
2786 /* a drop out was detected */
2791 /* mark profile for drop-out processing */
2799 ras
.Proc_Sweep_Span( RAS_VARS y
, x1
, x2
, P_Left
, P_Right
);
2803 P_Left
= P_Left
->link
;
2804 P_Right
= P_Right
->link
;
2807 /* now perform the dropouts _after_ the span drawing -- */
2808 /* drop-outs processing has been moved out of the loop */
2809 /* for performance tuning */
2815 ras
.Proc_Sweep_Step( RAS_VAR
);
2822 Sort( &draw_right
);
2826 /* Now finalize the profiles that needs it */
2836 if ( P
->height
== 0 )
2837 DelOld( &draw_left
, P
);
2843 PProfile Q
, P
= draw_right
;
2849 if ( P
->height
== 0 )
2850 DelOld( &draw_right
, P
);
2856 /* for gray-scaling, flushes the bitmap scanline cache */
2857 while ( y
<= max_Y
)
2859 ras
.Proc_Sweep_Step( RAS_VAR
);
2868 P_Right
= draw_right
;
2872 if ( P_Left
->countL
)
2876 dropouts
--; /* -- this is useful when debugging only */
2878 ras
.Proc_Sweep_Drop( RAS_VARS y
,
2885 P_Left
= P_Left
->link
;
2886 P_Right
= P_Right
->link
;
2893 /*************************************************************************/
2896 /* Render_Single_Pass */
2899 /* Performs one sweep with sub-banding. */
2902 /* flipped :: If set, flip the direction of the outline. */
2905 /* Renderer error code. */
2908 int Render_Single_Pass( RAS_ARGS Bool flipped
)
2913 while ( ras
.band_top
>= 0 )
2915 ras
.maxY
= (Long
)ras
.band_stack
[ras
.band_top
].y_max
* ras
.precision
;
2916 ras
.minY
= (Long
)ras
.band_stack
[ras
.band_top
].y_min
* ras
.precision
;
2920 ras
.error
= Raster_Err_None
;
2922 if ( Convert_Glyph( RAS_VARS flipped
) )
2924 if ( ras
.error
!= Raster_Err_Overflow
)
2927 ras
.error
= Raster_Err_None
;
2932 ClearBand( RAS_VARS
TRUNC( ras
.minY
), TRUNC( ras
.maxY
) );
2935 i
= ras
.band_stack
[ras
.band_top
].y_min
;
2936 j
= ras
.band_stack
[ras
.band_top
].y_max
;
2940 if ( ras
.band_top
>= 7 || k
< i
)
2943 ras
.error
= Raster_Err_Invalid
;
2948 ras
.band_stack
[ras
.band_top
+ 1].y_min
= k
;
2949 ras
.band_stack
[ras
.band_top
+ 1].y_max
= j
;
2951 ras
.band_stack
[ras
.band_top
].y_max
= k
- 1;
2958 if ( Draw_Sweep( RAS_VAR
) )
2968 /*************************************************************************/
2974 /* Renders a glyph in a bitmap. Sub-banding if needed. */
2977 /* FreeType error code. 0 means success. */
2979 /* XXX Fixme: ftraster's error codes don't harmonize with FT2's ones! */
2982 FT_Error
Render_Glyph( RAS_ARG
)
2987 Set_High_Precision( RAS_VARS ras
.outline
.flags
&
2988 ft_outline_high_precision
);
2989 ras
.scale_shift
= ras
.precision_shift
;
2990 ras
.dropOutControl
= 2;
2991 ras
.second_pass
= !( ras
.outline
.flags
& ft_outline_single_pass
);
2993 /* Vertical Sweep */
2994 ras
.Proc_Sweep_Init
= Vertical_Sweep_Init
;
2995 ras
.Proc_Sweep_Span
= Vertical_Sweep_Span
;
2996 ras
.Proc_Sweep_Drop
= Vertical_Sweep_Drop
;
2997 ras
.Proc_Sweep_Step
= Vertical_Sweep_Step
;
3000 ras
.band_stack
[0].y_min
= 0;
3001 ras
.band_stack
[0].y_max
= ras
.target
.rows
- 1;
3003 ras
.bWidth
= ras
.target
.width
;
3004 ras
.bTarget
= (Byte
*)ras
.target
.buffer
;
3006 if ( ( error
= Render_Single_Pass( RAS_VARS
0 ) ) != 0 )
3009 /* Horizontal Sweep */
3010 if ( ras
.second_pass
&& ras
.dropOutControl
!= 0 )
3012 ras
.Proc_Sweep_Init
= Horizontal_Sweep_Init
;
3013 ras
.Proc_Sweep_Span
= Horizontal_Sweep_Span
;
3014 ras
.Proc_Sweep_Drop
= Horizontal_Sweep_Drop
;
3015 ras
.Proc_Sweep_Step
= Horizontal_Sweep_Step
;
3018 ras
.band_stack
[0].y_min
= 0;
3019 ras
.band_stack
[0].y_max
= ras
.target
.width
- 1;
3021 if ( ( error
= Render_Single_Pass( RAS_VARS
1 ) ) != 0 )
3029 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3032 /*************************************************************************/
3035 /* Render_Gray_Glyph */
3038 /* Renders a glyph with grayscaling. Sub-banding if needed. */
3041 /* FreeType error code. 0 means success. */
3044 FT_Error
Render_Gray_Glyph( RAS_ARG
)
3050 Set_High_Precision( RAS_VARS ras
.outline
.flags
&
3051 ft_outline_high_precision
);
3052 ras
.scale_shift
= ras
.precision_shift
+ 1;
3053 ras
.dropOutControl
= 2;
3054 ras
.second_pass
= !( ras
.outline
.flags
& ft_outline_single_pass
);
3056 /* Vertical Sweep */
3059 ras
.band_stack
[0].y_min
= 0;
3060 ras
.band_stack
[0].y_max
= 2 * ras
.target
.rows
- 1;
3062 ras
.bWidth
= ras
.gray_width
;
3063 pixel_width
= 2 * ( ( ras
.target
.width
+ 3 ) >> 2 );
3065 if ( ras
.bWidth
> pixel_width
)
3066 ras
.bWidth
= pixel_width
;
3068 ras
.bWidth
= ras
.bWidth
* 8;
3069 ras
.bTarget
= (Byte
*)ras
.gray_lines
;
3070 ras
.gTarget
= (Byte
*)ras
.target
.buffer
;
3072 ras
.Proc_Sweep_Init
= Vertical_Gray_Sweep_Init
;
3073 ras
.Proc_Sweep_Span
= Vertical_Sweep_Span
;
3074 ras
.Proc_Sweep_Drop
= Vertical_Sweep_Drop
;
3075 ras
.Proc_Sweep_Step
= Vertical_Gray_Sweep_Step
;
3077 error
= Render_Single_Pass( RAS_VARS
0 );
3081 /* Horizontal Sweep */
3082 if ( ras
.second_pass
&& ras
.dropOutControl
!= 0 )
3084 ras
.Proc_Sweep_Init
= Horizontal_Sweep_Init
;
3085 ras
.Proc_Sweep_Span
= Horizontal_Gray_Sweep_Span
;
3086 ras
.Proc_Sweep_Drop
= Horizontal_Gray_Sweep_Drop
;
3087 ras
.Proc_Sweep_Step
= Horizontal_Sweep_Step
;
3090 ras
.band_stack
[0].y_min
= 0;
3091 ras
.band_stack
[0].y_max
= ras
.target
.width
* 2 - 1;
3093 error
= Render_Single_Pass( RAS_VARS
1 );
3101 #else /* FT_RASTER_OPTION_ANTI_ALIASING */
3104 FT_Error
Render_Gray_Glyph( RAS_ARG
)
3108 return FT_Err_Cannot_Render_Glyph
;
3111 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
3115 void ft_black_init( TRaster_Instance
* raster
)
3121 /* setup count table */
3122 for ( n
= 0; n
< 256; n
++ )
3124 c
= ( n
& 0x55 ) + ( ( n
& 0xAA ) >> 1 );
3126 c
= ( ( c
<< 6 ) & 0x3000 ) |
3127 ( ( c
<< 4 ) & 0x0300 ) |
3128 ( ( c
<< 2 ) & 0x0030 ) |
3131 raster
->count_table
[n
] = c
;
3134 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3136 /* set default 5-levels gray palette */
3137 for ( n
= 0; n
< 5; n
++ )
3138 raster
->grays
[n
] = n
* 255 / 4;
3140 raster
->gray_width
= RASTER_GRAY_LINES
/ 2;
3146 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
3147 /**** a static object. *****/
3154 int ft_black_new( void* memory
,
3155 FT_Raster
*araster
)
3157 static FT_RasterRec_ the_raster
;
3160 *araster
= &the_raster
;
3161 memset( &the_raster
, sizeof ( the_raster
), 0 );
3162 ft_black_init( &the_raster
);
3169 void ft_black_done( FT_Raster raster
)
3176 #else /* _STANDALONE_ */
3180 int ft_black_new( FT_Memory memory
,
3181 TRaster_Instance
** araster
)
3184 TRaster_Instance
* raster
;
3188 if ( !ALLOC( raster
, sizeof ( *raster
) ) )
3190 raster
->memory
= memory
;
3191 ft_black_init( raster
);
3201 void ft_black_done( TRaster_Instance
* raster
)
3203 FT_Memory memory
= (FT_Memory
)raster
->memory
;
3208 #endif /* _STANDALONE_ */
3212 void ft_black_reset( TRaster_Instance
* raster
,
3213 const char* pool_base
,
3216 if ( raster
&& pool_base
&& pool_size
>= 4096 )
3219 raster
->buff
= (PLong
)pool_base
;
3220 raster
->sizeBuff
= raster
->buff
+ pool_size
/ sizeof ( Long
);
3226 void ft_black_set_mode( TRaster_Instance
* raster
,
3228 const char* palette
)
3230 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3232 if ( mode
== FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )
3234 /* set 5-levels gray palette */
3235 raster
->grays
[0] = palette
[0];
3236 raster
->grays
[1] = palette
[1];
3237 raster
->grays
[2] = palette
[2];
3238 raster
->grays
[3] = palette
[3];
3239 raster
->grays
[4] = palette
[4];
3244 FT_UNUSED( raster
);
3246 FT_UNUSED( palette
);
3253 int ft_black_render( TRaster_Instance
* raster
,
3254 FT_Raster_Params
* params
)
3256 FT_Outline
* outline
= (FT_Outline
*)params
->source
;
3257 FT_Bitmap
* target_map
= params
->target
;
3260 if ( !raster
|| !raster
->buff
|| !raster
->sizeBuff
)
3261 return Raster_Err_Not_Ini
;
3263 if ( !outline
|| !outline
->contours
|| !outline
->points
)
3264 return Raster_Err_Invalid
;
3266 /* return immediately if the outline is empty */
3267 if ( outline
->n_points
== 0 || outline
->n_contours
<= 0 )
3268 return Raster_Err_None
;
3270 if ( outline
->n_points
!= outline
->contours
[outline
->n_contours
- 1] + 1 )
3271 return Raster_Err_Invalid
;
3273 if ( !target_map
|| !target_map
->buffer
)
3274 return Raster_Err_Invalid
;
3276 /* this version of the raster does not support direct rendering, sorry */
3277 if ( params
->flags
& ft_raster_flag_direct
)
3278 return Raster_Err_Unsupported
;
3280 ras
.outline
= *outline
;
3281 ras
.target
= *target_map
;
3283 return ( ( params
->flags
& ft_raster_flag_aa
)
3284 ? Render_Gray_Glyph( raster
)
3285 : Render_Glyph( raster
) );
3289 FT_Raster_Funcs ft_standard_raster
=
3291 ft_glyph_format_outline
,
3292 (FT_Raster_New_Func
) ft_black_new
,
3293 (FT_Raster_Reset_Func
) ft_black_reset
,
3294 (FT_Raster_Set_Mode_Func
)ft_black_set_mode
,
3295 (FT_Raster_Render_Func
) ft_black_render
,
3296 (FT_Raster_Done_Func
) ft_black_done