ref: d89489210a9a410fd93288b11813313374fe1502
parent: 06b73729c189ff3c206e6dc6d9c43f3e63df4197
author: David Turner <[email protected]>
date: Tue May 2 09:29:29 EDT 2000
ftrast.c
--- /dev/null
+++ b/demos/src/ftrast.c
@@ -1,0 +1,2905 @@
+/*******************************************************************
+ *
+ * ftraster.c 1.5
+ *
+ * The FreeType glyph rasterizer (body).
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ *
+ * This is a rewrite of the FreeType 1.x scan-line converter
+ *
+ *
+ *
+ ******************************************************************/
+
+#include "frast.h"
+#include <ftcalc.h> /* for FT_MulDiv only */
+
+ /****************************************************************/
+ /****************************************************************/
+ /** **/
+ /** CONFIGURATION MACROS **/
+ /** **/
+ /****************************************************************/
+ /****************************************************************/
+
+/* define DEBUG_RASTER if you want to compile a debugging version */
+#define xxxDEBUG_RASTER
+
+/* required by the tracing mode */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_raster
+
+#include <ftdebug.h>
+
+
+/* The default render pool size */
+#define RASTER_RENDER_POOL 8192
+
+/* The size of the two-lines intermediate bitmap used */
+/* for anti-aliasing */
+#define RASTER_GRAY_LINES 1024
+
+#define Raster_Err_None TT_Err_Ok
+#define Raster_Err_Not_Ini TT_Err_Raster_Not_Initialized
+#define Raster_Err_Overflow TT_Err_Raster_Pool_Overflow
+#define Raster_Err_Neg_Height TT_Err_Raster_Negative_Height
+#define Raster_Err_Invalid TT_Err_Raster_Invalid_Value
+#define Raster_Err_Gray_Unsupported TT_Err_Raster_Gray_Unsupported
+
+
+/* FMulDiv means "Fast MulDiv", it is uses in case where 'b' is typically */
+/* a small value and the result of (a*b) is known to fit in 32 bits. */
+#define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
+
+/* On the other hand, SMulDiv is for "Slow MulDiv", and is used typically */
+/* for clipping computations. It simply uses the TT_MulDiv() function */
+/* defined in "ttcalc.h" */
+/* */
+/* So, the following definition fits the bill nicely, and we don't need */
+/* to use the one in 'ttcalc' anymore, even for 16-bit systems... */
+#define SMulDiv FT_MulDiv
+
+
+
+/* The rasterizer is a very general purpose component, please leave */
+/* the following redefinitions there (you never know your target */
+/* environment). */
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+
+
+
+#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
+ /* Setting this constant to more than 32 is a */
+ /* pure waste of space. */
+
+#define Pixel_Bits 6 /* fractional bits of *input* coordinates */
+
+ /****************************************************************/
+ /****************************************************************/
+ /** **/
+ /** SIMPLE TYPE DECLARATIONS **/
+ /** **/
+ /****************************************************************/
+ /****************************************************************/
+
+ typedef int Int;
+ typedef unsigned int UInt;
+ typedef short Short;
+ typedef unsigned short UShort, *PUShort;
+ typedef long Long, *PLong;
+ typedef unsigned long ULong;
+
+ typedef unsigned char Byte, *PByte;
+ typedef char Bool;
+
+ typedef struct TPoint_
+ {
+ Long x;
+ Long y;
+
+ } TPoint;
+
+
+ typedef enum TFlow_
+ {
+ Flow_None = 0,
+ Flow_Up,
+ Flow_Down
+
+ } TFlow;
+
+
+ /* States of each line, arc and profile */
+ typedef enum TStates_
+ {
+ Unknown,
+ Ascending,
+ Descending,
+ Flat
+
+ } TStates;
+
+
+ typedef struct TProfile_ TProfile;
+ typedef TProfile* PProfile;
+
+ struct TProfile_
+ {
+ FT_F26Dot6 X; /* current coordinate during sweep */
+ PProfile link; /* link to next profile - various purpose */
+ PStorage offset; /* start of profile's data in render pool */
+ Int flow; /* Profile orientation: Asc/Descending */
+ Long height; /* profile's height in scanlines */
+ Long start; /* profile's starting scanline */
+
+ UShort countL; /* number of lines to step before this */
+ /* profile becomes drawable */
+
+ PProfile next; /* next profile in same contour, used */
+ /* during drop-out control */
+ };
+
+ typedef PProfile TProfileList;
+ typedef PProfile* PProfileList;
+
+
+ /* Simple record used to implement a stack of bands, required */
+ /* by the sub-banding mechanism */
+ typedef struct TBand_
+ {
+ Short y_min; /* band's minimum */
+ Short y_max; /* band's maximum */
+
+ } TBand;
+
+
+
+#define AlignProfileSize \
+ (( sizeof(TProfile)+sizeof(long)-1 ) / sizeof(long))
+
+
+ /* Left fill bitmask */
+ static const Byte LMask[8] =
+ { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
+
+ /* Right fill bitmask */
+ static const Byte RMask[8] =
+ { 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF };
+
+ /* We provide two different builds of the scan-line converter */
+ /* The static build uses global variables and isn't */
+ /* re-entrant. */
+ /* The indirect build is re-entrant but accesses all variables */
+ /* indirectly. */
+ /* */
+ /* As a consequence, the indirect build is about 10% slower */
+ /* than the static one on a _Pentium_ (this could get worse */
+ /* on older processors), but the code size is reduced by */
+ /* more than 30% ! */
+ /* */
+ /* The indirect build is now the default, defined in */
+ /* ttconfig.h. Be careful if you experiment with this. */
+
+ /* Note also that, though its code can be re-entrant, the */
+ /* component is always used in thread-safe mode. This is */
+ /* simply due to the fact that we want to use a single */
+ /* render pool (of 64 Kb), and not to waste memory. */
+
+#ifdef TT_STATIC_RASTER
+
+#define RAS_ARGS /* void */
+#define RAS_ARG /* void */
+
+#define RAS_VARS /* void */
+#define RAS_VAR /* void */
+
+#else
+
+#define RAS_ARGS TRaster_Instance* raster,
+#define RAS_ARG TRaster_Instance* raster
+
+#define RAS_VARS raster,
+#define RAS_VAR raster
+
+#endif
+
+
+ typedef struct TRaster_Instance_ TRaster_Instance;
+
+
+ /* prototypes used for sweep function dispatch */
+ typedef void Function_Sweep_Init( RAS_ARGS Short* min,
+ Short* max );
+
+ typedef void Function_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right );
+
+ typedef void Function_Sweep_Step( RAS_ARG );
+
+
+/* NOTE: These operations are only valid on 2's complement processors */
+
+#define FLOOR( x ) ( (x) & -ras.precision )
+#define CEILING( x ) ( ((x) + ras.precision - 1) & -ras.precision )
+#define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )
+#define FRAC( x ) ( (x) & (ras.precision - 1) )
+#define SCALED( x ) ( ((x) << ras.scale_shift) - ras.precision_half )
+
+ /* Note that I have moved the location of some fields in the */
+ /* structure to ensure that the most used variables are used */
+ /* at the top. Thus, their offset can be coded with less */
+ /* opcodes, and it results in a smaller executable. */
+
+ struct TRaster_Instance_
+ {
+ Int precision_bits; /* precision related variables */
+ Int precision;
+ Int precision_half;
+ Long precision_mask;
+ Int precision_shift;
+ Int precision_step;
+ Int precision_jitter;
+
+ Int scale_shift; /* == precision_shift for bitmaps */
+ /* == precision_shift+1 for pixmaps */
+
+ PLong buff; /* The profiles buffer */
+ PLong sizeBuff; /* Render pool size */
+ PLong maxBuff; /* Profiles buffer size */
+ PLong top; /* Current cursor in buffer */
+
+ FT_Error error;
+
+ PByte flags; /* current flags table */
+ PUShort outs; /* current outlines table */
+
+ UShort nPoints; /* number of points in current glyph */
+ Short nContours; /* number of contours in current glyph */
+ Int numTurns; /* number of Y-turns in outline */
+
+ TPoint* arc; /* current Bezier arc pointer */
+
+ UShort bWidth; /* target bitmap width */
+ PByte bTarget; /* target bitmap buffer */
+ PByte gTarget; /* target pixmap buffer */
+
+ Long lastX, lastY, minY, maxY;
+
+ UShort num_Profs; /* current number of profiles */
+
+ Bool fresh; /* signals a fresh new profile which */
+ /* 'start' field must be completed */
+ Bool joint; /* signals that the last arc ended */
+ /* exactly on a scanline. Allows */
+ /* removal of doublets */
+ PProfile cProfile; /* current profile */
+ PProfile fProfile; /* head of linked list of profiles */
+ PProfile gProfile; /* contour's first profile in case */
+ /* of impact */
+ TStates state; /* rendering state */
+
+ FT_Bitmap target; /* description of target bit/pixmap */
+
+ Long traceOfs; /* current offset in target bitmap */
+ Long traceG; /* current offset in target pixmap */
+
+ Short traceIncr; /* sweep's increment in target bitmap */
+
+ Short gray_min_x; /* current min x during gray rendering */
+ Short gray_max_x; /* current max x during gray rendering */
+
+ /* dispatch variables */
+
+ Function_Sweep_Init* Proc_Sweep_Init;
+ Function_Sweep_Span* Proc_Sweep_Span;
+ Function_Sweep_Span* Proc_Sweep_Drop;
+ Function_Sweep_Step* Proc_Sweep_Step;
+
+ FT_Vector* coords;
+
+ Byte dropOutControl; /* current drop_out control method */
+
+ Byte grays[5]; /* Palette of gray levels used for render */
+
+ Byte* gray_lines; /* Intermediate table used to render the */
+ /* graylevels pixmaps. */
+ /* gray_lines is a buffer holding two */
+ /* monochrome scanlines */
+ Short gray_width; /* width in bytes of one monochrome */
+ /* intermediate scanline of gray_lines. */
+ /* Each gray pixel takes 2 bits long there */
+
+ /* The gray_lines must hold 2 lines, thus with size */
+ /* in bytes of at least 'gray_width*2' */
+
+ Bool second_pass; /* indicates wether a horizontal pass */
+ /* should be performed to control drop-out */
+ /* accurately when calling Render_Glyph. */
+ /* Note that there is no horizontal pass */
+ /* during gray rendering. */
+ TPoint arcs[2 * MaxBezier + 1]; /* The Bezier stack */
+
+ TBand band_stack[16]; /* band stack used for sub-banding */
+ Int band_top; /* band stack top */
+
+ Int count_table[256]; /* Look-up table used to quickly count */
+ /* set bits in a gray 2x2 cell */
+ };
+
+
+#ifdef FT_CONFIG_OPTION_STATIC_RASTER
+
+ static TRaster_Instance cur_ras;
+ #define ras cur_ras
+
+#else
+
+ #define ras (*raster)
+
+#endif /* FT_STATIC_RASTER */
+
+ /****************************************************************/
+ /****************************************************************/
+ /** **/
+ /** PROFILES COMPUTATION **/
+ /** **/
+ /****************************************************************/
+ /****************************************************************/
+
+
+/************************************************************************/
+/* */
+/* Function: Set_High_Precision */
+/* */
+/* Description: Sets precision variables according to param flag. */
+/* */
+/* Input: High set to True for high precision (typically for */
+/* ppem < 18), false otherwise. */
+/* */
+/************************************************************************/
+
+ static void Set_High_Precision( RAS_ARGS Bool High )
+ {
+ if ( High )
+ {
+ ras.precision_bits = 10;
+ ras.precision_step = 128;
+ ras.precision_jitter = 24;
+ }
+ else
+ {
+ ras.precision_bits = 6;
+ ras.precision_step = 32;
+ ras.precision_jitter = 2;
+ }
+
+ FT_TRACE7(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
+
+ ras.precision = 1L << ras.precision_bits;
+ ras.precision_half = ras.precision / 2;
+ ras.precision_shift = ras.precision_bits - Pixel_Bits;
+ ras.precision_mask = -ras.precision;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: New_Profile */
+/* */
+/* Description: Creates a new Profile in the render pool. */
+/* */
+/* Input: aState state/orientation of the new Profile */
+/* */
+/* Returns: SUCCESS on success. */
+/* FAILURE in case of overflow or of incoherent Profile. */
+/* */
+/****************************************************************************/
+
+ static Bool New_Profile( RAS_ARGS TStates aState )
+ {
+ if ( !ras.fProfile )
+ {
+ ras.cProfile = (PProfile)ras.top;
+ ras.fProfile = ras.cProfile;
+ ras.top += AlignProfileSize;
+ }
+
+ if ( ras.top >= ras.maxBuff )
+ {
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ switch ( aState )
+ {
+ case Ascending:
+ ras.cProfile->flow = FT_Flow_Up;
+ FT_TRACE7(( "New ascending profile = %lx\n", (long)ras.cProfile ));
+ break;
+
+ case Descending:
+ ras.cProfile->flow = FT_Flow_Down;
+ FT_TRACE7(( "New descending profile = %lx\n", (long)ras.cProfile ));
+ break;
+
+ default:
+ FT_ERROR(( "Invalid profile direction in Raster:New_Profile !!\n" ));
+ ras.error = Raster_Err_Invalid;
+ return FAILURE;
+ }
+
+ ras.cProfile->start = 0;
+ ras.cProfile->height = 0;
+ ras.cProfile->offset = ras.top;
+ ras.cProfile->link = (PProfile)0;
+ ras.cProfile->next = (PProfile)0;
+
+ if ( !ras.gProfile )
+ ras.gProfile = ras.cProfile;
+
+ ras.state = aState;
+ ras.fresh = TRUE;
+ ras.joint = FALSE;
+
+ return SUCCESS;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: End_Profile */
+/* */
+/* Description: Finalizes the current Profile. */
+/* */
+/* Input: None */
+/* */
+/* Returns: SUCCESS on success. */
+/* FAILURE in case of overflow or incoherency. */
+/* */
+/****************************************************************************/
+
+ static Bool End_Profile( RAS_ARG )
+ {
+ Long h;
+ PProfile oldProfile;
+
+
+ h = ras.top - ras.cProfile->offset;
+
+ if ( h < 0 )
+ {
+ FT_ERROR(( "Negative height encountered in End_Profile!\n" ));
+ ras.error = Raster_Err_Neg_Height;
+ return FAILURE;
+ }
+
+ if ( h > 0 )
+ {
+ FT_TRACE1(( "Ending profile %lx, start = %ld, height = %ld\n",
+ (long)ras.cProfile, ras.cProfile->start, h ));
+
+ oldProfile = ras.cProfile;
+ ras.cProfile->height = h;
+ ras.cProfile = (PProfile)ras.top;
+
+ ras.top += AlignProfileSize;
+
+ ras.cProfile->height = 0;
+ ras.cProfile->offset = ras.top;
+ oldProfile->next = ras.cProfile;
+ ras.num_Profs++;
+ }
+
+ if ( ras.top >= ras.maxBuff )
+ {
+ FT_TRACE1(( "overflow in End_Profile\n" ));
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ ras.joint = FALSE;
+
+ return SUCCESS;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Insert_Y_Turn */
+/* */
+/* Description: Insert a salient into the sorted list placed on top */
+/* of the render pool */
+/* */
+/* Input: New y scanline position */
+/* */
+/****************************************************************************/
+
+ static
+ Bool Insert_Y_Turn( RAS_ARGS Int y )
+ {
+ PStorage y_turns;
+ Int y2, n;
+
+ n = ras.numTurns-1;
+ y_turns = ras.sizeBuff - ras.numTurns;
+
+ /* look for first y value that is <= */
+ while ( n >= 0 && y < y_turns[n] )
+ n--;
+
+ /* if it is <, simply insert it, ignore if == */
+ if ( n >= 0 && y > y_turns[n] )
+ while ( n >= 0 )
+ {
+ y2 = y_turns[n];
+ y_turns[n] = y;
+ y = y2;
+ n--;
+ }
+
+ if ( n < 0 )
+ {
+ if (ras.maxBuff <= ras.top)
+ {
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+ ras.maxBuff--;
+ ras.numTurns++;
+ ras.sizeBuff[-ras.numTurns] = y;
+ }
+
+ return SUCCESS;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Finalize_Profile_Table */
+/* */
+/* Description: Adjusts all links in the Profiles list. */
+/* */
+/* Input: None */
+/* */
+/* Returns: None. */
+/* */
+/****************************************************************************/
+
+ static
+ Bool Finalize_Profile_Table( RAS_ARG )
+ {
+ Int bottom, top;
+ UShort n;
+ PProfile p;
+
+
+ n = ras.num_Profs;
+
+ if ( n > 1 )
+ {
+ p = ras.fProfile;
+ while ( n > 0 )
+ {
+ if ( n > 1 )
+ p->link = (PProfile)( p->offset + p->height );
+ else
+ p->link = NULL;
+
+ switch ( p->flow )
+ {
+ case FT_Flow_Down:
+ bottom = p->start - p->height+1;
+ top = p->start;
+ p->start = bottom;
+ p->offset += p->height-1;
+ break;
+
+ case FT_Flow_Up:
+ default:
+ bottom = p->start;
+ top = p->start + p->height-1;
+ }
+
+ if ( Insert_Y_Turn( RAS_VARS bottom ) ||
+ Insert_Y_Turn( RAS_VARS top+1 ) )
+ return FAILURE;
+
+ p = p->link;
+ n--;
+ }
+ }
+ else
+ ras.fProfile = NULL;
+
+ return SUCCESS;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Split_Conic */
+/* */
+/* Description: Subdivides one conic bezier into two joint */
+/* sub-arcs in the Bezier stack. */
+/* */
+/* Input: None (subdivided bezier is taken from the top of the */
+/* stack). */
+/* */
+/* Returns: None. */
+/* */
+/* */
+/* Note: This routine is the 'beef' of this component. It is _the_ */
+/* inner loop that should be optimized to hell to get the */
+/* best performance. */
+/* */
+/****************************************************************************/
+
+ static void Split_Conic( TPoint* base )
+ {
+ Long a, b;
+
+
+ base[4].x = base[2].x;
+ b = base[1].x;
+ a = base[3].x = ( base[2].x + b ) / 2;
+ b = base[1].x = ( base[0].x + b ) / 2;
+ base[2].x = ( a + b ) / 2;
+
+ base[4].y = base[2].y;
+ b = base[1].y;
+ a = base[3].y = ( base[2].y + b ) / 2;
+ b = base[1].y = ( base[0].y + b ) / 2;
+ base[2].y = ( a + b ) / 2;
+
+ /* hand optimized. gcc doesn't seem too good at common expression */
+ /* substitution and instruction scheduling ;-) */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> Split_Cubic */
+ /* */
+ /* <Description> */
+ /* Subdivides a third-order Bezier arc into two joint sub-arcs in */
+ /* the Bezier stack. */
+ /* */
+ /* <Note> */
+ /* This routine is the `beef' of the component. It is one of _the_ */
+ /* inner loops that should be optimized like hell to get the best */
+ /* performance. */
+ /* */
+ static
+ void Split_Cubic( TPoint* base )
+ {
+ Long a, b, c, d;
+
+ base[6].x = base[3].x;
+ c = base[1].x;
+ d = base[2].x;
+ base[1].x = a = ( base[0].x + c + 1 ) >> 1;
+ base[5].x = b = ( base[3].x + d + 1 ) >> 1;
+ c = ( c + d + 1 ) >> 1;
+ base[2].x = a = ( a + c + 1 ) >> 1;
+ base[4].x = b = ( b + c + 1 ) >> 1;
+ base[3].x = ( a + b + 1 ) >> 1;
+
+ base[6].y = base[3].y;
+ c = base[1].y;
+ d = base[2].y;
+ base[1].y = a = ( base[0].y + c + 1 ) >> 1;
+ base[5].y = b = ( base[3].y + d + 1 ) >> 1;
+ c = ( c + d + 1 ) >> 1;
+ base[2].y = a = ( a + c + 1 ) >> 1;
+ base[4].y = b = ( b + c + 1 ) >> 1;
+ base[3].y = ( a + b + 1 ) >> 1;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Push_Cubic */
+ /* */
+ /* <Description> */
+ /* Clears the Bezier stack and pushes a new third-order Bezier arc on */
+ /* top of it. */
+ /* */
+ /* <Input> */
+ /* p2 :: A pointer to the second (control) point. */
+ /* p3 :: A pointer to the third (control) point. */
+ /* p4 :: A pointer to the fourth (end) point. */
+ /* */
+ /* <Note> */
+ /* The first point is taken as `raster->last', so it doesn't appear */
+ /* in the signature. */
+ /* */
+ /* This is the same as Push_Conic(), except that it deals with */
+ /* third-order Beziers. */
+ /* */
+ static
+ static void Push_Cubic( RAS_ARGS Long x1, Long y1,
+ Long x2, Long y2,
+ Long x3, Long y3,
+ Long x4, Long y4 )
+ {
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Line_Up */
+/* */
+/* Description: Computes the x-coordinates of an ascending line segment */
+/* and stores them in the render pool. */
+/* */
+/* Input: x1,y1,x2,y2 Segment start (x1,y1) and end (x2,y2) points */
+/* */
+/* Returns: SUCCESS on success. */
+/* FAILURE on Render Pool overflow. */
+/* */
+/****************************************************************************/
+
+ static Bool Line_Up( RAS_ARGS Long x1, Long y1,
+ Long x2, Long y2,
+ Long miny, Long maxy )
+ {
+ Long Dx, Dy;
+ Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
+ Long Ix, Rx, Ax;
+
+ PStorage top;
+
+
+ Dx = x2 - x1;
+ Dy = y2 - y1;
+
+ if ( Dy <= 0 || y2 < miny || y1 > maxy )
+ return SUCCESS;
+
+ if ( y1 < miny )
+ {
+ /* Take care : miny-y1 can be a very large value, we use */
+ /* a slow MulDiv function to avoid clipping bugs */
+ x1 += SMulDiv( Dx, miny - y1, Dy );
+ e1 = TRUNC( miny );
+ f1 = 0;
+ }
+ else
+ {
+ e1 = TRUNC( y1 );
+ f1 = FRAC( y1 );
+ }
+
+ if ( y2 > maxy )
+ {
+ /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
+ e2 = TRUNC( maxy );
+ f2 = 0;
+ }
+ else
+ {
+ e2 = TRUNC( y2 );
+ f2 = FRAC( y2 );
+ }
+
+ if ( f1 > 0 )
+ {
+ if ( e1 == e2 ) return SUCCESS;
+ else
+ {
+ x1 += FMulDiv( Dx, ras.precision - f1, Dy );
+ e1 += 1;
+ }
+ }
+ else
+ if ( ras.joint )
+ {
+ ras.top--;
+ ras.joint = FALSE;
+ }
+
+ ras.joint = ( f2 == 0 );
+
+ if ( ras.fresh )
+ {
+ ras.cProfile->start = e1;
+ ras.fresh = FALSE;
+ }
+
+ size = e2 - e1 + 1;
+ if ( ras.top + size >= ras.maxBuff )
+ {
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ if ( Dx > 0 )
+ {
+ Ix = (ras.precision*Dx) / Dy;
+ Rx = (ras.precision*Dx) % Dy;
+ Dx = 1;
+ }
+ else
+ {
+ Ix = -( (ras.precision*-Dx) / Dy );
+ Rx = (ras.precision*-Dx) % Dy;
+ Dx = -1;
+ }
+
+ Ax = -Dy;
+ top = ras.top;
+
+ while ( size > 0 )
+ {
+ *top++ = x1;
+
+ x1 += Ix;
+ Ax += Rx;
+ if ( Ax >= 0 )
+ {
+ Ax -= Dy;
+ x1 += Dx;
+ }
+ size--;
+ }
+
+ ras.top = top;
+ return SUCCESS;
+ }
+
+
+ static Bool Line_Down( RAS_ARGS Long x1, Long y1,
+ Long x2, Long y2,
+ Long miny, Long maxy )
+ {
+ Bool result, fresh;
+
+
+ fresh = ras.fresh;
+
+ result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
+
+ if ( fresh && !ras.fresh )
+ ras.cProfile->start = -ras.cProfile->start;
+
+ return result;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Bezier_Up */
+/* */
+/* Description: Computes thes x-coordinates of an ascending bezier arc */
+/* and stores them in the render pool. */
+/* */
+
+ /* A function type describing the functions used to split bezier arcs */
+ typedef void (*TSplitter)( TPoint* base );
+
+ static Bool Bezier_Up( RAS_ARGS Int degree,
+ TSplitter splitter,
+ Long miny,
+ Long maxy )
+ {
+ Long y1, y2, e, e2, e0;
+ Short f1;
+
+ TPoint* arc;
+ TPoint* start_arc;
+
+ PStorage top;
+
+
+ arc = ras.arc;
+ y1 = arc[degree].y;
+ y2 = arc[0].y;
+ top = ras.top;
+
+ if ( y2 < miny || y1 > maxy )
+ goto Fin;
+
+ e2 = FLOOR( y2 );
+
+ if ( e2 > maxy )
+ e2 = maxy;
+
+ e0 = miny;
+
+ if ( y1 < miny )
+ e = miny;
+ else
+ {
+ e = CEILING( y1 );
+ f1 = FRAC( y1 );
+ e0 = e;
+
+ if ( f1 == 0 )
+ {
+ if ( ras.joint )
+ {
+ top--;
+ ras.joint = FALSE;
+ }
+
+ *top++ = arc[degree].x;
+
+ e += ras.precision;
+ }
+ }
+
+ if ( ras.fresh )
+ {
+ ras.cProfile->start = TRUNC( e0 );
+ ras.fresh = FALSE;
+ }
+
+ if ( e2 < e )
+ goto Fin;
+
+ if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
+ {
+ ras.top = top;
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ start_arc = arc;
+
+ while ( arc >= start_arc && e <= e2 )
+ {
+ ras.joint = FALSE;
+
+ y2 = arc[0].y;
+
+ if ( y2 > e )
+ {
+ y1 = arc[degree].y;
+ if ( y2 - y1 >= ras.precision_step )
+ {
+ splitter( arc );
+ arc += degree;
+ }
+ else
+ {
+ *top++ = arc[degree].x + FMulDiv( arc[0].x-arc[degree].x,
+ e - y1, y2 - y1 );
+ arc -= degree;
+ e += ras.precision;
+ }
+ }
+ else
+ {
+ if ( y2 == e )
+ {
+ ras.joint = TRUE;
+ *top++ = arc[0].x;
+
+ e += ras.precision;
+ }
+ arc -= degree;
+ }
+ }
+
+ Fin:
+ ras.top = top;
+ ras.arc -= degree;
+ return SUCCESS;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Bezier_Down */
+/* */
+/* Description: Computes the x-coordinates of a descending bezier arc */
+/* and stores them in the render pool. */
+/* */
+
+ static Bool Bezier_Down( RAS_ARGS Int degree,
+ TSplitter splitter,
+ Long miny,
+ Long maxy )
+ {
+ TPoint* arc = ras.arc;
+ Bool result, fresh;
+
+
+ arc[0].y = -arc[0].y;
+ arc[1].y = -arc[1].y;
+ arc[2].y = -arc[2].y;
+ if (degree > 2)
+ arc[3].y = -arc[3].y;
+
+ fresh = ras.fresh;
+
+ result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
+
+ if ( fresh && !ras.fresh )
+ ras.cProfile->start = -ras.cProfile->start;
+
+ arc[0].y = -arc[0].y;
+ return result;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Line_To */
+/* */
+/* Description: Injects a new line segment and adjusts Profiles list. */
+/* */
+/* Input: x, y : segment endpoint (start point in LastX,LastY) */
+/* */
+/* Returns: SUCCESS on success. */
+/* FAILURE on Render Pool overflow or Incorrect Profile. */
+/* */
+/****************************************************************************/
+
+ static Bool Line_To( RAS_ARGS Long x, Long y )
+ {
+ /* First, detect a change of direction */
+
+ switch ( ras.state )
+ {
+ case Unknown:
+ if ( y > ras.lastY )
+ {
+ if ( New_Profile( RAS_VARS Ascending ) ) return FAILURE;
+ }
+ else
+ {
+ if ( y < ras.lastY )
+ if ( New_Profile( RAS_VARS Descending ) ) return FAILURE;
+ }
+ break;
+
+ case Ascending:
+ if ( y < ras.lastY )
+ {
+ if ( End_Profile( RAS_VAR ) ||
+ New_Profile( RAS_VARS Descending ) ) return FAILURE;
+ }
+ break;
+
+ case Descending:
+ if ( y > ras.lastY )
+ {
+ if ( End_Profile( RAS_VAR ) ||
+ New_Profile( RAS_VARS Ascending ) ) return FAILURE;
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ /* Then compute the lines */
+
+ switch ( ras.state )
+ {
+ case Ascending:
+ if ( Line_Up ( RAS_VARS ras.lastX, ras.lastY,
+ x, y, ras.minY, ras.maxY ) )
+ return FAILURE;
+ break;
+
+ case Descending:
+ if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
+ x, y, ras.minY, ras.maxY ) )
+ return FAILURE;
+ break;
+
+ default:
+ ;
+ }
+
+ ras.lastX = x;
+ ras.lastY = y;
+
+ return SUCCESS;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Conic_To */
+/* */
+/* Description: Injects a new conic arc and adjusts the profile list. */
+/* */
+
+ static Bool Conic_To( RAS_ARGS Long x,
+ Long y,
+ Long cx,
+ Long cy )
+ {
+ Long y1, y2, y3, x3;
+ TStates state_bez;
+
+
+ ras.arc = ras.arcs;
+ ras.arc[2].x = ras.lastX;
+ ras.arc[2].y = ras.lastY;
+ ras.arc[1].x = cx; ras.arc[1].y = cy;
+ ras.arc[0].x = x; ras.arc[0].y = y;
+
+ do
+ {
+ y1 = ras.arc[2].y;
+ y2 = ras.arc[1].y;
+ y3 = ras.arc[0].y;
+ x3 = ras.arc[0].x;
+
+ /* first, categorize the Bezier arc */
+
+ if ( y1 <= y3 )
+ {
+ ymin = y1;
+ ymax = y3;
+ }
+ else
+ {
+ ymin = y3;
+ ymax = y1;
+ }
+
+ if ( y2 < ymin || y2 > ymax )
+ {
+ /* this arc has no given direction, split it !! */
+ Split_Conic( ras.arc );
+ ras.arc += 2;
+ }
+ else if ( y1 == y3 )
+ {
+ /* this arc is flat, ignore it and pop it from the bezier stack */
+ ras.arc -= 2;
+ }
+ else
+ {
+ /* the arc is y-monotonous, either ascending or descending */
+ /* detect a change of direction */
+ state_bez = y1 < y3 ? Ascending : Descending;
+ if ( ras.state != state_bez )
+ {
+ /* finalize current profile if any */
+ if ( ras.state != Unknown &&
+ End_Profile( RAS_VAR ) )
+ goto Fail;
+
+ /* create a new profile */
+ if ( New_Profile( RAS_VAR_ state_bez ) )
+ goto Fail;
+ }
+
+ /* now call the appropriate routine */
+ if ( state_bez == Ascending )
+ {
+ if ( Bezier_Up( RAS_VAR_ 2, Split_Conic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+ else
+ if ( Bezier_Down( RAS_VAR_ 2, Split_Conic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+
+ } while ( ras.arc >= ras.arcs );
+
+ ras.lastX = x3;
+ ras.lastY = y3;
+
+ return SUCCESS;
+ }
+
+/****************************************************************************/
+/* */
+/* Function: Cubic_To */
+/* */
+/* Description: Injects a new cubic arc and adjusts the profile list. */
+/* */
+
+ static Bool Cubic_To( RAS_ARGS Long x,
+ Long y,
+ Long cx1,
+ Long cy1,
+ Lonc cx2,
+ Long cy2 )
+ {
+ TPos y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
+ Long y0, y1, y2, y3, x3;
+ TStates state_bez;
+
+
+ ras.arc = ras.arcs;
+ ras.arc[3].x = ras.lastX;
+ ras.arc[3].y = ras.lastY;
+ ras.arc[2].x = cx1; ras.arc[2].y = cy1;
+ ras.arc[1].x = cx2; ras.arc[1].y = cy2;
+ ras.arc[0].x = x; ras.arc[0].y = y;
+
+ do
+ {
+ y1 = ras.arc[3].y;
+ y2 = ras.arc[2].y;
+ y3 = ras.arc[1].y;
+ y4 = ras.arc[0].y;
+ x4 = ras.arc[0].x;
+
+ /* first, categorize the Bezier arc */
+
+ if ( y1 <= y4 )
+ {
+ ymin1 = y1;
+ ymax1 = y4;
+ }
+ else
+ {
+ ymin1 = y4;
+ ymax1 = y1;
+ }
+
+ if ( y2 <= y3 )
+ {
+ ymin2 = y2;
+ ymax2 = y3;
+ }
+ else
+ {
+ ymin2 = y3;
+ ymax2 = y2;
+ }
+
+ if ( ymin2 < ymin1 || ymax2 > ymax1 )
+ {
+ /* this arc has no given direction, split it! */
+ Split_Cubic( ras.arc );
+ ras.arc += 3;
+ }
+ else if ( y1 == y4 )
+ {
+ /* this arc is flat, ignore it and pop it from the bezier stack */
+ ras.arc -= 3;
+ }
+ else
+ {
+ state_bez = ( y1 <= y4 ) ? Ascending : Descending;
+
+ /* detect a change of direction */
+ if ( ras.state != state_bez )
+ {
+ if ( ras.state != Unknown &&
+ End_Profile( RAS_VAR ) )
+ goto Fail;
+
+ if ( New_Profile( RAS_VAR_ state_bez ) )
+ goto Fail;
+ }
+
+ /* compute intersections */
+ if ( state_bez == Ascending )
+ {
+ if ( Bezier_Up ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+ else
+ if ( Bezier_Down ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+
+ } while ( ras.arc >= ras.arcs );
+
+ ras.lastX = x4
+ ras.lastY = y4
+
+ return SUCCESS;
+ Fail:
+ return FAILURE;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Decompose_Curve */
+/* */
+/* Description: Scans the outline arays in order to emit individual */
+/* segments and beziers by calling Line_To() and Bezier_To(). */
+/* It handles all weird cases, like when the first point */
+/* is off the curve, or when there are simply no 'on' */
+/* points in the contour! */
+/* */
+/* Input: first, last : indexes of first and last point in */
+/* contour. */
+/* */
+/* Returns: SUCCESS on success. */
+/* FAILURE on error. */
+/* */
+/****************************************************************************/
+
+#undef SWAP_
+#define SWAP_(x,y) { Long swap = x; x = y; y = swap; }
+
+
+ static Bool Decompose_Curve( RAS_ARGS UShort first,
+ UShort last,
+ Bool flipped )
+ {
+ FT_Vector v_last;
+ FT_Vector v_control;
+ FT_Vector v_start;
+
+ FT_Vector* points;
+ FT_Vector* point;
+ FT_Vector* limit;
+ char* tags;
+
+ int n; /* index of contour in outline */
+ int first; /* index of first point in contour */
+ int error;
+ char tag; /* current point's state */
+
+ points = ras.outline.points;
+ limit = points + last;
+
+ v_start.x = SCALED(points[first].x);
+ v_start.y = SCALED(points[first].y);
+ v_last.x = SCALED(points[last].x);
+ v_last.y = SCALED(points[last].y);
+ if (flipped)
+ {
+ SWAP_(v_start.x,v_start.y);
+ SWAP_(v_last.x,v_last.y);
+ }
+
+ v_control = v_start;
+
+ point = points + first;
+ tags = ras.outline->tags + first;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == FT_Curve_Tag_Cubic )
+ goto Invalid_Outline;
+
+ /* check first point to determine origin */
+ if ( tag == FT_Curve_Tag_Conic )
+ {
+ /* first point is conic control. Yes, this happens. */
+ if ( FT_CURVE_TAG( ras.outline->tags[last] ) == FT_Curve_Tag_On )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ else
+ {
+ /* if both first and last points are conic, */
+ /* start at their middle and record its position */
+ /* for closure */
+ v_start.x = ( v_start.x + v_last.x ) / 2;
+ v_start.y = ( v_start.y + v_last.y ) / 2;
+
+ v_last = v_start;
+ }
+ point--;
+ tags--;
+ }
+
+ while (point < limit)
+ {
+ point++;
+ tags++;
+
+ tag = FT_CURVE_TAG( tags[0] );
+ switch (tag)
+ {
+ case FT_Curve_Tag_On: /* emit a single line_to */
+ {
+ Long x, y;
+
+ x = SCALED(point->x);
+ y = SCALED(point->y);
+ if (flipped) SWAP_(x,y);
+
+ if (Line_To( RAS_VARS x, y )) goto Fail;
+ continue;
+ }
+
+
+ case FT_Curve_Tag_Conic: /* consume conic arcs */
+ {
+ v_control.x = SCALED(point[0].x);
+ v_control.y = SCALED(point[0].y);
+ if (flipped) SWAP_(v_control.x,v_control.y);
+
+ Do_Conic:
+ if (point < limit)
+ {
+ FT_Vector v_middle;
+ Long x, y;
+
+ point++;
+ tags++;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ x = SCALED(point[0].x);
+ y = SCALED(point[0].y);
+ if (flipped) SWAP_(x,y);
+
+ if (tag == FT_Curve_Tag_On)
+ {
+ if (Conic_To( RAS_VARS v_control.x, v_control.y, x, y ))
+ goto Fail;
+ continue;
+ }
+
+ if (tag != FT_Curve_Tag_Conic)
+ goto Invalid_Outline;
+
+ v_middle.x = (v_control.x + x)/2;
+ v_middle.y = (v_control.y + y)/2;
+
+ if (Conic_To( RAS_VARS v_control.x, v_control.y,
+ v_middle.x, v_middle.y )) goto Fail;
+
+ v_control.x = x;
+ v_control.y = y;
+ goto Do_Conic;
+ }
+
+ if (Conic_To( RAS_VARS v_control.x, v_control.y,
+ v_start.x, v_start.y )) goto Fail;
+ goto Close;
+ }
+
+ default: /* FT_Curve_Tag_Cubic */
+ {
+ Long x1, y1, x2, y2, x3, y3;
+
+ if ( point+1 > limit ||
+ FT_CURVE_TAG( tags[1] ) != FT_Curve_Tag_Cubic )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ x1 = SCALED(point[-2].x);
+ y1 = SCALED(point[-2].y);
+ x2 = SCALED(point[-1].x);
+ y2 = SCALED(point[-1].y);
+ x3 = SCALED(point[ 0].x);
+ y3 = SCALED(point[ 0].y);
+ if (flipped)
+ {
+ SWAP_(x1,y1);
+ SWAP_(x2,y2);
+ SWAP_(x3,y3);
+ }
+ if (point <= limit)
+ {
+ if (Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ))
+ goto Fail;
+ continue;
+ }
+
+ if (Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ))
+ goto Fail;
+ goto Close;
+ }
+ }
+ }
+
+ /* close the contour with a line segment */
+ if (Line_To( RAS_VARS v_start.x, v_start.y ))
+ goto Fail;
+ }
+
+Close:
+ return SUCCESS;
+
+Invalid_Outline:
+ ras.error = Raster_Err_Invalid;
+
+Fail:
+ return FAILURE;
+}
+
+/****************************************************************************/
+/* */
+/* Function: Convert_Glyph */
+/* */
+/* Description: Converts a glyph into a series of segments and arcs */
+/* and makes a Profiles list with them. */
+/* */
+/* Input: _xCoord, _yCoord : coordinates tables. */
+/* */
+/* Uses the 'Flag' table too. */
+/* */
+/* Returns: SUCCESS on success. */
+/* FAILURE if any error was encountered during rendering. */
+/* */
+/****************************************************************************/
+
+ static Bool Convert_Glyph( RAS_ARGS int flipped )
+ {
+ Short i;
+ UShort start;
+
+ PProfile lastProfile;
+
+
+ ras.fProfile = NULL;
+ ras.joint = FALSE;
+ ras.fresh = FALSE;
+
+ ras.maxBuff = ras.sizeBuff - AlignProfileSize;
+
+ ras.numTurns = 0;
+
+ ras.cProfile = (PProfile)ras.top;
+ ras.cProfile->offset = ras.top;
+ ras.num_Profs = 0;
+
+ start = 0;
+
+ for ( i = 0; i < ras.nContours; i++ )
+ {
+ ras.state = Unknown;
+ ras.gProfile = NULL;
+
+ if ( Decompose_Curve( RAS_VARS start, ras.outs[i], flipped ) )
+ return FAILURE;
+
+ start = ras.outs[i] + 1;
+
+ /* We must now see if the extreme arcs join or not */
+ if ( ( FRAC( ras.lastY ) == 0 &&
+ ras.lastY >= ras.minY &&
+ ras.lastY <= ras.maxY ) )
+ if ( ras.gProfile && ras.gProfile->flow == ras.cProfile->flow )
+ ras.top--;
+ /* Note that ras.gProfile can be nil if the contour was too small */
+ /* to be drawn. */
+
+ lastProfile = ras.cProfile;
+ if ( End_Profile( RAS_VAR ) ) return FAILURE;
+
+ /* close the 'next profile in contour' linked list */
+ if ( ras.gProfile )
+ lastProfile->next = ras.gProfile;
+ }
+
+ if (Finalize_Profile_Table( RAS_VAR ))
+ return FAILURE;
+
+ return (ras.top < ras.maxBuff ? SUCCESS : FAILURE );
+ }
+
+
+ /****************************************************************/
+ /****************************************************************/
+ /** **/
+ /** SCAN-LINE SWEEPS AND DRAWING **/
+ /** **/
+ /****************************************************************/
+ /****************************************************************/
+
+
+/************************************************/
+/* */
+/* Init_Linked */
+/* */
+/* Inits an empty linked list. */
+/* */
+/************************************************/
+
+ static void Init_Linked( TProfileList* l )
+ {
+ *l = NULL;
+ }
+
+
+/************************************************/
+/* */
+/* InsNew : */
+/* */
+/* Inserts a new Profile in a linked list. */
+/* */
+/************************************************/
+
+ static void InsNew( PProfileList list,
+ PProfile profile )
+ {
+ PProfile *old, current;
+ Long x;
+
+
+ old = list;
+ current = *old;
+ x = profile->X;
+
+ while ( current )
+ {
+ if ( x < current->X )
+ break;
+ old = ¤t->link;
+ current = *old;
+ }
+
+ profile->link = current;
+ *old = profile;
+ }
+
+
+/*************************************************/
+/* */
+/* DelOld : */
+/* */
+/* Removes an old Profile from a linked list. */
+/* */
+/*************************************************/
+
+ static void DelOld( PProfileList list,
+ PProfile profile )
+ {
+ PProfile *old, current;
+
+
+ old = list;
+ current = *old;
+
+ while ( current )
+ {
+ if ( current == profile )
+ {
+ *old = current->link;
+ return;
+ }
+
+ old = ¤t->link;
+ current = *old;
+ }
+
+ /* we should never get there, unless the Profile was not part of */
+ /* the list. */
+ }
+
+
+/************************************************/
+/* */
+/* Update : */
+/* */
+/* Update all X offsets of a drawing list */
+/* */
+/************************************************/
+
+ static void Update( PProfile first )
+ {
+ PProfile current = first;
+
+
+ while ( current )
+ {
+ current->X = *current->offset;
+ current->offset += current->flow;
+ current->height--;
+ current = current->link;
+ }
+ }
+
+
+/************************************************/
+/* */
+/* Sort : */
+/* */
+/* Sorts a trace list. In 95%, the list */
+/* is already sorted. We need an algorithm */
+/* which is fast in this case. Bubble sort */
+/* is enough and simple. */
+/* */
+/************************************************/
+
+ static void Sort( PProfileList list )
+ {
+ PProfile *old, current, next;
+
+
+ /* First, set the new X coordinate of each profile */
+ Update( *list );
+
+ /* Then sort them */
+ old = list;
+ current = *old;
+
+ if ( !current )
+ return;
+
+ next = current->link;
+
+ while ( next )
+ {
+ if ( current->X <= next->X )
+ {
+ old = ¤t->link;
+ current = *old;
+
+ if ( !current )
+ return;
+ }
+ else
+ {
+ *old = next;
+ current->link = next->link;
+ next->link = current;
+
+ old = list;
+ current = *old;
+ }
+
+ next = current->link;
+ }
+ }
+
+
+/***********************************************************************/
+/* */
+/* Vertical Sweep Procedure Set : */
+/* */
+/* These three routines are used during the vertical black/white */
+/* sweep phase by the generic Draw_Sweep() function. */
+/* */
+/***********************************************************************/
+
+ static void Vertical_Sweep_Init( RAS_ARGS Short* min, Short* max )
+ {
+ switch ( ras.target.flow )
+ {
+ case FT_Flow_Up:
+ ras.traceOfs = *min * ras.target.cols;
+ ras.traceIncr = ras.target.cols;
+ break;
+
+ default:
+ ras.traceOfs = ( ras.target.rows - 1 - *min ) * ras.target.cols;
+ ras.traceIncr = -ras.target.cols;
+ }
+
+ ras.gray_min_x = 0;
+ ras.gray_max_x = 0;
+ }
+
+
+ static void Vertical_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ Short c1, c2;
+ Short f1, f2;
+ Byte* target;
+
+
+ /* Drop-out control */
+
+ e1 = TRUNC( CEILING( x1 ) );
+
+ if ( x2-x1-ras.precision <= ras.precision_jitter )
+ e2 = e1;
+ else
+ e2 = TRUNC( FLOOR( x2 ) );
+
+ if ( e2 >= 0 && e1 < ras.bWidth )
+ {
+ if ( e1 < 0 ) e1 = 0;
+ if ( e2 >= ras.bWidth ) e2 = ras.bWidth-1;
+
+ c1 = (Short)(e1 >> 3);
+ c2 = (Short)(e2 >> 3);
+
+ f1 = e1 & 7;
+ f2 = e2 & 7;
+
+ if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
+ if ( ras.gray_max_x < c2 ) ras.gray_max_x = c2;
+
+ target = ras.bTarget + ras.traceOfs + c1;
+
+ if ( c1 != c2 )
+ {
+ *target |= LMask[f1];
+
+ if ( c2 > c1 + 1 )
+ MEM_Set( target + 1, 0xFF, c2 - c1 - 1 );
+
+ target[c2 - c1] |= RMask[f2];
+ }
+ else
+ *target |= ( LMask[f1] & RMask[f2] );
+ }
+ }
+
+
+ static void Vertical_Sweep_Drop( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ Short c1, f1;
+
+
+ /* Drop-out control */
+
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 > e2 )
+ {
+ if ( e1 == e2 + ras.precision )
+ {
+ switch ( ras.dropOutControl )
+ {
+ case 1:
+ e1 = e2;
+ break;
+
+ case 4:
+ e1 = CEILING( (x1 + x2 + 1) / 2 );
+ break;
+
+ case 2:
+ case 5:
+ /* Drop-out Control Rule #4 */
+
+ /* The spec is not very clear regarding rule #4. It */
+ /* presents a method that is way too costly to implement */
+ /* while the general idea seems to get rid of 'stubs'. */
+ /* */
+ /* Here, we only get rid of stubs recognized when: */
+ /* */
+ /* upper stub: */
+ /* */
+ /* - P_Left and P_Right are in the same contour */
+ /* - P_Right is the successor of P_Left in that contour */
+ /* - y is the top of P_Left and P_Right */
+ /* */
+ /* lower stub: */
+ /* */
+ /* - P_Left and P_Right are in the same contour */
+ /* - P_Left is the successor of P_Right in that contour */
+ /* - y is the bottom of P_Left */
+ /* */
+
+ /* FIXXXME : uncommenting this line solves the disappearing */
+ /* bit problem in the '7' of verdana 10pts, but */
+ /* makes a new one in the 'C' of arial 14pts */
+
+ /* if ( x2-x1 < ras.precision_half ) */
+ {
+ /* upper stub test */
+
+ if ( left->next == right && left->height <= 0 ) return;
+
+ /* lower stub test */
+
+ if ( right->next == left && left->start == y ) return;
+ }
+
+ /* check that the rightmost pixel isn't set */
+
+ e1 = TRUNC( e1 );
+
+ c1 = (Short)(e1 >> 3);
+ f1 = e1 & 7;
+
+ if ( e1 >= 0 && e1 < ras.bWidth &&
+ ras.bTarget[ras.traceOfs + c1] & (0x80 >> f1) )
+ return;
+
+ if ( ras.dropOutControl == 2 )
+ e1 = e2;
+ else
+ e1 = CEILING( (x1 + x2 + 1) / 2 );
+
+ break;
+
+ default:
+ return; /* unsupported mode */
+ }
+ }
+ else
+ return;
+ }
+
+ e1 = TRUNC( e1 );
+
+ if ( e1 >= 0 && e1 < ras.bWidth )
+ {
+ c1 = (Short)(e1 >> 3);
+ f1 = e1 & 7;
+
+ if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
+ if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1;
+
+ ras.bTarget[ras.traceOfs + c1] |= (Char)(0x80 >> f1);
+ }
+ }
+
+
+ static void Vertical_Sweep_Step( RAS_ARG )
+ {
+ ras.traceOfs += ras.traceIncr;
+ }
+
+
+/***********************************************************************/
+/* */
+/* Horizontal Sweep Procedure Set : */
+/* */
+/* These three routines are used during the horizontal black/white */
+/* sweep phase by the generic Draw_Sweep() function. */
+/* */
+/***********************************************************************/
+
+ static void Horizontal_Sweep_Init( RAS_ARGS Short* min, Short* max )
+ {
+ /* nothing, really */
+ }
+
+
+ static void Horizontal_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ PByte bits;
+ Byte f1;
+
+
+ if ( x2-x1 < ras.precision )
+ {
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 == e2 )
+ {
+ bits = ras.bTarget + (y >> 3);
+ f1 = (Byte)(0x80 >> (y & 7));
+
+ e1 = TRUNC( e1 );
+
+ if ( e1 >= 0 && e1 < ras.target.rows )
+ {
+ if ( ras.target.flow == FT_Flow_Down )
+ bits[(ras.target.rows-1 - e1) * ras.target.cols] |= f1;
+ else
+ bits[e1 * ras.target.cols] |= f1;
+ }
+ }
+ }
+#if 0
+ e2 = TRUNC( e2 );
+
+ if ( e2 >= 0 && e2 < ras.target.rows )
+ if ( ras.target.flow == FT_Flow_Down )
+ bits[(ras.target.rows-1-e2) * ras.target.cols] |= f1;
+ else
+ bits[e2 * ras.target.cols] |= f1;
+#endif
+ }
+
+
+ static void Horizontal_Sweep_Drop( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ PByte bits;
+ Byte f1;
+
+
+ /* During the horizontal sweep, we only take care of drop-outs */
+
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 > e2 )
+ {
+ if ( e1 == e2 + ras.precision )
+ {
+ switch ( ras.dropOutControl )
+ {
+ case 1:
+ e1 = e2;
+ break;
+
+ case 4:
+ e1 = CEILING( (x1 + x2 + 1) / 2 );
+ break;
+
+ case 2:
+ case 5:
+
+ /* Drop-out Control Rule #4 */
+
+ /* The spec is not very clear regarding rule #4. It */
+ /* presents a method that is way too costly to implement */
+ /* while the general idea seems to get rid of 'stubs'. */
+ /* */
+
+ /* rightmost stub test */
+
+ if ( left->next == right && left->height <= 0 ) return;
+
+ /* leftmost stub test */
+
+ if ( right->next == left && left->start == y ) return;
+
+ /* check that the rightmost pixel isn't set */
+
+ e1 = TRUNC( e1 );
+
+ bits = ras.bTarget + (y >> 3);
+ f1 = (Byte)(0x80 >> (y & 7));
+
+ if ( ras.target.flow == FT_Flow_Down )
+ bits += (ras.target.rows-1-e1) * ras.target.cols;
+ else
+ bits += e1 * ras.target.cols;
+
+ if ( e1 >= 0 &&
+ e1 < ras.target.rows &&
+ *bits & f1 )
+ return;
+
+ if ( ras.dropOutControl == 2 )
+ e1 = e2;
+ else
+ e1 = CEILING( (x1 + x2 + 1) / 2 );
+
+ break;
+
+ default:
+ return; /* unsupported mode */
+ }
+ }
+ else
+ return;
+ }
+
+ bits = ras.bTarget + (y >> 3);
+ f1 = (Byte)(0x80 >> (y & 7));
+
+ e1 = TRUNC( e1 );
+
+ if ( e1 >= 0 && e1 < ras.target.rows )
+ {
+ if (ras.target.flow==FT_Flow_Down)
+ bits[(ras.target.rows-1-e1) * ras.target.cols] |= f1;
+ else
+ bits[e1 * ras.target.cols] |= f1;
+ }
+ }
+
+
+ static void Horizontal_Sweep_Step( RAS_ARG )
+ {
+ /* Nothing, really */
+ }
+
+
+#ifdef FT_CONFIG_OPTION_GRAY_SCALING
+
+/***********************************************************************/
+/* */
+/* Vertical Gray Sweep Procedure Set: */
+/* */
+/* These two routines are used during the vertical gray-levels */
+/* sweep phase by the generic Draw_Sweep() function. */
+/* */
+/* */
+/* NOTES: */
+/* */
+/* - The target pixmap's width *must* be a multiple of 4. */
+/* */
+/* - you have to use the function Vertical_Sweep_Span() for */
+/* the gray span call. */
+/* */
+/***********************************************************************/
+
+ static void Vertical_Gray_Sweep_Init( RAS_ARGS Short* min, Short* max )
+ {
+ *min = *min & -2;
+ *max = ( *max + 3 ) & -2;
+
+ ras.traceOfs = 0;
+
+ switch ( ras.target.flow )
+ {
+ case FT_Flow_Up:
+ ras.traceG = (*min / 2) * ras.target.cols;
+ ras.traceIncr = ras.target.cols;
+ break;
+
+ default:
+ ras.traceG = (ras.target.rows-1 - *min/2) * ras.target.cols;
+ ras.traceIncr = -ras.target.cols;
+ }
+
+ ras.gray_min_x = ras.target.cols;
+ ras.gray_max_x = -ras.target.cols;
+ }
+
+
+ static void Vertical_Gray_Sweep_Step( RAS_ARG )
+ {
+ Int c1, c2;
+ PByte pix, bit, bit2;
+ Int* count = ras.count_table;
+ Byte* grays;
+
+
+ ras.traceOfs += ras.gray_width;
+
+ if ( ras.traceOfs > ras.gray_width )
+ {
+ pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
+ grays = ras.grays;
+
+ if ( ras.gray_max_x >= 0 )
+ {
+ if ( ras.gray_max_x >= ras.target.width )
+ ras.gray_max_x = ras.target.width-1;
+
+ if ( ras.gray_min_x < 0 )
+ ras.gray_min_x = 0;
+
+ bit = ras.bTarget + ras.gray_min_x;
+ bit2 = bit + ras.gray_width;
+
+ c1 = ras.gray_max_x - ras.gray_min_x;
+
+ while ( c1 >= 0 )
+ {
+ c2 = count[*bit] + count[*bit2];
+
+ if ( c2 )
+ {
+ pix[0] = grays[(c2 & 0xF000) >> 12];
+ pix[1] = grays[(c2 & 0x0F00) >> 8];
+ pix[2] = grays[(c2 & 0x00F0) >> 4];
+ pix[3] = grays[(c2 & 0x000F) ];
+
+ *bit = 0;
+ *bit2 = 0;
+ }
+
+ bit ++;
+ bit2++;
+ pix += 4;
+ c1 --;
+ }
+ }
+
+ ras.traceOfs = 0;
+ ras.traceG += ras.traceIncr;
+
+ ras.gray_min_x = ras.target.cols;
+ ras.gray_max_x = -ras.target.cols;
+ }
+ }
+
+
+ static void Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ /* nothing, really */
+ }
+
+ static void Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ PByte pixel;
+ Byte color;
+
+
+ /* During the horizontal sweep, we only take care of drop-outs */
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 > e2 )
+ {
+ if ( e1 == e2 + ras.precision )
+ {
+ switch ( ras.dropOutControl )
+ {
+ case 1:
+ e1 = e2;
+ break;
+
+ case 4:
+ e1 = CEILING( (x1 + x2 + 1) / 2 );
+ break;
+
+ case 2:
+ case 5:
+
+ /* Drop-out Control Rule #4 */
+
+ /* The spec is not very clear regarding rule #4. It */
+ /* presents a method that is way too costly to implement */
+ /* while the general idea seems to get rid of 'stubs'. */
+ /* */
+
+ /* rightmost stub test */
+ if ( left->next == right && left->height <= 0 ) return;
+
+ /* leftmost stub test */
+ if ( right->next == left && left->start == y ) return;
+
+ if ( ras.dropOutControl == 2 )
+ e1 = e2;
+ else
+ e1 = CEILING( (x1 + x2 + 1) / 2 );
+
+ break;
+
+ default:
+ return; /* unsupported mode */
+ }
+ }
+ else
+ return;
+ }
+
+ if ( e1 >= 0 )
+ {
+ if ( x2 - x1 >= ras.precision_half )
+ color = ras.grays[2];
+ else
+ color = ras.grays[1];
+
+ e1 = TRUNC( e1 ) / 2;
+ if ( e1 < ras.target.rows )
+ {
+ if ( ras.target.flow == FT_Flow_Down )
+ pixel = ras.gTarget +
+ (ras.target.rows - 1 - e1) * ras.target.cols + y / 2;
+ else
+ pixel = ras.gTarget +
+ e1 * ras.target.cols + y / 2;
+
+ if (pixel[0] == ras.grays[0])
+ pixel[0] = color;
+ }
+ }
+ }
+
+#endif /* FT_CONFIG_OPTION_GRAY_SCALING */
+
+
+/********************************************************************/
+/* */
+/* Generic Sweep Drawing routine */
+/* */
+/********************************************************************/
+
+ static Bool Draw_Sweep( RAS_ARG )
+ {
+ Short y, y_change, y_height;
+
+ PProfile P, Q, P_Left, P_Right;
+
+ Short min_Y, max_Y, top, bottom, dropouts;
+
+ Long x1, x2, xs, e1, e2;
+
+ TProfileList wait;
+ TProfileList draw_left, draw_right;
+
+
+ /* Init empty linked lists */
+
+ Init_Linked( &wait );
+
+ Init_Linked( &draw_left );
+ Init_Linked( &draw_right );
+
+ /* first, compute min and max Y */
+
+ P = ras.fProfile;
+ max_Y = (short)TRUNC( ras.minY );
+ min_Y = (short)TRUNC( ras.maxY );
+
+ while ( P )
+ {
+ Q = P->link;
+
+ bottom = P->start;
+ top = P->start + P->height-1;
+
+ if ( min_Y > bottom ) min_Y = bottom;
+ if ( max_Y < top ) max_Y = top;
+
+ P->X = 0;
+ InsNew( &wait, P );
+
+ P = Q;
+ }
+
+ /* Check the Y-turns */
+ if ( ras.numTurns == 0 )
+ {
+ ras.error = Raster_Err_Invalid;
+ return FAILURE;
+ }
+
+ /* Now inits the sweep */
+
+ ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
+
+ /* Then compute the distance of each profile from min_Y */
+
+ P = wait;
+
+ while ( P )
+ {
+ P->countL = P->start - min_Y;
+ P = P->link;
+ }
+
+ /* Let's go */
+
+ y = min_Y;
+ y_height = 0;
+
+ if ( ras.numTurns > 0 &&
+ ras.sizeBuff[-ras.numTurns] == min_Y )
+ ras.numTurns--;
+
+ while ( ras.numTurns > 0 )
+ {
+ /* look in the wait list for new activations */
+
+ P = wait;
+
+ while ( P )
+ {
+ Q = P->link;
+ P->countL -= y_height;
+ if ( P->countL == 0 )
+ {
+ DelOld( &wait, P );
+
+ switch ( P->flow )
+ {
+ case FT_Flow_Up: InsNew( &draw_left, P ); break;
+ case FT_Flow_Down: InsNew( &draw_right, P ); break;
+ }
+ }
+
+ P = Q;
+ }
+
+ /* Sort the drawing lists */
+
+ Sort( &draw_left );
+ Sort( &draw_right );
+
+ y_change = (Short)ras.sizeBuff[-ras.numTurns--];
+ y_height = y_change - y;
+
+ while ( y < y_change )
+ {
+
+ /* Let's trace */
+
+ dropouts = 0;
+
+ P_Left = draw_left;
+ P_Right = draw_right;
+
+ while ( P_Left )
+ {
+ x1 = P_Left ->X;
+ x2 = P_Right->X;
+
+ if ( x1 > x2 )
+ {
+ xs = x1;
+ x1 = x2;
+ x2 = xs;
+ }
+
+ if ( x2-x1 <= ras.precision )
+ {
+ e1 = FLOOR( x1 );
+ e2 = CEILING( x2 );
+
+ if ( ras.dropOutControl != 0 &&
+ (e1 > e2 || e2 == e1 + ras.precision) )
+ {
+ /* a drop out was detected */
+
+ P_Left ->X = x1;
+ P_Right->X = x2;
+
+ /* mark profile for drop-out processing */
+ P_Left->countL = 1;
+ dropouts++;
+
+ goto Skip_To_Next;
+ }
+ }
+
+ ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
+
+ Skip_To_Next:
+
+ P_Left = P_Left->link;
+ P_Right = P_Right->link;
+ }
+
+ /* now perform the dropouts _after_ the span drawing */
+ /* drop-outs processing has been moved out of the loop */
+ /* for performance tuning */
+ if (dropouts > 0)
+ goto Scan_DropOuts;
+
+ Next_Line:
+
+ ras.Proc_Sweep_Step( RAS_VAR );
+
+ y++;
+
+ if ( y < y_change )
+ {
+ Sort( &draw_left );
+ Sort( &draw_right );
+ }
+
+ }
+
+ /* Now finalize the profiles that needs it */
+
+ {
+ PProfile Q, P;
+ P = draw_left;
+ while ( P )
+ {
+ Q = P->link;
+ if ( P->height == 0 )
+ DelOld( &draw_left, P );
+ P = Q;
+ }
+ }
+
+ {
+ PProfile Q, P = draw_right;
+ while ( P )
+ {
+ Q = P->link;
+ if ( P->height == 0 )
+ DelOld( &draw_right, P );
+ P = Q;
+ }
+ }
+ }
+
+ /* for gray-scaling, flushes the bitmap scanline cache */
+ while ( y <= max_Y )
+ {
+ ras.Proc_Sweep_Step( RAS_VAR );
+ y++;
+ }
+
+ return SUCCESS;
+
+Scan_DropOuts :
+
+ P_Left = draw_left;
+ P_Right = draw_right;
+
+ while ( P_Left )
+ {
+ if ( P_Left->countL )
+ {
+ P_Left->countL = 0;
+ /* dropouts--; -- this is useful when debugging only */
+ ras.Proc_Sweep_Drop( RAS_VARS y,
+ P_Left->X,
+ P_Right->X,
+ P_Left,
+ P_Right );
+ }
+
+ P_Left = P_Left->link;
+ P_Right = P_Right->link;
+ }
+
+ goto Next_Line;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Render_Single_Pass */
+/* */
+/* Description: Performs one sweep with sub-banding. */
+/* */
+/* Input: _XCoord, _YCoord : x and y coordinates arrays */
+/* */
+/* Returns: SUCCESS on success */
+/* FAILURE if any error was encountered during render. */
+/* */
+/****************************************************************************/
+
+ static FT_Error Render_Single_Pass( RAS_ARGS Bool flipped )
+ {
+ Short i, j, k;
+
+
+ while ( ras.band_top >= 0 )
+ {
+ ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
+ ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
+
+ ras.top = ras.buff;
+
+ ras.error = Raster_Err_None;
+
+ if ( Convert_Glyph( RAS_VARS flipped ) )
+ {
+ if ( ras.error != Raster_Err_Overflow ) return FAILURE;
+
+ ras.error = Raster_Err_None;
+
+ /* sub-banding */
+
+#ifdef DEBUG_RASTER
+ ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
+#endif
+
+ i = ras.band_stack[ras.band_top].y_min;
+ j = ras.band_stack[ras.band_top].y_max;
+
+ k = ( i + j ) / 2;
+
+ if ( ras.band_top >= 7 || k < i )
+ {
+ ras.band_top = 0;
+ ras.error = Raster_Err_Invalid;
+ return ras.error;
+ }
+
+ ras.band_stack[ras.band_top+1].y_min = k;
+ ras.band_stack[ras.band_top+1].y_max = j;
+
+ ras.band_stack[ras.band_top].y_max = k - 1;
+
+ ras.band_top++;
+ }
+ else
+ {
+ if ( ras.fProfile )
+ if ( Draw_Sweep( RAS_VAR ) ) return ras.error;
+ ras.band_top--;
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+/****************************************************************************/
+/* */
+/* Function: Render_Glyph */
+/* */
+/* Description: Renders a glyph in a bitmap. Sub-banding if needed. */
+/* */
+/* Input: AGlyph Glyph record */
+/* */
+/* Returns: SUCCESS on success. */
+/* FAILURE if any error was encountered during rendering. */
+/* */
+/****************************************************************************/
+
+ LOCAL_FUNC
+ FT_Error Render_Glyph( RAS_ARGS FT_Outline* glyph,
+ FT_Raster_Map* target_map )
+ {
+ FT_Error error;
+
+
+ if ( glyph->n_points == 0 || glyph->n_contours <= 0 )
+ return FT_Err_Ok;
+
+ if ( !ras.buff )
+ {
+ ras.error = Raster_Err_Not_Ini;
+ return ras.error;
+ }
+
+ if ( glyph->n_points < glyph->contours[glyph->n_contours - 1] )
+ {
+ ras.error = FT_Err_Too_Many_Points;
+ return ras.error;
+ }
+
+ if ( target_map )
+ ras.target = *target_map;
+
+ ras.outs = glyph->contours;
+ ras.flags = glyph->flags;
+ ras.nPoints = glyph->n_points;
+ ras.nContours = glyph->n_contours;
+ ras.coords = glyph->points;
+
+ Set_High_Precision( RAS_VARS glyph->high_precision );
+ ras.scale_shift = ras.precision_shift;
+ ras.dropOutControl = glyph->dropout_mode;
+ ras.second_pass = glyph->second_pass;
+
+
+ /* Vertical Sweep */
+ ras.Proc_Sweep_Init = Vertical_Sweep_Init;
+ ras.Proc_Sweep_Span = Vertical_Sweep_Span;
+ ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
+ ras.Proc_Sweep_Step = Vertical_Sweep_Step;
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = ras.target.rows - 1;
+
+ ras.bWidth = ras.target.width;
+ ras.bTarget = (Byte*)ras.target.bitmap;
+
+ if ( (error = Render_Single_Pass( RAS_VARS 0 )) != 0 )
+ return error;
+
+ /* Horizontal Sweep */
+
+ if ( ras.second_pass && ras.dropOutControl != 0 )
+ {
+ ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
+ ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
+ ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
+ ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = ras.target.width - 1;
+
+ if ( (error = Render_Single_Pass( RAS_VARS 1 )) != 0 )
+ return error;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+#ifdef FT_CONFIG_OPTION_GRAY_SCALING
+
+/****************************************************************************/
+/* */
+/* Function: Render_Gray_Glyph */
+/* */
+/* Description: Renders a glyph with grayscaling. Sub-banding if needed. */
+/* */
+/* Input: AGlyph Glyph record */
+/* */
+/* Returns: SUCCESS on success */
+/* FAILURE if any error was encountered during rendering. */
+/* */
+/****************************************************************************/
+
+ LOCAL_FUNC
+ FT_Error Render_Gray_Glyph( RAS_ARGS FT_Outline* glyph,
+ FT_Raster_Map* target_map,
+ Byte* palette )
+ {
+ Int i;
+ FT_Error error;
+
+ if ( !ras.buff )
+ {
+ ras.error = Raster_Err_Not_Ini;
+ return ras.error;
+ }
+
+ if ( glyph->n_points == 0 || glyph->n_contours <= 0 )
+ return FT_Err_Ok;
+
+ if ( glyph->n_points < glyph->contours[glyph->n_contours - 1] )
+ {
+ ras.error = FT_Err_Too_Many_Points;
+ return ras.error;
+ }
+
+ if ( palette )
+ {
+ for ( i = 0; i < 5; i++ )
+ ras.grays[i] = palette[i];
+ }
+
+ if ( target_map )
+ ras.target = *target_map;
+
+ ras.outs = glyph->contours;
+ ras.flags = glyph->flags;
+ ras.nPoints = glyph->n_points;
+ ras.nContours = glyph->n_contours;
+ ras.coords = glyph->points;
+
+ Set_High_Precision( RAS_VARS glyph->high_precision );
+ ras.scale_shift = ras.precision_shift+1;
+ ras.dropOutControl = glyph->dropout_mode;
+ ras.second_pass = glyph->second_pass;
+
+
+ /* Vertical Sweep */
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
+
+ ras.bWidth = ras.gray_width;
+ if ( ras.bWidth > ras.target.cols/4 )
+ ras.bWidth = ras.target.cols/4;
+
+ ras.bWidth = ras.bWidth * 8;
+ ras.bTarget = (Byte*)ras.gray_lines;
+ ras.gTarget = (Byte*)ras.target.bitmap;
+
+ ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
+ ras.Proc_Sweep_Span = Vertical_Sweep_Span;
+ ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
+ ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
+
+ error = Render_Single_Pass( RAS_VARS 0 );
+ if (error)
+ return error;
+
+ /* Horizontal Sweep */
+
+ if ( ras.second_pass && ras.dropOutControl != 0 )
+ {
+ ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
+ ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
+ ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
+ ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = ras.target.width * 2 - 1;
+
+ error = Render_Single_Pass( RAS_VARS 1 );
+ if (error)
+ return error;
+ }
+
+ return FT_Err_Ok;
+ }
+
+#endif /* FT_CONFIG_OPTION_GRAY_SCALING */
+
+
+/************************************************/
+/* */
+/* InitRasterizer */
+/* */
+/* Raster Initialization. */
+/* Gets the bitmap description and render pool */
+/* addresses. */
+/* */
+/************************************************/
+
+#undef ras
+
+ LOCAL_FUNC
+ FT_Error TTRaster_Done( PEngine_Instance engine )
+ {
+ TRaster_Instance* ras = (TRaster_Instance*)engine->raster_component;
+
+
+ if ( !ras )
+ return FT_Err_Ok;
+
+ FREE( ras->buff );
+ FREE( ras->gray_lines );
+
+ return FT_Err_Ok;
+ }
+
+
+ LOCAL_FUNC
+ FT_Error TTRaster_Init( PEngine_Instance engine )
+ {
+ FT_Error error;
+
+ Int i, l, j, c;
+
+ TRaster_Instance* ras;
+
+
+#ifdef FT_CONFIG_OPTION_STATIC_RASTER
+ ras = engine->raster_component = &cur_ras;
+#else
+ if ( ALLOC( engine->raster_component, sizeof ( TRaster_Instance ) ) )
+ return error;
+
+ ras = (TRaster_Instance*)engine->raster_component;
+#endif
+
+ if ( ALLOC( ras->buff, RASTER_RENDER_POOL ) ||
+ ALLOC( ras->gray_lines, RASTER_GRAY_LINES ) )
+ return error;
+
+ ras->sizeBuff = ras->buff + ( RASTER_RENDER_POOL/sizeof(long) );
+ ras->gray_width = RASTER_GRAY_LINES/2;
+
+ /* Initialization of Count_Table */
+
+ for ( i = 0; i < 256; i++ )
+ {
+ l = 0;
+ j = i;
+
+ for ( c = 0; c < 4; c++ )
+ {
+ l <<= 4;
+
+ if ( j & 0x80 ) l++;
+ if ( j & 0x40 ) l++;
+
+ j = ( j << 2 ) & 0xFF;
+ }
+
+ ras->count_table[i] = l;
+ }
+
+ ras->dropOutControl = 2;
+ ras->error = Raster_Err_None;
+
+ return FT_Err_Ok;
+ }
+
+
+/* END */