shithub: freetype+ttf2subf

ref: 08c3875589d6cc419be17050d39c8e3641d64373
dir: /demos/src/ftrast2.c/

View raw version
/*******************************************************************
 *
 *  ftraster.c                                                  2.0
 *
 *  The FreeType glyph rasterizer (body).
 *
 *  Copyright 1996-1998 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.
 *
 *  The "raster" component implements FreeType's scan-line converter,
 *  the one used to generate bitmaps and pixmaps from vectorial outlines
 *  descriptions.
 *
 *  It has been rewritten entirely for FreeType 2.0, in order to become
 *  completely independent of the rest of the library. It should now be
 *  possible to include it more easily in all kinds of libraries and
 *  applications, which do not necessarily need the font engines and
 *  API.
 *
 *  This version features :
 *
 *   - support for third-order bezier arcs
 *
 *   - improved performance of the 5-levels anti-aliasing algorithm
 *
 *   - 17-levels anti-aliasing for smoother curves, though the
 *     difference isn't always noticeable, depending on your palette
 *
 *   - an API to decompose a raster outline into a path (i.e. into
 *     a series of segments and arcs).
 *
 ******************************************************************/

#include "ftraster.h"
#include <freetype.h>  /* for FT_Outline_Decompose */

#ifndef EXPORT_FUNC
#define EXPORT_FUNC  /* nothing */
#endif


#ifndef _xxFREETYPE_

/**************************************************************************/
/*                                                                        */
/* The following defines are used when the raster is compiled as a        */
/* stand-alone object. Each of them is commented, and you're free to      */
/* toggle them to suit your needs..                                       */
/*                                                                        */

/**************************************************************************/
/*                                                                        */
/* FT_RASTER_OPTION_ANTI_ALIAS                                            */
/*                                                                        */
/*   Define this configuration macro if you want to support anti-aliasing */
/*                                                                        */
#define FT_RASTER_OPTION_ANTI_ALIAS

/**************************************************************************/
/*                                                                        */
/* FT_RASTER_OPTION_CONIC_BEZIERS                                         */
/*                                                                        */
/*   Define this configuration macro if your source outlines contain      */
/*   second-order Bezier arcs. Typically, these are TrueType outlines..   */
/*                                                                        */
#define FT_RASTER_CONIC_BEZIERS

/**************************************************************************/
/*                                                                        */
/* FT_RASTER_OPTION_CUBIC_BEZIERS                                         */
/*                                                                        */
/*   Define this configuration macro if your source outlines contain      */
/*   third-order Bezier arcs. Typically, these are Type1 outlines..       */
/*                                                                        */
#define FT_RASTER_CUBIC_BEZIERS

/**************************************************************************/
/*                                                                        */
/* FT_RASTER_ANTI_ALIAS_5                                                 */
/*                                                                        */
/*   Define this configuration macro if you want to enable the 5-grays    */
/*   anti-aliasing mode.. Ignored if FT_RASTER_OPTION_ANTI_ALIAS isn't    */
/*   defined..                                                            */
/*                                                                        */
#define FT_RASTER_ANTI_ALIAS_5

/**************************************************************************/
/*                                                                        */
/* FT_RASTER_ANTI_ALIAS_17                                                */
/*                                                                        */
/*   Define this configuration macro if you want to enable the 17-grays   */
/*   anti-aliasing mode.. Ignored if FT_RASTER_OPTION_ANTI_ALIAS isn't    */
/*   defined..                                                            */
/*                                                                        */
#define FT_RASTER_ANTI_ALIAS_17

/**************************************************************************/
/*                                                                        */
/* FT_RASTER_LITTLE_ENDIAN                                                */
/* FT_RASTER_BIG_ENDIAN                                                   */
/*                                                                        */
/*   The default anti-alias routines are processor-independent, but slow. */
/*   Define one of these macros to suit your own system, and enjoy        */
/*   greatly improved rendering speed                                     */
/*                                                                        */

/* #define FT_RASTER_LITTLE_ENDIAN */
/* #define FT_RASTER_BIG_ENDIAN    */

#else  /* _FREETYPE_ */

/**************************************************************************/
/*                                                                        */
/* The following defines are used when the raster is compiled within      */
/* the FreeType base layer. Don't change these unless you really know     */
/* what you're doing..                                                    */
/*                                                                        */

#ifdef FT_CONFIG_OPTION_ANTI_ALIAS
#define FT_RASTER_OPTION_ANTI_ALIAS
#endif

#define FT_RASTER_CONIC_BEZIERS
#define FT_RASTER_CUBIC_BEZIERS

#define FT_RASTER_ANTI_ALIAS_5
#undef  FT_RASTER_ANTI_ALIAS_17

#ifdef FT_CONFIG_OPTION_LITTLE_ENDIAN
#define FT_RASTER_LITTLE_ENDIAN
#endif

#ifdef FT_CONFIG_OPTION_BIG_ENDIAN
#define FT_RASTER_BIG_ENDIAN
#endif

#endif /* _FREETYPE_ */


/* FT_RASTER_ANY_ENDIAN indicates that no endianess was defined */
/* through one of the configuration macros                      */
/*                                                              */
#if !defined(FT_RASTER_LITTLE_ENDIAN) && !defined(FT_RASTER_BIG_ENDIAN)
#define FT_RASTER_ANY_ENDIAN
#endif


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


#undef  FAILURE
#define FAILURE  TRUE

#undef  SUCCESS
#define SUCCESS  FALSE


/* Please don't touch the following macros. Their importance is historical */
/* to FreeType, but they have some nice effects, like getting rid of all   */
/* '->' symbols when accessing the raster object.. (replacing them with    */
/* a simple '.' )                                                          */

/* used in function signatures to define the _first_ argument */
#define  RAS_ARGS  FT_Raster  raster,
#define  RAS_ARG   FT_Raster  raster

/* used to call a function within this component, first parameter */
#define  RAS_VARS  raster,
#define  RAS_VAR   raster

/* used to access the current raster object, with a '.' instead of a '->' */
#define  ras       (*raster)

#define  UNUSED_RASTER  (void)raster;

/* For anti-aliasing modes, we use a 2 or 4 lines intermediate bitmap which */
/* is filtered repeatedly to render each pixmap row. The following macro    */
/* defines this buffer's size in bytes (which is part of raster objects)    */
#define ANTI_ALIAS_BUFFER_SIZE   2048


/* Error codes returned by the scan-line converter/raster */

#define ErrRaster_Ok                      0
#define ErrRaster_Uninitialised_Object    1
#define ErrRaster_Overflow                2
#define ErrRaster_Negative_Height         3
#define ErrRaster_Invalid_Outline         4
#define ErrRaster_Invalid_Map             5
#define ErrRaster_AntiAlias_Unsupported   6
#define ErrRaster_Invalid_Pool            7
#define ErrRaster_Unimplemented           8
#define ErrRaster_Bad_Palette_Count       9


#define SET_High_Precision(p)  Set_High_Precision( RAS_VARS p )

/* Fast MulDiv, as 'b' is always < 64, don't use intermediate precision */
#define FMulDiv( a, b, c )  ( (a) * (b) / (c) )


/* Define DEBUG_RASTER if you want to generate a debug version of the  */
/* rasterizer.  This will progressively draw the glyphs while all the  */
/* computation are done directly on the graphics screen (the glyphs    */
/* will be inverted).                                                  */

/* Note that DEBUG_RASTER should only be used for debugging with b/w   */
/* rendering, not with gray levels.                                    */

/* The definition of DEBUG_RASTER should appear in the file            */
/* "ftconfig.h".                                                       */

#ifdef DEBUG_RASTER
  extern char*  vio;  /* A pointer to VRAM or display buffer */
#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   */
                        /* We always use 26.6, but hackers are free */
                        /* to experiment with different values      */


  /* The type of the pixel coordinates used within the render pool during   */
  /* scan-line conversion. We use longs to store either 26.6 or 22.10 fixed */
  /* float values, depending on the "precision" we want to use (resp. low   */
  /* or high). These are ideals in order to subdivise bezier arcs in halves */
  /* though simple additions and shifts.                                    */

  typedef  long   TPos, *PPos;


  /* The type of a scanline position/coordinate within a map */
  typedef  int    TScan, *PScan;




  /* boolean type */
  typedef  char   TBool;
  
  /* unsigned char type and array */
  typedef  unsigned char   TByte, *PByte;

  /* unsigned short type and array */
  typedef  unsigned short  UShort, *PUShort;

  /* flow */
  enum _TFlow
  {
    FT_Flow_Error = 0,
    FT_Flow_Down  = -1,
    FT_Flow_Up    = 1
  };


  /* states/directions of each line, arc and profile */
  enum  _TDirection
  {
    Unknown,
    Ascending,
    Descending,
    Flat
  };
  typedef enum _TDirection  TDirection;


  struct  _TProfile;
  typedef struct _TProfile  TProfile;
  typedef TProfile*         PProfile;

  struct  _TProfile
  {                                                                     
    TPos      X;           /* current coordinate during sweep          */
    PProfile  link;        /* link to next profile - various purpose   */
    PPos      offset;      /* start of profile's data in render pool   */
    int       flow;        /* Profile orientation: Asc/Descending      */
    TScan     height;      /* profile's height in scanlines            */
    TScan     start;       /* profile's starting scanline              */

    TScan     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                               */
  /*                                                            */
  struct  _TBand
  {
    TScan  y_min;   /* band's minimum */
    TScan  y_max;   /* band's maximum */
  };

  typedef struct _TBand  TBand;


/* The size in _TPos_ of a profile record in the render pool */
#define AlignProfileSize  ((sizeof(TProfile)+sizeof(TPos)-1) / sizeof(TPos))


  /* prototypes used for sweep function dispatch */
  typedef void  Function_Sweep_Init( RAS_ARGS int*  min, int*  max );

  typedef void  Function_Sweep_Span( RAS_ARGS  TScan  y,
                                               TPos   x1,
                                               TPos   x2 );

  typedef int   Function_Test_Pixel( RAS_ARGS TScan  y,
                                              int    x );
                                              
  typedef void  Function_Set_Pixel( RAS_ARGS  TScan  y,
                                              int    x,
                                              int    color );

  typedef void  Function_Sweep_Step( RAS_ARG );


/* compute lowest integer coordinate below a given x */
#define FLOOR( x )    ( (x) & ras.precision_mask )

/* compute highest integer coordinate above a given x */
#define CEILING( x )  ( ((x) + ras.precision - 1) & ras.precision_mask )

/* get integer coordinate of a given 26.6 or 22.10 'x' coordinate - no round */
#define TRUNC( x )    ( (signed long)(x) >> ras.precision_bits )

/* get the fractional part of a given coordinate */
#define FRAC( x )     ( (x) & (ras.precision - 1) )

/* scale an 'input coordinate' (as found in FT_Outline structures) into */
/* a 'work coordinate', which depends on current resolution and render  */
/* mode..                                                               */
#define SCALED( x )   ( ((x) << ras.scale_shift) - ras.precision_half )


/* DEBUG_PSET is used to plot a single pixel in VRam during debug mode */
#ifdef DEBUG_RASTER
#define DEBUG_PSET  Pset()
#else
#define DEBUG_PSET  
#endif

  struct  _TPoint
  {
    TPos  x, y;
  };
  typedef struct _TPoint  TPoint;


  /* 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  FT_RasterRec_
  {
    PPos      cursor;              /* Current cursor in render pool  */

    PPos      pool;                /* The render pool base address   */
    PPos      pool_size;           /* The render pool's size         */
    PPos      pool_limit;          /* Limit of profiles zone in pool */

    int       bit_width;            /* target bitmap width  */
    PByte     bit_buffer;           /* target bitmap buffer */
    PByte     pix_buffer;           /* target pixmap buffer */

    TPoint    last;
    long      minY, maxY;

    int       error;

    int       precision_bits;       /* precision related variables */
    int       precision;
    int       precision_half;
    long      precision_mask;
    int       precision_shift;
    int       precision_step;
    int       precision_jitter;

    FT_Outline*  outline;
    
    int       n_points;             /* number of points in current glyph   */
    int       n_contours;           /* number of contours in current glyph */
    int       n_turns;              /* number of Y-turns in outline        */

    TPoint*   arc;                  /* current Bezier arc pointer */

    int       num_profs;            /* current number of profiles */

    TBool      fresh;                /* signals a fresh new profile which */ 
                                    /* 'start' field must be completed   */
    TBool      joint;                /* signals that the last arc ended   */
                                    /* exactly on a scanline.  Allows    */
                                    /* removal of doublets               */
    PProfile  cur_prof;             /* current profile                   */
    PProfile  start_prof;           /* head of linked list of profiles   */
    PProfile  first_prof;           /* contour's first profile in case   */
                                    /* of impact                         */
    TDirection state;               /* rendering state */
   
    FT_Bitmap  target;              /* description of target bit/pixmap */

    int       trace_bit;            /* current offset in target bitmap */
    int       trace_pix;            /* current offset in target pixmap */

    int       trace_incr;           /* sweep's increment in target bitmap */

    int       gray_min_x;           /* current min x during gray rendering */
    int       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_Step*  Proc_Sweep_Step;
    Function_Test_Pixel*  Proc_Test_Pixel;
    Function_Set_Pixel*   Proc_Set_Pixel;

    int       scale_shift;      /* == 0  for bitmaps           */
                                /* == 1  for 5-levels pixmaps  */
                                /* == 2  for 17-levels pixmaps */

    TByte      dropout_mode;     /* current drop_out control method */

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

    TBool      flipped;          /* this flag is set during the rendering to */
                                /* indicate the second pass..               */
                                
    TBand     band_stack[16];       /* band stack used for sub-banding */
    int       band_top;             /* band stack top                  */

    TPoint    arcs[ 2*MaxBezier+1 ];      /* The Bezier stack */

    void*     memory;
   
#if defined(FT_RASTER_OPTION_ANTI_ALIAS)

    long      grays[20];        /* Palette of gray levels used for render */

    int       gray_width;       /* length in bytes of the onochrome        */
                                /* intermediate scanline of gray_lines.    */
                                /* Each gray pixel takes 2 or 4 bits long  */

                        /* The gray_lines must hold 2 lines, thus with size */
                        /* in bytes of at least 'gray_width*2'              */

    int       grays_count;      /* number of entries in the palette */

    char      gray_lines[ANTI_ALIAS_BUFFER_SIZE];       
                                /* Intermediate table used to render the   */
                                /* graylevels pixmaps.                     */
                                /* gray_lines is a buffer holding 2 or 4   */
                                /* monochrome scanlines                    */

    int       count_table[256];     /* Look-up table used to quickly count */
                                    /* set bits in a gray 2x2 cell         */
#endif
  };



#ifdef DEBUG_RASTER

  /************************************************/
  /*                                              */
  /* Pset:                                        */
  /*                                              */
  /*  Used for debugging only.  Plots a point     */
  /*  in VRAM during rendering (not afterwards).  */
  /*                                              */
  /* NOTE:  This procedure relies on the value    */
  /*        of cProfile->start, which may not     */
  /*        be set when Pset is called sometimes. */
  /*        This will usually result in a dot     */
  /*        plotted on the first screen scanline  */
  /*        (far away its original position).     */
  /*                                              */
  /*        This "bug" reflects nothing wrong     */
  /*        in the current implementation, and    */
  /*        the bitmap is rendered correctly,     */
  /*        so don't panic if you see 'flying'    */
  /*        dots in debugging mode.               */
  /*                                              */
  /*  - David                                     */
  /*                                              */
  /************************************************/

  static void  Pset( RAS_ARG )
  {
    long  o;
    long  x;

    x = ras.cursor[-1];

    switch ( ras.cur_prof->flow )
    {
    case FT_Flow_Up:
      o = Vio_ScanLineWidth *
         ( ras.cursor - ras.cur_prof->offset + ras.cur_prof->start ) +
         ( x / (ras.precision*8) );
      break;

    case FT_Flow_Down:
      o = Vio_ScanLineWidth *
         ( ras.cur_prof->start - ras.cursor + ras.cur_prof->offset ) +
         ( x / (ras.precision*8) );
      break;
    }

    if ( o > 0 )
      Vio[o] |= (unsigned)0x80 >> ( (x/ras.precision) & 7 );
  }


  static void  Clear_Band( RAS_ARGS Int  y1, Int  y2 )
  {
    MEM_Set( Vio + y1*Vio_ScanLineWidth, (y2-y1+1)*Vio_ScanLineWidth, 0 );
  }

#endif /* DEBUG_RASTER */


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

    ras.precision       = 1 << 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                    */
/*                                                                        */
/* <Return>                                                               */
/*    SUCCESS or FAILURE                                                  */
/*                                                                        */
/**************************************************************************/

  static 
  TBool  New_Profile( RAS_ARGS TDirection  direction )
  {
    if ( ras.start_prof == NULL )
    {
      ras.cur_prof   = (PProfile)ras.cursor;
      ras.start_prof = ras.cur_prof;
      ras.cursor    += AlignProfileSize;
    }

    if ( ras.cursor >= ras.pool_limit )
    {
      ras.error = ErrRaster_Overflow;
      return FAILURE;
    }

    switch ( direction )
    {
    case Ascending:
      ras.cur_prof->flow = FT_Flow_Up;
      break;

    case Descending:
      ras.cur_prof->flow = FT_Flow_Down;
      break;

    default:
      ras.error = ErrRaster_Invalid_Map;
      return FAILURE;
    }

    {
      PProfile  cur = ras.cur_prof;
      
      cur->start  = 0;
      cur->height = 0;
      cur->offset = ras.cursor;
      cur->link   = (PProfile)0;
      cur->next   = (PProfile)0;
    }

    if ( ras.first_prof == NULL )
      ras.first_prof = ras.cur_prof;

    ras.state  = direction;
    ras.fresh  = TRUE;
    ras.joint  = FALSE;

    return SUCCESS;
  }


/****************************************************************************/
/*                                                                          */
/* <Function> End_Profile                                                   */
/*                                                                          */
/* <Description> Finalizes the current Profile.                             */
/*                                                                          */
/* <Return>                                                                 */
/*    SUCCESS or FAILURE                                                    */
/*                                                                          */
/****************************************************************************/

  static 
  TBool  End_Profile( RAS_ARG )
  {
    int  h;

    h = ras.cursor - ras.cur_prof->offset;

    if ( h < 0 )
    {
      ras.error = ErrRaster_Negative_Height;
      return FAILURE;
    }

    if ( h > 0 )
    {
      PProfile  old, new;
      
      old          = ras.cur_prof;
      old->height  = h;
      ras.cur_prof = new = (PProfile)ras.cursor;

      ras.cursor  += AlignProfileSize;

      new->height  = 0;
      new->offset  = ras.cursor;
      old->next    = new;

      ras.num_profs++;
    }

    if ( ras.cursor >= ras.pool_limit )
    {
      ras.error = ErrRaster_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>                                                                  */
/*     y   ::  new scanline position                                        */
/*                                                                          */
/****************************************************************************/

  static  
  TBool  Insert_Y_Turn( RAS_ARGS  TScan  y )
  {
    PPos   y_turns;
    TScan  y2;
    int    n;

    n       = ras.n_turns-1;
    y_turns = ras.pool_size - ras.n_turns;

    /* 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)
    {
      ras.pool_limit--;
      ras.n_turns++;
      ras.pool_size[-ras.n_turns ] = y;

      if ( ras.pool_limit <= ras.cursor )
      {
        ras.error = ErrRaster_Overflow;
        return FAILURE;
      }
    }
    return SUCCESS;
  }


/****************************************************************************/
/*                                                                          */
/* <Function>   Finalize_Profile_Table                                      */
/*                                                                          */
/* <Description>                                                            */
/*     Adjusts all links in the Profiles list.                              */
/*                                                                          */
/****************************************************************************/

  static 
  TBool  Finalize_Profile_Table( RAS_ARG )
  {
    int       n, bottom, top;
    PProfile  p;


    n = ras.num_profs;

    if ( n > 1 )
    {
      p = ras.start_prof;
      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.start_prof = NULL;
      
    return SUCCESS;
  }



/****************************************************************************/
/*                                                                          */
/* <Function>   Line_Up                                                     */
/*                                                                          */
/* <Description>                                                            */
/*     Computes the scan-line intersections of an ascending line segment    */
/*     and stores them in the render pool.                                  */
/*                                                                          */
/* <Input>                                                                  */
/*     x1  :: start x coordinate                                            */
/*     y1  :: start y coordinates                                           */
/*     x2  :: end x coordinate                                              */
/*     y2  :: end y coordinate                                              */
/*   miny  :: minimum vertical grid coordinate                              */
/*   maxy  :: maximum vertical grid coordinate                              */
/*                                                                          */
/* <Return>                                                                 */
/*     SUCCESS or FAILURE.                                                  */
/*                                                                          */
/****************************************************************************/

  static 
  TBool  Line_Up( RAS_ARGS TPos  x1,   TPos  y1, 
                          TPos  x2,   TPos  y2,
                          TPos  miny, TPos  maxy )
  {
    TPos   Dx, Dy;
    int    e1, e2, f1, f2, size;
    TPos   Ix, Rx, Ax;

    PPos  top;


    Dx = x2 - x1;
    Dy = y2 - y1;

    if ( Dy <= 0 || y2 < miny || y1 > maxy )
      return SUCCESS;

    if ( y1 < miny )
    {
      x1 += FMulDiv( 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.cursor--;
        ras.joint = FALSE;
      }

    ras.joint = ( f2 == 0 );

    if ( ras.fresh )
    {
      ras.cur_prof->start = e1;
      ras.fresh           = FALSE;
    }

    size = e2 - e1 + 1;
    if ( ras.cursor + size >= ras.pool_limit )
    {
      ras.error = ErrRaster_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.cursor;

    while ( size > 0 ) 
    {
      *top++ = x1;

      DEBUG_PSET;

      x1 += Ix;
      Ax += Rx;
      if ( Ax >= 0 )
      {
        Ax -= Dy;
        x1 += Dx;
      }
      size--;
    }

    ras.cursor = top;
    return SUCCESS;
  }


/****************************************************************************/
/*                                                                          */
/* <Function>   Line_Down                                                   */
/*                                                                          */
/* <Description>                                                            */
/*     Computes the scan-line intersections of a descending line segment    */
/*     and stores them in the render pool.                                  */
/*                                                                          */
/* <Input>                                                                  */
/*     x1  :: start x coordinate                                            */
/*     y1  :: start y coordinates                                           */
/*     x2  :: end x coordinate                                              */
/*     y2  :: end y coordinate                                              */
/*   miny  :: minimum vertical grid coordinate                              */
/*   maxy  :: maximum vertical grid coordinate                              */
/*                                                                          */
/* <Return>                                                                 */
/*     SUCCESS or FAILURE.                                                  */
/*                                                                          */
/****************************************************************************/

  static 
  TBool  Line_Down( RAS_ARGS TPos  x1,   TPos  y1, 
                            TPos  x2,   TPos  y2,
                            TPos  miny, TPos  maxy )
  {
    TBool result, fresh;

    fresh  = ras.fresh;
    result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );

    if ( fresh && !ras.fresh ) 
      ras.cur_prof->start = -ras.cur_prof->start;

    return result;
  }




#ifdef FT_RASTER_CONIC_BEZIERS
/****************************************************************************/
/*                                                                          */
/* <Function>   Split_Conic                                                 */
/*                                                                          */
/* <Description>                                                            */
/*     Subdivides one second-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_Conic( TPoint*  base )
  {
    TPos   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;
  }
#endif

#ifdef FT_RASTER_CUBIC_BEZIERS
/****************************************************************************/
/*                                                                          */
/* <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 )
  {
    TPos   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 )/2;
    base[5].x = b = ( base[3].x + d )/2;
    c = (c+d)/2;
    base[2].x = a = (a+c)/2;
    base[4].x = b = (b+c)/2;
    base[3].x = (a+b)/2;
    
    base[6].y = base[3].y;
    c = base[1].y;
    d = base[2].y;
    base[1].y = a = ( base[0].y + c )/2;
    base[5].y = b = ( base[3].y + d )/2;
    c = (c+d)/2;
    base[2].y = a = (a+c)/2;
    base[4].y = b = (b+c)/2;
    base[3].y = (a+b)/2;
  }
#endif




#ifdef FT_RASTER_CONIC_BEZIERS
/****************************************************************************/
/*                                                                          */
/* <Function>   Conic_Up                                                    */
/*                                                                          */
/* <Description>                                                            */
/*     Computes the scan-line intersections of an ascending second-order    */
/*     Bezier arc and stores them in the render pool. The arc is taken      */
/*     from the top of the stack..                                          */
/*                                                                          */
/* <Input>                                                                  */
/*     miny     :: minimum vertical grid coordinate                         */
/*     maxy     :: maximum vertical grid coordinate                         */
/*                                                                          */
/* <Return>                                                                 */
/*     SUCCESS or FAILURE                                                   */
/*                                                                          */
/****************************************************************************/

  static 
  TBool  Conic_Up( RAS_ARGS TPos  miny, TPos  maxy )
  {
    TPos  y1, y2, e, e2, e0;
    int   f1;

    TPoint*  arc;
    TPoint*  start_arc;

    PPos top;


    arc = ras.arc;
    y1  = arc[2].y;
    y2  = arc[0].y;
    top = ras.cursor; 

    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[2].x;

        DEBUG_PSET;

        e += ras.precision;
      }
    }

    if ( ras.fresh )
    {
      ras.cur_prof->start = TRUNC( e0 );
      ras.fresh = FALSE;
    }

    if ( e2 < e )
      goto Fin;

    if ( ( top+TRUNC(e2-e)+1 ) >= ras.pool_limit )
    {
      ras.cursor = top;
      ras.error  = ErrRaster_Overflow;
      return FAILURE;
    }

    start_arc = arc;

    while ( arc >= start_arc && e <= e2 )
    {
      ras.joint = FALSE;

      y2 = arc[0].y;

      if ( y2 > e )
      {
        y1 = arc[2].y;
        if ( y2 - y1 >= ras.precision_step )
        {
          Split_Conic( arc );
          arc += 2;
        }
        else
        {
          *top++ = arc[2].x + 
                   FMulDiv( arc[0].x-arc[2].x, 
                            e  - y1, 
                            y2 - y1 );
          DEBUG_PSET;
            
          arc -= 2;
          e   += ras.precision;
        }
      }
      else
      {
        if ( y2 == e )
        {
          ras.joint  = TRUE;
          *top++     = arc[0].x;
        
          DEBUG_PSET;
        
          e += ras.precision;
        }
        arc -= 2;
      }
    }

  Fin:
    ras.cursor = top;
    ras.arc   -= 2;
    return SUCCESS;
  }


/****************************************************************************/
/*                                                                          */
/* <Function>   Conic_Down                                                  */
/*                                                                          */
/* <Description>                                                            */
/*     Computes the scan-line intersections of a descending second-order    */
/*     Bezier arc and stores them in the render pool. The arc is taken      */
/*     from the top of the stack..                                          */
/*                                                                          */
/* <Input>                                                                  */
/*     miny     :: minimum vertical grid coordinate                         */
/*     maxy     :: maximum vertical grid coordinate                         */
/*                                                                          */
/* <Return>                                                                 */
/*     SUCCESS or FAILURE                                                   */
/*                                                                          */
/****************************************************************************/

  static 
  TBool  Conic_Down( RAS_ARGS TPos  miny, TPos  maxy )
  {
    TPoint*  arc = ras.arc;
    TBool     result, fresh;


    arc[0].y = -arc[0].y;
    arc[1].y = -arc[1].y; 
    arc[2].y = -arc[2].y;

    fresh = ras.fresh;

    result = Conic_Up( RAS_VARS -maxy, -miny );

    if ( fresh && !ras.fresh )
      ras.cur_prof->start = -ras.cur_prof->start;

    arc[0].y = -arc[0].y;
    return result;
  }

#endif /* FT_RASTER_CONIC_BEZIERS */



#ifdef FT_RASTER_CUBIC_BEZIERS
/****************************************************************************/
/*                                                                          */
/* <Function>   Cubic_Up                                                    */
/*                                                                          */
/* <Description>                                                            */
/*     Computes the scan-line intersections of an ascending third-order     */
/*     bezier arc and stores them in the render pool                        */
/*                                                                          */
/* <Input>                                                                  */
/*     miny     :: minimum vertical grid coordinate                         */
/*     maxy     :: maximum vertical grid coordinate                         */
/*                                                                          */
/* <Return>                                                                 */
/*     SUCCESS or FAILURE                                                   */
/*                                                                          */
/****************************************************************************/
  static
  TBool  Cubic_Up( RAS_ARGS TPos  miny, TPos  maxy )
  {
    TPos  y1, y2, e, e2, e0;
    int   f1;

    TPoint*  arc;
    TPoint*  start_arc;

    TPos*    top;


    arc = ras.arc;
    y1  = arc[3].y;
    y2  = arc[0].y;
    top = ras.cursor; 

    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[3].x;

        DEBUG_PSET;

        e += ras.precision;
      }
    }

    if ( ras.fresh )
    {
      ras.cur_prof->start = TRUNC( e0 );
      ras.fresh = FALSE;
    }

    if ( e2 < e )
      goto Fin;

    if ( ( top+TRUNC(e2-e)+1 ) >= ras.pool_limit )
    {
      ras.cursor = top;
      ras.error  = ErrRaster_Overflow;
      return FAILURE;
    }

    start_arc = arc;

    while ( arc >= start_arc && e <= e2 )
    {
      ras.joint = FALSE;

      y2 = arc[0].y;

      if ( y2 > e )
      {
        y1 = arc[3].y;
        if ( y2 - y1 >= ras.precision_step )
        {
          Split_Cubic( arc );
          arc += 3;
        }
        else
        {
          *top++ = arc[3].x + 
                   FMulDiv( arc[0].x-arc[3].x, 
                            e  - y1, 
                            y2 - y1 );
          DEBUG_PSET;
            
          arc -= 3;
          e   += ras.precision;
        }
      }
      else
      {
        if ( y2 == e )
        {
          ras.joint  = TRUE;
          *top++     = arc[0].x;
        
          DEBUG_PSET;
        
          e += ras.precision;
        }
        arc -= 3;
      }
    }

  Fin:
    ras.cursor = top;
    ras.arc   -= 3;
    return SUCCESS;
  }


/****************************************************************************/
/*                                                                          */
/* <Function>   Cubic_Down                                                  */
/*                                                                          */
/* <Description>                                                            */
/*     Computes the scan-line intersections of a descending third-order     */
/*     bezier arc and stores them in the render pool                        */
/*                                                                          */
/* <Input>                                                                  */
/*     miny     :: minimum vertical grid coordinate                         */
/*     maxy     :: maximum vertical grid coordinate                         */
/*                                                                          */
/* <Return>                                                                 */
/*     SUCCESS or FAILURE                                                   */
/*                                                                          */
/****************************************************************************/
  static 
  TBool  Cubic_Down( RAS_ARGS TPos  miny, TPos  maxy )
  {
    TPoint*  arc = ras.arc;
    TBool     result, fresh;


    arc[0].y = -arc[0].y;
    arc[1].y = -arc[1].y; 
    arc[2].y = -arc[2].y;
    arc[3].y = -arc[3].y;

    fresh = ras.fresh;

    result = Cubic_Up( RAS_VARS -maxy, -miny );

    if ( fresh && !ras.fresh )
      ras.cur_prof->start = -ras.cur_prof->start;

    arc[0].y = -arc[0].y;
    return result;
  }

#endif /* FT_RASTER_CUBIC_BEZIERS */





 /* A function type describing the functions used to split bezier arcs */
  typedef  void  (*TSplitter)( TPoint*  base );

#ifdef FT_DYNAMIC_BEZIER_STEPS
  static
  TPos  Dynamic_Bezier_Threshold( RAS_ARGS int degree, TPoint*  arc )
  {
    TPos    min_x,  max_x,  min_y, max_y, A, B;
    TPos    wide_x, wide_y, threshold;
    TPoint* cur   = arc;
    TPoint* limit = cur + degree;

    /* first of all, set the threshold to the maximum x or y extent */
    min_x = max_x = arc[0].x;
    min_y = max_y = arc[0].y;
    cur++;
    for ( ; cur < limit; cur++ )
    {
      TPos  x = cur->x;
      TPos  y = cur->y;

      if ( x < min_x ) min_x = x;
      if ( x > max_x ) max_x = x;

      if ( y < min_y ) min_y = y;
      if ( y > max_y ) max_y = y;
    }
    wide_x = (max_x - min_x) << 4;
    wide_y = (max_y - min_y) << 4;

    threshold = wide_x;
    if (threshold < wide_y) threshold = wide_y;

    /* now compute the second and third order error values */
    
    wide_x = arc[0].x + arc[1].x - arc[2].x*2;
    wide_y = arc[0].y + arc[1].y - arc[2].y*2;

    if (wide_x < 0) wide_x = -wide_x;
    if (wide_y < 0) wide_y = -wide_y;

    A = wide_x; if ( A < wide_y ) A = wide_y;

    if (degree >= 3)
    {
      wide_x = arc[3].x - arc[0].x + 3*(arc[2].x - arc[3].x);
      wide_y = arc[3].y - arc[0].y + 3*(arc[2].y - arc[3].y);

      if (wide_x < 0) wide_x = -wide_x;
      if (wide_y < 0) wide_y = -wide_y;

      B = wide_x; if ( B < wide_y ) B = wide_y;
    }
    else
      B = 0;

    while ( A > 0 || B > 0 )
    {
      threshold >>= 1;
      A         >>= 2;
      B         >>= 3;
    }

    if (threshold < PRECISION_STEP)
      threshold = PRECISION_STEP;

    return threshold;
  }
#endif

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Bezier_Up                                                          */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Computes the scan-line intersections of an ascending second-order  */
  /*    Bezier arc and stores them in the render pool.  The arc is taken   */
  /*    from the top of the stack.                                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    miny :: The minimum vertical grid coordinate.                      */
  /*    maxy :: The maximum vertical grid coordinate.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    SUCCESS or FAILURE.                                                */
  /*                                                                       */
  static
  TBool    Bezier_Up( RAS_ARGS int        degree,
                               TSplitter  splitter,
                               TPos       miny,
                               TPos       maxy )
  {
    TPos  y1, y2, e, e2, e0, threshold;
    int   f1;

    TPoint*  arc;
    TPoint*  start_arc;

    PPos top;


    arc = ras.arc;
    y1  = arc[degree].y;
    y2  = arc[0].y;
    top = ras.cursor;

    if ( y2 < miny || y1 > maxy )
      goto Fin;

    e2 = FLOOR( y2 );  /* integer end y */

    if ( e2 > maxy )
      e2 = maxy;

    e0 = miny;

    if ( y1 < miny )
    {
      e = e0;        /* integer start y == current scanline */
    }
    else
    {
      e  = CEILING( y1 );   /* integer start y == current scanline */
      f1 = FRAC( y1 );      /* fractional shift of start y         */
      e0 = e;               /* first integer scanline to be pushed */

      if ( f1 == 0 )        /* do we start on an integer scanline? */
      {
        if ( ras.joint )
        {
          top--;
          ras.joint = FALSE;
        }

        *top++ = arc[degree].x;  /* write directly start position */

        DEBUG_PSET;

        e += ras.precision; /* go to next scanline */
      }
    }

    /* record start position if necessary */
    if ( ras.fresh )
    {
      ras.cur_prof->start = TRUNC( e0 );
      ras.fresh = FALSE;
    }

    /* exit if the current scanline is already above the max scanline */
    if ( e2 < e )
      goto Fin;

    /* check for overflow */
    if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.pool_limit )
    {
      ras.cursor = top;
      ras.error  = ErrRaster_Overflow;
      return FAILURE;
    }

    
#ifdef FT_DYNAMIC_BEZIER_STEPS
    /* compute dynamic bezier step threshold */
    threshold = Dynamic_Bezier_Threshold( RAS_VAR_ degree, arc );
#else
    threshold = ras.precision_step;
#endif

    start_arc = arc;

    /* loop while there is still an arc on the bezier stack */
    /* and the current scan line is below y max == e2       */
    while ( arc >= start_arc && e <= e2 )
    {
      ras.joint = FALSE;

      y2 = arc[0].y;  /* final y of the top-most arc */

      if ( y2 > e )   /* the arc intercepts the current scanline */
      {
        y1 = arc[degree].y;  /* start y of top-most arc */

#ifdef OLD
        if ( y2-y1 >= ras.precision_step )
#else        
        if ( y2 >= e + ras.precision || y2 - y1 >= threshold )
#endif        
        {
          /* if the arc's height is too great, split it */
          splitter( arc );
          arc += degree;
        }
        else
        {
          /* otherwise, approximate it as a segment and compute */
          /* its intersection with the current scanline         */
          *top++ = arc[degree].x +
                   FMulDiv( arc[0].x-arc[degree].x,
                            e  - y1,
                            y2 - y1 );

          DEBUG_PSET;

          arc -= degree;         /* pop the arc         */
          e   += ras.precision;  /* go to next scanline */
        }
      }
      else
      {
        if ( y2 == e )        /* if the arc falls on the scanline */
        {                     /* record its _joint_ intersection  */
          ras.joint  = TRUE;
          *top++     = arc[0].x;

          DEBUG_PSET;

          e += ras.precision; /* go to next scanline */
        }
        arc -= degree;        /* pop the arc */
      }
    }

  Fin:
    ras.cursor = top;
    ras.arc   -= degree;
    return SUCCESS;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Bezier_Down                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Computes the scan-line intersections of a descending second-order  */
  /*    Bezier arc and stores them in the render pool.  The arc is taken   */
  /*    from the top of the stack.                                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    miny :: The minimum vertical grid coordinate.                      */
  /*    maxy :: The maximum vertical grid coordinate.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    SUCCESS or FAILURE.                                                */
  /*                                                                       */
  static
  TBool    Bezier_Down( RAS_ARGS int        degree,
                                 TSplitter  splitter,
                                 TPos       miny,
                                 TPos       maxy )
  {
    TPoint*  arc = ras.arc;
    TBool    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.cur_prof->start = -ras.cur_prof->start;

    arc[0].y = -arc[0].y;
    return result;
  }


/****************************************************************************/
/*                                                                          */
/* <Function>   Check_Contour                                               */
/*                                                                          */
/* <Description>                                                            */
/*     perform some check at contour closure.                               */
/*                                                                          */
/* <Return>                                                                 */
/*     SUCCESS or FAILURE                                                   */
/*                                                                          */
/****************************************************************************/

  static  
  TBool  Check_Contour( RAS_ARG )
  {
    PProfile  lastProfile;
      
    /* We must now see if the extreme arcs join or not */
    if ( ( FRAC( ras.last.y ) == 0     &&
           ras.last.y >= ras.minY      &&
           ras.last.y <= ras.maxY )    )
    {
      if ( ras.first_prof && ras.first_prof->flow == ras.cur_prof->flow )
        ras.cursor--;
    }
    
    lastProfile = ras.cur_prof;
    if ( End_Profile( RAS_VAR ) ) return FAILURE;
    
    /* close the 'next profile in contour' linked list */
    lastProfile->next = ras.first_prof;
    
    return SUCCESS;
  }

/****************************************************************************/
/*                                                                          */
/* <Function>   Move_To                                                     */
/*                                                                          */
/* <Description>                                                            */
/*     This function injects a new contour in the render pool..             */
/*                                                                          */
/* <Input>                                                                  */
/*     to       :: pointer to the contour's first point                     */
/*     raster   :: a pointer to the current raster object                   */
/*                                                                          */
/* <Return>                                                                 */
/*     Error code, 0 means success                                          */
/*                                                                          */
/* <Note>                                                                   */
/*     This function is used as a "FTRasterMoveTo_Func" by the outline      */
/*     decomposer..                                                         */
/*                                                                          */
/****************************************************************************/

  static
  int  Move_To( FT_Vector*  to,
                FT_Raster   raster )
  {
    /* if there was already a contour being built, perform some checks */
    if ( ras.start_prof )
      if ( Check_Contour( RAS_VAR ) )
        return FAILURE;

    /* set the "current last point" */
    if (ras.flipped)
    {
      ras.last.x = to->y;
      ras.last.y = to->x;
    }
    else
    {
      ras.last.x = to->x;
      ras.last.y = to->y;
    }

    ras.state      = Unknown;
    ras.first_prof = NULL;
    
    return SUCCESS;
  }

/****************************************************************************/
/*                                                                          */
/* <Function>   Line_To                                                     */
/*                                                                          */
/* <Description>                                                            */
/*     This function injects a new line segment in the render pool and      */
/*     adjusts the profiles list accordingly..                              */
/*                                                                          */
/* <Input>                                                                  */
/*     to       :: pointer to the target position                           */
/*     raster   :: a pointer to the current raster object                   */
/*                                                                          */
/* <Return>                                                                 */
/*     Error code, 0 means success                                          */
/*                                                                          */
/* <Note>                                                                   */
/*     This function is used as a "FTRasterLineTo_Func" by the outline      */
/*     decomposer..                                                         */
/*                                                                          */
/****************************************************************************/
  
  static 
  int  Line_To( FT_Vector*  to,
                FT_Raster   raster )
  {
    TPos    x;
    TPos    y;
    TDirection  new_state;
    
    if ( ras.flipped )
    {
      x = to->y;
      y = to->x;
    }
    else
    {
      x = to->x;
      y = to->y;
    }
    
    /* First, detect a change of direction */
    if ( y != ras.last.y )
    {
      new_state = ( y > ras.last.y ? Ascending : Descending );
      if (new_state != ras.state)
      {
        if (ras.state != Unknown && End_Profile( RAS_VAR ))
          goto Fail;
          
        if ( New_Profile( RAS_VARS  new_state) )
          goto Fail;
      }
    }

    /* Then compute the lines */
    if ( (ras.state == Ascending ? Line_Up : Line_Down)
            ( RAS_VARS  ras.last.x, ras.last.y, x, y, ras.minY, ras.maxY ) )
      goto Fail;
      
    ras.last.x = x;
    ras.last.y = y;

    return SUCCESS;
  Fail:
    return FAILURE;
  }

#ifdef FT_RASTER_CONIC_BEZIERS

/****************************************************************************/
/*                                                                          */
/* <Function>   Conic_To                                                    */
/*                                                                          */
/* <Description>                                                            */
/*      Injects a new conic bezier arc and adjusts the profile list         */
/*      accordingly.                                                        */
/*                                                                          */
/* <Input>                                                                  */
/*      control  :: pointer to intermediate control point                   */
/*      to       :: pointer to end point                                    */
/*      raster   :: handle to current raster object                         */
/*                                                                          */
/* <Return>                                                                 */
/*     Error code, 0 means success                                          */
/*                                                                          */
/* <Note>                                                                   */
/*     This function is used as a "FTRasterConicTo_Func" by the outline     */
/*     decomposer..                                                         */
/*                                                                          */
/****************************************************************************/

    static 
    void  Push_Conic( RAS_ARGS FT_Vector*  p2,
                               FT_Vector*  p3 )
    {
      #undef  STORE
      #define STORE( _arc, point )                  \
              {                                     \
                TPos  x = point->x;                 \
                TPos  y = point->y;                 \
                if (ras.flipped)                    \
                {                                   \
                  _arc.x = y;                       \
                  _arc.y = x;                       \
                }                                   \
                else                                \
                {                                   \
                  _arc.x = x;                       \
                  _arc.y = y;                       \
                }                                   \
              }
  
      TPoint*  arc;
      ras.arc = arc = ras.arcs;
  
      arc[2] = ras.last;
      STORE( arc[1], p2 );
      STORE( arc[0], p3 );
      #undef  STORE
    }


  static 
  int  Conic_To( FT_Vector*  control,
                 FT_Vector*  to,
                 FT_Raster   raster )
  {
    TPos        y1, y2, y3, x3;
    TDirection  state_bez;


    Push_Conic( RAS_VARS  control, to );

    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 )
      {
        if ( y2 == y1 )
          state_bez = Flat;
        else
          state_bez = Unknown;
      }
      else if ( y1 < y3 )
      {
        if ( y2 < y1 || y2 > y3 )
          state_bez = Unknown;
        else
          state_bez = Ascending;
      }
      else
      {
        if ( y2 < y3 || y2 > y1 )
          state_bez = Unknown;
        else
          state_bez = Descending;
      }

      /* split non-monotonic arcs, ignore flat ones, or */
      /* computes the up and down ones                  */

      switch ( state_bez )
      {
      case Flat:
        ras.arc -= 2;
        break;

      case Unknown:
        Split_Conic( ras.arc );
        ras.arc += 2;
        break;

      default:
        /* 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 ( (ras.state == Ascending ? Bezier_Up : Bezier_Down)
                ( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
          goto Fail;
      }
    } while ( ras.arc >= ras.arcs );

    ras.last.x = x3;
    ras.last.y = y3;

    return 0;
  Fail:
    return FAILURE;
  }

#else
  static 
  int  Conic_To( FT_Vector*  control,
                 FT_Vector*  to,
                 FT_Raster   raster )
  {
    (void)control;
    (void)to;
    (void)raster;
    
    return ErrRaster_Invalid_Outline;
  }
#endif /* CONIC_BEZIERS */


#ifdef FT_RASTER_CUBIC_BEZIERS
/****************************************************************************/
/*                                                                          */
/* <Function>   Cubic_To                                                    */
/*                                                                          */
/* <Description>                                                            */
/*      Injects a new cubic bezier arc and adjusts the profile list         */
/*      accordingly.                                                        */
/*                                                                          */
/* <Input>                                                                  */
/*      control1 :: pointer to first control point                          */
/*      control2 :: pointer to second control point                         */
/*      to       :: pointer to end point                                    */
/*      raster   :: handle to current raster object                         */
/*                                                                          */
/* <Return>                                                                 */
/*     Error code, 0 means success                                          */
/*                                                                          */
/* <Note>                                                                   */
/*     This function is used as a "FTRasterCubicTo_Func" by the outline     */
/*     decomposer..                                                         */
/*                                                                          */
/****************************************************************************/

      static 
      void  Push_Cubic( RAS_ARGS FT_Vector*  p2,
                                 FT_Vector*  p3,
                                 FT_Vector*  p4 )
      {
        #undef  STORE
        #define STORE( _arc, point )                  \
                {                                     \
                  TPos  x = point->x;                 \
                  TPos  y = point->y;                 \
                  if (ras.flipped)                    \
                  {                                   \
                    _arc.x = y;                       \
                    _arc.y = x;                       \
                  }                                   \
                  else                                \
                  {                                   \
                    _arc.x = x;                       \
                    _arc.y = y;                       \
                  }                                   \
                }
    
        TPoint*  arc;
        ras.arc = arc = ras.arcs;
    
        arc[3] = ras.last;
        STORE( arc[2], p2 );
        STORE( arc[1], p3 );
        STORE( arc[0], p4 );
    
        #undef STORE
      }


  static 
  int  Cubic_To( FT_Vector*  control1,
                 FT_Vector*  control2,
                 FT_Vector*  to,
                 FT_Raster   raster )
  {
    TPos        y1, y2, y3, y4, x4;
    TDirection  state_bez;

   
    Push_Cubic( RAS_VARS  control1, control2, to );

    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 )
      {
        if ( y1 == y2 && y1 == y3 )
          state_bez = Flat;
        else
          state_bez = Unknown;
      }
      else if ( y1 < y4 )
      {
        if ( y2 < y1 || y2 > y4 || y3 < y1 || y3 > y4 )
          state_bez = Unknown;
        else
          state_bez = Ascending;
      }
      else
      {
        if ( y2 < y4 || y2 > y1 || y3 < y4 || y3 > y1 )
          state_bez = Unknown;
        else
          state_bez = Descending;
      }

      /* split non-monotonic arcs, ignore flat ones, or */
      /* computes the up and down ones                  */

      switch ( state_bez )
      {
      case Flat:
        ras.arc -= 3;
        break;

      case Unknown:
        Split_Cubic( ras.arc );
        ras.arc += 3;
        break;

      default:
        /* 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 ( (ras.state == Ascending ? Bezier_Up : Bezier_Down)
                ( RAS_VARS  3, Split_Cubic, ras.minY, ras.maxY ) )
          goto Fail;
      }
    } while ( ras.arc >= ras.arcs );

    ras.last.x = x4;
    ras.last.y = y4;

    return 0;
  Fail:
    return FAILURE;
  }

#else
  int  Cubic_To( FT_Vector*  control1,
                 FT_Vector*  control2,
                 FT_Vector*  to,
                 FT_Raster   raster )
  {
    (void)control1;
    (void)control2;
    (void)to;
    (void)raster;
    
    return ErrRaster_Invalid_Outline;
  }
#endif /* CUBIC_BEZIERS */


/****************************************************************************/
/*                                                                          */
/* <Function>   Convert_Glyph                                               */
/*                                                                          */
/* <Description>                                                            */
/*     Converts a glyph into a series of segments and arcs and makes        */
/*     a profiles list with them..                                          */
/*                                                                          */
/* <Input>                                                                  */
/*     outline  :: glyph outline                                            */
/*                                                                          */
/* <Return>                                                                 */
/*     SUCCESS or FAILURE                                                   */
/*                                                                          */
/****************************************************************************/

  static 
  TBool  Convert_Glyph( RAS_ARGS  FT_Outline*  outline )
  {
    static
    FT_Outline_Funcs  interface =
    {
      (FT_Outline_MoveTo_Func)Move_To,
      (FT_Outline_LineTo_Func)Line_To,
      (FT_Outline_ConicTo_Func)Conic_To,
      (FT_Outline_CubicTo_Func)Cubic_To,
      0,
      0
    };

    /* Set up state in the raster object */
    ras.start_prof = NULL;
    ras.joint      = FALSE;
    ras.fresh      = FALSE;

    ras.pool_limit  = ras.pool_size - AlignProfileSize;

    ras.n_turns = 0;

    ras.cur_prof         = (PProfile)ras.cursor;
    ras.cur_prof->offset = ras.cursor;
    ras.num_profs        = 0;

    interface.shift = ras.scale_shift;
    interface.delta = ras.precision_half;

    /* Now decompose curve */
    if ( FT_Outline_Decompose( outline, &interface, &ras ) ) return FAILURE;
    /* XXX : the error condition is in ras.error */

    /* Check the last contour if needed */
    if ( Check_Contour( RAS_VAR ) ) return FAILURE;
    
    /* Finalize profiles list */
    return Finalize_Profile_Table( RAS_VAR );
  }


/************************************************/
/*                                              */
/*  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;
    TPos       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 Bitmap Sweep Routines               *********/
/********                                                         *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/


/***********************************************************************/
/*                                                                     */
/* <Function> Vertical_Sweep_Init                                      */
/*                                                                     */
/* <Description>                                                       */
/*     Initialise the vertical bitmap sweep. Called by the generic     */
/*     sweep/draw routine before its loop..                            */
/*                                                                     */
/* <Input>                                                             */
/*    min  :: address of current minimum scanline                      */
/*    max  :: address of current maximum scanline                      */
/*                                                                     */
/***********************************************************************/

  static 
  void  Vertical_Sweep_Init( RAS_ARGS int*  min, int*  max )
  {
    long  pitch = ras.target.pitch;
    
    (void)max;
    
    ras.trace_incr = -pitch;
    ras.trace_bit  = -*min*pitch;
    if (pitch > 0)
      ras.trace_bit += (ras.target.rows-1)*pitch;
      
    ras.gray_min_x = 0;
    ras.gray_max_x = 0;
  }

/***********************************************************************/
/*                                                                     */
/* <Function> Vertical_Sweep_Span                                      */
/*                                                                     */
/* <Description>                                                       */
/*     draws a single horizontal bitmap span during the vertical       */
/*     bitmap sweep.                                                   */
/*                                                                     */
/* <Input>                                                             */
/*     y  :: current scanline                                          */
/*     x1 :: left span edge                                            */
/*     x2 :: right span edge                                           */
/*                                                                     */
/***********************************************************************/

  static void  Vertical_Sweep_Span( RAS_ARGS TScan  y,
                                             TPos   x1,
                                             TPos   x2 )
  {
    TPos   e1, e2;
    int    c1, c2;
    TByte   f1, f2;
    TByte*  target;

    /* Drop-out control */
    (void)y;

    e1 = TRUNC( CEILING( x1 ) );
    if ( x2-x1-ras.precision <= ras.precision_jitter )
      e2 = e1;
    else
      e2 = TRUNC( FLOOR( x2 ) );

    if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width )
    {
      if ( e1 < 0 )              e1 = 0;
      if ( e2 >= ras.bit_width ) e2 = ras.bit_width-1;

      c1 = e1 >> 3;
      c2 = e2 >> 3;

      f1 =  ((unsigned char)0xFF >> (e1 & 7));
      f2 = ~((unsigned char)0x7F >> (e2 & 7));

      target = ras.bit_buffer + ras.trace_bit + 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 );
    }
  }

/***********************************************************************/
/*                                                                     */
/* <Function> Vertical_Test_Pixel                                      */
/*                                                                     */
/* <Description>                                                       */
/*     test a pixel 'light' during the vertical bitmap sweep. Used     */
/*     during drop-out control only..                                  */
/*                                                                     */
/* <Input>                                                             */
/*     y  :: current scanline                                          */
/*     x  :: current x coordinate                                      */
/*                                                                     */
/***********************************************************************/

  static 
  int  Vertical_Test_Pixel( RAS_ARGS TScan  y,
                                     int    x )
  {
    int c1 = x >> 3;
    (void)y;
    return ( x >= 0 && x < ras.bit_width && 
             ras.bit_buffer[ras.trace_bit + c1] & (0x80 >> (x & 7)) );
  }

/***********************************************************************/
/*                                                                     */
/* <Function> Vertical_Set_Pixel                                       */
/*                                                                     */
/* <Description>                                                       */
/*     Sets a single pixel in a bitmap during the vertical sweep.      */
/*     Used during drop-out control..                                  */
/*                                                                     */
/* <Input>                                                             */
/*     y  :: current scanline                                          */
/*     x  :: current x coordinate                                      */
/* color  :: ignored by this function                                  */
/*                                                                     */
/***********************************************************************/

  static 
  void  Vertical_Set_Pixel( RAS_ARGS  int  y,
                                      int  x,
                                      int  color )
  {
    (void)color;  /* unused here */
    (void)y;
    
    if ( x >= 0 && x < ras.bit_width )
    {
      int c1 = x >> 3;

      if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
      if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1;

      ras.bit_buffer[ras.trace_bit+c1] |= (char)(0x80 >> (x & 7));
    }
  }

/***********************************************************************/
/*                                                                     */
/* <Function> Vertical_Sweep_Step                                      */
/*                                                                     */
/* <Description>                                                       */
/*     Called whenever the sweep jumps to anothr scanline.             */
/*     Only updates the pointers in the vertical bitmap sweep          */
/*                                                                     */
/***********************************************************************/

  static
  void Vertical_Sweep_Step( RAS_ARG )
  {
    ras.trace_bit += ras.trace_incr;
  }


/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/********                                                         *********/
/********           Horizontal Bitmap Sweep Routines              *********/
/********                                                         *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/

/***********************************************************************/
/*                                                                     */
/* <Function> Horizontal_Sweep_Init                                    */
/*                                                                     */
/* <Description>                                                       */
/*     Initialise the horizontal bitmap sweep. Called by the generic   */
/*     sweep/draw routine before its loop..                            */
/*                                                                     */
/* <Input>                                                             */
/*    min  :: address of current minimum pixel column                  */
/*    max  :: address of current maximum pixel column                  */
/*                                                                     */
/***********************************************************************/

  static void  Horizontal_Sweep_Init( RAS_ARGS int*  min, int*  max )
  {
    /* nothing, really */
    UNUSED_RASTER
    (void)min;
    (void)max;
  }


/***********************************************************************/
/*                                                                     */
/* <Function> Horizontal_Sweep_Span                                    */
/*                                                                     */
/* <Description>                                                       */
/*     draws a single vertical bitmap span during the horizontal       */
/*     bitmap sweep. Actually, this function is only used to           */
/*     check for weird drop-out cases..                                */
/*                                                                     */
/* <Input>                                                             */
/*     y  :: current pixel column                                      */
/*     x1 :: top    span edge                                          */
/*     x2 :: bottom span edge                                          */
/*                                                                     */
/***********************************************************************/

  static void  Horizontal_Sweep_Span( RAS_ARGS TScan  y,
                                               TPos   x1,
                                               TPos   x2 )
  {
    TPos  e1, e2;
    PByte bits;
    TByte  f1;

    (void)y;

    /* During the horizontal sweep, we only take care of drop-outs */
    if ( x2-x1 < ras.precision )
    {
      e1 = CEILING( x1 );
      e2 = FLOOR( x2 );
      
      if ( e1 == e2 )
      {
        bits = ras.bit_buffer + (y >> 3);
        f1   = (TByte)(0x80 >> (y & 7));
        
        e1 = TRUNC( e1 );

        if ( e1 >= 0 && e1 < ras.target.rows )
        {
          long  pitch = ras.target.pitch;
          
          bits -= e1*pitch;
          if (pitch > 0)
            bits += (ras.target.rows-1)*pitch;
            
          bits[0] |= f1;
        }
      }
    }
  }


/***********************************************************************/
/*                                                                     */
/* <Function> Horizontal_Test_Pixel                                    */
/*                                                                     */
/* <Description>                                                       */
/*     test a pixel 'light' during the horizontal bitmap sweep. Used   */
/*     during drop-out control only..                                  */
/*                                                                     */
/* <Input>                                                             */
/*     y  :: current pixel column                                      */
/*     x  :: current row/scanline                                      */
/*                                                                     */
/***********************************************************************/

  static
  int   Horizontal_Test_Pixel( RAS_ARGS  int  y,
                                         int  x )
  {
    char*  bits  = (char*)ras.bit_buffer + (y >> 3);
    int    f1    = (TByte)(0x80 >> (y & 7));
    long   pitch = ras.target.pitch;
    
    bits -= x*pitch;
    if (pitch > 0)
      bits += (ras.target.rows-1)*pitch;
      
    return ( x >= 0 && x < ras.target.rows && (bits[0] & f1) );
  }

/***********************************************************************/
/*                                                                     */
/* <Function> Horizontal_Set_Pixel                                     */
/*                                                                     */
/* <Description>                                                       */
/*     Sets a single pixel in a bitmap during the horizontal sweep.    */
/*     Used during drop-out control..                                  */
/*                                                                     */
/* <Input>                                                             */
/*     y  :: current pixel column                                      */
/*     x  :: current row/scanline                                      */
/* color  :: ignored by this function                                  */
/*                                                                     */
/***********************************************************************/

  static
  void  Horizontal_Set_Pixel( RAS_ARGS  int  y,
                                        int  x,
                                        int  color )
  {
    char*  bits  = (char*)ras.bit_buffer + (y >> 3);
    int    f1    = (TByte)(0x80 >> (y  & 7));
    long   pitch = ras.target.pitch;

    (void)color;   /* unused here */

    bits -= x*pitch;
    if (pitch > 0)
      bits += (ras.target.rows-1)*pitch;
      
    if ( x >= 0 && x < ras.target.rows )
      bits[0] |= f1;
  }

/***********************************************************************/
/*                                                                     */
/* <Function> Horizontal_Sweep_Step                                    */
/*                                                                     */
/* <Description>                                                       */
/*     Called whenever the sweep jumps to another pixel column.        */
/*                                                                     */
/***********************************************************************/

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


/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/********                                                         *********/
/********      Anti-Aliased Vertical Bitmap Sweep Routines        *********/
/********                                                         *********/
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/

#ifdef FT_RASTER_OPTION_ANTI_ALIAS

#ifdef FT_RASTER_ANTI_ALIAS_5
/***********************************************************************/
/*                                                                     */
/*  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_Gray5_Sweep_Init( RAS_ARGS int*  min, int*  max )
  {
    long  pitch;
    
    *min = *min & -2;
    *max = ( *max+3 ) & -2;

    ras.trace_bit = 0;

    pitch          = ras.target.pitch;
    ras.trace_incr = -pitch;
    ras.trace_pix  = - (*min/2)*pitch;
    if (pitch > 0)
      ras.trace_pix += (ras.target.rows-1)*pitch;
      
    ras.gray_min_x =  32000;
    ras.gray_max_x = -32000;
  }


  static void  Vertical_Gray5_Sweep_Span( RAS_ARGS TScan  y,
                                                   TPos   x1,
                                                   TPos   x2 )
  {
    static
    const unsigned int  LMask[17] =
#ifdef FT_RASTER_LITTLE_ENDIAN
             { 0xF0F0F0F0, 0xF0F0F070, 0xF0F0F030, 0xF0F0F010,
               0xF0F0F000, 0xF0F07000, 0xF0F03000, 0xF0F01000,
               0xF0F00000, 0xF0700000, 0xF0300000, 0xF0100000,
               0xF0000000, 0x70000000, 0x30000000, 0x10000000,
               0x00000000 };
#else
             { 0xF0F0F0F0, 0x70F0F0F0, 0x30F0F0F0, 0x10F0F0F0,
               0x00F0F0F0, 0x0070F0F0, 0x0030F0F0, 0x0010F0F0,
               0x0000F0F0, 0x000070F0, 0x000030F0, 0x000010F0,
               0x000000F0, 0x00000070, 0x00000030, 0x00000010,
               0x00000000 };
#endif

    TPos   e1, e2;
    int    c1, c2;
    TByte*  target;

    unsigned int    f1, f2;
    unsigned int    shift, fill;

    /* Drop-out control */

    e1 = TRUNC( CEILING( x1 ) );
    if ( x2-x1 <= ras.precision+ras.precision_jitter )
      e2 = e1;
    else
      e2 = FLOOR ( x2 );

    e2 = TRUNC( e2 );
 
    if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width )
    {
      if ( e1 < 0 )              e1 = 0;
      if ( e2 >= ras.bit_width ) e2 = ras.bit_width-1;

      shift = (y & 1)*4;

      c1 = e1 >> 4;
      c2 = e2 >> 4;

      fill = LMask[0];
      f1   = LMask[ e1 & 15 ];
      f2   = fill ^ LMask[ 1+(e2 & 15) ];

      f1   >>= shift;
      f2   >>= shift;
      fill >>= shift;

      if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
      if ( ras.gray_max_x < c2 ) ras.gray_max_x = c2;

      target = ras.bit_buffer + c1*4;
      c2 -= c1;

      if (c2 > 0)
      {
        int*  slice = (int*)target;
        
        slice[0] |= f1;
        c2--;
        while (c2 > 0)
        {
          * ++slice |= fill;
          c2--;
        }
        slice[1] |= f2;
      }
      else
        ((int*)target)[0] |= ( f1 & f2 );
    }
  }


  static 
  int  Vertical_Gray5_Test_Pixel( RAS_ARGS int  y,
                                           int  x )
  {
    int c1    = x >> 2;
    int f1    = x  & 3;
    int mask  = (0x80 >> f1) >> ((y & 1)*4);

    (void)y;
    
    return ( x >= 0                    && 
             x < ras.bit_width         && 
             ras.bit_buffer[c1] & mask );
  }


  static 
  void  Vertical_Gray5_Set_Pixel( RAS_ARGS  int  y,
                                            int  x,
                                            int  color )
  {
    (void)color;  /* unused here */
    (void)y;

    if ( x >= 0 && x < ras.bit_width )
    {
      int c1   = x >> 2;
      int f1   = x  & 3;
      int mask = (0x80 >> f1) >> ((y & 1)*4);
  
      if ( ras.gray_min_x > c1/4 ) ras.gray_min_x = c1/4;
      if ( ras.gray_max_x < c1/4 ) ras.gray_max_x = c1/4;

      ras.bit_buffer[c1] |= mask;
    }
  }


  static void  Vertical_Gray5_Sweep_Step( RAS_ARG )
  {
    int    c1;
    PByte  pix, bit;
    int*   count = ras.count_table;
    long*  grays;


    ras.trace_bit++;

    if ( ras.trace_bit > 1 )
    {
      pix   = ras.pix_buffer + ras.trace_pix + ras.gray_min_x*8;
      grays = ras.grays;

      if ( ras.gray_max_x >= 0 )
      {
        if ( ras.gray_max_x*8 >= ras.target.width ) 
          ras.gray_max_x = (ras.target.width+7)/8-1;

        if ( ras.gray_min_x < 0 ) 
          ras.gray_min_x = 0;

        bit = ras.bit_buffer + ras.gray_min_x*4;
        c1  = (ras.gray_max_x - ras.gray_min_x + 1);

        while ( c1 >= 0 )
        {
          if ( *(short*)bit )
          {
#if defined( FT_RASTER_LITTLE_ENDIAN )
            /* little endian storage */
            *((long*)pix) = (count[bit[1]] << 16) | count[bit[0]];
#elif defined( FT_RASTER_BIG_ENDIAN )
            /* big-endian storage */
            *((long*)pix) = (count[bit[0]] << 16) | count[bit[1]];
#else
            /* endian-independent storage */
            int  c;
            c = count[bit[1]];
            pix[4] = (TByte)(c >> 8);
            pix[5] = (TByte)(c & 0xFF);
            c = count[bit[0]];
            pix[6] = (TByte)(c >> 8);
            pix[7] = (TByte)(c & 0xFF);
#endif
            *(short*)bit = 0;
          }

          bit += 2;
          
          if ( *(short*)bit )
          {
#if defined( FT_RASTER_LITTLE_ENDIAN )
            /* little endian storage */
            *((long*)pix + 1)= (count[bit[1]] << 16) | count[bit[0]];
#elif defined( FT_RASTER_BIG_ENDIAN )
            /* big-endian storage */
            *((long*)pix + 1) = (count[bit[0]] << 16) | count[bit[1]];
#else
            /* endian-independent storage */
            int  c;
            c = count[bit[1]];
            pix[0] = (TByte)(c >> 8);
            pix[1] = (TByte)(c & 0xFF);
            c = count[bit[0]];
            pix[2] = (TByte)(c >> 8);
            pix[3] = (TByte)(c & 0xFF);
#endif
            *(short*)bit = 0;
          }
          pix += 8;
          bit += 2;
          c1 --;
        }
      }
     
      ras.trace_bit  = 0;
      ras.trace_pix += ras.trace_incr;

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



  static void  Horizontal_Gray5_Sweep_Span( RAS_ARGS TScan  y,
                                                     TPos   x1,
                                                     TPos   x2 )
  {
    /* nothing, really */
    UNUSED_RASTER
    (void)y;
    (void)x1;
    (void)x2;
  }


  static
  int   Horizontal_Gray5_Test_Pixel( RAS_ARGS  TScan  y,
                                               int    x )
  {
    /* don't do anything here */
    UNUSED_RASTER
    (void)x;
    (void)y;
    
    return 0;
  }


  static
  void  Horizontal_Gray5_Set_Pixel( RAS_ARGS  TScan  y,
                                              int    x,
                                              int    color )
  {
    char*  pixel;

    x = x/2;
      
    if (x < ras.target.rows)
    {
      long  pitch;
      
      pixel = (char*)ras.pix_buffer + (y/2);
      pitch = ras.target.pitch;
      pixel -= x*pitch;
      if (pitch > 0)
        pixel += (ras.target.rows-1)*pitch;
        
      if (pixel[0] == ras.grays[0])
        pixel[0] = (char)ras.grays[1+color];
    }
  }
#endif /* FT_RASTER_ANTI_ALIAS_5 */
  
#ifdef FT_RASTER_ANTI_ALIAS_17
/***********************************************************************/
/*                                                                     */
/*  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_Gray17_Sweep_Init( RAS_ARGS int*  min, int*  max )
  {
    long  pitch = ras.target.pitch;
    
    *min = *min & -4;
    *max = ( *max+7 ) & -4;

    ras.trace_bit = 0;
    ras.trace_incr = -pitch;
    ras.trace_bit  = - (*min/4)*pitch;
    if (pitch > 0)
      ras.trace_bit += (ras.target.rows-1)*pitch;

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


  static void  Vertical_Gray17_Sweep_Span( RAS_ARGS TScan  y,
                                                    TPos   x1,
                                                    TPos   x2 )
  {
    static
    const unsigned int  LMask[9] =
#ifdef FT_RASTER_LITTLE_ENDIAN
             { 0xF000F000, 0xF0007000, 0xF0003000, 0xF0001000,
               0xF0000000, 0x70000000, 0x30000000, 0x10000000,
               0x00000000 };
#else
             { 0xF000F000, 0x7000F000, 0x3000F000, 0x1000F000,
               0x0000F000, 0x00007000, 0x00003000, 0x00001000,
               0x00000000 };
#endif

    TPos   e1, e2;
    int    c1, c2;
    TByte*  target;

    unsigned int    f1, f2;
    unsigned int    shift, fill;

    /* Drop-out control */

    e1 = TRUNC( CEILING( x1 ) );
    if ( x2-x1 <= ras.precision+ras.precision_jitter )
      e2 = e1;
    else
      e2 = FLOOR ( x2 );
    e2 = TRUNC( e2 );
 
    if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width )
    {
      if ( e1 < 0 )              e1 = 0;
      if ( e2 >= ras.bit_width ) e2 = ras.bit_width-1;

      shift = (y & 3)*4;

      c1 = e1 >> 3;
      c2 = e2 >> 3;

      fill = LMask[0];
      f1   = LMask[ e1 & 7 ];
      f2   = fill ^ LMask[ 1+(e2 & 7) ];

      f1   >>= shift;
      f2   >>= shift;
      fill >>= shift;

      if ( ras.gray_min_x > c1/2 ) ras.gray_min_x = c1/2;
      if ( ras.gray_max_x < c2/2 ) ras.gray_max_x = c2/2;

      target = ras.bit_buffer + c1*4;
      c2 -= c1;

      if (c2 > 0)
      {
        int*  slice = (int*)target;

        slice[0] |= f1;
        c2--;
        while (c2 > 0)
        {
          * ++slice |= fill;
          c2--;
        }
        slice[1] |= f2;
      }
      else
        ((int*)target)[0] |= ( f1 & f2 );
    }
  }


  static 
  int  Vertical_Gray17_Test_Pixel( RAS_ARGS int  y,
                                            int  x )
  {
    int c1    = x >> 2;
    int f1    = x  & 3;
    int mask  = (0x8000 >> f1) >> ((y & 3)*4);

    return ( x >= 0                    && 
             x < ras.bit_width         && 
             ((short*)ras.bit_buffer)[c1] & mask );
  }


  static 
  void  Vertical_Gray17_Set_Pixel( RAS_ARGS  int  y,
                                             int  x,
                                             int  color )
  {
    (void)color;  /* unused here */

    if ( x >= 0 && x < ras.bit_width )
    {
      int c1   = x >> 2;
      int f1   = x  & 3;
      int mask = (0x8000 >> f1) >> ((y & 3)*4);
  
      if ( ras.gray_min_x > c1/4 ) ras.gray_min_x = c1/4;
      if ( ras.gray_max_x < c1/4 ) ras.gray_max_x = c1/4;

      ((short*)ras.bit_buffer)[c1] |= mask;
    }
  }


  static void  Vertical_Gray17_Sweep_Step( RAS_ARG )
  {
    int    c1;
    PByte  pix, bit;
    long*  grays;


    ras.trace_bit++;

    if ( ras.trace_bit > 3 )
    {
      pix   = ras.pix_buffer + ras.trace_pix + ras.gray_min_x*4;
      grays = ras.grays;

      if ( ras.gray_max_x >= 0 )
      {
        if ( ras.gray_max_x >= ras.target.width/4 ) 
          ras.gray_max_x = (ras.target.width+3)/4-1;

        if ( ras.gray_min_x < 0 ) 
          ras.gray_min_x = 0;

        bit = ras.bit_buffer + ras.gray_min_x*8;
        c1  = (ras.gray_max_x - ras.gray_min_x + 1);

        while ( c1 >= 0 )
        {
          if ( *(long*)bit || *(long*)(bit+4) )
          {
            int*  table = ras.count_table;

#if defined( FT_RASTER_LITTLE_ENDIAN )
            /* little-endian specific storage */
            *(long*)pix =   grays[ table[ bit[0] ] +
                                   table[ bit[1] ] ]         |
                                      
                          ( grays[ table[ bit[2] ] +
                                   table[ bit[3] ] ] << 8 )  |
                                   
                          ( grays[ table[ bit[4] ] +
                                   table[ bit[5] ] ] << 16 ) |
                                   
                          ( grays[ table[ bit[6] ] +
                                   table[ bit[7] ] ] << 24 );

#elif defined( FT_RASTER_BIG_ENDIAN )
            /* big-endian specific storage */
            *(long*)pix = ( grays[ table[ bit[0] ] +
                                   table[ bit[1] ] ] << 24    |
                                      
                          ( grays[ table[ bit[2] ] +
                                   table[ bit[3] ] ] << 16 )  |
                                   
                          ( grays[ table[ bit[4] ] +
                                   table[ bit[5] ] ] << 8 ) |
                                   
                          ( grays[ table[ bit[6] ] +
                                   table[ bit[7] ] ]  );
#else
            /* endianess-independent storage */
            pix[0] = grays[ table[bit[0]] + table[bit[1]] ];
            pix[1] = grays[ table[bit[2]] + table[bit[3]] ];
            pix[2] = grays[ table[bit[4]] + table[bit[5]] ];
            pix[3] = grays[ table[bit[6]] + table[bit[7]] ];
#endif
            *(long*)( bit ) = 0;
            *(long*)(bit+4) = 0;
          }
          pix += 4;
          bit += 8;
          c1 --;
        }
      }
      ras.trace_bit   = 0;
      ras.trace_pix  += ras.trace_incr;

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



  static void  Horizontal_Gray17_Sweep_Span( RAS_ARGS TScan  y,
                                                      TPos   x1,
                                                      TPos   x2 )
  {
    /* nothing, really */
    UNUSED_RASTER
    (void)y;
    (void)x1;
    (void)x2;
  }


  static
  int   Horizontal_Gray17_Test_Pixel( RAS_ARGS  TScan  y,
                                                int    x )
  {
    /* don't do anything here */
    UNUSED_RASTER
    (void)x;
    (void)y;
    
    return 0;
  }


  static
  void  Horizontal_Gray17_Set_Pixel( RAS_ARGS  TScan  y,
                                               int    x,
                                               int    color )
  {
    char*  pixel;

    x = x/4;
      
    if (x < ras.target.rows)
    {
      long  pitch = ras.target.pitch;
      
      pixel  = (char*)ras.pix_buffer + (y/4);
      pixel -= x*pitch;
      if (pitch > 0)
        pixel += (ras.target.rows-1)*pitch;
        
      if (pixel[0] == ras.grays[0])
        pixel[0] = ras.grays[1+color];
    }
  }
#endif /* FT_RASTER_ANTI_ALIAS_17 */
  
#endif /* FT_RASTER_OPTION_ANTI_ALIAS */

/********************************************************************/
/*                                                                  */
/*  Generic Sweep Drawing routine                                   */
/*                                                                  */
/********************************************************************/

  static TBool  Draw_Sweep( RAS_ARG )
  {
    TScan  y, y_change, y_height;

    PProfile  P, Q, P_Left, P_Right;

    TScan   min_Y, max_Y, top, bottom, dropouts;

    TPos x1, x2, e1, e2;

    TProfileList  wait;
    TProfileList  draw;

    /* Init empty linked lists */

    Init_Linked( &wait );
    Init_Linked( &draw );

    /* first, compute min and max Y */

    P     = ras.start_prof;
    max_Y = TRUNC( ras.minY );
    min_Y = 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.n_turns == 0 )
    {
      ras.error = ErrRaster_Invalid_Outline;
      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.n_turns > 0 &&
         ras.pool_size[-ras.n_turns] == min_Y )
      ras.n_turns--;

    while (ras.n_turns > 0)
    {
      PProfile  prof = wait;
      
      /* look in the wait list for new activations */
      while (prof)
      {
        PProfile  next = prof->link;
        
        prof->countL -= y_height;
        if ( prof->countL == 0 )
        {
          DelOld( &wait, prof );
          InsNew( &draw, prof );
        }
        prof = next;
      }

      /* Sort the drawing lists */
      Sort( &draw );

      y_change = ras.pool_size[-ras.n_turns--];
      y_height = y_change - y;

      while ( y < y_change )
      {
        int       window;
        PProfile  left;

        /* Let's trace */
  
        dropouts = 0;

        if (!draw)
          goto Next_Line;
          
        left   = draw;
        window = left->flow;
        prof   = left->link;

        while (prof)
        {
          PProfile  next = prof->link;
          
          window += prof->flow;
          
          if ( window == 0 )
          {
            x1 = left->X;
            x2 = prof->X;
#if 1
            if ( x1 > x2 )
            {
              TPos  xs = x1;
              x1 = x2;
              x2 = xs;
            }
#endif
            if ( x2-x1 <= ras.precision && ras.dropout_mode )
            {
#if 1
              e1 = CEILING( x1 );
              e2 = FLOOR( x2 );
#else
              e1 = FLOOR(x1);
              e2 = CEILING(x2);
#endif
              if ( e1 > e2 || e2 == e1+ ras.precision )
              {
                /* a drop out was detected */
   
                left->X = x1;
                prof->X = x2;

                /* mark profiles for drop-out processing */
                left->countL = 1;
                prof->countL = 2;
                dropouts++;
                goto Skip_To_Next;
              }
            }

            ras.Proc_Sweep_Span( RAS_VARS  y, x1, x2 );
   
   Skip_To_Next:
            left = next;
          }       
          prof = next;
        }

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

      /* Now finalize the profiles that needs it */

      {
        PProfile  prof, next;
        prof = draw;
        while (prof)
        {
          next = prof->link;
          if (prof->height == 0)
            DelOld( &draw, prof );
          prof = next;
        }
      }
    }

    /* 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;
    while (dropouts > 0)
    {
      TPos      e1,   e2;
      PProfile  left, right;
    
      while (P_Left->countL != 1) P_Left = P_Left->link;
      P_Right = P_Left->link;
      while (P_Right->countL != 2) P_Right = P_Right->link;
       
      P_Left->countL  = 0;
      P_Right->countL = 0;

      /* Now perform the dropout control */      
      x1 = P_Left->X;
      x2 = P_Right->X;

      left  = ( ras.flipped ? P_Right : P_Left  );
      right = ( ras.flipped ? P_Left  : P_Right );
      
      e1 = CEILING( x1 );
      e2 = FLOOR  ( x2 );

      if ( e1 > e2 )
      {
        if ( e1 == e2 + ras.precision )
        {
          switch ( ras.dropout_mode )
          {
          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                          */
          /*                                                        */

            /* upper stub test */
            if ( ( left->next == right && left->height <= 0 ) ||

            /* lower stub test */
                 ( right->next == left && left->start == y )  ||

            /* check that the rightmost pixel isn't set */
                 ras.Proc_Test_Pixel( RAS_VARS  y, TRUNC(e1))       )
              goto Next_Dropout;

            if ( ras.dropout_mode == 2 )
              e1 = e2;
            else
              e1 = CEILING( (x1+x2+1)/2 );
                    
            break;

          default:
            goto Next_Dropout;  /* Unsupported mode */
          }
        }
        else
          goto Next_Dropout;
      }

      ras.Proc_Set_Pixel( RAS_VARS y, 
                                   TRUNC(e1), 
                                   (x2-x1 >= ras.precision_half) & 1 );
    Next_Dropout:
    
      dropouts--;
    }
    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 
  int  Render_Single_Pass( RAS_ARGS  int  flipped )
  {
    int  i, j, k;

    ras.flipped = flipped;

    while ( ras.band_top >= 0 )
    {
#if 1
      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;
#else    
      ras.maxY = ((long)ras.band_stack[ras.band_top].y_max <<
                    (ras.scale_shift+6))-1;

      ras.minY = (long)ras.band_stack[ras.band_top].y_min <<
                    (ras.scale_shift+6);
#endif
      ras.cursor = ras.pool;
      ras.error  = 0;

      if ( Convert_Glyph( RAS_VARS  ras.outline ) )
      {
        if ( ras.error != ErrRaster_Overflow ) return FAILURE;
        ras.error = ErrRaster_Ok;

        /* 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 = ( j-i ) >> 1;

        if ( ras.band_top >= 7 || k == 0 )
        {
          ras.band_top     = 0;
          ras.error = ErrRaster_Invalid_Outline;
          return ras.error;
        }

        ras.band_stack[ras.band_top+1].y_min = i + k;
        ras.band_stack[ras.band_top+1].y_max = j;

        ras.band_stack[ras.band_top].y_max = i + k;

        ras.band_top++;
      }
      else
      {
        if ( ras.start_prof )
          if ( Draw_Sweep( RAS_VAR ) ) return ras.error;
        ras.band_top--;
      }
    }

    return 0;  /* success */
  }


#if 0
  void  Compute_BBox( FT_Outline*  outline,
                      FT_BBox*     bbox )
  {
    int         n;
    FT_Vector*  vec;
    
    vec   = outline->points;
    bbox->xMin = bbox->xMax = vec->x;
    bbox->yMin = bbox->yMax = vec->y;

    n = outline->n_points-1;
    while ( n > 0 )
    {
      FT_Pos  x, y;
      
      vec++;     
       
      x = vec->x;
      if ( bbox->xMin > x ) bbox->xMin = x;
      if ( bbox->xMax < x ) bbox->xMax = x;
      
      y = vec->y;
      if ( bbox->yMin > y ) bbox->yMin = y;
      if ( bbox->yMax < y ) bbox->yMax = y;
      
      n--;
    }
  }
#endif

  static
  int  Raster_Render1( FT_Raster       raster )
  {
    int  error;
    long byte_len;

    byte_len = ras.target.pitch;
    if (byte_len < 0) byte_len = -byte_len;
    
    if ( ras.target.width > byte_len*8 )
      return ErrRaster_Invalid_Map;
    
    ras.scale_shift  = (ras.precision_bits-6);

    /* Vertical Sweep */
    ras.band_top            = 0;
    ras.band_stack[0].y_min = 0;
    ras.band_stack[0].y_max = ras.target.rows - 1;

    ras.Proc_Sweep_Init = Vertical_Sweep_Init;
    ras.Proc_Sweep_Span = Vertical_Sweep_Span;
    ras.Proc_Sweep_Step = Vertical_Sweep_Step;
    ras.Proc_Test_Pixel = Vertical_Test_Pixel;
    ras.Proc_Set_Pixel  = Vertical_Set_Pixel;

    ras.bit_width  = ras.target.width;
    ras.bit_buffer = (unsigned char*)ras.target.buffer;

    if ( (error = Render_Single_Pass( RAS_VARS 0 )) != 0 )
      return error;

    /* Horizontal Sweep */

    if ( ras.second_pass && ras.dropout_mode != 0 )
    {
      ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
      ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
      ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
      ras.Proc_Test_Pixel = Horizontal_Test_Pixel;
      ras.Proc_Set_Pixel  = Horizontal_Set_Pixel;

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



#ifdef FT_RASTER_OPTION_ANTI_ALIAS
  static
  int  Raster_Render8( FT_Raster       raster )
  {
    int  error;
    long byte_len;
    
    byte_len = ras.target.pitch;
    if (byte_len < 0) byte_len = -byte_len;
    
    if ( ras.target.width > byte_len )
      return ErrRaster_Invalid_Map;

    /* Vertical Sweep */
    ras.band_top            = 0;
    ras.band_stack[0].y_min = 0;
    ras.band_stack[0].y_max = ras.target.rows;

#if !defined(FT_RASTER_ANTI_ALIAS_5) && !defined(FT_RASTER_ANTI_ALIAS_17)
    return ErrRaster_Unimplemented;
#endif

#ifdef FT_RASTER_ANTI_ALIAS_5
    if ( ras.grays_count == 5 )
    {
      ras.scale_shift  = (ras.precision_bits-5);
      ras.bit_width    = ras.gray_width/2;
      if ( ras.bit_width > (ras.target.width+3)/4 )
        ras.bit_width = (ras.target.width+3)/4;
    
      ras.bit_width  = ras.bit_width * 8;
      ras.bit_buffer = (unsigned char*)ras.gray_lines;
      ras.pix_buffer = (unsigned char*)ras.target.buffer;
    
      ras.Proc_Sweep_Init = Vertical_Gray5_Sweep_Init;
      ras.Proc_Sweep_Span = Vertical_Gray5_Sweep_Span;
      ras.Proc_Sweep_Step = Vertical_Gray5_Sweep_Step;
      ras.Proc_Test_Pixel = Vertical_Gray5_Test_Pixel;
      ras.Proc_Set_Pixel  = Vertical_Gray5_Set_Pixel;
    }
#endif

#ifdef FT_RASTER_ANTI_ALIAS_17
    if ( ras.grays_count == 17 )
    {
      ras.scale_shift  = (ras.precision_bits-4);
      ras.bit_width    = ras.gray_width/4;
      if ( ras.bit_width > (ras.target.width+1)/2 )
        ras.bit_width = (ras.target.width+1)/2;
    
      ras.bit_width  = ras.bit_width * 8;
      ras.bit_buffer = (unsigned char*)ras.gray_lines;
      ras.pix_buffer = (unsigned char*)ras.target.buffer;
    
      ras.Proc_Sweep_Init = Vertical_Gray17_Sweep_Init;
      ras.Proc_Sweep_Span = Vertical_Gray17_Sweep_Span;
      ras.Proc_Sweep_Step = Vertical_Gray17_Sweep_Step;
      ras.Proc_Test_Pixel = Vertical_Gray17_Test_Pixel;
      ras.Proc_Set_Pixel  = Vertical_Gray17_Set_Pixel;
    }
#endif

    error = Render_Single_Pass( RAS_VARS  0 );
    if (error)
      return error;

    /* Horizontal Sweep */

    if ( ras.second_pass && ras.dropout_mode != 0 )
    {
#ifdef FT_RASTER_ANTI_ALIAS_5
      if ( ras.grays_count == 5 )
      {
        ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
        ras.Proc_Sweep_Span = Horizontal_Gray5_Sweep_Span;
        ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
        ras.Proc_Test_Pixel = Horizontal_Gray5_Test_Pixel;
        ras.Proc_Set_Pixel  = Horizontal_Gray5_Set_Pixel;
      }
#endif
      
#ifdef FT_RASTER_ANTI_ALIAS_17
      if ( ras.grays_count == 17 )
      {
        ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
        ras.Proc_Sweep_Span = Horizontal_Gray17_Sweep_Span;
        ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
        ras.Proc_Test_Pixel = Horizontal_Gray17_Test_Pixel;
        ras.Proc_Set_Pixel  = Horizontal_Gray17_Set_Pixel;
      }
#endif
      ras.band_top            = 0;
      ras.band_stack[0].y_min = 0;
      ras.band_stack[0].y_max = ras.target.width-1;

      error = Render_Single_Pass( RAS_VARS  1 );
      if (error)
        return error;
    }

    return ErrRaster_Ok;
  }

#else  /* ANTI_ALIAS */

  static
  int  Raster_Render8( FT_Raster       raster )
  {
    return ErrRaster_Unimplemented;
  }

#endif /* FT_RASTER_OPTION_ANTI_ALIAS */



#ifdef FT_RASTER_OPTION_ANTI_ALIAS
/****************************************************************************/
/*                                                                          */
/* <Function>   Reset_Palette_5                                             */
/*                                                                          */
/* <Description> Resets lookup table when the 5-gray-levels palette changes */
/*                                                                          */
/****************************************************************************/

static
void  Reset_Palette_5( RAS_ARG )
{
  int  i;

  for ( i = 0; i < 256; i++ )
  {
    int  cnt1, cnt2;
    
    cnt1 = ((i & 128) >> 7) +
           ((i & 64)  >> 6) +
           ((i & 8)   >> 3) +
           ((i & 4)   >> 2);
           
    cnt2 = ((i & 32) >> 5) +
           ((i & 16) >> 4) +
           ((i & 2)  >> 1) +
            (i & 1);

  /*                                                                 */
  /* Note that when the endianess isn't specified through one of the */
  /* configuration, we use the big-endian storage in 'count_table'   */
  /*                                                                 */

#if defined( FT_RASTER_LITTLE_ENDIAN )
    ras.count_table[i] = (ras.grays[cnt2] << 8) | ras.grays[cnt1];
#else
    ras.count_table[i] = (ras.grays[cnt1] << 8) | ras.grays[cnt2];
#endif
  }
}

/****************************************************************************/
/*                                                                          */
/* <Function>    Reset_Palette_17                                           */
/*                                                                          */
/* <Description> Resets lookup table when 17-gray-levels palette changes    */
/*                                                                          */
/****************************************************************************/

#ifdef FT_RASTER_ANTI_ALIAS_17
static
void  Reset_Palette_17( RAS_ARG )
{
  int  i;

  for ( i = 0; i < 256; i++ )
    ras.count_table[i] = 
           ((i & 128) >> 7) +
           ((i & 64)  >> 6) +
           ((i & 8)   >> 3) +
           ((i & 4)   >> 2) +
           ((i & 32)  >> 5) +
           ((i & 16)  >> 4) +
           ((i & 2)   >> 1) +
            (i & 1);
}
#endif /* ANTI_ALIAS_17 */

#endif /* TT_RASTER_OPTION_ANTI_ALIAS */



  /**********************************************************************/
  /*                                                                    */
  /* <Function> FT_Raster_SetPalette                                    */
  /*                                                                    */
  /* <Description>                                                      */
  /*     Set the pixmap rendering palette. anti-aliasing modes are      */
  /*     implemented/possible, they differ from the number of           */
  /*     entries in the palette.                                        */
  /*                                                                    */
  /* <Input>                                                            */
  /*     count   :: the number of palette entries. Valid values are     */
  /*                2, 5 and 17, which are the number of intermediate   */
  /*                gray levels supported                               */
  /*                                                                    */
  /*     palette :: an array of 'count' chars giving the 8-bit palette  */
  /*                of intermediate "gray" levels for anti-aliased      */
  /*                rendering.                                          */
  /*                                                                    */
  /*         In all modes, palette[0] corresponds to the background,    */
  /*         while palette[count-1] to the foreground. Hence, a count   */
  /*         of 2 corresponds to no anti-aliasing; a count of 5 uses    */
  /*         3 intermediate levels between the background and           */
  /*         foreground, while a count of 17 uses 15 of them..          */
  /*                                                                    */
  /* <Return>                                                           */
  /*     An error code, used as a FT_Error by the FreeType library.     */
  /*                                                                    */
  /* <Note>                                                             */
  /*     By default, a new object uses mode 5, with a palette of        */
  /*     0,1,2,3 and 4. You don't need to set the palette if you        */
  /*     don't need to render pixmaps..                                 */
  /*                                                                    */
  /**********************************************************************/

  EXPORT_FUNC
  int   FT_Raster_SetPalette( FT_Raster    raster,
                              int          count,
                              const char*  palette )
  {
    switch (count)
    {
#ifdef FT_RASTER_OPTION_ANTI_ALIAS

      /******************************/
      /* The case of 17 gray levels */
      /******************************/
    
      case 17:
#ifdef FT_RASTER_ANTI_ALIAS_17
      {
        int  n;
        
        raster->grays_count = count;
        for ( n = 0; n < count; n++ )
          raster->grays[n] = (unsigned char)palette[n];
        Reset_Palette_17( RAS_VAR );
        break;
      }
#else
      return ErrRaster_Unimplemented;
#endif

      /*****************************/
      /* The case of 5 gray levels */
      /*****************************/

      case 5:      
#ifdef FT_RASTER_ANTI_ALIAS_5
      {
        int  n;
        
        raster->grays_count = count;
        for ( n = 0; n < count; n++ )
          raster->grays[n] = (unsigned char)palette[n];
        Reset_Palette_5( RAS_VAR );
        break;
      }
#else
      return ErrRaster_Unimplemented;
#endif

#endif /* FT_RASTER_OPTION_ANTI_ALIAS */
      default:
        return ErrRaster_Bad_Palette_Count;
    }

    return ErrRaster_Ok;
  }  


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

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

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

#else

#include <ftobjs.h>

  static
  int  ft_black2_new( FT_Memory  memory, FT_Raster  *araster )
  {
    FT_Error   error;
    FT_Raster  raster;
    
    *araster = 0;
    if ( !ALLOC( raster, sizeof(*raster) ))
    {
      raster->memory = memory;
      *araster = raster;
    }
      
    return error;
  }
  
  static
  void ft_black2_done( FT_Raster  raster )
  {
    FT_Memory  memory = (FT_Memory)raster->memory;
    FREE( raster );
  }
  
#endif


  static void ft_black2_reset( FT_Raster         raster,
                               const char*       pool_base,
                               long              pool_size )
  {
    static const char  default_palette[5] = { 0, 1, 2, 3, 4 };
  
    /* check the object address */
    if ( !raster )
      return;
      
    /* check the render pool - we won't go under 4 Kb */
    if ( !pool_base || pool_size < 4096 )
      return;
    
    /* save the pool */
    raster->pool      = (PPos)pool_base;
    raster->pool_size = (PPos)(pool_base + (pool_size & -8));

#ifdef FT_RASTER_OPTION_ANTI_ALIAS
    raster->gray_width = ANTI_ALIAS_BUFFER_SIZE/2;
    /* clear anti-alias intermediate lines */
    {
      char*  p    = raster->gray_lines;
      int    size = ANTI_ALIAS_BUFFER_SIZE;
      do
      {
        *p++ = 0;
        size--;

      } while (size > 0);
    }
#endif
    
    /* set the default palette : 5 levels =  0, 1, 2, 3 and 4 */
    FT_Raster_SetPalette( raster, 5, default_palette );
  }


  static
  int  ft_black2_render( FT_Raster          raster,
                         FT_Raster_Params*  params )
  {
    FT_Outline*  outline    = (FT_Outline*)params->source;
    FT_Bitmap*   target_map = params->target;
    
    if ( !raster || !raster->pool || !raster->pool_size )
      return ErrRaster_Uninitialised_Object;

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

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

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

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

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

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

    ras.dropout_mode = 2;
    ras.second_pass  = !(outline->flags & ft_outline_single_pass);
    SET_High_Precision( (outline->flags & ft_outline_high_precision) );

    return ( params->flags & ft_raster_flag_aa
           ? Raster_Render8(raster)
           : Raster_Render1(raster) );
  }


  FT_Raster_Funcs      ft_black2_raster =
  {
    ft_glyph_format_outline,
    (FT_Raster_New_Func)       ft_black2_new,
    (FT_Raster_Reset_Func)     ft_black2_reset,
    (FT_Raster_Set_Mode_Func)  0,
    (FT_Raster_Render_Func)    ft_black2_render,
    (FT_Raster_Done_Func)      ft_black2_done
  };