shithub: freetype+ttf2subf

ref: 5413644b5e296b705377eebdf3cda12f95718912
dir: /src/base/ftraster.c/

View raw version
/*******************************************************************
 *
 *  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 <freetype/ftraster.h>
#include <freetype/internal/ftcalc.h>      /* for FT_MulDiv only */

  /*************************************************************************/
  /*                                                                       */
  /* A simple technical note on how the raster works:                      */
  /*                                                                       */
  /*   Converting an outline into a bitmap is achieved in several steps    */
  /*   which are:                                                          */
  /*                                                                       */
  /*   1 - Decomposing the outline into successive `profiles'.  Each       */
  /*       profile is simply an array of scanline intersections on a given */
  /*       dimension.  A profile's main attributes are                     */
  /*                                                                       */
  /*       o its scanline position boundaries, i.e. `Ymin' and `Ymax'.     */
  /*                                                                       */
  /*       o an array of intersection coordinates for each scanline        */
  /*         between `Ymin' and `Ymax'.                                    */
  /*                                                                       */
  /*       o a direction, indicating wether is was built going `up' or     */
  /*         `down', as this is very important for filling rules.          */
  /*                                                                       */
  /*   2 - Sweeping the target map's scanlines in order to compute segment */
  /*       `spans' which are then filled.  Additionaly, this pass performs */
  /*       drop-out control.                                               */
  /*                                                                       */
  /*   The outline data is parsed during step 1 only.  The profiles are    */
  /*   built from the bottom of the render pool, used as a stack.  The     */
  /*   following graphics shows the profile list under construction:       */
  /*                                                                       */
  /*     ____________________________________________________________ _ _  */
  /*    |         |                   |         |                 |        */
  /*    | profile | coordinates for   | profile | coordinates for |-->     */
  /*    |    1    |  profile 1        |    2    |  profile 2      |-->     */
  /*    |_________|___________________|_________|_________________|__ _ _  */
  /*                                                                       */
  /*    ^                                                         ^        */
  /*    |                                                         |        */
  /*    start of render pool                                   top         */
  /*                                                                       */
  /*   The top of the profile stack is kept in the `top' variable.         */
  /*                                                                       */
  /*   As you can see, a profile record is pushed on top of the render     */
  /*   pool, which is then followed by its coordinates/intersections.  If  */
  /*   a change of direction is detected in the outline, a new profile is  */
  /*   generated until the end of the outline.                             */
  /*                                                                       */
  /*   Note that when all profiles have been generated, the function       */
  /*   Finalize_Profile_Table() is used to record, for each profile, its   */
  /*   bottom-most scanline as well as the scanline above its upmost       */
  /*   boundary.  These positions are called `y-turns' because they (sort  */
  /*   of) correspond to local extrema.  They are stored in a sorted list  */
  /*   built from the top of the render pool as a downwards stack:         */
  /*                                                                       */
  /*      _ _ _______________________________________                      */
  /*                            |                    |                     */
  /*                         <--| sorted list of     |                     */
  /*                         <--|  extrema scanlines |                     */
  /*      _ _ __________________|____________________|                     */
  /*                                                                       */
  /*                            ^                    ^                     */
  /*                            |                    |                     */
  /*                       maxBuff             sizeBuff = end of pool      */
  /*                                                                       */
  /*   This list is later used during the sweep phase in order to          */
  /*   optimize performance (see technical note on the sweep below).       */
  /*                                                                       */
  /*   Of course, the raster detects whether the two stacks collide and    */
  /*   handles the situation propertly.                                    */
  /*                                                                       */
  /*************************************************************************/

  /****************************************************************/
  /****************************************************************/
  /**                                                            **/
  /**  CONFIGURATION MACROS                                      **/
  /**                                                            **/
  /****************************************************************/
  /****************************************************************/

/* define DEBUG_RASTER if you want to compile a debugging version */
#define xxxDEBUG_RASTER

/* The default render pool size in bytes */
#define  RASTER_RENDER_POOL   8192

/* undefine FT_RASTER_OPTION_ANTI_ALIASING if you do not want to support */
/* 5-levels anti-aliasing                                                */
#ifdef FT_CONFIG_OPTION_5_GRAY_LEVELS
#define FT_RASTER_OPTION_ANTI_ALIASING
#endif

/* The size of the two-lines intermediate bitmap used */
/* for anti-aliasing, in bytes..                      */
#define  RASTER_GRAY_LINES    2048


  /****************************************************************/
  /****************************************************************/
  /**                                                            **/
  /**  OTHER MACROS (do not change)                              **/
  /**                                                            **/
  /****************************************************************/
  /****************************************************************/

/* required by the tracing mode */
#undef  FT_COMPONENT
#define FT_COMPONENT      trace_raster

#include <freetype/internal/ftdebug.h>

#define Raster_Err_None               0
#define Raster_Err_Not_Ini           -1
#define Raster_Err_Overflow          -2
#define Raster_Err_Neg_Height        -3
#define Raster_Err_Invalid           -4
#define Raster_Err_Gray_Unsupported  -5
#define Raster_Err_Unsupported       -6

/* FMulDiv means "Fast MulDiv", it is used 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 FT_MulDiv() function    */
/* defined in "ftcalc.h"                                                  */
/*                                                                        */
#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

#ifndef SUCCESS
#define SUCCESS 0
#endif

#ifndef FAILURE
#define FAILURE 1
#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   = 1,
    Flow_Down = -1

  } 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   */
    PLong       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))



#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;

    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 */
    FT_Outline outline;

    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;

    Byte      dropOutControl;       /* current drop_out control method */

    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         */

    void*     memory;

#ifdef FT_RASTER_OPTION_ANTI_ALIASING

    Byte      grays[5];         /* Palette of gray levels used for render */

    Byte      gray_lines[RASTER_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'              */

#endif /* FT_RASTER_ANTI_ALIASING */

#if 0
    PByte     flags;                /* current flags table    */
    PUShort   outs;                 /* current outlines table */
    FT_Vector*  coords;

    UShort    nPoints;              /* number of points in current glyph   */
    Short     nContours;            /* number of contours in current glyph */
#endif


  };


#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 Int  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 = Flow_Up;
      FT_TRACE7(( "New ascending profile = %lx\n", (long)ras.cProfile ));
      break;

    case Descending:
      ras.cProfile->flow = 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 )
  {
    PLong     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 Flow_Down:
          bottom     = p->start - p->height+1;
          top        = p->start;
          p->start   = bottom;
          p->offset += p->height-1;
          break;

        case 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:    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;

    PLong  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;

    PLong 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  cx,
                                  Long  cy,
                                  Long  x,
                                  Long  y )
  {
    Long     y1, y2, y3, x3, ymin, ymax;
    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_VARS state_bez ) )
            goto Fail;
        }

        /* now call the appropriate routine */
        if ( state_bez == Ascending )
        {
          if ( Bezier_Up( RAS_VARS  2, Split_Conic, ras.minY, ras.maxY ) )
            goto Fail;
        }
        else
          if ( Bezier_Down( RAS_VARS  2, Split_Conic, ras.minY, ras.maxY ) )
            goto Fail;
      }

    } while ( ras.arc >= ras.arcs );

    ras.lastX = x3;
    ras.lastY = y3;

    return SUCCESS;
  Fail:
    return FAILURE;
  }

/****************************************************************************/
/*                                                                          */
/* Function:    Cubic_To                                                    */
/*                                                                          */
/* Description: Injects a new cubic arc and adjusts the profile list.       */
/*                                                                          */

  static Bool  Cubic_To( RAS_ARGS Long  cx1,
                                  Long  cy1,
                                  Long  cx2,
                                  Long  cy2,
                                  Long  x,
                                  Long  y )
  {
    Long     y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
    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_VARS state_bez ) )
            goto Fail;
        }

        /* compute intersections */
        if ( state_bez == Ascending )
        {
          if ( Bezier_Up ( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
            goto Fail;
        }
        else
          if ( Bezier_Down ( RAS_VARS 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,
                                         int     flipped )
  {
    FT_Vector  v_last;
    FT_Vector  v_control;
    FT_Vector  v_start;

    FT_Vector* points;
    FT_Vector* point;
    FT_Vector* limit;
    char*      tags;

    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--;
    }

    ras.lastX = v_start.x;
    ras.lastY = v_start.y;

    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.outline.n_contours; i++ )
    {
      ras.state    = Unknown;
      ras.gProfile = NULL;

      if ( Decompose_Curve( RAS_VARS  start, ras.outline.contours[i], flipped ) )
        return FAILURE;

      start = ras.outline.contours[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     = &current->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     = &current->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     = &current->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 )
  {
    Long  pitch = ras.target.pitch;

    UNUSED(max);

    ras.traceIncr = (Short)- pitch;
    ras.traceOfs  = - *min * pitch;
    if (pitch > 0)
      ras.traceOfs += (ras.target.rows-1)*pitch;

    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;
    Byte   f1, f2;
    Byte*  target;

    UNUSED(y);
    UNUSED(left);
    UNUSED(right);

    /* 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 =  ((unsigned char)0xFF >> (e1 & 7));
      f2 = ~((unsigned char)0x7F >> (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;
      c2 -= c1;

      if ( c2 > 0 )
      {
        target[0] |= f1;

        /* memset() is slower than the following code on many platforms. */
        /* This is due to the fact that, in the vast majority of cases,  */
        /* the span length in bytes is relatively small.                 */
        c2--;
        while ( c2 > 0 )
        {
          *(++target) = 0xFF;
          c2--;
        }
        target[1] |= f2;
      }
      else
        *target |= ( f1 & 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 */
    UNUSED(raster);
    UNUSED(min);
    UNUSED(max);
  }


  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;

    UNUSED(left);
    UNUSED(right);

    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 )
        {
          PByte  p;

          p = bits - e1*ras.target.pitch;
          if (ras.target.pitch > 0)
            p += (ras.target.rows-1)*ras.target.pitch;

          p[0] |= f1;
        }
      }
    }
  }


  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));

          bits -= e1*ras.target.pitch;
          if (ras.target.pitch > 0)
            bits += (ras.target.rows-1)*ras.target.pitch;

          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 )
    {
      bits -= e1*ras.target.pitch;
      if (ras.target.pitch > 0)
        bits += (ras.target.rows-1)*ras.target.pitch;

      bits[0] |= f1;
    }
  }


  static void Horizontal_Sweep_Step( RAS_ARG )
  {
    /* Nothing, really */
    UNUSED(raster);
  }


#ifdef FT_RASTER_OPTION_ANTI_ALIASING

/***********************************************************************/
/*                                                                     */
/*  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 )
  {
    Long  pitch, byte_len;

    *min = *min & -2;
    *max = ( *max + 3 ) & -2;

    ras.traceOfs  = 0;
    pitch         = ras.target.pitch;
    byte_len      = -pitch;
    ras.traceIncr = (Short)byte_len;
    ras.traceG    = (*min/2)*byte_len;
    if (pitch > 0)
    {
      ras.traceG += (ras.target.rows-1)*pitch;
      byte_len    = -byte_len;
    }

    ras.gray_min_x =  (Short)byte_len;
    ras.gray_max_x = -(Short)byte_len;
  }


  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 )
      {
        Long   last_pixel = ras.target.width-1;
        Int    last_cell  = last_pixel >> 2;
        Int    last_bit   = last_pixel & 3;
        Bool   over       = 0;

        if (ras.gray_max_x >= last_cell && last_bit != 3)
        {
          ras.gray_max_x = last_cell-1;
          over = 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 >> 12) & 0x000F];
            pix[1] = grays[(c2 >> 8 ) & 0x000F];
            pix[2] = grays[(c2 >> 4 ) & 0x000F];
            pix[3] = grays[ c2        & 0x000F];

            *bit  = 0;
            *bit2 = 0;
          }

          bit ++;
          bit2++;
          pix += 4;
          c1  --;
        }

        if (over)
        {
          c2 = count[*bit] + count[*bit2];
          if (c2)
          {
            switch (last_bit)
            {
              case 2:  pix[2] = grays[(c2 >> 4 ) & 0x000F];
              case 1:  pix[1] = grays[(c2 >> 8 ) & 0x000F];
              default: pix[0] = grays[(c2 >> 12) & 0x000F];
            }

            *bit  = 0;
            *bit2 = 0;
          }
        }
      }

      ras.traceOfs = 0;
      ras.traceG  += ras.traceIncr;

      ras.gray_min_x =  32000;
      ras.gray_max_x = -32000;
    }
  }


  static void  Horizontal_Gray_Sweep_Span( RAS_ARGS Short       y,
                                                    FT_F26Dot6  x1,
                                                    FT_F26Dot6  x2,
                                                    PProfile    left,
                                                    PProfile    right )
  {
    /* nothing, really */
    UNUSED(raster);
    UNUSED(y);
    UNUSED(x1);
    UNUSED(x2);
    UNUSED(left);
    UNUSED(right);
  }

  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 )
      {
        pixel = ras.gTarget - e1*ras.target.pitch + y/2;
        if (ras.target.pitch > 0)
          pixel += (ras.target.rows-1)*ras.target.pitch;

        if (pixel[0] == ras.grays[0])
          pixel[0] = color;
      }
    }
  }

#endif /* FT_RASTER_OPTION_ANTI_ALIASING */


/********************************************************************/
/*                                                                  */
/*  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 = (Short)P->start;
      top    = (Short)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 Flow_Up:    InsNew( &draw_left,  P ); break;
            case 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_ARG )
  {
    FT_Error  error;

    Set_High_Precision( RAS_VARS ras.outline.flags & ft_outline_high_precision );
    ras.scale_shift    = ras.precision_shift;
    ras.dropOutControl = 2;
    ras.second_pass    = !(ras.outline.flags & ft_outline_single_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.buffer;

    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;
  }


/****************************************************************************/
/*                                                                          */
/* 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.      */
/*                                                                          */
/****************************************************************************/

#ifdef FT_RASTER_OPTION_ANTI_ALIASING
  LOCAL_FUNC
  FT_Error  Render_Gray_Glyph( RAS_ARG )
  {
    Long      pixel_width;
    FT_Error  error;

    Set_High_Precision( RAS_VARS ras.outline.flags & ft_outline_high_precision );
    ras.scale_shift    = ras.precision_shift+1;
    ras.dropOutControl = 2;
    ras.second_pass    = !(ras.outline.flags & ft_outline_single_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;
    pixel_width = 2*((ras.target.width + 3) >> 2);

    if ( ras.bWidth > pixel_width )
      ras.bWidth = pixel_width;

    ras.bWidth  = ras.bWidth * 8;
    ras.bTarget = (Byte*)ras.gray_lines;
    ras.gTarget = (Byte*)ras.target.buffer;

    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;
  }
#else
  LOCAL_FUNC
  FT_Error  Render_Gray_Glyph( RAS_ARG )
  {
    UNUSED_RASTER
    return Raster_Err_Unsupported;
  }
#endif


  static  void  ft_black_init( TRaster_Instance*  raster )
  {
    FT_UInt  n;
    FT_ULong c;

    /* setup count table */
    for ( n = 0; n < 256; n++ )
    {
      c = (n & 0x55) + ((n & 0xAA) >> 1);

      c = ((c << 6) & 0x3000) |
          ((c << 4) & 0x0300) |
          ((c << 2) & 0x0030) |
                (c  & 0x0003);

      raster->count_table[n] = c;
    }

    /* set default 5-levels gray palette */
    for ( n = 0; n < 5; n++ )
      raster->grays[n] = (n*255/4);

    raster->gray_width = RASTER_GRAY_LINES/2;
  }

  /**** RASTER OBJECT CREATION : in standalone mode, we simply use *****/
  /****                          a static object ..                *****/
#ifdef _STANDALONE_

  static
  int  ft_black_new( void*  memory, FT_Raster *araster )
  {
     static FT_RasterRec_  the_raster;
     *araster = &the_raster;
     memset( &the_raster, sizeof(the_raster), 0 );
     ft_black_init( &the_raster );
     return 0;
  }

  static
  void  ft_black_done( FT_Raster  raster )
  {
    /* nothing */
    raster->init = 0;
  }

#else

#include <freetype/internal/ftobjs.h>

  static
  int  ft_black_new( FT_Memory  memory, TRaster_Instance*  *araster )
  {
    FT_Error           error;
    TRaster_Instance*  raster;

    *araster = 0;
    if ( !ALLOC( raster, sizeof(*raster) ))
    {
      raster->memory = memory;
      ft_black_init( raster );

      *araster = raster;
    }

    return error;
  }

  static
  void ft_black_done( TRaster_Instance*  raster )
  {
    FT_Memory  memory = (FT_Memory)raster->memory;
    FREE( raster );
  }

#endif


  static void ft_black_reset( TRaster_Instance* raster,
                              const char*       pool_base,
                              long              pool_size )
  {
    if ( raster && pool_base && pool_size >= 4096 )
    {
      /* save the pool */
      raster->buff     = (PLong)pool_base;
      raster->sizeBuff = raster->buff + pool_size / sizeof (Long);
    }
  }

  static void ft_black_set_mode( TRaster_Instance* raster,
                                 unsigned long     mode,
                                 const char*       palette )
  {
    if (mode==FT_MAKE_TAG('p','a','l','5'))
    {
      /* set 5-levels gray palette */
      raster->grays[0] = palette[0];
      raster->grays[1] = palette[1];
      raster->grays[2] = palette[2];
      raster->grays[3] = palette[3];
      raster->grays[4] = palette[4];
    }
  }


  static
  int  ft_black_render( TRaster_Instance*  raster,
                        FT_Raster_Params*  params )
  {
    FT_Outline*  outline    = (FT_Outline*)params->source;
    FT_Bitmap*   target_map = params->target;

    if ( !raster || !raster->buff || !raster->sizeBuff )
      return Raster_Err_Not_Ini;

    if ( !outline || !outline->contours || !outline->points )
      return Raster_Err_Invalid;

    /* return immediately if the outline is empty */
    if ( outline->n_points == 0 || outline->n_contours <= 0 )
      return Raster_Err_None;

    if ( outline->n_points != outline->contours[outline->n_contours - 1] + 1 )
      return Raster_Err_Invalid;

    if ( !target_map || !target_map->buffer )
      return Raster_Err_Invalid;

    /* this version of the raster does not support direct rendering, sorry */
    if ( params->flags & ft_raster_flag_direct )
      return Raster_Err_Unsupported;

    ras.outline  = *outline;
    ras.target   = *target_map;

    return ( params->flags & ft_raster_flag_aa
           ? Render_Gray_Glyph( raster )
           : Render_Glyph( raster ) );
  }


  FT_Raster_Funcs      ft_default_raster =
  {
    ft_glyph_format_outline,
    (FT_Raster_New_Func)       ft_black_new,
    (FT_Raster_Reset_Func)     ft_black_reset,
    (FT_Raster_Set_Mode_Func)  ft_black_set_mode,
    (FT_Raster_Render_Func)    ft_black_render,
    (FT_Raster_Done_Func)      ft_black_done
  };


/* END */