shithub: freetype+ttf2subf

ref: aa50b821c883b597a0be9ce0e47c98756be3be9b
dir: /src/base/ftraster.c/

View raw version
/***************************************************************************/
/*                                                                         */
/*  ftraster2.c                                                            */
/*                                                                         */
/*    The FreeType glyph rasterizer (body).                                */
/*                                                                         */
/*  Copyright 1996-1999 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 outline       */
  /* 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 contains the following 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      */
  /*   a series of segments and arcs).                                     */
  /*                                                                       */
  /* Planned additions:                                                    */
  /*                                                                       */
  /* - Getting rid of the second pass for horizontal drop-out detection.   */
  /*   I've got a few ideas, but I'll have to experiment in Pascal with    */
  /*   them.  to avoid damaging of the rendering of glyphs at small sizes. */
  /*                                                                       */
  /* - Adding a `composition' callback, which should be invoked during     */
  /*   anti-aliased rendering.  In short, it will allow line-by-line       */
  /*   composition (i.e., transparencies, etc.) of the output in a fairly  */
  /*   portable way.  Of course, a single sweep is required there.         */
  /*                                                                       */
  /*************************************************************************/


#include <ftimage.h>
#ifndef _STANDALONE_
#include <ftconfig.h>
#endif

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

#undef  FT_COMPONENT
#define FT_COMPONENT  trace_raster

#ifdef _STANDALONE_

  /*************************************************************************/
  /*                                                                       */
  /* 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_INT_IS_32                                                   */
  /*                                                                       */
  /*   Set this configuration macro to the unsigned type which has 32      */
  /*   bits.                                                               */
  /*                                                                       */
#define FT_RASTER_INT_IS_32


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

#include <freetype.h>
#include <ftconfig.h>

  /*************************************************************************/
  /*                                                                       */
  /* 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
/* #define  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 /* _STANDALONE_ */


/* to keep the compiler happy */
#ifndef PTRACE2
#define PTRACE2(x)  /*void*/
#endif

  /*************************************************************************/
  /*                                                                       */
  /* FT_RASTER_ANY_ENDIAN indicates that no endianess was defined by 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 here (you never know your target              */
  /* environment).                                                         */
  /*                                                                       */
  /*************************************************************************/

#ifndef TRUE
#define TRUE   1
#endif

#ifndef FALSE
#define FALSE  0
#endif

#ifndef NULL
#define NULL  (void*)0
#endif


#ifndef UNUSED
#define UNUSED( arg )  ( (void)(arg) )
#endif


#undef  FAILURE
#define FAILURE  TRUE

#undef  SUCCESS
#define SUCCESS  FALSE

#ifndef ABS
#define ABS(x)  ( (x) < 0 ? -(x) : (x) )
#endif

  /*************************************************************************/
  /*                                                                       */
  /* 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_ARG_  FT_Raster  raster,
#define RAS_ARG   FT_Raster  raster

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

  /* used to access the current raster object, with a `.' instead of a */
  /* `->'                                                              */
#define ras       (*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_Uninitialized_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 Flow_Up     1
#define Flow_Down  -1

#define SET_High_Precision( p )  Set_High_Precision( RAS_VAR_  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 */
  /* will be shown 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


  /*************************************************************************/
  /*                                                                       */
  /* The maximum number of stacked Bezier curves.  Setting this constant   */
  /* to more than 32 is a pure waste of space.                             */
  /*                                                                       */
#define MaxBezier  32


  /*************************************************************************/
  /*                                                                       */
  /* The number fractional bits of *input* coordinates.  We always use the */
  /* 26.6 format (i.e, 6 bits for the fractional part), but hackers are    */
  /* free to experiment with different values.                             */
  /*                                                                       */
#define Pixel_Bits  6


  /*************************************************************************/
  /*                                                                       */
  /* An unsigned type that is exactly 32 bits on your platform.  This      */
  /* means `unsigned long' on 16-bit machines, and `unsigned int' on       */
  /* others.                                                               */
  /*                                                                       */
#ifdef _STANDALONE_
#if defined( FT_RASTER_INT_IS_32 )
  typedef unsigned int   FT_Word32;
#elif defined( FT_RASTER_LONG_IS_32 )
  typedef unsigned long  FT_Word32;
#else
#error "no 32bit type found - please check your configuration"
#endif
#endif


  /*************************************************************************/
  /*                                                                       */
  /* A pointer to an unsigned char.                                        */
  /*                                                                       */
  typedef unsigned char*  PByte;

  typedef char  TResult;

  typedef unsigned char   Byte;

  /*************************************************************************/
  /*                                                                       */
  /* 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       */
  /* (i.e., low resp. high precision).  These are ideals in order to       */
  /* subdivise Bezier arcs in halves by simple additions and shifts.       */
  /*                                                                       */
  /* Note that this is an 8-bytes integer on 64 bits systems.              */
  /*                                                                       */
  typedef long  TPos, *PPos;


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


  /*************************************************************************/
  /*                                                                       */
  /* States and directions of each line, arc, and profile.                 */
  /*                                                                       */
  typedef enum  _TDirection
  {
    Unknown,
    Ascending,
    Descending,
    Flat

  } TDirection;


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


  /*************************************************************************/
  /*                                                                       */
  /* The `master' structure used for decomposing outlines.                 */
  /*                                                                       */
  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;


  /*************************************************************************/
  /*                                                                       */
  /* A simple record used to implement a stack of bands, required by the   */
  /* sub-banding mechanism.                                                */
  /*                                                                       */
  typedef struct  _TBand
  {
    TScan  y_min;   /* band's minimum */
    TScan  y_max;   /* band's maximum */

  } 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_ARG_ int*  min,
                                                 int*  max );

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

  typedef int   (*Function_Test_Pixel)( RAS_ARG_ TScan  y,
                                                 int    x );

  typedef void  (*Function_Set_Pixel)( RAS_ARG_  TScan  y,
                                                 int    x,
                                                 int    color );

  typedef void  (*Function_Sweep_Step)( RAS_ARG );

  typedef struct Raster_Render_
  {
    Function_Sweep_Init  init;
    Function_Sweep_Span  span;
    Function_Sweep_Step  step;
    Function_Test_Pixel  test_pixel;
    Function_Set_Pixel   set_pixel;

  } Raster_Render;


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


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


  /*************************************************************************/
  /*                                                                       */
  /* Get integer coordinate of a given 26.6 or 22.10 `x' coordinate -- no  */
  /* rounding.                                                             */
  /*                                                                       */
#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.scale_delta )


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


  /*************************************************************************/
  /*                                                                       */
  /* This structure defines a point in a plane.                            */
  /*                                                                       */
  typedef struct  _TPoint
  {
    TPos  x, y;

  } TPoint;


  /*************************************************************************/
  /*                                                                       */
  /* The most used variables are at the beginning of the structure.  Thus, */
  /* their offset can be coded with less opcodes which 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_extrema;            /* number of `extrema' scanlines       */

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

    int       num_profs;            /* current number of profiles */

    char      fresh;                /* signals a fresh new profile which */
                                    /* `start' field must be completed   */
    char      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 */

    Raster_Render     render;

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

    int       scale_delta;      /* ras.precision_half for bitmaps */
                                /* 0 for pixmaps                  */

    char      dropout_mode;     /* current drop_out control method */

    char      second_pass;      /* indicates whether 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.                  */

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

#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 monochrome       */
                                /* 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 several gray 2x2 cells   */
                                   /* at once.                             */

#endif /* FT_RASTER_OPTION_ANTI_ALIAS */
  };



#ifdef DEBUG_RASTER

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Pset                                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    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     */
  /*    from its original position).                                       */
  /*                                                                       */
  /*    This `feature' 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.                  */
  /*                                                                       */
  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_ARG_ TScan  y1,
                             TScan  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 the parameter flag.          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    High :: Set to True for high precision (typically for ppem < 18),  */
  /*    false otherwise.                                                   */
  /*                                                                       */
  static
  void  Set_High_Precision( RAS_ARG_  char  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;
  }

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


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    New_Profile                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Creates a new Profile in the render pool.                          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    aState :: The state/orientation of the new profile.                */
  /*                                                                       */
  /* <Return>                                                              */
  /*    SUCCESS or FAILURE.                                                */
  /*                                                                       */
  static
  TResult  New_Profile( RAS_ARG_ TDirection  direction )
  {
    if ( ras.start_prof == NULL )
    {
      ras.cur_prof   = (PProfile)ras.cursor; /* current profile          */
      ras.start_prof = ras.cur_prof;         /* first profile in pool    */
      ras.cursor    += AlignProfileSize;     /* record profile in buffer */
    }

    /* check for overflow */
    if ( ras.cursor >= ras.pool_limit )
    {
      ras.error = ErrRaster_Overflow;
      return FAILURE;
    }

    /* record profile direction */
    switch ( direction )
    {
    case Ascending:
      ras.cur_prof->flow = Flow_Up;
      break;

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

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

    /* initialize a few fields */
    {
      PProfile  cur = ras.cur_prof;


      cur->start  = 0;            /* current start scanline          */
      cur->height = 0;            /* current height                  */
      cur->offset = ras.cursor;   /* address of first coordinate     */
      cur->link   = (PProfile)0;  /* link to next profile in pool    */
      cur->next   = (PProfile)0;  /* link to next profile in contour */
    }

    /* record the first profile in a contour */
    if ( ras.first_prof == NULL )
      ras.first_prof = ras.cur_prof;

    ras.state  = direction;
    ras.fresh  = TRUE;       /* this profile has no coordinates yet */
    ras.joint  = FALSE;

    return SUCCESS;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    End_Profile                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Finalizes the current Profile and computes its height.  If it is   */
  /*    not 0, the profile's fields are updated and a new profile is       */
  /*    pushed on top of its coordinates.  Otherwise the current profile   */
  /*    is kept and the recording of intersections is restarted.           */
  /*                                                                       */
  /* <Return>                                                              */
  /*    SUCCESS or FAILURE.                                                */
  /*                                                                       */
  static
  TResult  End_Profile( RAS_ARG )
  {
    int  h;


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

    if ( h < 0 )
    {
      /* This error should _never_ occur unless the raster is buggy */
      ras.error = ErrRaster_Negative_Height;
      return FAILURE;
    }

    if ( h > 0 )
    {
      PProfile  old, new;

      /* record scanline height in current profile, create a new one */
      /* and set a link from the old one to it                       */
      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++;
    }

    /* check for overflow */
    if ( ras.cursor >= ras.pool_limit )
    {
      ras.error = ErrRaster_Overflow;
      return FAILURE;
    }

    ras.joint = FALSE;

    return SUCCESS;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Insert_Extrema                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Records that a given scanline contains at least one local          */
  /*    extremum.  The table of extrema is placed at the end of the render */
  /*    pool and grows downwards.  It is used during the sweep phase.      */
  /*                                                                       */
  /* <Input>                                                               */
  /*     y :: The coordinate of the scanline containing an extremum.       */
  /*                                                                       */
  static
  TResult  Insert_Extrema( RAS_ARG_ TScan  y )
  {
    PPos   extrema;
    TScan  y2;
    int    n;


    PTRACE2(( "EXTREMA += %d", y ));
    n       = ras.n_extrema - 1;
    extrema = ras.pool_size - ras.n_extrema;

    /* look for first y extremum that is <= */
    while ( n >= 0 && y < extrema[n] )
      n--;

    /* if it is <, simply insert it, ignore if == */
    if ( n >= 0 && y > extrema[n] )
      while ( n >= 0 )
      {
        y2 = extrema[n];
        extrema[n] = y;
        y = y2;
        n--;
      }

    if ( n < 0 )
    {
      ras.pool_limit--;
      ras.n_extrema++;
      ras.pool_size[-ras.n_extrema] = 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.  Called when the outline   */
  /*    parsing is done.                                                   */
  /*                                                                       */
  /* <Return>                                                              */
  /*    SUCCESS or FAILURE.                                                */
  /*                                                                       */
  static
  TResult  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 Flow_Down:
            PTRACE2(( "FLOW DOWN (start = %d, height = %d)",
                      p->start, p->height ));
            bottom     = p->start - p->height+1;
            top        = p->start;
            p->start   = bottom;
            p->offset += p->height-1;
            break;

          case Flow_Up:
          default:
            PTRACE2(( "FLOW UP (start = %d, height = %d)",
                      p->start, p->height ));
            bottom = p->start;
            top    = p->start + p->height-1;
        }

        if ( Insert_Extrema( RAS_VAR_  bottom ) ||
             Insert_Extrema( RAS_VAR_  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   :: The start x coordinate.                                    */
  /*    y1   :: The start y coordinate.                                    */
  /*    x2   :: The end x coordinate.                                      */
  /*    y2   :: The end y coordinate.                                      */
  /*    miny :: The minimum vertical grid coordinate.                      */
  /*    maxy :: The maximum vertical grid coordinate.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    SUCCESS or FAILURE.                                                */
  /*                                                                       */
  static
  TResult  Line_Up( RAS_ARG_ 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;

    /* clip to higher scanline when necessary */
    if ( y2 > maxy )
    {
      /* x2 += FMulDiv( Dx, maxy-y2, Dy );  UNNECESSARY */
      e2  = TRUNC( maxy );
      f2  = 0;
    }
    else
    {
      e2 = TRUNC( y2 );
      f2 = FRAC( y2 );
    }

    /* clip to lower scanline when necessary */
    if ( y1 < miny )
    {
      TPos  x, y;

      /* we use a binary search to compute the lower
      // clipping intersection. That's because we don't
      // want to use an external function like FT_MulDiv
      // to compute it directly.
      */
      if ( y2 == miny ) goto Exit;
      do
      {
        x = (x1 + x2) >> 1;
        y = (y1 + y2) >> 1;

        if (y <= miny)
        {
          x1 = x;
          y1 = y;
        }
        else
        {
          x2 = x;
          y2 = y;
        }
      }
      while ( y1 < miny );

      e1  = TRUNC( miny );
      f1  = 0;
    }
    else
    {
      e1 = TRUNC( y1 );
      f1 = FRAC( y1 );
    }

    /* adjust start point so that we begin on an integer scanline position */
    if ( f1 > 0 )
    {
      if ( e1 == e2 ) goto Exit;
      else
      {
        x1 += FMulDiv( Dx, ras.precision - f1, Dy );
        e1 += 1;
      }
    }
    else
      if ( ras.joint )
      {
        ras.cursor--;
        ras.joint = FALSE;
      }

    ras.joint = ( f2 == 0 );

    /* if this is a `fresh' profile, record its starting scanline */
    if ( ras.fresh )
    {
      ras.cur_prof->start = e1;
      ras.fresh           = FALSE;
    }

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

    /* compute decision variables and push the intersections on top */
    /* of the render pool                                           */
    Dx <<= ras.precision_bits;
    Ix   = Dx / Dy;
    Rx   = Dx % Dy;
    if (Rx < 0)
    {
      Ix --;
      Rx += Dy;
    }
    
    Ax   = -Dy;
    Rx <<= 1;
    Dy <<= 1;

    top = ras.cursor;

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

      DEBUG_PSET;

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

    ras.cursor = top;
  Exit:
    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   :: The start x coordinate.                                    */
  /*    y1   :: The start y coordinate.                                    */
  /*    x2   :: The end x coordinate.                                      */
  /*    y2   :: The end y coordinate.                                      */
  /*    miny :: The minimum vertical grid coordinate.                      */
  /*    maxy :: The maximum vertical grid coordinate.                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    SUCCESS or FAILURE.                                                */
  /*                                                                       */
  static
  TResult  Line_Down( RAS_ARG_ TPos  x1,   TPos  y1,
                               TPos  x2,   TPos  y2,
                               TPos  miny, TPos  maxy )
  {
    TResult  result, fresh;


    /* simply invert the coordinates and call Line_Up */
    fresh  = ras.fresh;
    result = Line_Up( RAS_VAR_ x1, -y1, x2, -y2, -maxy, -miny );

    /* if this was a fresh profile, invert the recorded start position */
    if ( fresh && !ras.fresh )
      ras.cur_prof->start = -ras.cur_prof->start;

    return result;
  }




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

  /*************************************************************************/
  /*                                                                       */
  /* <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
  TResult  Bezier_Up( RAS_ARG_ int        degree,
                               TSplitter  splitter,
                               TPos       miny,
                               TPos       maxy )
  {
    TPos  y1, y2, e, e2, e0;
    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 = FLOOR(maxy);

    e0 = CEILING(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;
    }

    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 */
        
        if ( y2 - y1 >= ras.precision_step )
        {
          /* 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
  TResult  Bezier_Down( RAS_ARG_ int        degree,
                                 TSplitter  splitter,
                                 TPos       miny,
                                 TPos       maxy )
  {
    TPoint*  arc = ras.arc;
    TResult  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_VAR_ degree, splitter, -maxy, -miny );

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

    arc[0].y = -arc[0].y;
    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;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Push_Conic                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Clears the Bezier stack and pushes a new arc on top of it.         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    p2 :: A pointer to the second (control) point.                     */
  /*    p3 :: A pointer to the third (end) point.                          */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The first point is taken as `raster->last', so it doesn't appear   */
  /*    in the signature.                                                  */
  /*                                                                       */
  static
  void  Push_Conic( RAS_ARG_ FT_Vector*  p2,
                             FT_Vector*  p3 )
  {
#undef  STORE
#define STORE( _arc, point )                    \
          {                                     \
            TPos  x = SCALED( point->x );       \
            TPos  y = SCALED( 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
  }

#endif /* FT_RASTER_CONIC_BEZIERS */



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


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Push_Cubic                                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Clears the Bezier stack and pushes a new third-order Bezier arc on */
  /*    top of it.                                                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    p2 :: A pointer to the second (control) point.                     */
  /*    p3 :: A pointer to the third (control) point.                      */
  /*    p4 :: A pointer to the fourth (end) point.                         */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The first point is taken as `raster->last', so it doesn't appear   */
  /*    in the signature.                                                  */
  /*                                                                       */
  /*    This is the same as Push_Conic(), except that it deals with        */
  /*    third-order Beziers.                                               */
  /*                                                                       */
  static
  void  Push_Cubic( RAS_ARG_ FT_Vector*  p2,
                             FT_Vector*  p3,
                             FT_Vector*  p4 )
  {
#undef  STORE
#define STORE( _arc, point )                    \
          {                                     \
            TPos  x = SCALED( point->x );       \
            TPos  y = SCALED( 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
  }

#endif /* FT_RASTER_CUBIC_BEZIERS */


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Check_Contour                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Performs some checks at contour closure.                           */
  /*                                                                       */
  /* <Return>                                                              */
  /*    SUCCESS or FAILURE.                                                */
  /*                                                                       */
  static
  TResult  Check_Contour( RAS_ARG )
  {
    PProfile  lastProfile;


    /* Sometimes, the first and last profile in a contour join on      */
    /* an integer scan-line; we must then remove the last intersection */
    /* from the last profile to get rid of doublets                    */
    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     :: A 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 )
  {
    TPos  scaled_x, scaled_y;

    
    /* 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' */
    scaled_x = SCALED( to->x );
    scaled_y = SCALED( to->y );
    
    if ( ras.flipped )
    {
      ras.last.x = scaled_y;
      ras.last.y = scaled_x;
    }
    else
    {
      ras.last.x = scaled_x;
      ras.last.y = scaled_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     :: A 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, scaled_x;
    TPos  y, scaled_y;


    scaled_x = SCALED( to->x );
    scaled_y = SCALED( to->y );

    if ( ras.flipped )
    {
      x = scaled_y;
      y = scaled_x;
    }
    else
    {
      x = scaled_x;
      y = scaled_y;
    }

    /* First, detect a change of direction */
    if ( y != ras.last.y )
    {
      TDirection  new_state = ( (y > ras.last.y) ? Ascending : Descending );


      if ( ras.state != new_state )
      {      
        if ( ras.state != Unknown   &&
             End_Profile( RAS_VAR ) )
          goto Fail;

        if ( New_Profile( RAS_VAR_  new_state ) )
          goto Fail;
      }
    }        

    /* Then compute the lines */
    switch ( ras.state )
    {
    case Ascending:
      if ( Line_Up ( RAS_VAR_  ras.last.x, ras.last.y,
                               x, y, ras.minY, ras.maxY ) )
        goto Fail;
      break;

    case Descending:
      if ( Line_Down( RAS_VAR_ ras.last.x, ras.last.y,
                               x, y, ras.minY, ras.maxY ) )
        goto Fail;
      break;

    default:
      ;
    }

    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 :: A pointer to an intermediate control point.             */
  /*    to      :: A pointer to the end point.                             */
  /*    raster  :: A handle to the current raster object.                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0 means success.                                      */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function is used as a `FTRasterConicTo_Func' by the outline   */
  /*    decomposer.                                                        */
  /*                                                                       */
  static
  int  Conic_To( FT_Vector*  control,
                 FT_Vector*  to,
                 FT_Raster   raster )
  {
    TPos        y1, y2, y3, x3, ymin, ymax;
    TDirection  state_bez;


    Push_Conic( RAS_VAR_  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 )
      {
        ymin = y1;
        ymax = y3;
      }
      else
      {
        ymin = y3;
        ymax = y1;
      }
      
      if ( y2 < ymin || y2 > ymax )
      {
        /* this arc has no given direction, split it !! */
        Split_Conic( ras.arc );
        ras.arc += 2;
      }
      else if ( y1 == y3 )
      {
        /* this arc is flat, ignore it and pop it from the bezier stack */
        ras.arc -= 2;
      }
      else
      {
        /* the arc is y-monotonous, either ascending or descending */
        /* detect a change of direction                            */
        state_bez =  y1 < y3 ? Ascending : Descending;
        if ( ras.state != state_bez )
        {
          /* finalize current profile if any */
          if ( ras.state != Unknown   &&
               End_Profile( RAS_VAR ) )
            goto Fail;
            
          /* create a new profile */
          if ( New_Profile( RAS_VAR_ state_bez ) )
            goto Fail;
        }
        
        /* now call the appropriate routine */
        if ( state_bez == Ascending )
        {
          if ( Bezier_Up( RAS_VAR_  2, Split_Conic, ras.minY, ras.maxY ) )
            goto Fail;
        }
        else
          if ( Bezier_Down( RAS_VAR_  2, Split_Conic, ras.minY, ras.maxY ) )
            goto Fail;
      }    

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

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

    return SUCCESS;

  Fail:
    return FAILURE;
  }

#else /* FT_RASTER_CONIC_BEZIERS */


  static
  int  Conic_To( FT_Vector*  control,
                 FT_Vector*  to,
                 FT_Raster   raster )
  {
    UNUSED( control );
    UNUSED( to );
    UNUSED( raster );

    return ErrRaster_Invalid_Outline;
  }


#endif /* FT_RASTER_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 :: A pointer to the first control point.                  */
  /*    control2 :: A pointer to the second control point.                 */
  /*    to       :: A pointer to the end point.                            */
  /*    raster   :: A handle to the current raster object.                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0 means success.                                      */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function is used as a `FTRasterCubicTo_Func' by the outline   */
  /*    decomposer.                                                        */
  /*                                                                       */
  static
  int  Cubic_To( FT_Vector*  control1,
                 FT_Vector*  control2,
                 FT_Vector*  to,
                 FT_Raster   raster )
  {
    TPos        y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
    TDirection  state_bez;


    Push_Cubic( RAS_VAR_  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 )
      {
        ymin1 = y1;
        ymax1 = y4;
      }
      else
      {
        ymin1 = y4;
        ymax1 = y1;
      }
      
      if ( y2 <= y3 )
      {
        ymin2 = y2;
        ymax2 = y3;
      }
      else
      {
        ymin2 = y3;
        ymax2 = y2;
      }
      
      if ( ymin2 < ymin1 || ymax2 > ymax1 )
      {
        /* this arc has no given direction, split it! */
        Split_Cubic( ras.arc );
        ras.arc += 3;
      }
      else if ( y1 == y4 )
      {
        /* this arc is flat, ignore it and pop it from the bezier stack */
        ras.arc -= 3;
      }        
      else
      {
        state_bez = ( y1 <= y4 ) ? Ascending : Descending;
     
        /* detect a change of direction */
        if ( ras.state != state_bez )
        {
          if ( ras.state != Unknown   &&
               End_Profile( RAS_VAR ) )
            goto Fail;

          if ( New_Profile( RAS_VAR_ state_bez ) )
            goto Fail;
        }

        /* compute intersections */
        if ( state_bez == Ascending )
        {
          if ( Bezier_Up ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) )
            goto Fail;
        }
        else
          if ( Bezier_Down ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) )
            goto Fail;
      }

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

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

    return SUCCESS;

  Fail:
    return FAILURE;
  }


#else /* FT_RASTER_CUBIC_BEZIERS */


  int  Cubic_To( FT_Vector*  control1,
                 FT_Vector*  control2,
                 FT_Vector*  to,
                 FT_Raster   raster )
  {
    UNUSED( control1 );
    UNUSED( control2 );
    UNUSED( to );
    UNUSED( raster );

    return ErrRaster_Invalid_Outline;
  }


#endif /* FT_RASTER_CUBIC_BEZIERS */


/********************************************************************/
/*                                                                  */
/* The following function is compiled in the raster only when it is */
/* compile as a stand-alone module..                                */

/* It can, otherwise, be found in the FreeType base layer           */

#ifdef _STANDALONE_
  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Decompose_Outline                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Walks over an outline's structure to decompose it into individual  */
  /*    segments and Bezier arcs.  This function is also able to emit      */
  /*    `move to' and `close to' operations to indicate the start and end  */
  /*    of new contours in the outline.                                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    outline   :: A pointer to the source target.                       */
  /*                                                                       */
  /*    interface :: A table of `emitters', i.e,. function pointers called */
  /*                 during decomposition to indicate path operations.     */
  /*                                                                       */
  /*    user      :: A typeless pointer which is passed to each emitter    */
  /*                 during the decomposition.  It can be used to store    */
  /*                 the state during the decomposition.                   */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0 means sucess.                                       */
  /*                                                                       */

#if 0
  typedef int  (*FTRasterMoveTo_Func)( FT_Vector*  to,
                                       void*       user );

  typedef int  (*FTRasterLineTo_Func)( FT_Vector*  to,
                                       void*       user );

  typedef int  (*FTRasterConicTo_Func)( FT_Vector*  control,
                                        FT_Vector*  to,
                                        void*       user );

  typedef int  (*FTRasterCubicTo_Func)( FT_Vector*  control1,
                                        FT_Vector*  control2,
                                        FT_Vector*  to,
                                        void*       user );

  typedef struct  FT_Raster_Funcs_
  {
    FTRasterMoveTo_Func   move_to;
    FTRasterLineTo_Func   line_to;
    FTRasterConicTo_Func  conic_to;
    FTRasterCubicTo_Func  cubic_to;

  } FT_Raster_Funcs;
#endif

  int  FT_Decompose_Outline( FT_Outline*       outline,
                             FT_Raster_Funcs*  interface,
                             void*             user )
  {
    typedef enum _phases
    {
      phase_point,
      phase_conic,
      phase_cubic,
      phase_cubic2

    } TPhase;

    FT_Vector  v_first;
    FT_Vector  v_last;
    FT_Vector  v_control;
    FT_Vector  v_control2;
    FT_Vector  v_start;

    FT_Vector* point;
    PByte   flags;

    int    n;         /* index of contour in outline     */
    int    first;     /* index of first point in contour */
    int    index;     /* current point's index           */

    int    error;

    char   tag;       /* current point's state           */
    TPhase phase;


    first = 0;

    for ( n = 0; n < outline->n_contours; n++ )
    {
      int  last;  /* index of last point in contour */


      last = outline->contours[n];

      v_first = outline->points[first];
      v_last  = outline->points[last];

      v_start = v_control = v_first;

      tag   = FT_CURVE_TAG( outline->flags[first] );
      index = first;

      /* A contour cannot start with a cubic control point! */

      if ( tag == FT_Curve_Tag_Cubic )
        return ErrRaster_Invalid_Outline;


      /* check first point to determine origin */

      if ( tag == FT_Curve_Tag_Conic )
      {
        /* first point is conic control.  Yes, this happens. */
        if ( FT_CURVE_TAG( outline->flags[last] ) == FT_Curve_Tag_On )
        {
          /* start at last point if it is on the curve */
          v_start = v_last;
        }
        else
        {
          /* if both first and last points are conic,         */
          /* start at their middle and record its position    */
          /* for closure                                      */
          v_start.x = ( v_start.x + v_last.x ) / 2;
          v_start.y = ( v_start.y + v_last.y ) / 2;

          v_last = v_start;
        }
        phase = phase_conic;
      }
      else
        phase = phase_point;


      /* Begin a new contour with MOVE_TO */

      error = interface->move_to( &v_start, user );
      if ( error )
        return error;

      point = outline->points + first;
      flags = outline->flags  + first;

      /* now process each contour point individually */

      while ( index < last )
      {
        index++;
        point++;
        flags++;

        tag = FT_CURVE_TAG( flags[0] );

        switch ( phase )
        {
        case phase_point:     /* the previous point was on the curve */

          switch ( tag )
          {
            /* two succesive on points -> emit segment */
          case FT_Curve_Tag_On:
            error = interface->line_to( point, user );
            break;

            /* on point + conic control -> remember control point */
          case FT_Curve_Tag_Conic:
            v_control = point[0];
            phase     = phase_conic;
            break;

            /* on point + cubic control -> remember first control */
          default:
            v_control = point[0];
            phase     = phase_cubic;
            break;
          }
          break;

        case phase_conic:   /* the previous point was a conic control */

          switch ( tag )
          {
            /* conic control + on point -> emit conic arc */
          case  FT_Curve_Tag_On:
            error = interface->conic_to( &v_control, point, user );
            phase = phase_point;
            break;

            /* two successive conics -> emit conic arc `in between' */
          case FT_Curve_Tag_Conic:
            {
              FT_Vector  v_middle;


              v_middle.x = (v_control.x + point->x)/2;
              v_middle.y = (v_control.y + point->y)/2;

              error = interface->conic_to( &v_control,
                                           &v_middle, user );
              v_control = point[0];
            }
             break;

          default:
            error = ErrRaster_Invalid_Outline;
          }
          break;

        case phase_cubic:  /* the previous point was a cubic control */

          /* this point _must_ be a cubic control too */
          if ( tag != FT_Curve_Tag_Cubic )
            return ErrRaster_Invalid_Outline;

          v_control2 = point[0];
          phase      = phase_cubic2;
          break;


        case phase_cubic2:  /* the two previous points were cubics */

          /* this point _must_ be an on point */
          if ( tag != FT_Curve_Tag_On )
            error = ErrRaster_Invalid_Outline;
          else
            error = interface->cubic_to( &v_control, &v_control2,
                                         point, user );
          phase = phase_point;
          break;
        }

        /* lazy error testing */
        if ( error )
          return error;
      }

      /* end of contour, close curve cleanly */
      error = 0;

      tag = FT_CURVE_TAG( outline->flags[first] );

      switch ( phase )
      {
      case phase_point:
        if ( tag == FT_Curve_Tag_On )
          error = interface->line_to( &v_first, user );
        break;

      case phase_conic:
        error = interface->conic_to( &v_control, &v_start, user );
        break;

      case phase_cubic2:
        if ( tag == FT_Curve_Tag_On )
          error = interface->cubic_to( &v_control, &v_control2,
                                       &v_first,   user );
        else
          error = ErrRaster_Invalid_Outline;
        break;

      default:
        error = ErrRaster_Invalid_Outline;
        break;
      }

      if ( error )
        return error;

      first = last + 1;
    }

    return SUCCESS;
  }
#endif

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Convert_Glyph                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Converts a glyph into a series of segments and arcs and makes a    */
  /*    profiles list with them.                                           */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    outline :: The glyph outline.                                      */
  /*                                                                       */
  /* <Return>                                                              */
  /*    SUCCESS or FAILURE.                                                */
  /*                                                                       */
  static
  TResult  Convert_Glyph( RAS_ARG_ 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
    };

    /* 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_extrema = 0;

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

    /* Now decompose curve */
    if ( FT_Decompose_Outline( 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 reach this place, unless the Profile was not  */
    /* part of the list.                                             */
  }


  /*************************************************************************/
  /*                                                                       */
  /* Update                                                                */
  /*                                                                       */
  /*    Updates 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 to implement.                                           */
  /*                                                                       */
  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>                                                         */
  /*    Initializes the vertical bitmap sweep.  Called by the generic      */
  /*    sweep/draw routine before its loop.                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    min :: The address of the current minimum scanline.                */
  /*    max :: The address of the current maximum scanline.                */
  /*                                                                       */
  static
  void  Vertical_Sweep_Init( RAS_ARG_ int*  min, int*  max )
  {
    long  pitch;
    
    UNUSED( max );

    pitch          = ras.target.pitch;
    
    /* start from the bottom line, going up !! */
    ras.trace_bit  = - *min * pitch;
    ras.trace_incr = -pitch;

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

    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  :: The current scanline.                                        */
  /*    x1 :: The left span edge.                                          */
  /*    x2 :: The right span edge.                                         */
  /*                                                                       */
  static
  void  Vertical_Sweep_Span( RAS_ARG_ TScan  y,
                                      TPos   x1,
                                      TPos   x2 )
  {
    TPos   e1, e2;
    int    c1, c2;
    Byte   f1, f2;
    PByte  target;


    UNUSED( y );

    /* Drop-out control */

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

#ifdef FT_RASTER_ANY_ENDIAN
      if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
      if ( ras.gray_max_x < c2 ) ras.gray_max_x = c2;
#endif

      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>                                                         */
  /*    Tests a pixel `light' during the vertical bitmap sweep.  Used      */
  /*    during drop-out control only.                                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y :: The current scanline.                                         */
  /*    x :: The current x coordinate.                                     */
  /*                                                                       */
  static
  int  Vertical_Test_Pixel( RAS_ARG_ TScan  y,
                                     int    x )
  {
    int c1 = x >> 3;


    UNUSED( 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     :: The current scanline.                                     */
  /*    x     :: The current x coordinate.                                 */
  /*    color :: Ignored by this function.                                 */
  /*                                                                       */
  static
  void  Vertical_Set_Pixel( RAS_ARG_ int  y,
                                     int  x,
                                     int  color )
  {
    UNUSED( color );
    UNUSED( 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 another scanline.  Only updates */
  /*    the pointers in the vertical bitmap sweep.                         */
  /*                                                                       */
  static
  void Vertical_Sweep_Step( RAS_ARG )
  {
    ras.trace_bit += ras.trace_incr;
  }


  static
  const  Raster_Render   vertical_render_mono =
  {
    &Vertical_Sweep_Init,
    &Vertical_Sweep_Span,
    &Vertical_Sweep_Step,
    &Vertical_Test_Pixel,
    &Vertical_Set_Pixel
  };

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


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Horizontal_Sweep_Init                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Initializes the horizontal bitmap sweep.  Called by the generic    */
  /*    sweep/draw routine before its loop.                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    min :: The address of the current minimum pixel column.            */
  /*    max :: The address of the current maximum pixel column.            */
  /*                                                                       */
  static
  void  Horizontal_Sweep_Init( RAS_ARG_ int*  min,
                                        int*  max )
  {
    UNUSED( ras );
    UNUSED( min );
    UNUSED( max );

    /* nothing, really */
  }


  /*************************************************************************/
  /*                                                                       */
  /* <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  :: The current pixel column.                                    */
  /*    x1 :: The top span edge.                                           */
  /*    x2 :: The bottom span edge.                                        */
  /*                                                                       */
  static
  void  Horizontal_Sweep_Span( RAS_ARG_ TScan  y,
                                        TPos   x1,
                                        TPos   x2 )
  {
    TPos      e1, e2;
    PByte  bits;
    Byte   f1;

    UNUSED( 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   = (Byte)(0x80 >> (y & 7));

        e1 = TRUNC( e1 );

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


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Horizontal_Test_Pixel                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Tests a pixel `light' during the horizontal bitmap sweep.  Used    */
  /*    during drop-out control only.                                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y :: The current pixel column.                                     */
  /*    x :: The current row/scanline.                                     */
  /*                                                                       */
  static
  int   Horizontal_Test_Pixel( RAS_ARG_ int  y,
                                        int  x )
  {
    char*  bits   = (char*)ras.bit_buffer + (y >> 3);
    int    f1     = (Byte)(0x80 >> (y & 7));
    long   pitch  = ras.target.pitch;
    long   offset = - pitch * x;
    
    if (pitch > 0)
      offset += (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     :: The current pixel column.                                 */
  /*    x     :: The current row/scanline.                                 */
  /*    color :: Ignored by this function.                                 */
  /*                                                                       */
  static
  void  Horizontal_Set_Pixel( RAS_ARG_ int  y,
                                       int  x,
                                       int  color )
  {
    char*  bits = (char*)ras.bit_buffer + (y >> 3);
    int    f1   = (Byte)(0x80 >> (y  & 7));


    UNUSED( color );

    if ( x >= 0 && x < ras.target.rows )
    {
      long pitch  = ras.target.pitch;
      long offset = - x*pitch;
      
      if (pitch > 0)
        offset += (ras.target.rows-1)*pitch; 

      bits[offset] |= f1;
    }
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Horizontal_Sweep_Step                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Called whenever the sweep jumps to another pixel column.           */
  /*                                                                       */
  static
  void  Horizontal_Sweep_Step( RAS_ARG )
  {
    UNUSED( ras.target );

    /* Nothing, really */
  }


  static
  const  Raster_Render   horizontal_render_mono =
  {
    &Horizontal_Sweep_Init,
    &Horizontal_Sweep_Span,
    &Horizontal_Sweep_Step,
    &Horizontal_Test_Pixel,
    &Horizontal_Set_Pixel
  };


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

#ifdef FT_RASTER_OPTION_ANTI_ALIAS

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Vertical_Gray_Sweep_Init                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Initializes the vertical bitmap sweep.  Called by the generic      */
  /*    sweep/draw routine before its loop.                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    min :: The address of the current minimum scanline.                */
  /*    max :: The address of the current maximum scanline.                */
  /*                                                                       */
  static
  void  Vertical_Gray_Sweep_Init( RAS_ARG_ int*  min, int*  max )
  {
    long  pitch;
    
    UNUSED( max );

    pitch          = ras.target.pitch;
    
    /* start from the bottom line, going up */
    ras.trace_incr = -pitch;
    ras.trace_bit  = - *min * pitch;
    
    if (pitch > 0)
      ras.trace_bit += (ras.target.rows-1)*pitch;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Vertical_Gray_Sweep_Span                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Draws a single horizontal bitmap span during the vertical bitmap   */
  /*    sweep.                                                             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y  :: The current scanline.                                        */
  /*    x1 :: The left span edge.                                          */
  /*    x2 :: The right span edge.                                         */
  /*                                                                       */
  static
  void  Vertical_Gray_Sweep_Span( RAS_ARG_ TScan  y,
                                           TPos   x1,
                                           TPos   x2 )
  {
    TPos   e1, e2;
    int    shift = ras.precision_bits - 6;
    PByte  target;

    UNUSED( y );

    x1 += ras.precision_half;
    x2 += ras.precision_half;
   
#ifdef FT_RASTER_OPTION_CONTRAST   
    if ( x2-x1 < ras.precision )
    {
	  x1 = ((x1+x2) >> 1) - ras.precision_half;
	  x2 = x1 + ras.precision;
	}
#endif
        
    e1 = TRUNC( x1 );
    e2 = TRUNC( x2 );

    if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width )
    {
      x1 = FRAC(x1) >> shift;
      x2 = FRAC(x2) >> shift;

      if ( e1 < 0 )
      {
        e1 = 0;
        x1 = 0;
      }

      if ( e2 > ras.bit_width )
      {
        e2 = ras.bit_width-1;
        x2 = 0;
      }

      target = ras.bit_buffer + ras.trace_bit + e1;
      e2    -= e1;
      
      if ( e2 > 0 )
      {
        if (x1 > 0) target[0] += (Byte)(64-x1) << 1;
               else target[0]  = 127;
        e2--;
        while (e2 > 0)
        {
          *(++target) = 127;
          e2--;
        }
        if (x2)
          target[1] += (Byte)x2 << 1;
      }
      else
      {
        target[0] += (Byte)(x2-x1) << 1;
      }
    }
  }

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Vertical_Gray_Test_Pixel                                           */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Tests a pixel `light' during the vertical bitmap sweep.  Used      */
  /*    during drop-out control only.                                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y :: The current scanline.                                         */
  /*    x :: The current x coordinate.                                     */
  /*                                                                       */
  static
  int  Vertical_Gray_Test_Pixel( RAS_ARG_ TScan  y,
                                          int    x )
  {
    UNUSED( y );

#if 0
    /* as a rule of thumb, do not add a drop-out if the current */
    /* gray level is over 0.5                                   */
    
    return ( x >= 0 && x < ras.bit_width &&
             ras.bit_buffer[ras.trace_bit + x] >= 64 );
#else
    UNUSED(x);
    return 0;
#endif
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Vertical_Gray_Set_Pixel                                            */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Sets a single pixel in a bitmap during the vertical sweep.  Used   */
  /*    during drop-out control.                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y     :: The current scanline.                                     */
  /*    x     :: The current x coordinate.                                 */
  /*    color :: Ignored by this function.                                 */
  /*                                                                       */
  static
  void  Vertical_Gray_Set_Pixel( RAS_ARG_ int  y,
                                          int  x,
                                          int  color )
  {
    UNUSED( y );

    if ( x >= 0 && x < ras.bit_width )
    {
      unsigned char*  pixel;
      
      pixel = ras.bit_buffer + ras.trace_bit + x;
      
      /* do not add too much to the pixel gray level */
      color += *pixel;
      if (color < 64)
        color = 64;

      *pixel = ( color >= 127 ? 127 : (unsigned char)color );
    }
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Vertical_Sweep_Step                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Called whenever the sweep jumps to another scanline.  Only updates */
  /*    the pointers in the vertical bitmap sweep.                         */
  /*                                                                       */
  static
  void Vertical_Gray_Sweep_Step( RAS_ARG )
  {
    ras.trace_bit += ras.trace_incr;
  }



  static
  const  Raster_Render   vertical_render_gray =
  {
    &Vertical_Gray_Sweep_Init,
    &Vertical_Gray_Sweep_Span,
    &Vertical_Gray_Sweep_Step,
    &Vertical_Gray_Test_Pixel,
    &Vertical_Gray_Set_Pixel
  };



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


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Horizontal_Sweep_Init                                              */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Initializes the horizontal bitmap sweep.  Called by the generic    */
  /*    sweep/draw routine before its loop.                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    min :: The address of the current minimum pixel column.            */
  /*    max :: The address of the current maximum pixel column.            */
  /*                                                                       */
  static
  void  Horizontal_Gray_Sweep_Init( RAS_ARG_ int*  min,
                                             int*  max )
  {
    UNUSED( ras );
    UNUSED( min );
    UNUSED( max );

    /* nothing, really */
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Horizontal_Gray_Sweep_Span                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Draws a single vertical bitmap span during the horizontal bitmap   */
  /*    sweep.                                                             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y  :: The current scanline.                                        */
  /*    x1 :: The left span edge.                                          */
  /*    x2 :: The right span edge.                                         */
  /*                                                                       */
  static
  void  Horizontal_Gray_Sweep_Span( RAS_ARG_ TScan  y,
                                             TPos   x1,
                                             TPos   x2 )
  {
    TPos   e1, e2;
    int    shift = ras.precision_bits - 6;
    int    incr;
    PByte  bits;
    Byte   b;


    UNUSED( y );

    x1 += ras.precision_half;
    x2 += ras.precision_half;

#ifdef FT_RASTER_OPTION_CONTRAST   
    if (x2-x1 < ras.precision)
	{
	  x1 = ((x1+x2) >> 1) - ras.precision_half;
	  x2 = x1 + ras.precision;
	}
#endif
        
    e1 = TRUNC( x1 );
    e2 = TRUNC( x2 );

    if ( e1 <= e2 && e2 >= 0 && e1 < ras.bit_width )
    {
      x1 = FRAC(x1) >> shift;
      x2 = FRAC(x2) >> shift;

      if ( e1 < 0 )
      {
        e1 = 0;
        x1 = 0;
      }

      if ( e2 >= ras.bit_width )
      {
        e2 = ras.bit_width;
        x2 = 0;
      }

      incr  = -ras.target.pitch;
      bits  = ras.bit_buffer + y;
      bits += incr * e1;
      if (incr < 0)
        bits -= incr*(ras.target.rows-1); 

      e2 -= e1;
      
      if ( e2 > 0 )
      {
        b = bits[0];
        if (b < 127) b++;
        bits[0] = (64-x1) + (b >> 1);
        
        e2--;
        while (e2 > 0)
        {
          bits += incr;
          b     = bits[0];
          
          if (b < 127)
            bits[0] = (Byte)(63+((b+1) >> 1));  
          
          e2--;
        }
        
        if (x2)
        {
          bits += incr;
          b     = bits[0];
          if (b < 127) b++;
          bits[0] = (Byte)(x2 + (b >> 1));
        }
        
      }
      else
      {
        b = bits[0];
        if (b < 127) b++;
        bits[0] = (Byte)((b >> 1)+(x2-x1));
      }
    }
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Horizontal_Gray_Test_Pixel                                         */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Tests a pixel `light' during the horizontal bitmap sweep.  Used    */
  /*    during drop-out control only.                                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y :: The current pixel column.                                     */
  /*    x :: The current row/scanline.                                     */
  /*                                                                       */
  static
  int   Horizontal_Gray_Test_Pixel( RAS_ARG_ int  y,
                                             int  x )
  {
#if 0
    unsigned char*  pixel = (unsigned char*)ras.bit_buffer + y;

    if ( ras.target.flow == Flow_Down )
      pixel += (ras.target.rows-1 - x) * ras.target.cols;
    else
      pixel += x * ras.target.cols;

    return ( x >= 0 && x < ras.target.rows &&
            *pixel >= 64 );
#else
    UNUSED(y);
    UNUSED(x);
    return 0;
#endif
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Horizontal_Set_Pixel                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Sets a single pixel in a bitmap during the horizontal sweep.  Used */
  /*    during drop-out control.                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    y     :: The current pixel column.                                 */
  /*    x     :: The current row/scanline.                                 */
  /*    color :: Ignored by this function.                                 */
  /*                                                                       */
  static
  void  Horizontal_Gray_Set_Pixel( RAS_ARG_ int  y,
                                            int  x,
                                            int  color )
  {
    unsigned char*  pixel = (unsigned char*)ras.bit_buffer + y;

    if ( x >= 0 && x < ras.target.rows )
    {
      long  pitch  = ras.target.pitch;
      
      pixel -= pitch*x;
      if (pitch > 0)
        pixel += pitch*(ras.target.rows-1); 

      color += *pixel;
      if (color < 64)
        color = 64;
/*
      *pixel = (color >= 127 ? 127 : (unsigned char)color );
 */
    }
  }


  static
  void Gray_Ignore( void )
  {
    ;
  }


  static
  const  Raster_Render   horizontal_render_gray =
  {
    &Horizontal_Gray_Sweep_Init,
    &Horizontal_Gray_Sweep_Span,

    (Function_Sweep_Step)  &Gray_Ignore,
    &Horizontal_Gray_Test_Pixel,
    &Horizontal_Gray_Set_Pixel,
  };

#endif /* FT_RASTER_OPTION_ANTI_ALIAS */


  /*************************************************************************/
  /*                                                                       */
  /* A technical note to explain how the scanline sweep is performed:      */
  /*                                                                       */
  /*   The function Draw_Sweep() is used to sweep the scanlines of the     */
  /*   target bitmap or pixmap.  For each scanline, it must do the         */
  /*   following:                                                          */
  /*                                                                       */
  /*   - Get the set of all outline intersections for the current          */
  /*     scanline.                                                         */
  /*                                                                       */
  /*   - Sort these intersections (in increasing order).                   */
  /*                                                                       */
  /*   - Pair intersections to create spans (horizontal pixel segments)    */
  /*     that are then `drawn' by calling a `sweep_span' function.         */
  /*                                                                       */
  /*   - Check for dropouts: If a span is too small to be drawn, it must   */
  /*     be re-adjusted in order to make it visible again.                 */
  /*                                                                       */
  /*   The sweep starts from the bottom of the outline (ymin) and goes     */
  /*   upwards (to ymax).  Thus, the function manages the following:       */
  /*                                                                       */
  /*   - A linked list of the profiles which are above the current         */
  /*     scanline.  It is called the `wait' list as it contains all the    */
  /*     profiles waiting to be `activated' during the sweep.  It contains */
  /*     all profiles initially.                                           */
  /*                                                                       */
  /*   - A linked list of the profiles covering the current scanline,      */
  /*     i.e., all the profiles that contain an intersection for the       */
  /*     current scanline.  It is called the `draw' list.                  */
  /*                                                                       */
  /*   A profile travels from the wait list to the draw list if the        */
  /*   current scanline reaches its bottom border (its ymin).  It is also  */
  /*   removed from the draw list (and becomes unlisted) when the current  */
  /*   scanline reaches the scanline above its upper border (its ymax).    */
  /*                                                                       */
  /*   These positions correspond to the `extrema' table built by          */
  /*   Finalize_Profile_Table().                                           */
  /*                                                                       */
  /*   The draw list is always sorted in increasing order of the X         */
  /*   coordinates.  We use a bubble sort because it is easy to implement  */
  /*   on a linked list, and because in 95% cases, the list is already     */
  /*   correctly sorted when going from one scanline to the other.         */
  /*                                                                       */
  /*   The extrema table gives the scanline coordinates at which at least  */
  /*   one profile must be removed from the `draw' list, or another one    */
  /*   must be moved from the `wait' to `draw' lists.                      */
  /*                                                                       */
  /*   Note that when a dropout is detected, the corresponding span is not */
  /*   drawn immediately but kept on a temporary list.  All dropout spans  */
  /*   are drawn after the regular spans on a given scanline.  This is a   */
  /*   requirement of the TrueType specification to properly implement     */
  /*   some drop-out control modes -- yes, it's weird!                     */
  /*                                                                       */
  /*   Finally, the parser contains four function pointers that are called */
  /*   by Draw_Sweep().  Each rendering mode (monochrome, anti-aliased-5,  */
  /*   and anti-aliased-17) provide its own set of such functions.  These  */
  /*   are:                                                                */
  /*                                                                       */
  /*     sweep_init:       Called only when the sweep starts.  Used to set */
  /*                       up some variables.                              */
  /*                                                                       */
  /*     sweep_span:       Used to draw a horizontal span on the current   */
  /*                       scanline.                                       */
  /*                                                                       */
  /*     sweep_test_pixel: Used to test a pixel's intensity, as it is      */
  /*                       required for drop-out control.                  */
  /*                                                                       */
  /*     sweep_put_pixel:  Used to write a single pixel when a drop-out    */
  /*                       needs to be lighted/drawn.                      */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /*  Generic Sweep Drawing routine                                        */
  /*                                                                       */
  static
  TResult  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 -- and add profiles to the wait list */
    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 extrema table */
    if ( ras.n_extrema == 0 )
    {
      ras.error = ErrRaster_Invalid_Outline;
      return FAILURE;
    }

    /* Now inits the sweep */
    PTRACE2(( "draw_sweep: initialize sweep\n" ));
    ras.render.init( RAS_VAR_  &min_Y, &max_Y );
    PTRACE2(( "  init min_y = %d, max_y = %d\n", 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_extrema > 0 &&
         ras.pool_size[-ras.n_extrema] == min_Y )
      ras.n_extrema--;

    PTRACE2(( "starting loop with n_extrema = %d", ras.n_extrema ));
    while ( ras.n_extrema > 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 )
        {
          /* move the profile from the wait list to the draw list */
          DelOld( &wait, prof );
          InsNew( &draw, prof );
        }
        prof = next;
      }

      /* Sort the draw list */
      Sort( &draw );

      /* compute next y extremum scanline; we won't change the */
      /* elements of the wait and draw lists until there       */
      y_change = ras.pool_size[-ras.n_extrema--];
      y_height = y_change - y;

      PTRACE2(( ">>> y = %d, y_change = %d, y_height = %d",
                y, y_change, y_height ));

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


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

        /* skip to next line if there is no active profile there */
        if ( !draw ) goto Next_Line;

        left   = draw;
        window = left->flow;
        prof   = left->link;

        PTRACE2(( ">>>  line y = %d", y ));

        while ( prof )
        {
          PProfile  next = prof->link;


          window += prof->flow;

          if ( window == 0 )
          {
            x1 = left->X;
            x2 = prof->X;

            if ( x1 > x2 )
            {
              TPos  xs = x1;


              x1 = x2;
              x2 = xs;
            }

            if ( x2 - x1 <= ras.precision && ras.dropout_mode )
            {
              e1 = CEILING( x1 );
              e2 = FLOOR( x2 );

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

            PTRACE2(( "drawing span ( y=%d, x1=%d, x2=%d )",
                      y, x1, x2 ));
            ras.render.span( RAS_VAR_  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.render.step( RAS_VAR );

        y++;

        if ( y < y_change )
          Sort( &draw );

        PTRACE2(( "line sorted for next operation" ));
      }

      /* Now finalize the profiles that needs it */

      PTRACE2(( "finalizing profiles..." ));
      {
        PProfile  prof, next;


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

      PTRACE2(( "profiles finalized for this run" ));
    }

    /* for gray-scaling, flushes the bitmap scanline cache */
    while ( y <= max_Y )
    {
        ras.render.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 );

      PTRACE2(( "performing drop-out control ( x1= %d, x2 = %d )",
                x1, x2 ));

      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.render.test_pixel( RAS_VAR_  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;
      }

      PTRACE2(( "  -> setting pixel" ));
      ras.render.set_pixel( RAS_VAR_ y,
                            TRUNC( e1 ),
                            (x2 - x1) >> ras.scale_shift );
    Next_Dropout:

      dropouts--;
    }
    goto Next_Line;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Render_Single_Pass                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Performs one sweep with sub-banding.                               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    flipped :: whether or not we have to flip.                         */
  /*                                                                       */
  /* <Returns>                                                             */
  /*    Error code.  0 means success.                                      */
  /*                                                                       */
  static
  int  Render_Single_Pass( RAS_ARG_ int  flipped )
  {
    TBand*  band;


    ras.flipped = flipped;

    band = ras.band_stack;

    PTRACE2(( "raster: entering render_single_pass (flipped = %d)\n",
              flipped ));

    while ( band >= ras.band_stack )
    {
      ras.maxY = ((long)band[0].y_max << (ras.scale_shift+6)) - 1;

      ras.minY = (long)band[0].y_min << (ras.scale_shift+6);

      ras.cursor = ras.pool;
      ras.error  = 0;

      PTRACE2(( "raster: band = [ %d, %d ]\n",
                band[0].y_min,
                band[0].y_max ));

      if ( Convert_Glyph( RAS_VAR_  ras.outline ) )
      {
        int  bottom, top, half;


        if ( ras.error != ErrRaster_Overflow )
          return FAILURE;
        ras.error = ErrRaster_Ok;

        PTRACE2(( "conversion failure, performing sub-banding\n" ));

        /* sub-banding */

#ifdef DEBUG_RASTER
        ClearBand( RAS_VAR_  TRUNC( ras.minY ), TRUNC( ras.maxY ) );
#endif

        bottom = band[0].y_min;
        top    = band[0].y_max;
        half   = ( top - bottom ) >> 1;

        if ( band >= ras.band_stack + 7 || half == 0 )
        {
          ras.band_top = 0;
          ras.error    = ErrRaster_Invalid_Outline;
          return ras.error;
        }

        band[1].y_min = bottom + half;
        band[1].y_max = top;
        band[0].y_max = bottom + half;

        band ++;
      }
      else
      {
        PTRACE2(( "conversion succeeded, span drawing sweep\n" ));
        if ( ras.start_prof )
          if ( Draw_Sweep( RAS_VAR ) )
            return ras.error;
        band --;
      }
    }

    PTRACE2(( "raster: exiting render_single_pass\n" ));

    return SUCCESS;  /* success */
  }


  static
  int  Raster_Render1( FT_Raster  raster )
  {
    int  error;


    if ( ras.target.width > ABS(ras.target.pitch)*8 )
      return ErrRaster_Invalid_Map;

    ras.scale_shift  = ras.precision_bits - 6;
    ras.scale_delta  = ras.precision_half;

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

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

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

    /* Horizontal Sweep */

    if ( ras.second_pass && ras.dropout_mode != 0 )
    {
      ras.render              = horizontal_render_mono;
      ras.band_top            = 0;
      ras.band_stack[0].y_min = 0;
      ras.band_stack[0].y_max = ras.target.width;

      if ( (error = Render_Single_Pass( RAS_VAR_  1 )) != 0 )
        return error;
    }

    return ErrRaster_Ok;
  }


#ifdef FT_RASTER_OPTION_ANTI_ALIAS


  static
  int  Raster_Render8( FT_Raster  raster )
  {
    int  error;

    if ( ras.target.width > ABS(ras.target.pitch) )
      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;

    ras.scale_shift  = (ras.precision_bits-6);
    ras.scale_delta  = ras.precision_half;
    ras.dropout_mode = 2;

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

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

#if 1
    /* Horizontal Sweep */
    ras.render              = horizontal_render_gray;
    ras.band_top            = 0;
    ras.bit_width           = ras.target.rows;
    ras.band_stack[0].y_min = 0;
    ras.band_stack[0].y_max = ras.target.width;

    return Render_Single_Pass( RAS_VAR_  1 );
#else
    return 0;
#endif
  }


#else  /* FT_RASTER_OPTION_ANTI_ALIAS */


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


#endif /* FT_RASTER_OPTION_ANTI_ALIAS */


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Raster_Render                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Renders an outline into a target bitmap.                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    raster  :: A handle to the raster object used during rendering.    */
  /*    outline :: A pointer to the source outline record/object.          */
  /*    bitmap  :: A pointer to the target bitmap descriptor.              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code, interpreted as a FT_Error by FreeType.  0 means        */
  /*    success.                                                           */
  /*                                                                       */
  EXPORT_FUNC
  int  FT_Raster_Render( FT_Raster    raster,
                         FT_Outline*  outline,
                         FT_Bitmap*   target_map )
  {
    if ( !raster || !raster->pool || !raster->pool_size )
      return ErrRaster_Uninitialized_Object;

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

    if ( !outline || !outline->contours || !outline->points )
      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;

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

    ras.dropout_mode = outline->dropout_mode;
    ras.second_pass  = outline->second_pass;
    SET_High_Precision( outline->high_precision );

    switch ( target_map->pixel_mode )
    {
    case ft_pixel_mode_mono:
      return Raster_Render1( raster );
    case ft_pixel_mode_grays:
      return Raster_Render8( raster );

    default:
      return ErrRaster_Unimplemented;
    }
  }


#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 )
  {


#ifdef FT_RASTER_ANY_ENDIAN


    int  i, j, l, c;


    for ( i = 0; i < 256; i++ )
    {
      l = 0;
      j = i;

      for ( c = 0; c < 4; c++ )
      {
        l <<= 4;

        if ( j & 0x80 ) l++;
        if ( j & 0x40 ) l++;

        j = ( j << 2 ) & 0xFF;
      }

      ras.count_table[i] = l;
    }


#else /* FT_RASTER_ANY_ENDIAN */


    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
    }


#endif /* FT_RASTER_ANY_ENDIAN */
  }


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


#endif /* TT_RASTER_OPTION_ANTI_ALIAS */


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Raster_ObjSize                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function returns the size of a raster object in bytes.        */
  /*    Client applications are thus able to allocate objects in their own */
  /*    heap/memory space, without revealing the internal structures of    */
  /*    the scan-line converter.                                           */
  /*                                                                       */
  /* <Return>                                                              */
  /*    The size in bytes of a single raster object.                       */
  /*                                                                       */
  EXPORT_FUNC
  long  FT_Raster_ObjSize( void )
  {
    return (long)sizeof( struct FT_RasterRec_ );
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Raster_Init                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Initializes a fresh raster object which should have been allocated */
  /*    by client applications.  This function is also used to set the     */
  /*    object's render pool.  It can be used repeatedly on a single       */
  /*    object if one wants to change the pool's address or size.          */
  /*                                                                       */
  /*    Note that the render pool has no state and is only used during a   */
  /*    call to FT_Raster_Render().  It is thus theorically possible to    */
  /*    share it between several non-concurrent components of your         */
  /*    applications when memory is a scarce resource.                     */
  /*                                                                       */
  /* <Input>                                                               */
  /*    pool_size :: The render pool's size in bytes.  This must be at     */
  /*                 least 4 kByte.                                        */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    raster    :: A handle to the target raster object.                 */
  /*                                                                       */
  /*    pool_base :: The render pool's base address in memory.             */
  /*                                                                       */
  /* <Return>                                                              */
  /*    An error condition, used as a FT_Error in the FreeType library.    */
  /*    0 means success.                                                   */
  /*                                                                       */
  EXPORT_FUNC
  int  FT_Raster_Init( 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 ErrRaster_Uninitialized_Object;

    /* check the render pool - we won't go under 4 Kb */
    if ( !pool_base || pool_size < 4096 )
      return ErrRaster_Invalid_Pool;

    /* save the pool */
    raster->pool      = (PPos)pool_base;
    raster->pool_size = raster->pool + pool_size / sizeof ( TPos );

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

      do *p++ = 0; while ( p < limit );
    }
#endif

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

    return ErrRaster_Ok;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Raster_SetPalette                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Sets 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;
  }

  FT_Raster_Interface  ft_default_raster =
  {
    sizeof( struct FT_RasterRec_ ),
    ft_glyph_format_outline,
    
    (FT_Raster_Init_Proc)     FT_Raster_Init,
    (FT_Raster_Set_Mode_Proc) 0,
    (FT_Raster_Render_Proc)   FT_Raster_Render    
  };


/* END */