text: /****************************************************************************
   *
   * ftraster.c
   *
   *   The FreeType glyph rasterizer (body).
   *
   * Copyright (C) 1996-2020 by
   * David Turner, Robert Wilhelm, and Werner Lemberg.
   *
   * This file is part of the FreeType project, and may only be used,
   * modified, and distributed under the terms of the FreeType project
   * license, LICENSE.TXT.  By continuing to use, modify, or distribute
   * this file you indicate that you have read the license and
   * understand and accept it fully.
   *
   */
  
    /**************************************************************************
     *
     * This file can be compiled without the rest of the FreeType engine, by
     * defining the STANDALONE_ macro when compiling it.  You also need to
     * put the files `ftimage.h' and `ftmisc.h' into the $(incdir)
     * directory.  Typically, you should do something like
     *
     * - copy `src/raster/ftraster.c' (this file) to your current directory
     *
     * - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' to your
     *   current directory
     *
     * - compile `ftraster' with the STANDALONE_ macro defined, as in
     *
     *     cc -c -DSTANDALONE_ ftraster.c
     *
     * The renderer can be initialized with a call to
     * `ft_standard_raster.raster_new'; a bitmap can be generated
     * with a call to `ft_standard_raster.raster_render'.
     *
     * See the comments and documentation in the file `ftimage.h' for more
     * details on how the raster works.
     *
     */
  
  
    /**************************************************************************
     *
     * This is a rewrite of the FreeType 1.x scan-line converter
     *
     */
  
  #ifdef STANDALONE_
  
    /* The size in bytes of the render pool used by the scan-line converter  */
    /* to do all of its work.                                                */
  #define FT_RENDER_POOL_SIZE  16384L
  
  #define FT_CONFIG_STANDARD_LIBRARY_H  <stdlib.h>
  
  #include <string.h>           /* for memset */
  
  #include "ftmisc.h"
  #include "ftimage.h"
  
  #else /* !STANDALONE_ */
  
  #include <ft2build.h>
  #include "ftraster.h"
  #include FT_INTERNAL_CALC_H   /* for FT_MulDiv and FT_MulDiv_No_Round */
  #include FT_OUTLINE_H         /* for FT_Outline_Get_CBox              */
  
  #endif /* !STANDALONE_ */
  
  
    /**************************************************************************
     *
     * A simple technical note on how the raster works
     * -----------------------------------------------
     *
     *   Converting an outline into a bitmap is achieved in several steps:
     *
     *   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 whether it was built going `up' or
     *         `down', as this is very important for filling rules
     *
     *       o its drop-out mode
     *
     *   2 - Sweeping the target map's scanlines in order to compute segment
     *       `spans' which are then filled.  Additionally, this pass
     *       performs drop-out control.
     *
     *   The outline data is parsed during step 1 only.  The profiles are
     *   built from the bottom of the render pool, used as a stack.  The
     *   following graphics shows the profile list under construction:
     *
     *    __________________________________________________________ _ _
     *   |         |                 |         |                 |
     *   | profile | coordinates for | profile | coordinates for |-->
     *   |    1    |  profile 1      |    2    |  profile 2      |-->
     *   |_________|_________________|_________|_________________|__ _ _
     *
     *   ^                                                       ^
     *   |                                                       |
     * start of render pool                                      top
     *
     *   The top of the profile stack is kept in the `top' variable.
     *
     *   As you can see, a profile record is pushed on top of the render
     *   pool, which is then followed by its coordinates/intersections.  If
     *   a change of direction is detected in the outline, a new profile is
     *   generated until the end of the outline.
     *
     *   Note that when all profiles have been generated, the function
     *   Finalize_Profile_Table() is used to record, for each profile, its
     *   bottom-most scanline as well as the scanline above its upmost
     *   boundary.  These positions are called `y-turns' because they (sort
     *   of) correspond to local extrema.  They are stored in a sorted list
     *   built from the top of the render pool as a downwards stack:
     *
     *     _ _ _______________________________________
     *                           |                    |
     *                        <--| sorted list of     |
     *                        <--|  extrema scanlines |
     *     _ _ __________________|____________________|
     *
     *                           ^                    ^
     *                           |                    |
     *                         maxBuff           sizeBuff = end of pool
     *
     *   This list is later used during the sweep phase in order to
     *   optimize performance (see technical note on the sweep below).
     *
     *   Of course, the raster detects whether the two stacks collide and
     *   handles the situation properly.
     *
     */
  
  
    /*************************************************************************/
    /*************************************************************************/
    /**                                                                     **/
    /**  CONFIGURATION MACROS                                               **/
    /**                                                                     **/
    /*************************************************************************/
    /*************************************************************************/
  
    /* define DEBUG_RASTER if you want to compile a debugging version */
  /* #define DEBUG_RASTER */
  
  
    /*************************************************************************/
    /*************************************************************************/
    /**                                                                     **/
    /**  OTHER MACROS (do not change)                                       **/
    /**                                                                     **/
    /*************************************************************************/
    /*************************************************************************/
  
    /**************************************************************************
     *
     * The macro FT_COMPONENT is used in trace mode.  It is an implicit
     * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
     * messages during execution.
     */
  #undef  FT_COMPONENT
  #define FT_COMPONENT  raster
  
  
  #ifdef STANDALONE_
  
    /* Auxiliary macros for token concatenation. */
  #define FT_ERR_XCAT( x, y )  x ## y
  #define FT_ERR_CAT( x, y )   FT_ERR_XCAT( x, y )
  
    /* This macro is used to indicate that a function parameter is unused. */
    /* Its purpose is simply to reduce compiler warnings.  Note also that  */
    /* simply defining it as `(void)x' doesn't avoid warnings with certain */
    /* ANSI compilers (e.g. LCC).                                          */
  #define FT_UNUSED( x )  (x) = (x)
  
    /* Disable the tracing mechanism for simplicity -- developers can      */
    /* activate it easily by redefining these macros.                      */
  #ifndef FT_ERROR
  #define FT_ERROR( x )  do { } while ( 0 )     /* nothing */
  #endif
  
  #ifndef FT_TRACE
  #define FT_TRACE( x )   do { } while ( 0 )    /* nothing */
  #define FT_TRACE1( x )  do { } while ( 0 )    /* nothing */
  #define FT_TRACE6( x )  do { } while ( 0 )    /* nothing */
  #define FT_TRACE7( x )  do { } while ( 0 )    /* nothing */
  #endif
  
  #ifndef FT_THROW
  #define FT_THROW( e )  FT_ERR_CAT( Raster_Err_, e )
  #endif
  
  #define Raster_Err_None          0
  #define Raster_Err_Not_Ini      -1
  #define Raster_Err_Overflow     -2
  #define Raster_Err_Neg_Height   -3
  #define Raster_Err_Invalid      -4
  #define Raster_Err_Unsupported  -5
  
  #define ft_memset  memset
  
  #define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \
                                  raster_reset_, raster_set_mode_,    \
                                  raster_render_, raster_done_ )      \
            const FT_Raster_Funcs class_ =                            \
            {                                                         \
              glyph_format_,                                          \
              raster_new_,                                            \
              raster_reset_,                                          \
              raster_set_mode_,                                       \
              raster_render_,                                         \
              raster_done_                                            \
           };
  
  #else /* !STANDALONE_ */
  
  
  #include FT_INTERNAL_OBJECTS_H
  #include FT_INTERNAL_DEBUG_H       /* for FT_TRACE, FT_ERROR, and FT_THROW */
  
  #include "rasterrs.h"
  
  #define Raster_Err_None         FT_Err_Ok
  #define Raster_Err_Not_Ini      Raster_Err_Raster_Uninitialized
  #define Raster_Err_Overflow     Raster_Err_Raster_Overflow
  #define Raster_Err_Neg_Height   Raster_Err_Raster_Negative_Height
  #define Raster_Err_Invalid      Raster_Err_Invalid_Outline
  #define Raster_Err_Unsupported  Raster_Err_Cannot_Render_Glyph
  
  
  #endif /* !STANDALONE_ */
  
  
  #ifndef FT_MEM_SET
  #define FT_MEM_SET( d, s, c )  ft_memset( d, s, c )
  #endif
  
  #ifndef FT_MEM_ZERO
  #define FT_MEM_ZERO( dest, count )  FT_MEM_SET( dest, 0, count )
  #endif
  
  #ifndef FT_ZERO
  #define FT_ZERO( p )  FT_MEM_ZERO( p, sizeof ( *(p) ) )
  #endif
  
    /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is       */
    /* typically a small value and the result of a*b is known to fit into */
    /* 32 bits.                                                           */
  #define FMulDiv( a, b, c )  ( (a) * (b) / (c) )
  
    /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
    /* for clipping computations.  It simply uses the FT_MulDiv() function   */
    /* defined in `ftcalc.h'.                                                */
  #define SMulDiv           FT_MulDiv
  #define SMulDiv_No_Round  FT_MulDiv_No_Round
  
    /* The rasterizer is a very general purpose component; please leave */
    /* the following redefinitions there (you never know your target    */
    /* environment).                                                    */
  
  #ifndef TRUE
  #define TRUE   1
  #endif
  
  #ifndef FALSE
  #define FALSE  0
  #endif
  
  #ifndef NULL
  #define NULL  (void*)0
  #endif
  
  #ifndef SUCCESS
  #define SUCCESS  0
  #endif
  
  #ifndef FAILURE
  #define FAILURE  1
  #endif
  
  
  #define MaxBezier  32   /* The maximum number of stacked Bezier curves. */
                          /* Setting this constant to more than 32 is a   */
                          /* pure waste of space.                         */
  
  #define Pixel_Bits  6   /* fractional bits of *input* coordinates */
  
  
    /*************************************************************************/
    /*************************************************************************/
    /**                                                                     **/
    /**  SIMPLE TYPE DECLARATIONS                                           **/
    /**                                                                     **/
    /*************************************************************************/
    /*************************************************************************/
  
    typedef int             Int;
    typedef unsigned int    UInt;
    typedef short           Short;
    typedef unsigned short  UShort, *PUShort;
    typedef long            Long, *PLong;
    typedef unsigned long   ULong;
  
    typedef unsigned char   Byte, *PByte;
    typedef char            Bool;
  
  
    typedef union  Alignment_
    {
      Long    l;
      void*   p;
      void  (*f)(void);
  
    } Alignment, *PAlignment;
  
  
    typedef struct  TPoint_
    {
      Long  x;
      Long  y;
  
    } TPoint;
  
  
    /* values for the `flags' bit field */
  #define Flow_Up           0x08U
  #define Overshoot_Top     0x10U
  #define Overshoot_Bottom  0x20U
  
  
    /* States of each line, arc, and profile */
    typedef enum  TStates_
    {
      Unknown_State,
      Ascending_State,
      Descending_State,
      Flat_State
  
    } TStates;
  
  
    typedef struct TProfile_  TProfile;
    typedef TProfile*         PProfile;
  
    struct  TProfile_
    {
      FT_F26Dot6  X;           /* current coordinate during sweep          */
      PProfile    link;        /* link to next profile (various purposes)  */
      PLong       offset;      /* start of profile's data in render pool   */
      UShort      flags;       /* Bit 0-2: drop-out mode                   */
                               /* Bit 3: profile orientation (up/down)     */
                               /* Bit 4: is top profile?                   */
                               /* Bit 5: is bottom profile?                */
      Long        height;      /* profile's height in scanlines            */
      Long        start;       /* profile's starting scanline              */
  
      Int         countL;      /* number of lines to step before this      */
                               /* profile becomes drawable                 */
  
      PProfile    next;        /* next profile in same contour, used       */
                               /* during drop-out control                  */
    };
  
    typedef PProfile   TProfileList;
    typedef PProfile*  PProfileList;
  
  
    /* Simple record used to implement a stack of bands, required */
    /* by the sub-banding mechanism                               */
    typedef struct  black_TBand_
    {
      Short  y_min;   /* band's minimum */
      Short  y_max;   /* band's maximum */
  
    } black_TBand;
  
  
  #define AlignProfileSize \
    ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( Long ) )
  
  
  #undef RAS_ARG
  #undef RAS_ARGS
  #undef RAS_VAR
  #undef RAS_VARS
  
  #ifdef FT_STATIC_RASTER
  
  
  #define RAS_ARGS       /* void */
  #define RAS_ARG        void
  
  #define RAS_VARS       /* void */
  #define RAS_VAR        /* void */
  
  #define FT_UNUSED_RASTER  do { } while ( 0 )
  
  
  #else /* !FT_STATIC_RASTER */
  
  
  #define RAS_ARGS       black_PWorker  worker,
  #define RAS_ARG        black_PWorker  worker
  
  #define RAS_VARS       worker,
  #define RAS_VAR        worker
  
  #define FT_UNUSED_RASTER  FT_UNUSED( worker )
  
  
  #endif /* !FT_STATIC_RASTER */
  
  
    typedef struct black_TWorker_  black_TWorker, *black_PWorker;
  
  
    /* prototypes used for sweep function dispatch */
    typedef void
    Function_Sweep_Init( RAS_ARGS Short*  min,
                                  Short*  max );
  
    typedef void
    Function_Sweep_Span( RAS_ARGS Short       y,
                                  FT_F26Dot6  x1,
                                  FT_F26Dot6  x2,
                                  PProfile    left,
                                  PProfile    right );
  
    typedef void
    Function_Sweep_Step( RAS_ARG );
  
  
    /* NOTE: These operations are only valid on 2's complement processors */
  #undef FLOOR
  #undef CEILING
  #undef TRUNC
  #undef SCALED
  
  #define FLOOR( x )    ( (x) & -ras.precision )
  #define CEILING( x )  ( ( (x) + ras.precision - 1 ) & -ras.precision )
  #define TRUNC( x )    ( (Long)(x) >> ras.precision_bits )
  #define FRAC( x )     ( (x) & ( ras.precision - 1 ) )
  
    /* scale and shift grid to pixel centers */
  #define SCALED( x )   ( (x) * ras.precision_scale - ras.precision_half )
  
  #define IS_BOTTOM_OVERSHOOT( x ) \
            (Bool)( CEILING( x ) - x >= ras.precision_half )
  #define IS_TOP_OVERSHOOT( x )    \
            (Bool)( x - FLOOR( x ) >= ras.precision_half )
  
  #if FT_RENDER_POOL_SIZE > 2048
  #define FT_MAX_BLACK_POOL  ( FT_RENDER_POOL_SIZE / sizeof ( Long ) )
  #else
  #define FT_MAX_BLACK_POOL  ( 2048 / sizeof ( Long ) )
  #endif
  
    /* The most used variables are positioned at the top of the structure. */
    /* Thus, their offset can be coded with less opcodes, resulting in a   */
    /* smaller executable.                                                 */
  
    struct  black_TWorker_
    {
      Int         precision_bits;     /* precision related variables         */
      Int         precision;
      Int         precision_half;
      Int         precision_scale;
      Int         precision_step;
      Int         precision_jitter;
  
      PLong       buff;               /* The profiles buffer                 */
      PLong       sizeBuff;           /* Render pool size                    */
      PLong       maxBuff;            /* Profiles buffer size                */
      PLong       top;                /* Current cursor in buffer            */
  
      FT_Error    error;
  
      Int         numTurns;           /* number of Y-turns in outline        */
  
      TPoint*     arc;                /* current Bezier arc pointer          */
  
      UShort      bWidth;             /* target bitmap width                 */
      PByte       bOrigin;            /* target bitmap bottom-left origin    */
  
      Long        lastX, lastY;
      Long        minY, maxY;
  
      UShort      num_Profs;          /* current number of profiles          */
  
      Bool        fresh;              /* signals a fresh new profile which   */
                                      /* `start' field must be completed     */
      Bool        joint;              /* signals that the last arc ended     */
                                      /* exactly on a scanline.  Allows      */
                                      /* removal of doublets                 */
      PProfile    cProfile;           /* current profile                     */
      PProfile    fProfile;           /* head of linked list of profiles     */
      PProfile    gProfile;           /* contour's first profile in case     */
                                      /* of impact                           */
  
      TStates     state;              /* rendering state                     */
  
      FT_Bitmap   target;             /* description of target bit/pixmap    */
      FT_Outline  outline;
  
      Long        traceOfs;           /* current offset in target bitmap     */
      Short       traceIncr;          /* sweep's increment in target bitmap  */
  
      /* dispatch variables */
  
      Function_Sweep_Init*  Proc_Sweep_Init;
      Function_Sweep_Span*  Proc_Sweep_Span;
      Function_Sweep_Span*  Proc_Sweep_Drop;
      Function_Sweep_Step*  Proc_Sweep_Step;
  
      Byte        dropOutControl;     /* current drop_out control method     */
  
      Bool        second_pass;        /* indicates whether a horizontal pass */
                                      /* should be performed to control      */
                                      /* drop-out accurately when calling    */
                                      /* Render_Glyph.                       */
  
      TPoint      arcs[3 * MaxBezier + 1]; /* The Bezier stack               */
  
      black_TBand  band_stack[16];    /* band stack used for sub-banding     */
      Int          band_top;          /* band stack top                      */
  
    };
  
  
    typedef struct  black_TRaster_
    {
      void*          memory;
  
    } black_TRaster, *black_PRaster;
  
  #ifdef FT_STATIC_RASTER
  
    static black_TWorker  ras;
  
  #else /* !FT_STATIC_RASTER */
  
  #define ras  (*worker)
  
  #endif /* !FT_STATIC_RASTER */
  
  
    /*************************************************************************/
    /*************************************************************************/
    /**                                                                     **/
    /**  PROFILES COMPUTATION                                               **/
    /**                                                                     **/
    /*************************************************************************/
    /*************************************************************************/
  
  
    /**************************************************************************
     *
     * @Function:
     *   Set_High_Precision
     *
     * @Description:
     *   Set precision variables according to param flag.
     *
     * @Input:
     *   High ::
     *     Set to True for high precision (typically for ppem < 24),
     *     false otherwise.
     */
    static void
    Set_High_Precision( RAS_ARGS Int  High )
    {
      /*
       * `precision_step' is used in `Bezier_Up' to decide when to split a
       * given y-monotonous Bezier arc that crosses a scanline before
       * approximating it as a straight segment.  The default value of 32 (for
       * low accuracy) corresponds to
       *
       *   32 / 64 == 0.5 pixels,
       *
       * while for the high accuracy case we have
       *
       *   256 / (1 << 12) = 0.0625 pixels.
       *
       * `precision_jitter' is an epsilon threshold used in
       * `Vertical_Sweep_Span' to deal with small imperfections in the Bezier
       * decomposition (after all, we are working with approximations only);
       * it avoids switching on additional pixels which would cause artifacts
       * otherwise.
       *
       * The value of `precision_jitter' has been determined heuristically.
       *
       */
  
      if ( High )
      {
        ras.precision_bits   = 12;
        ras.precision_step   = 256;
        ras.precision_jitter = 30;
      }
      else
      {
        ras.precision_bits   = 6;
        ras.precision_step   = 32;
        ras.precision_jitter = 2;
      }
  
      FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
  
      ras.precision       = 1 << ras.precision_bits;
      ras.precision_half  = ras.precision >> 1;
      ras.precision_scale = ras.precision >> Pixel_Bits;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   New_Profile
     *
     * @Description:
     *   Create a new profile in the render pool.
     *
     * @Input:
     *   aState ::
     *     The state/orientation of the new profile.
     *
     *   overshoot ::
     *     Whether the profile's unrounded start position
     *     differs by at least a half pixel.
     *
     * @Return:
     *  SUCCESS on success.  FAILURE in case of overflow or of incoherent
     *  profile.
     */
    static Bool
    New_Profile( RAS_ARGS TStates  aState,
                          Bool     overshoot )
    {
      if ( !ras.fProfile )
      {
        ras.cProfile  = (PProfile)ras.top;
        ras.fProfile  = ras.cProfile;
        ras.top      += AlignProfileSize;
      }
  
      if ( ras.top >= ras.maxBuff )
      {
        ras.error = FT_THROW( Overflow );
        return FAILURE;
      }
  
      ras.cProfile->start  = 0;
      ras.cProfile->height = 0;
      ras.cProfile->offset = ras.top;
      ras.cProfile->link   = (PProfile)0;
      ras.cProfile->next   = (PProfile)0;
      ras.cProfile->flags  = ras.dropOutControl;
  
      switch ( aState )
      {
      case Ascending_State:
        ras.cProfile->flags |= Flow_Up;
        if ( overshoot )
          ras.cProfile->flags |= Overshoot_Bottom;
  
        FT_TRACE6(( "  new ascending profile = %p\n", ras.cProfile ));
        break;
  
      case Descending_State:
        if ( overshoot )
          ras.cProfile->flags |= Overshoot_Top;
        FT_TRACE6(( "  new descending profile = %p\n", ras.cProfile ));
        break;
  
      default:
        FT_ERROR(( "New_Profile: invalid profile direction\n" ));
        ras.error = FT_THROW( Invalid );
        return FAILURE;
      }
  
      if ( !ras.gProfile )
        ras.gProfile = ras.cProfile;
  
      ras.state = aState;
      ras.fresh = TRUE;
      ras.joint = FALSE;
  
      return SUCCESS;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   End_Profile
     *
     * @Description:
     *   Finalize the current profile.
     *
     * @Input:
     *   overshoot ::
     *     Whether the profile's unrounded end position differs
     *     by at least a half pixel.
     *
     * @Return:
     *   SUCCESS on success.  FAILURE in case of overflow or incoherency.
     */
    static Bool
    End_Profile( RAS_ARGS Bool  overshoot )
    {
      Long  h;
  
  
      h = (Long)( ras.top - ras.cProfile->offset );
  
      if ( h < 0 )
      {
        FT_ERROR(( "End_Profile: negative height encountered\n" ));
        ras.error = FT_THROW( Neg_Height );
        return FAILURE;
      }
  
      if ( h > 0 )
      {
        PProfile  oldProfile;
  
  
        FT_TRACE6(( "  ending profile %p, start = %ld, height = %ld\n",
                    ras.cProfile, ras.cProfile->start, h ));
  
        ras.cProfile->height = h;
        if ( overshoot )
        {
          if ( ras.cProfile->flags & Flow_Up )
            ras.cProfile->flags |= Overshoot_Top;
          else
            ras.cProfile->flags |= Overshoot_Bottom;
        }
  
        oldProfile   = ras.cProfile;
        ras.cProfile = (PProfile)ras.top;
  
        ras.top += AlignProfileSize;
  
        ras.cProfile->height = 0;
        ras.cProfile->offset = ras.top;
  
        oldProfile->next = ras.cProfile;
        ras.num_Profs++;
      }
  
      if ( ras.top >= ras.maxBuff )
      {
        FT_TRACE1(( "overflow in End_Profile\n" ));
        ras.error = FT_THROW( Overflow );
        return FAILURE;
      }
  
      ras.joint = FALSE;
  
      return SUCCESS;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Insert_Y_Turn
     *
     * @Description:
     *   Insert a salient into the sorted list placed on top of the render
     *   pool.
     *
     * @Input:
     *   New y scanline position.
     *
     * @Return:
     *   SUCCESS on success.  FAILURE in case of overflow.
     */
    static Bool
    Insert_Y_Turn( RAS_ARGS Int  y )
    {
      PLong  y_turns;
      Int    n;
  
  
      n       = ras.numTurns - 1;
      y_turns = ras.sizeBuff - ras.numTurns;
  
      /* look for first y value that is <= */
      while ( n >= 0 && y < y_turns[n] )
        n--;
  
      /* if it is <, simply insert it, ignore if == */
      if ( n >= 0 && y > y_turns[n] )
        do
        {
          Int  y2 = (Int)y_turns[n];
  
  
          y_turns[n] = y;
          y = y2;
        } while ( --n >= 0 );
  
      if ( n < 0 )
      {
        ras.maxBuff--;
        if ( ras.maxBuff <= ras.top )
        {
          ras.error = FT_THROW( Overflow );
          return FAILURE;
        }
        ras.numTurns++;
        ras.sizeBuff[-ras.numTurns] = y;
      }
  
      return SUCCESS;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Finalize_Profile_Table
     *
     * @Description:
     *   Adjust all links in the profiles list.
     *
     * @Return:
     *   SUCCESS on success.  FAILURE in case of overflow.
     */
    static Bool
    Finalize_Profile_Table( RAS_ARG )
    {
      UShort    n;
      PProfile  p;
  
  
      n = ras.num_Profs;
      p = ras.fProfile;
  
      if ( n > 1 && p )
      {
        do
        {
          Int  bottom, top;
  
  
          if ( n > 1 )
            p->link = (PProfile)( p->offset + p->height );
          else
            p->link = NULL;
  
          if ( p->flags & Flow_Up )
          {
            bottom = (Int)p->start;
            top    = (Int)( p->start + p->height - 1 );
          }
          else
          {
            bottom     = (Int)( p->start - p->height + 1 );
            top        = (Int)p->start;
            p->start   = bottom;
            p->offset += p->height - 1;
          }
  
          if ( Insert_Y_Turn( RAS_VARS bottom )  ||
               Insert_Y_Turn( RAS_VARS top + 1 ) )
            return FAILURE;
  
          p = p->link;
        } while ( --n );
      }
      else
        ras.fProfile = NULL;
  
      return SUCCESS;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Split_Conic
     *
     * @Description:
     *   Subdivide one conic Bezier into two joint sub-arcs in the Bezier
     *   stack.
     *
     * @Input:
     *   None (subdivided Bezier is taken from the top of the stack).
     *
     * @Note:
     *   This routine is the `beef' of this component.  It is  _the_ inner
     *   loop that should be optimized to hell to get the best performance.
     */
    static void
    Split_Conic( TPoint*  base )
    {
      Long  a, b;
  
  
      base[4].x = base[2].x;
      a = base[0].x + base[1].x;
      b = base[1].x + base[2].x;
      base[3].x = b >> 1;
      base[2].x = ( a + b ) >> 2;
      base[1].x = a >> 1;
  
      base[4].y = base[2].y;
      a = base[0].y + base[1].y;
      b = base[1].y + base[2].y;
      base[3].y = b >> 1;
      base[2].y = ( a + b ) >> 2;
      base[1].y = a >> 1;
  
      /* hand optimized.  gcc doesn't seem to be too good at common      */
      /* expression substitution and instruction scheduling ;-)          */
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Split_Cubic
     *
     * @Description:
     *   Subdivide a third-order Bezier arc into two joint sub-arcs in the
     *   Bezier stack.
     *
     * @Note:
     *   This routine is the `beef' of the component.  It is one of _the_
     *   inner loops that should be optimized like hell to get the best
     *   performance.
     */
    static void
    Split_Cubic( TPoint*  base )
    {
      Long  a, b, c;
  
  
      base[6].x = base[3].x;
      a = base[0].x + base[1].x;
      b = base[1].x + base[2].x;
      c = base[2].x + base[3].x;
      base[5].x = c >> 1;
      c += b;
      base[4].x = c >> 2;
      base[1].x = a >> 1;
      a += b;
      base[2].x = a >> 2;
      base[3].x = ( a + c ) >> 3;
  
      base[6].y = base[3].y;
      a = base[0].y + base[1].y;
      b = base[1].y + base[2].y;
      c = base[2].y + base[3].y;
      base[5].y = c >> 1;
      c += b;
      base[4].y = c >> 2;
      base[1].y = a >> 1;
      a += b;
      base[2].y = a >> 2;
      base[3].y = ( a + c ) >> 3;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Line_Up
     *
     * @Description:
     *   Compute the x-coordinates of an ascending line segment and store
     *   them in the render pool.
     *
     * @Input:
     *   x1 ::
     *     The x-coordinate of the segment's start point.
     *
     *   y1 ::
     *     The y-coordinate of the segment's start point.
     *
     *   x2 ::
     *     The x-coordinate of the segment's end point.
     *
     *   y2 ::
     *     The y-coordinate of the segment's end point.
     *
     *   miny ::
     *     A lower vertical clipping bound value.
     *
     *   maxy ::
     *     An upper vertical clipping bound value.
     *
     * @Return:
     *   SUCCESS on success, FAILURE on render pool overflow.
     */
    static Bool
    Line_Up( RAS_ARGS Long  x1,
                      Long  y1,
                      Long  x2,
                      Long  y2,
                      Long  miny,
                      Long  maxy )
    {
      Long   Dx, Dy;
      Int    e1, e2, f1, f2, size;     /* XXX: is `Short' sufficient? */
      Long   Ix, Rx, Ax;
  
      PLong  top;
  
  
      Dx = x2 - x1;
      Dy = y2 - y1;
  
      if ( Dy <= 0 || y2 < miny || y1 > maxy )
        return SUCCESS;
  
      if ( y1 < miny )
      {
        /* Take care: miny-y1 can be a very large value; we use     */
        /*            a slow MulDiv function to avoid clipping bugs */
        x1 += SMulDiv( Dx, miny - y1, Dy );
        e1  = (Int)TRUNC( miny );
        f1  = 0;
      }
      else
      {
        e1 = (Int)TRUNC( y1 );
        f1 = (Int)FRAC( y1 );
      }
  
      if ( y2 > maxy )
      {
        /* x2 += FMulDiv( Dx, maxy - y2, Dy );  UNNECESSARY */
        e2  = (Int)TRUNC( maxy );
        f2  = 0;
      }
      else
      {
        e2 = (Int)TRUNC( y2 );
        f2 = (Int)FRAC( y2 );
      }
  
      if ( f1 > 0 )
      {
        if ( e1 == e2 )
          return SUCCESS;
        else
        {
          x1 += SMulDiv( Dx, ras.precision - f1, Dy );
          e1 += 1;
        }
      }
      else
        if ( ras.joint )
        {
          ras.top--;
          ras.joint = FALSE;
        }
  
      ras.joint = (char)( f2 == 0 );
  
      if ( ras.fresh )
      {
        ras.cProfile->start = e1;
        ras.fresh           = FALSE;
      }
  
      size = e2 - e1 + 1;
      if ( ras.top + size >= ras.maxBuff )
      {
        ras.error = FT_THROW( Overflow );
        return FAILURE;
      }
  
      if ( Dx > 0 )
      {
        Ix = SMulDiv_No_Round( ras.precision, Dx, Dy );
        Rx = ( ras.precision * Dx ) % Dy;
        Dx = 1;
      }
      else
      {
        Ix = -SMulDiv_No_Round( ras.precision, -Dx, Dy );
        Rx = ( ras.precision * -Dx ) % Dy;
        Dx = -1;
      }
  
      Ax  = -Dy;
      top = ras.top;
  
      while ( size > 0 )
      {
        *top++ = x1;
  
        x1 += Ix;
        Ax += Rx;
        if ( Ax >= 0 )
        {
          Ax -= Dy;
          x1 += Dx;
        }
        size--;
      }
  
      ras.top = top;
      return SUCCESS;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Line_Down
     *
     * @Description:
     *   Compute the x-coordinates of an descending line segment and store
     *   them in the render pool.
     *
     * @Input:
     *   x1 ::
     *     The x-coordinate of the segment's start point.
     *
     *   y1 ::
     *     The y-coordinate of the segment's start point.
     *
     *   x2 ::
     *     The x-coordinate of the segment's end point.
     *
     *   y2 ::
     *     The y-coordinate of the segment's end point.
     *
     *   miny ::
     *     A lower vertical clipping bound value.
     *
     *   maxy ::
     *     An upper vertical clipping bound value.
     *
     * @Return:
     *   SUCCESS on success, FAILURE on render pool overflow.
     */
    static Bool
    Line_Down( RAS_ARGS Long  x1,
                        Long  y1,
                        Long  x2,
                        Long  y2,
                        Long  miny,
                        Long  maxy )
    {
      Bool  result, fresh;
  
  
      fresh  = ras.fresh;
  
      result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
  
      if ( fresh && !ras.fresh )
        ras.cProfile->start = -ras.cProfile->start;
  
      return result;
    }
  
  
    /* A function type describing the functions used to split Bezier arcs */
    typedef void  (*TSplitter)( TPoint*  base );
  
  
    /**************************************************************************
     *
     * @Function:
     *   Bezier_Up
     *
     * @Description:
     *   Compute the x-coordinates of an ascending Bezier arc and store
     *   them in the render pool.
     *
     * @Input:
     *   degree ::
     *     The degree of the Bezier arc (either 2 or 3).
     *
     *   splitter ::
     *     The function to split Bezier arcs.
     *
     *   miny ::
     *     A lower vertical clipping bound value.
     *
     *   maxy ::
     *     An upper vertical clipping bound value.
     *
     * @Return:
     *   SUCCESS on success, FAILURE on render pool overflow.
     */
    static Bool
    Bezier_Up( RAS_ARGS Int        degree,
                        TSplitter  splitter,
                        Long       miny,
                        Long       maxy )
    {
      Long   y1, y2, e, e2, e0;
      Short  f1;
  
      TPoint*  arc;
      TPoint*  start_arc;
  
      PLong top;
  
  
      arc = ras.arc;
      y1  = arc[degree].y;
      y2  = arc[0].y;
      top = ras.top;
  
      if ( y2 < miny || y1 > maxy )
        goto Fin;
  
      e2 = FLOOR( y2 );
  
      if ( e2 > maxy )
        e2 = maxy;
  
      e0 = miny;
  
      if ( y1 < miny )
        e = miny;
      else
      {
        e  = CEILING( y1 );
        f1 = (Short)( FRAC( y1 ) );
        e0 = e;
  
        if ( f1 == 0 )
        {
          if ( ras.joint )
          {
            top--;
            ras.joint = FALSE;
          }
  
          *top++ = arc[degree].x;
  
          e += ras.precision;
        }
      }
  
      if ( ras.fresh )
      {
        ras.cProfile->start = TRUNC( e0 );
        ras.fresh = FALSE;
      }
  
      if ( e2 < e )
        goto Fin;
  
      if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
      {
        ras.top   = top;
        ras.error = FT_THROW( Overflow );
        return FAILURE;
      }
  
      start_arc = arc;
  
      do
      {
        ras.joint = FALSE;
  
        y2 = arc[0].y;
  
        if ( y2 > e )
        {
          y1 = arc[degree].y;
          if ( y2 - y1 >= ras.precision_step )
          {
            splitter( arc );
            arc += degree;
          }
          else
          {
            *top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x,
                                              e - y1, y2 - y1 );
            arc -= degree;
            e   += ras.precision;
          }
        }
        else
        {
          if ( y2 == e )
          {
            ras.joint  = TRUE;
            *top++     = arc[0].x;
  
            e += ras.precision;
          }
          arc -= degree;
        }
      } while ( arc >= start_arc && e <= e2 );
  
    Fin:
      ras.top  = top;
      ras.arc -= degree;
      return SUCCESS;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Bezier_Down
     *
     * @Description:
     *   Compute the x-coordinates of an descending Bezier arc and store
     *   them in the render pool.
     *
     * @Input:
     *   degree ::
     *     The degree of the Bezier arc (either 2 or 3).
     *
     *   splitter ::
     *     The function to split Bezier arcs.
     *
     *   miny ::
     *     A lower vertical clipping bound value.
     *
     *   maxy ::
     *     An upper vertical clipping bound value.
     *
     * @Return:
     *   SUCCESS on success, FAILURE on render pool overflow.
     */
    static Bool
    Bezier_Down( RAS_ARGS Int        degree,
                          TSplitter  splitter,
                          Long       miny,
                          Long       maxy )
    {
      TPoint*  arc = ras.arc;
      Bool     result, fresh;
  
  
      arc[0].y = -arc[0].y;
      arc[1].y = -arc[1].y;
      arc[2].y = -arc[2].y;
      if ( degree > 2 )
        arc[3].y = -arc[3].y;
  
      fresh = ras.fresh;
  
      result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
  
      if ( fresh && !ras.fresh )
        ras.cProfile->start = -ras.cProfile->start;
  
      arc[0].y = -arc[0].y;
      return result;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Line_To
     *
     * @Description:
     *   Inject a new line segment and adjust the Profiles list.
     *
     * @Input:
     *  x ::
     *    The x-coordinate of the segment's end point (its start point
     *    is stored in `lastX').
     *
     *  y ::
     *    The y-coordinate of the segment's end point (its start point
     *    is stored in `lastY').
     *
     * @Return:
     *  SUCCESS on success, FAILURE on render pool overflow or incorrect
     *  profile.
     */
    static Bool
    Line_To( RAS_ARGS Long  x,
                      Long  y )
    {
      /* First, detect a change of direction */
  
      switch ( ras.state )
      {
      case Unknown_State:
        if ( y > ras.lastY )
        {
          if ( New_Profile( RAS_VARS Ascending_State,
                                     IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
            return FAILURE;
        }
        else
        {
          if ( y < ras.lastY )
            if ( New_Profile( RAS_VARS Descending_State,
                                       IS_TOP_OVERSHOOT( ras.lastY ) ) )
              return FAILURE;
        }
        break;
  
      case Ascending_State:
        if ( y < ras.lastY )
        {
          if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
               New_Profile( RAS_VARS Descending_State,
                                     IS_TOP_OVERSHOOT( ras.lastY ) ) )
            return FAILURE;
        }
        break;
  
      case Descending_State:
        if ( y > ras.lastY )
        {
          if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
               New_Profile( RAS_VARS Ascending_State,
                                     IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
            return FAILURE;
        }
        break;
  
      default:
        ;
      }
  
      /* Then compute the lines */
  
      switch ( ras.state )
      {
      case Ascending_State:
        if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
                               x, y, ras.minY, ras.maxY ) )
          return FAILURE;
        break;
  
      case Descending_State:
        if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
                                 x, y, ras.minY, ras.maxY ) )
          return FAILURE;
        break;
  
      default:
        ;
      }
  
      ras.lastX = x;
      ras.lastY = y;
  
      return SUCCESS;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Conic_To
     *
     * @Description:
     *   Inject a new conic arc and adjust the profile list.
     *
     * @Input:
     *  cx ::
     *    The x-coordinate of the arc's new control point.
     *
     *  cy ::
     *    The y-coordinate of the arc's new control point.
     *
     *  x ::
     *    The x-coordinate of the arc's end point (its start point is
     *    stored in `lastX').
     *
     *  y ::
     *    The y-coordinate of the arc's end point (its start point is
     *    stored in `lastY').
     *
     * @Return:
     *  SUCCESS on success, FAILURE on render pool overflow or incorrect
     *  profile.
     */
    static Bool
    Conic_To( RAS_ARGS Long  cx,
                       Long  cy,
                       Long  x,
                       Long  y )
    {
      Long     y1, y2, y3, x3, ymin, ymax;
      TStates  state_bez;
  
  
      ras.arc      = ras.arcs;
      ras.arc[2].x = ras.lastX;
      ras.arc[2].y = ras.lastY;
      ras.arc[1].x = cx;
      ras.arc[1].y = cy;
      ras.arc[0].x = x;
      ras.arc[0].y = y;
  
      do
      {
        y1 = ras.arc[2].y;
        y2 = ras.arc[1].y;
        y3 = ras.arc[0].y;
        x3 = ras.arc[0].x;
  
        /* first, categorize the Bezier arc */
  
        if ( y1 <= y3 )
        {
          ymin = y1;
          ymax = y3;
        }
        else
        {
          ymin = y3;
          ymax = y1;
        }
  
        if ( y2 < ymin || y2 > ymax )
        {
          /* this arc has no given direction, split it! */
          Split_Conic( ras.arc );
          ras.arc += 2;
        }
        else if ( y1 == y3 )
        {
          /* this arc is flat, ignore it and pop it from the Bezier stack */
          ras.arc -= 2;
        }
        else
        {
          /* the arc is y-monotonous, either ascending or descending */
          /* detect a change of direction                            */
          state_bez = y1 < y3 ? Ascending_State : Descending_State;
          if ( ras.state != state_bez )
          {
            Bool  o = ( state_bez == Ascending_State )
                        ? IS_BOTTOM_OVERSHOOT( y1 )
                        : IS_TOP_OVERSHOOT( y1 );
  
  
            /* finalize current profile if any */
            if ( ras.state != Unknown_State &&
                 End_Profile( RAS_VARS o )  )
              goto Fail;
  
            /* create a new profile */
            if ( New_Profile( RAS_VARS state_bez, o ) )
              goto Fail;
          }
  
          /* now call the appropriate routine */
          if ( state_bez == Ascending_State )
          {
            if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
              goto Fail;
          }
          else
            if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
              goto Fail;
        }
  
      } while ( ras.arc >= ras.arcs );
  
      ras.lastX = x3;
      ras.lastY = y3;
  
      return SUCCESS;
  
    Fail:
      return FAILURE;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Cubic_To
     *
     * @Description:
     *   Inject a new cubic arc and adjust the profile list.
     *
     * @Input:
     *  cx1 ::
     *    The x-coordinate of the arc's first new control point.
     *
     *  cy1 ::
     *    The y-coordinate of the arc's first new control point.
     *
     *  cx2 ::
     *    The x-coordinate of the arc's second new control point.
     *
     *  cy2 ::
     *    The y-coordinate of the arc's second new control point.
     *
     *  x ::
     *    The x-coordinate of the arc's end point (its start point is
     *    stored in `lastX').
     *
     *  y ::
     *    The y-coordinate of the arc's end point (its start point is
     *    stored in `lastY').
     *
     * @Return:
     *  SUCCESS on success, FAILURE on render pool overflow or incorrect
     *  profile.
     */
    static Bool
    Cubic_To( RAS_ARGS Long  cx1,
                       Long  cy1,
                       Long  cx2,
                       Long  cy2,
                       Long  x,
                       Long  y )
    {
      Long     y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
      TStates  state_bez;
  
  
      ras.arc      = ras.arcs;
      ras.arc[3].x = ras.lastX;
      ras.arc[3].y = ras.lastY;
      ras.arc[2].x = cx1;
      ras.arc[2].y = cy1;
      ras.arc[1].x = cx2;
      ras.arc[1].y = cy2;
      ras.arc[0].x = x;
      ras.arc[0].y = y;
  
      do
      {
        y1 = ras.arc[3].y;
        y2 = ras.arc[2].y;
        y3 = ras.arc[1].y;
        y4 = ras.arc[0].y;
        x4 = ras.arc[0].x;
  
        /* first, categorize the Bezier arc */
  
        if ( y1 <= y4 )
        {
          ymin1 = y1;
          ymax1 = y4;
        }
        else
        {
          ymin1 = y4;
          ymax1 = y1;
        }
  
        if ( y2 <= y3 )
        {
          ymin2 = y2;
          ymax2 = y3;
        }
        else
        {
          ymin2 = y3;
          ymax2 = y2;
        }
  
        if ( ymin2 < ymin1 || ymax2 > ymax1 )
        {
          /* this arc has no given direction, split it! */
          Split_Cubic( ras.arc );
          ras.arc += 3;
        }
        else if ( y1 == y4 )
        {
          /* this arc is flat, ignore it and pop it from the Bezier stack */
          ras.arc -= 3;
        }
        else
        {
          state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;
  
          /* detect a change of direction */
          if ( ras.state != state_bez )
          {
            Bool  o = ( state_bez == Ascending_State )
                        ? IS_BOTTOM_OVERSHOOT( y1 )
                        : IS_TOP_OVERSHOOT( y1 );
  
  
            /* finalize current profile if any */
            if ( ras.state != Unknown_State &&
                 End_Profile( RAS_VARS o )  )
              goto Fail;
  
            if ( New_Profile( RAS_VARS state_bez, o ) )
              goto Fail;
          }
  
          /* compute intersections */
          if ( state_bez == Ascending_State )
          {
            if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
              goto Fail;
          }
          else
            if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
              goto Fail;
        }
  
      } while ( ras.arc >= ras.arcs );
  
      ras.lastX = x4;
      ras.lastY = y4;
  
      return SUCCESS;
  
    Fail:
      return FAILURE;
    }
  
  
  #undef  SWAP_
  #define SWAP_( x, y )  do                \
                         {                 \
                           Long  swap = x; \
                                           \
                                           \
                           x = y;          \
                           y = swap;       \
                         } while ( 0 )
  
  
    /**************************************************************************
     *
     * @Function:
     *   Decompose_Curve
     *
     * @Description:
     *   Scan the outline arrays in order to emit individual segments and
     *   Beziers by calling Line_To() and Bezier_To().  It handles all
     *   weird cases, like when the first point is off the curve, or when
     *   there are simply no `on' points in the contour!
     *
     * @Input:
     *   first ::
     *     The index of the first point in the contour.
     *
     *   last ::
     *     The index of the last point in the contour.
     *
     *   flipped ::
     *     If set, flip the direction of the curve.
     *
     * @Return:
     *   SUCCESS on success, FAILURE on error.
     */
    static Bool
    Decompose_Curve( RAS_ARGS UShort  first,
                              UShort  last,
                              Int     flipped )
    {
      FT_Vector   v_last;
      FT_Vector   v_control;
      FT_Vector   v_start;
  
      FT_Vector*  points;
      FT_Vector*  point;
      FT_Vector*  limit;
      char*       tags;
  
      UInt        tag;       /* current point's state           */
  
  
      points = ras.outline.points;
      limit  = points + last;
  
      v_start.x = SCALED( points[first].x );
      v_start.y = SCALED( points[first].y );
      v_last.x  = SCALED( points[last].x );
      v_last.y  = SCALED( points[last].y );
  
      if ( flipped )
      {
        SWAP_( v_start.x, v_start.y );
        SWAP_( v_last.x, v_last.y );
      }
  
      v_control = v_start;
  
      point = points + first;
      tags  = ras.outline.tags + first;
  
      /* set scan mode if necessary */
      if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE )
        ras.dropOutControl = (Byte)tags[0] >> 5;
  
      tag = FT_CURVE_TAG( tags[0] );
  
      /* A contour cannot start with a cubic control point! */
      if ( tag == FT_CURVE_TAG_CUBIC )
        goto Invalid_Outline;
  
      /* check first point to determine origin */
      if ( tag == FT_CURVE_TAG_CONIC )
      {
        /* first point is conic control.  Yes, this happens. */
        if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
        {
          /* start at last point if it is on the curve */
          v_start = v_last;
          limit--;
        }
        else
        {
          /* if both first and last points are conic,         */
          /* start at their middle and record its position    */
          /* for closure                                      */
          v_start.x = ( v_start.x + v_last.x ) / 2;
          v_start.y = ( v_start.y + v_last.y ) / 2;
  
       /* v_last = v_start; */
        }
        point--;
        tags--;
      }
  
      ras.lastX = v_start.x;
      ras.lastY = v_start.y;
  
      while ( point < limit )
      {
        point++;
        tags++;
  
        tag = FT_CURVE_TAG( tags[0] );
  
        switch ( tag )
        {
        case FT_CURVE_TAG_ON:  /* emit a single line_to */
          {
            Long  x, y;
  
  
            x = SCALED( point->x );
            y = SCALED( point->y );
            if ( flipped )
              SWAP_( x, y );
  
            if ( Line_To( RAS_VARS x, y ) )
              goto Fail;
            continue;
          }
  
        case FT_CURVE_TAG_CONIC:  /* consume conic arcs */
          v_control.x = SCALED( point[0].x );
          v_control.y = SCALED( point[0].y );
  
          if ( flipped )
            SWAP_( v_control.x, v_control.y );
  
        Do_Conic:
          if ( point < limit )
          {
            FT_Vector  v_middle;
            Long       x, y;
  
  
            point++;
            tags++;
            tag = FT_CURVE_TAG( tags[0] );
  
            x = SCALED( point[0].x );
            y = SCALED( point[0].y );
  
            if ( flipped )
              SWAP_( x, y );
  
            if ( tag == FT_CURVE_TAG_ON )
            {
              if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
                goto Fail;
              continue;
            }
  
            if ( tag != FT_CURVE_TAG_CONIC )
              goto Invalid_Outline;
  
            v_middle.x = ( v_control.x + x ) / 2;
            v_middle.y = ( v_control.y + y ) / 2;
  
            if ( Conic_To( RAS_VARS v_control.x, v_control.y,
                                    v_middle.x,  v_middle.y ) )
              goto Fail;
  
            v_control.x = x;
            v_control.y = y;
  
            goto Do_Conic;
          }
  
          if ( Conic_To( RAS_VARS v_control.x, v_control.y,
                                  v_start.x,   v_start.y ) )
            goto Fail;
  
          goto Close;
  
        default:  /* FT_CURVE_TAG_CUBIC */
          {
            Long  x1, y1, x2, y2, x3, y3;
  
  
            if ( point + 1 > limit                             ||
                 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
              goto Invalid_Outline;
  
            point += 2;
            tags  += 2;
  
            x1 = SCALED( point[-2].x );
            y1 = SCALED( point[-2].y );
            x2 = SCALED( point[-1].x );
            y2 = SCALED( point[-1].y );
  
            if ( flipped )
            {
              SWAP_( x1, y1 );
              SWAP_( x2, y2 );
            }
  
            if ( point <= limit )
            {
              x3 = SCALED( point[0].x );
              y3 = SCALED( point[0].y );
  
              if ( flipped )
                SWAP_( x3, y3 );
  
              if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
                goto Fail;
              continue;
            }
  
            if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
              goto Fail;
            goto Close;
          }
        }
      }
  
      /* close the contour with a line segment */
      if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
        goto Fail;
  
    Close:
      return SUCCESS;
  
    Invalid_Outline:
      ras.error = FT_THROW( Invalid );
  
    Fail:
      return FAILURE;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Convert_Glyph
     *
     * @Description:
     *   Convert a glyph into a series of segments and arcs and make a
     *   profiles list with them.
     *
     * @Input:
     *   flipped ::
     *     If set, flip the direction of curve.
     *
     * @Return:
     *   SUCCESS on success, FAILURE if any error was encountered during
     *   rendering.
     */
    static Bool
    Convert_Glyph( RAS_ARGS Int  flipped )
    {
      Int   i;
      UInt  start;
  
  
      ras.fProfile = NULL;
      ras.joint    = FALSE;
      ras.fresh    = FALSE;
  
      ras.maxBuff  = ras.sizeBuff - AlignProfileSize;
  
      ras.numTurns = 0;
  
      ras.cProfile         = (PProfile)ras.top;
      ras.cProfile->offset = ras.top;
      ras.num_Profs        = 0;
  
      start = 0;
  
      for ( i = 0; i < ras.outline.n_contours; i++ )
      {
        PProfile  lastProfile;
        Bool      o;
  
  
        ras.state    = Unknown_State;
        ras.gProfile = NULL;
  
        if ( Decompose_Curve( RAS_VARS (UShort)start,
                                       (UShort)ras.outline.contours[i],
                                       flipped ) )
          return FAILURE;
  
        start = (UShort)ras.outline.contours[i] + 1;
  
        /* we must now check whether the extreme arcs join or not */
        if ( FRAC( ras.lastY ) == 0 &&
             ras.lastY >= ras.minY  &&
             ras.lastY <= ras.maxY  )
          if ( ras.gProfile                        &&
               ( ras.gProfile->flags & Flow_Up ) ==
                 ( ras.cProfile->flags & Flow_Up ) )
            ras.top--;
          /* Note that ras.gProfile can be nil if the contour was too small */
          /* to be drawn.                                                   */
  
        lastProfile = ras.cProfile;
        if ( ras.top != ras.cProfile->offset &&
             ( ras.cProfile->flags & Flow_Up ) )
          o = IS_TOP_OVERSHOOT( ras.lastY );
        else
          o = IS_BOTTOM_OVERSHOOT( ras.lastY );
        if ( End_Profile( RAS_VARS o ) )
          return FAILURE;
  
        /* close the `next profile in contour' linked list */
        if ( ras.gProfile )
          lastProfile->next = ras.gProfile;
      }
  
      if ( Finalize_Profile_Table( RAS_VAR ) )
        return FAILURE;
  
      return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
    }
  
  
    /*************************************************************************/
    /*************************************************************************/
    /**                                                                     **/
    /**  SCAN-LINE SWEEPS AND DRAWING                                       **/
    /**                                                                     **/
    /*************************************************************************/
    /*************************************************************************/
  
  
    /**************************************************************************
     *
     * Init_Linked
     *
     *   Initializes an empty linked list.
     */
    static void
    Init_Linked( TProfileList*  l )
    {
      *l = NULL;
    }
  
  
    /**************************************************************************
     *
     * InsNew
     *
     *   Inserts a new profile in a linked list.
     */
    static void
    InsNew( PProfileList  list,
            PProfile      profile )
    {
      PProfile  *old, current;
      Long       x;
  
  
      old     = list;
      current = *old;
      x       = profile->X;
  
      while ( current )
      {
        if ( x < current->X )
          break;
        old     = &current->link;
        current = *old;
      }
  
      profile->link = current;
      *old          = profile;
    }
  
  
    /**************************************************************************
     *
     * DelOld
     *
     *   Removes an old profile from a linked list.
     */
    static void
    DelOld( PProfileList  list,
            PProfile      profile )
    {
      PProfile  *old, current;
  
  
      old     = list;
      current = *old;
  
      while ( current )
      {
        if ( current == profile )
        {
          *old = current->link;
          return;
        }
  
        old     = &current->link;
        current = *old;
      }
  
      /* we should never get there, unless the profile was not part of */
      /* the list.                                                     */
    }
  
  
    /**************************************************************************
     *
     * Sort
     *
     *   Sorts a trace list.  In 95%, the list is already sorted.  We need
     *   an algorithm which is fast in this case.  Bubble sort is enough
     *   and simple.
     */
    static void
    Sort( PProfileList  list )
    {
      PProfile  *old, current, next;
  
  
      /* First, set the new X coordinate of each profile */
      current = *list;
      while ( current )
      {
        current->X       = *current->offset;
        current->offset += ( current->flags & Flow_Up ) ? 1 : -1;
        current->height--;
        current = current->link;
      }
  
      /* Then sort them */
      old     = list;
      current = *old;
  
      if ( !current )
        return;
  
      next = current->link;
  
      while ( next )
      {
        if ( current->X <= next->X )
        {
          old     = &current->link;
          current = *old;
  
          if ( !current )
            return;
        }
        else
        {
          *old          = next;
          current->link = next->link;
          next->link    = current;
  
          old     = list;
          current = *old;
        }
  
        next = current->link;
      }
    }
  
  
    /**************************************************************************
     *
     * Vertical Sweep Procedure Set
     *
     * These four routines are used during the vertical black/white sweep
     * phase by the generic Draw_Sweep() function.
     *
     */
  
    static void
    Vertical_Sweep_Init( RAS_ARGS Short*  min,
                                  Short*  max )
    {
      Long  pitch = ras.target.pitch;
  
      FT_UNUSED( max );
  
  
      ras.traceIncr = (Short)-pitch;
      ras.traceOfs  = -*min * pitch;
    }
  
  
    static void
    Vertical_Sweep_Span( RAS_ARGS Short       y,
                                  FT_F26Dot6  x1,
                                  FT_F26Dot6  x2,
                                  PProfile    left,
                                  PProfile    right )
    {
      Long   e1, e2;
      Byte*  target;
  
      Int  dropOutControl = left->flags & 7;
  
      FT_UNUSED( y );
      FT_UNUSED( left );
      FT_UNUSED( right );
  
  
      /* in high-precision mode, we need 12 digits after the comma to */
      /* represent multiples of 1/(1<<12) = 1/4096                    */
      FT_TRACE7(( "  y=%d x=[%.12f;%.12f], drop-out=%d",
                  y,
                  x1 / (double)ras.precision,
                  x2 / (double)ras.precision,
                  dropOutControl ));
  
      /* Drop-out control */
  
      e1 = CEILING( x1 );
      e2 = FLOOR( x2 );
  
      /* take care of the special case where both the left */
      /* and right contour lie exactly on pixel centers    */
      if ( dropOutControl != 2                             &&
           x2 - x1 - ras.precision <= ras.precision_jitter &&
           e1 != x1 && e2 != x2                            )
        e2 = e1;
  
      e1 = TRUNC( e1 );
      e2 = TRUNC( e2 );
  
      if ( e2 >= 0 && e1 < ras.bWidth )
      {
        Int   c1, c2;
        Byte  f1, f2;
  
  
        if ( e1 < 0 )
          e1 = 0;
        if ( e2 >= ras.bWidth )
          e2 = ras.bWidth - 1;
  
        FT_TRACE7(( " -> x=[%d;%d]", e1, e2 ));
  
        c1 = (Short)( e1 >> 3 );
        c2 = (Short)( e2 >> 3 );
  
        f1 = (Byte)  ( 0xFF >> ( e1 & 7 ) );
        f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
  
        target = ras.bOrigin + ras.traceOfs + c1;
        c2 -= c1;
  
        if ( c2 > 0 )
        {
          target[0] |= f1;
  
          /* memset() is slower than the following code on many platforms. */
          /* This is due to the fact that, in the vast majority of cases,  */
          /* the span length in bytes is relatively small.                 */
          while ( --c2 > 0 )
            *(++target) = 0xFF;
  
          target[1] |= f2;
        }
        else
          *target |= ( f1 & f2 );
      }
  
      FT_TRACE7(( "\n" ));
    }
  
  
    static void
    Vertical_Sweep_Drop( RAS_ARGS Short       y,
                                  FT_F26Dot6  x1,
                                  FT_F26Dot6  x2,
                                  PProfile    left,
                                  PProfile    right )
    {
      Long   e1, e2, pxl;
      Short  c1, f1;
  
  
      FT_TRACE7(( "  y=%d x=[%.12f;%.12f]",
                  y,
                  x1 / (double)ras.precision,
                  x2 / (double)ras.precision ));
  
      /* Drop-out control */
  
      /*   e2            x2                    x1           e1   */
      /*                                                         */
      /*                 ^                     |                 */
      /*                 |                     |                 */
      /*   +-------------+---------------------+------------+    */
      /*                 |                     |                 */
      /*                 |                     v                 */
      /*                                                         */
      /* pixel         contour              contour       pixel  */
      /* center                                           center */
  
      /* drop-out mode    scan conversion rules (as defined in OpenType) */
      /* --------------------------------------------------------------- */
      /*  0                1, 2, 3                                       */
      /*  1                1, 2, 4                                       */
      /*  2                1, 2                                          */
      /*  3                same as mode 2                                */
      /*  4                1, 2, 5                                       */
      /*  5                1, 2, 6                                       */
      /*  6, 7             same as mode 2                                */
  
      e1  = CEILING( x1 );
      e2  = FLOOR  ( x2 );
      pxl = e1;
  
      if ( e1 > e2 )
      {
        Int  dropOutControl = left->flags & 7;
  
  
        FT_TRACE7(( ", drop-out=%d", dropOutControl ));
  
        if ( e1 == e2 + ras.precision )
        {
          switch ( dropOutControl )
          {
          case 0: /* simple drop-outs including stubs */
            pxl = e2;
            break;
  
          case 4: /* smart drop-outs including stubs */
            pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
            break;
  
          case 1: /* simple drop-outs excluding stubs */
          case 5: /* smart drop-outs excluding stubs  */
  
            /* Drop-out Control Rules #4 and #6 */
  
            /* The specification neither provides an exact definition */
            /* of a `stub' nor gives exact rules to exclude them.     */
            /*                                                        */
            /* Here the constraints we use to recognize a stub.       */
            /*                                                        */
            /*  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                          */
            /*                                                        */
            /* We draw a stub if the following constraints are met.   */
            /*                                                        */
            /*   - for an upper or lower stub, there is top or bottom */
            /*     overshoot, respectively                            */
            /*   - the covered interval is greater or equal to a half */
            /*     pixel                                              */
  
            /* upper stub test */
            if ( left->next == right                &&
                 left->height <= 0                  &&
                 !( left->flags & Overshoot_Top   &&
                    x2 - x1 >= ras.precision_half ) )
              goto Exit;
  
            /* lower stub test */
            if ( right->next == left                 &&
                 left->start == y                    &&
                 !( left->flags & Overshoot_Bottom &&
                    x2 - x1 >= ras.precision_half  ) )
              goto Exit;
  
            if ( dropOutControl == 1 )
              pxl = e2;
            else
              pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
            break;
  
          default: /* modes 2, 3, 6, 7 */
            goto Exit;  /* no drop-out control */
          }
  
          /* undocumented but confirmed: If the drop-out would result in a  */
          /* pixel outside of the bounding box, use the pixel inside of the */
          /* bounding box instead                                           */
          if ( pxl < 0 )
            pxl = e1;
          else if ( TRUNC( pxl ) >= ras.bWidth )
            pxl = e2;
  
          /* check that the other pixel isn't set */
          e1 = ( pxl == e1 ) ? e2 : e1;
  
          e1 = TRUNC( e1 );
  
          c1 = (Short)( e1 >> 3 );
          f1 = (Short)( e1 &  7 );
  
          if ( e1 >= 0 && e1 < ras.bWidth                      &&
               ras.bOrigin[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
            goto Exit;
        }
        else
          goto Exit;
      }
  
      e1 = TRUNC( pxl );
  
      if ( e1 >= 0 && e1 < ras.bWidth )
      {
        FT_TRACE7(( " -> x=%d (drop-out)", e1 ));
  
        c1 = (Short)( e1 >> 3 );
        f1 = (Short)( e1 & 7 );
  
        ras.bOrigin[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
      }
  
    Exit:
      FT_TRACE7(( "\n" ));
    }
  
  
    static void
    Vertical_Sweep_Step( RAS_ARG )
    {
      ras.traceOfs += ras.traceIncr;
    }
  
  
    /************************************************************************
     *
     * Horizontal Sweep Procedure Set
     *
     * These four routines are used during the horizontal black/white
     * sweep phase by the generic Draw_Sweep() function.
     *
     */
  
    static void
    Horizontal_Sweep_Init( RAS_ARGS Short*  min,
                                    Short*  max )
    {
      /* nothing, really */
      FT_UNUSED_RASTER;
      FT_UNUSED( min );
      FT_UNUSED( max );
    }
  
  
    static void
    Horizontal_Sweep_Span( RAS_ARGS Short       y,
                                    FT_F26Dot6  x1,
                                    FT_F26Dot6  x2,
                                    PProfile    left,
                                    PProfile    right )
    {
      FT_UNUSED( left );
      FT_UNUSED( right );
  
  
      if ( x2 - x1 < ras.precision )
      {
        Long  e1, e2;
  
  
        FT_TRACE7(( "  x=%d y=[%.12f;%.12f]",
                    y,
                    x1 / (double)ras.precision,
                    x2 / (double)ras.precision ));
  
        e1 = CEILING( x1 );
        e2 = FLOOR  ( x2 );
  
        if ( e1 == e2 )
        {
          e1 = TRUNC( e1 );
  
          if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
          {
            Byte   f1;
            PByte  bits;
  
  
            FT_TRACE7(( " -> y=%d (drop-out)", e1 ));
  
            bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
            f1   = (Byte)( 0x80 >> ( y & 7 ) );
  
            bits[0] |= f1;
          }
        }
  
        FT_TRACE7(( "\n" ));
      }
    }
  
  
    static void
    Horizontal_Sweep_Drop( RAS_ARGS Short       y,
                                    FT_F26Dot6  x1,
                                    FT_F26Dot6  x2,
                                    PProfile    left,
                                    PProfile    right )
    {
      Long   e1, e2, pxl;
      PByte  bits;
      Byte   f1;
  
  
      FT_TRACE7(( "  x=%d y=[%.12f;%.12f]",
                  y,
                  x1 / (double)ras.precision,
                  x2 / (double)ras.precision ));
  
      /* During the horizontal sweep, we only take care of drop-outs */
  
      /* e1     +       <-- pixel center */
      /*        |                        */
      /* x1  ---+-->    <-- contour      */
      /*        |                        */
      /*        |                        */
      /* x2  <--+---    <-- contour      */
      /*        |                        */
      /*        |                        */
      /* e2     +       <-- pixel center */
  
      e1  = CEILING( x1 );
      e2  = FLOOR  ( x2 );
      pxl = e1;
  
      if ( e1 > e2 )
      {
        Int  dropOutControl = left->flags & 7;
  
  
        FT_TRACE7(( ", dropout=%d", dropOutControl ));
  
        if ( e1 == e2 + ras.precision )
        {
          switch ( dropOutControl )
          {
          case 0: /* simple drop-outs including stubs */
            pxl = e2;
            break;
  
          case 4: /* smart drop-outs including stubs */
            pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
            break;
  
          case 1: /* simple drop-outs excluding stubs */
          case 5: /* smart drop-outs excluding stubs  */
            /* see Vertical_Sweep_Drop for details */
  
            /* rightmost stub test */
            if ( left->next == right                &&
                 left->height <= 0                  &&
                 !( left->flags & Overshoot_Top   &&
                    x2 - x1 >= ras.precision_half ) )
              goto Exit;
  
            /* leftmost stub test */
            if ( right->next == left                 &&
                 left->start == y                    &&
                 !( left->flags & Overshoot_Bottom &&
                    x2 - x1 >= ras.precision_half  ) )
              goto Exit;
  
            if ( dropOutControl == 1 )
              pxl = e2;
            else
              pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
            break;
  
          default: /* modes 2, 3, 6, 7 */
            goto Exit;  /* no drop-out control */
          }
  
          /* undocumented but confirmed: If the drop-out would result in a  */
          /* pixel outside of the bounding box, use the pixel inside of the */
          /* bounding box instead                                           */
          if ( pxl < 0 )
            pxl = e1;
          else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows )
            pxl = e2;
  
          /* check that the other pixel isn't set */
          e1 = ( pxl == e1 ) ? e2 : e1;
  
          e1 = TRUNC( e1 );
  
          bits = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
          f1   = (Byte)( 0x80 >> ( y & 7 ) );
  
          if ( e1 >= 0                     &&
               (ULong)e1 < ras.target.rows &&
               *bits & f1                  )
            goto Exit;
        }
        else
          goto Exit;
      }
  
      e1 = TRUNC( pxl );
  
      if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
      {
        FT_TRACE7(( " -> y=%d (drop-out)", e1 ));
  
        bits  = ras.bOrigin + ( y >> 3 ) - e1 * ras.target.pitch;
        f1    = (Byte)( 0x80 >> ( y & 7 ) );
  
        bits[0] |= f1;
      }
  
    Exit:
      FT_TRACE7(( "\n" ));
    }
  
  
    static void
    Horizontal_Sweep_Step( RAS_ARG )
    {
      /* Nothing, really */
      FT_UNUSED_RASTER;
    }
  
  
    /**************************************************************************
     *
     * Generic Sweep Drawing routine
     *
     */
  
    static Bool
    Draw_Sweep( RAS_ARG )
    {
      Short         y, y_change, y_height;
  
      PProfile      P, Q, P_Left, P_Right;
  
      Short         min_Y, max_Y, top, bottom, dropouts;
  
      Long          x1, x2, xs, e1, e2;
  
      TProfileList  waiting;
      TProfileList  draw_left, draw_right;
  
  
      /* initialize empty linked lists */
  
      Init_Linked( &waiting );
  
      Init_Linked( &draw_left  );
      Init_Linked( &draw_right );
  
      /* first, compute min and max Y */
  
      P     = ras.fProfile;
      max_Y = (Short)TRUNC( ras.minY );
      min_Y = (Short)TRUNC( ras.maxY );
  
      while ( P )
      {
        Q = P->link;
  
        bottom = (Short)P->start;
        top    = (Short)( P->start + P->height - 1 );
  
        if ( min_Y > bottom )
          min_Y = bottom;
        if ( max_Y < top )
          max_Y = top;
  
        P->X = 0;
        InsNew( &waiting, P );
  
        P = Q;
      }
  
      /* check the Y-turns */
      if ( ras.numTurns == 0 )
      {
        ras.error = FT_THROW( Invalid );
        return FAILURE;
      }
  
      /* now initialize the sweep */
  
      ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
  
      /* then compute the distance of each profile from min_Y */
  
      P = waiting;
  
      while ( P )
      {
        P->countL = P->start - min_Y;
        P = P->link;
      }
  
      /* let's go */
  
      y        = min_Y;
      y_height = 0;
  
      if ( ras.numTurns > 0                     &&
           ras.sizeBuff[-ras.numTurns] == min_Y )
        ras.numTurns--;
  
      while ( ras.numTurns > 0 )
      {
        /* check waiting list for new activations */
  
        P = waiting;
  
        while ( P )
        {
          Q = P->link;
          P->countL -= y_height;
          if ( P->countL == 0 )
          {
            DelOld( &waiting, P );
  
            if ( P->flags & Flow_Up )
              InsNew( &draw_left,  P );
            else
              InsNew( &draw_right, P );
          }
  
          P = Q;
        }
  
        /* sort the drawing lists */
  
        Sort( &draw_left );
        Sort( &draw_right );
  
        y_change = (Short)ras.sizeBuff[-ras.numTurns--];
        y_height = (Short)( y_change - y );
  
        while ( y < y_change )
        {
          /* let's trace */
  
          dropouts = 0;
  
          P_Left  = draw_left;
          P_Right = draw_right;
  
          while ( P_Left && P_Right )
          {
            x1 = P_Left ->X;
            x2 = P_Right->X;
  
            if ( x1 > x2 )
            {
              xs = x1;
              x1 = x2;
              x2 = xs;
            }
  
            e1 = FLOOR( x1 );
            e2 = CEILING( x2 );
  
            if ( x2 - x1 <= ras.precision &&
                 e1 != x1 && e2 != x2     )
            {
              if ( e1 > e2 || e2 == e1 + ras.precision )
              {
                Int  dropOutControl = P_Left->flags & 7;
  
  
                if ( dropOutControl != 2 )
                {
                  /* a drop-out was detected */
  
                  P_Left ->X = x1;
                  P_Right->X = x2;
  
                  /* mark profile for drop-out processing */
                  P_Left->countL = 1;
                  dropouts++;
                }
  
                goto Skip_To_Next;
              }
            }
  
            ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
  
          Skip_To_Next:
  
            P_Left  = P_Left->link;
            P_Right = P_Right->link;
          }
  
          /* handle drop-outs _after_ the span drawing --       */
          /* drop-out processing has been moved out of the loop */
          /* for performance tuning                             */
          if ( dropouts > 0 )
            goto Scan_DropOuts;
  
        Next_Line:
  
          ras.Proc_Sweep_Step( RAS_VAR );
  
          y++;
  
          if ( y < y_change )
          {
            Sort( &draw_left  );
            Sort( &draw_right );
          }
        }
  
        /* now finalize the profiles that need it */
  
        P = draw_left;
        while ( P )
        {
          Q = P->link;
          if ( P->height == 0 )
            DelOld( &draw_left, P );
          P = Q;
        }
  
        P = draw_right;
        while ( P )
        {
          Q = P->link;
          if ( P->height == 0 )
            DelOld( &draw_right, P );
          P = Q;
        }
      }
  
      /* for gray-scaling, flush the bitmap scanline cache */
      while ( y <= max_Y )
      {
        ras.Proc_Sweep_Step( RAS_VAR );
        y++;
      }
  
      return SUCCESS;
  
    Scan_DropOuts:
  
      P_Left  = draw_left;
      P_Right = draw_right;
  
      while ( P_Left && P_Right )
      {
        if ( P_Left->countL )
        {
          P_Left->countL = 0;
  #if 0
          dropouts--;  /* -- this is useful when debugging only */
  #endif
          ras.Proc_Sweep_Drop( RAS_VARS y,
                                        P_Left->X,
                                        P_Right->X,
                                        P_Left,
                                        P_Right );
        }
  
        P_Left  = P_Left->link;
        P_Right = P_Right->link;
      }
  
      goto Next_Line;
    }
  
  
  #ifdef STANDALONE_
  
    /**************************************************************************
     *
     * The following functions should only compile in stand-alone mode,
     * i.e., when building this component without the rest of FreeType.
     *
     */
  
    /**************************************************************************
     *
     * @Function:
     *   FT_Outline_Get_CBox
     *
     * @Description:
     *   Return an outline's `control box'.  The control box encloses all
     *   the outline's points, including Bézier control points.  Though it
     *   coincides with the exact bounding box for most glyphs, it can be
     *   slightly larger in some situations (like when rotating an outline
     *   that contains Bézier outside arcs).
     *
     *   Computing the control box is very fast, while getting the bounding
     *   box can take much more time as it needs to walk over all segments
     *   and arcs in the outline.  To get the latter, you can use the
     *   `ftbbox' component, which is dedicated to this single task.
     *
     * @Input:
     *   outline ::
     *     A pointer to the source outline descriptor.
     *
     * @Output:
     *   acbox ::
     *     The outline's control box.
     *
     * @Note:
     *   See @FT_Glyph_Get_CBox for a discussion of tricky fonts.
     */
  
    static void
    FT_Outline_Get_CBox( const FT_Outline*  outline,
                         FT_BBox           *acbox )
    {
      Long  xMin, yMin, xMax, yMax;
  
  
      if ( outline && acbox )
      {
        if ( outline->n_points == 0 )
        {
          xMin = 0;
          yMin = 0;
          xMax = 0;
          yMax = 0;
        }
        else
        {
          FT_Vector*  vec   = outline->points;
          FT_Vector*  limit = vec + outline->n_points;
  
  
          xMin = xMax = vec->x;
          yMin = yMax = vec->y;
          vec++;
  
          for ( ; vec < limit; vec++ )
          {
            Long  x, y;
  
  
            x = vec->x;
            if ( x < xMin ) xMin = x;
            if ( x > xMax ) xMax = x;
  
            y = vec->y;
            if ( y < yMin ) yMin = y;
            if ( y > yMax ) yMax = y;
          }
        }
        acbox->xMin = xMin;
        acbox->xMax = xMax;
        acbox->yMin = yMin;
        acbox->yMax = yMax;
      }
    }
  
  #endif /* STANDALONE_ */
  
  
    /**************************************************************************
     *
     * @Function:
     *   Render_Single_Pass
     *
     * @Description:
     *   Perform one sweep with sub-banding.
     *
     * @Input:
     *   flipped ::
     *     If set, flip the direction of the outline.
     *
     * @Return:
     *   Renderer error code.
     */
    static int
    Render_Single_Pass( RAS_ARGS Bool  flipped )
    {
      Short  i, j, k;
  
  
      while ( ras.band_top >= 0 )
      {
        ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
        ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
  
        ras.top = ras.buff;
  
        ras.error = Raster_Err_None;
  
        if ( Convert_Glyph( RAS_VARS flipped ) )
        {
          if ( ras.error != Raster_Err_Overflow )
            return FAILURE;
  
          ras.error = Raster_Err_None;
  
          /* sub-banding */
  
  #ifdef DEBUG_RASTER
          ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
  #endif
  
          i = ras.band_stack[ras.band_top].y_min;
          j = ras.band_stack[ras.band_top].y_max;
  
          k = (Short)( ( i + j ) / 2 );
  
          if ( ras.band_top >= 7 || k < i )
          {
            ras.band_top = 0;
            ras.error    = FT_THROW( Invalid );
  
            return ras.error;
          }
  
          ras.band_stack[ras.band_top + 1].y_min = k;
          ras.band_stack[ras.band_top + 1].y_max = j;
  
          ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );
  
          ras.band_top++;
        }
        else
        {
          if ( ras.fProfile )
            if ( Draw_Sweep( RAS_VAR ) )
               return ras.error;
          ras.band_top--;
        }
      }
  
      return SUCCESS;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   Render_Glyph
     *
     * @Description:
     *   Render a glyph in a bitmap.  Sub-banding if needed.
     *
     * @Return:
     *   FreeType error code.  0 means success.
     */
    static FT_Error
    Render_Glyph( RAS_ARG )
    {
      FT_Error  error;
  
  
      Set_High_Precision( RAS_VARS ras.outline.flags &
                                   FT_OUTLINE_HIGH_PRECISION );
  
      if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
        ras.dropOutControl = 2;
      else
      {
        if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
          ras.dropOutControl = 4;
        else
          ras.dropOutControl = 0;
  
        if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
          ras.dropOutControl += 1;
      }
  
      ras.second_pass = (Bool)( !( ras.outline.flags      &
                                   FT_OUTLINE_SINGLE_PASS ) );
  
      /* Vertical Sweep */
      FT_TRACE7(( "Vertical pass (ftraster)\n" ));
  
      ras.Proc_Sweep_Init = Vertical_Sweep_Init;
      ras.Proc_Sweep_Span = Vertical_Sweep_Span;
      ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
      ras.Proc_Sweep_Step = Vertical_Sweep_Step;
  
      ras.band_top            = 0;
      ras.band_stack[0].y_min = 0;
      ras.band_stack[0].y_max = (Short)( ras.target.rows - 1 );
  
      ras.bWidth  = (UShort)ras.target.width;
      ras.bOrigin = (Byte*)ras.target.buffer;
  
      if ( ras.target.pitch > 0 )
        ras.bOrigin += (Long)( ras.target.rows - 1 ) * ras.target.pitch;
  
      if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
        return error;
  
      /* Horizontal Sweep */
      if ( ras.second_pass && ras.dropOutControl != 2 )
      {
        FT_TRACE7(( "Horizontal pass (ftraster)\n" ));
  
        ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
        ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
        ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
        ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
  
        ras.band_top            = 0;
        ras.band_stack[0].y_min = 0;
        ras.band_stack[0].y_max = (Short)( ras.target.width - 1 );
  
        if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
          return error;
      }
  
      return Raster_Err_None;
    }
  
  
    static void
    ft_black_init( black_PRaster  raster )
    {
      FT_UNUSED( raster );
    }
  
  
    /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
    /****                         a static object.                  *****/
  
  
  #ifdef STANDALONE_
  
  
    static int
    ft_black_new( void*       memory,
                  FT_Raster  *araster )
    {
       static black_TRaster  the_raster;
       FT_UNUSED( memory );
  
  
       *araster = (FT_Raster)&the_raster;
       FT_ZERO( &the_raster );
       ft_black_init( &the_raster );
  
       return 0;
    }
  
  
    static void
    ft_black_done( FT_Raster  raster )
    {
      /* nothing */
      FT_UNUSED( raster );
    }
  
  
  #else /* !STANDALONE_ */
  
  
    static int
    ft_black_new( FT_Memory       memory,
                  black_PRaster  *araster )
    {
      FT_Error       error;
      black_PRaster  raster = NULL;
  
  
      *araster = 0;
      if ( !FT_NEW( raster ) )
      {
        raster->memory = memory;
        ft_black_init( raster );
  
        *araster = raster;
      }
  
      return error;
    }
  
  
    static void
    ft_black_done( black_PRaster  raster )
    {
      FT_Memory  memory = (FT_Memory)raster->memory;
  
  
      FT_FREE( raster );
    }
  
  
  #endif /* !STANDALONE_ */
  
  
    static void
    ft_black_reset( FT_Raster  raster,
                    PByte      pool_base,
                    ULong      pool_size )
    {
      FT_UNUSED( raster );
      FT_UNUSED( pool_base );
      FT_UNUSED( pool_size );
    }
  
  
    static int
    ft_black_set_mode( FT_Raster  raster,
                       ULong      mode,
                       void*      args )
    {
      FT_UNUSED( raster );
      FT_UNUSED( mode );
      FT_UNUSED( args );
  
      return 0;
    }
  
  
    static int
    ft_black_render( FT_Raster                raster,
                     const FT_Raster_Params*  params )
    {
      const FT_Outline*  outline    = (const FT_Outline*)params->source;
      const FT_Bitmap*   target_map = params->target;
  
  #ifndef FT_STATIC_RASTER
      black_TWorker  worker[1];
  #endif
  
      Long  buffer[FT_MAX_BLACK_POOL];
  
  
      if ( !raster )
        return FT_THROW( Not_Ini );
  
      if ( !outline )
        return FT_THROW( Invalid );
  
      /* return immediately if the outline is empty */
      if ( outline->n_points == 0 || outline->n_contours <= 0 )
        return Raster_Err_None;
  
      if ( !outline->contours || !outline->points )
        return FT_THROW( Invalid );
  
      if ( outline->n_points !=
             outline->contours[outline->n_contours - 1] + 1 )
        return FT_THROW( Invalid );
  
      /* this version of the raster does not support direct rendering, sorry */
      if ( params->flags & FT_RASTER_FLAG_DIRECT )
        return FT_THROW( Unsupported );
  
      if ( params->flags & FT_RASTER_FLAG_AA )
        return FT_THROW( Unsupported );
  
      if ( !target_map )
        return FT_THROW( Invalid );
  
      /* nothing to do */
      if ( !target_map->width || !target_map->rows )
        return Raster_Err_None;
  
      if ( !target_map->buffer )
        return FT_THROW( Invalid );
  
      ras.outline = *outline;
      ras.target  = *target_map;
  
      ras.buff     = buffer;
      ras.sizeBuff = (&buffer)[1]; /* Points to right after buffer. */
  
      return Render_Glyph( RAS_VAR );
    }
  
  
    FT_DEFINE_RASTER_FUNCS(
      ft_standard_raster,
  
      FT_GLYPH_FORMAT_OUTLINE,
  
      (FT_Raster_New_Func)     ft_black_new,       /* raster_new      */
      (FT_Raster_Reset_Func)   ft_black_reset,     /* raster_reset    */
      (FT_Raster_Set_Mode_Func)ft_black_set_mode,  /* raster_set_mode */
      (FT_Raster_Render_Func)  ft_black_render,    /* raster_render   */
      (FT_Raster_Done_Func)    ft_black_done       /* raster_done     */
    )
  
  
  /* END */
  