Edit

kc3-lang/freetype/src/base/ftraster.c

Branch :

  • Show log

    Commit

  • Author : David Turner
    Date : 2000-05-17 20:56:01
    Hash : 3475e7fb
    Message : removed TT_MAX_SUBGLYPHS macro, there is no static limit on the number of subglyphs in a TrueType font.. changed the default number of gray levels used by the smooth renderer to 256 (instead of 128). Of course, the human eye can't tell a difference ;-)

  • src/base/ftraster.c
  • /*******************************************************************
     *
     *  ftraster.c                                                  1.5
     *
     *  The FreeType glyph rasterizer (body).
     *
     *  Copyright 1996-2000 by
     *  David Turner, Robert Wilhelm, and Werner Lemberg.
     *
     *  This file is part of the FreeType project, and may only be used
     *  modified and distributed under the terms of the FreeType project
     *  license, LICENSE.TXT. By continuing to use, modify, or distribute
     *  this file you indicate that you have read the license and
     *  understand and accept it fully.
     *
     *
     *  This is a rewrite of the FreeType 1.x scan-line converter
     *
     *
     *
     ******************************************************************/
    
    #include <freetype/ftraster.h>
    #include <freetype/internal/ftcalc.h>      /* for FT_MulDiv only */
    
      /*************************************************************************/
      /*                                                                       */
      /* A simple technical note on how the raster works:                      */
      /*                                                                       */
      /*   Converting an outline into a bitmap is achieved in several steps    */
      /*   which are:                                                          */
      /*                                                                       */
      /*   1 - Decomposing the outline into successive `profiles'.  Each       */
      /*       profile is simply an array of scanline intersections on a given */
      /*       dimension.  A profile's main attributes are                     */
      /*                                                                       */
      /*       o its scanline position boundaries, i.e. `Ymin' and `Ymax'.     */
      /*                                                                       */
      /*       o an array of intersection coordinates for each scanline        */
      /*         between `Ymin' and `Ymax'.                                    */
      /*                                                                       */
      /*       o a direction, indicating wether is was built going `up' or     */
      /*         `down', as this is very important for filling rules.          */
      /*                                                                       */
      /*   2 - Sweeping the target map's scanlines in order to compute segment */
      /*       `spans' which are then filled.  Additionaly, this pass performs */
      /*       drop-out control.                                               */
      /*                                                                       */
      /*   The outline data is parsed during step 1 only.  The profiles are    */
      /*   built from the bottom of the render pool, used as a stack.  The     */
      /*   following graphics shows the profile list under construction:       */
      /*                                                                       */
      /*     ____________________________________________________________ _ _  */
      /*    |         |                   |         |                 |        */
      /*    | profile | coordinates for   | profile | coordinates for |-->     */
      /*    |    1    |  profile 1        |    2    |  profile 2      |-->     */
      /*    |_________|___________________|_________|_________________|__ _ _  */
      /*                                                                       */
      /*    ^                                                         ^        */
      /*    |                                                         |        */
      /*    start of render pool                                   top         */
      /*                                                                       */
      /*   The top of the profile stack is kept in the `top' variable.         */
      /*                                                                       */
      /*   As you can see, a profile record is pushed on top of the render     */
      /*   pool, which is then followed by its coordinates/intersections.  If  */
      /*   a change of direction is detected in the outline, a new profile is  */
      /*   generated until the end of the outline.                             */
      /*                                                                       */
      /*   Note that when all profiles have been generated, the function       */
      /*   Finalize_Profile_Table() is used to record, for each profile, its   */
      /*   bottom-most scanline as well as the scanline above its upmost       */
      /*   boundary.  These positions are called `y-turns' because they (sort  */
      /*   of) correspond to local extrema.  They are stored in a sorted list  */
      /*   built from the top of the render pool as a downwards stack:         */
      /*                                                                       */
      /*      _ _ _______________________________________                      */
      /*                            |                    |                     */
      /*                         <--| sorted list of     |                     */
      /*                         <--|  extrema scanlines |                     */
      /*      _ _ __________________|____________________|                     */
      /*                                                                       */
      /*                            ^                    ^                     */
      /*                            |                    |                     */
      /*                       maxBuff             sizeBuff = end of pool      */
      /*                                                                       */
      /*   This list is later used during the sweep phase in order to          */
      /*   optimize performance (see technical note on the sweep below).       */
      /*                                                                       */
      /*   Of course, the raster detects whether the two stacks collide and    */
      /*   handles the situation propertly.                                    */
      /*                                                                       */
      /*************************************************************************/
    
      /****************************************************************/
      /****************************************************************/
      /**                                                            **/
      /**  CONFIGURATION MACROS                                      **/
      /**                                                            **/
      /****************************************************************/
      /****************************************************************/
    
    /* define DEBUG_RASTER if you want to compile a debugging version */
    #define xxxDEBUG_RASTER
    
    /* The default render pool size in bytes */
    #define  RASTER_RENDER_POOL   8192
    
    /* undefine FT_RASTER_OPTION_ANTI_ALIASING if you do not want to support */
    /* 5-levels anti-aliasing                                                */
    #ifdef FT_CONFIG_OPTION_5_GRAY_LEVELS
    #define FT_RASTER_OPTION_ANTI_ALIASING
    #endif
    
    /* The size of the two-lines intermediate bitmap used */
    /* for anti-aliasing, in bytes..                      */
    #define  RASTER_GRAY_LINES    2048
    
    
      /****************************************************************/
      /****************************************************************/
      /**                                                            **/
      /**  OTHER MACROS (do not change)                              **/
      /**                                                            **/
      /****************************************************************/
      /****************************************************************/
    
    /* required by the tracing mode */
    #undef  FT_COMPONENT
    #define FT_COMPONENT      trace_raster
    
    #include <freetype/internal/ftdebug.h>
    
    #define Raster_Err_None               0
    #define Raster_Err_Not_Ini           -1
    #define Raster_Err_Overflow          -2
    #define Raster_Err_Neg_Height        -3
    #define Raster_Err_Invalid           -4
    #define Raster_Err_Gray_Unsupported  -5
    #define Raster_Err_Unsupported       -6
    
    /* FMulDiv means "Fast MulDiv", it is used in case where 'b' is typically */
    /* a small value and the result of (a*b) is known to fit in 32 bits.      */
    #define FMulDiv( a, b, c )  ( (a) * (b) / (c) )
    
    /* On the other hand, SMulDiv is for "Slow MulDiv", and is used typically */
    /* for clipping computations.  It simply uses the FT_MulDiv() function    */
    /* defined in "ftcalc.h"                                                  */
    /*                                                                        */
    #define SMulDiv   FT_MulDiv
    
    
    
    /* The rasterizer is a very general purpose component, please leave */
    /* the following redefinitions there (you never know your target    */
    /* environment).                                                    */
    
    #ifndef TRUE
    #define TRUE   1
    #endif
    
    #ifndef FALSE
    #define FALSE  0
    #endif
    
    #ifndef NULL
    #define NULL  (void*)0
    #endif
    
    #ifndef SUCCESS
    #define SUCCESS 0
    #endif
    
    #ifndef FAILURE
    #define FAILURE 1
    #endif
    
    
    #define MaxBezier  32   /* The maximum number of stacked Bezier curves. */
                            /* Setting this constant to more than 32 is a   */
                            /* pure waste of space.                         */
    
    #define Pixel_Bits  6   /* fractional bits of *input* coordinates */
    
      /****************************************************************/
      /****************************************************************/
      /**                                                            **/
      /**  SIMPLE TYPE DECLARATIONS                                  **/
      /**                                                            **/
      /****************************************************************/
      /****************************************************************/
    
      typedef int            Int;
      typedef unsigned int   UInt;
      typedef short          Short;
      typedef unsigned short UShort, *PUShort;
      typedef long           Long, *PLong;
      typedef unsigned long  ULong;
    
      typedef unsigned char  Byte, *PByte;
      typedef char           Bool;
    
      typedef struct TPoint_
      {
        Long  x;
        Long  y;
    
      } TPoint;
    
    
      typedef enum TFlow_
      {
        Flow_None = 0,
        Flow_Up   = 1,
        Flow_Down = -1
    
      } TFlow;
    
    
      /* States of each line, arc and profile */
      typedef enum  TStates_
      {
        Unknown,
        Ascending,
        Descending,
        Flat
    
      } TStates;
    
    
      typedef struct TProfile_  TProfile;
      typedef TProfile*         PProfile;
    
      struct  TProfile_
      {
        FT_F26Dot6  X;           /* current coordinate during sweep          */
        PProfile    link;        /* link to next profile - various purpose   */
        PLong       offset;      /* start of profile's data in render pool   */
        Int         flow;        /* Profile orientation: Asc/Descending      */
        Long        height;      /* profile's height in scanlines            */
        Long        start;       /* profile's starting scanline              */
    
        UShort      countL;      /* number of lines to step before this      */
                                 /* profile becomes drawable                 */
    
        PProfile    next;        /* next profile in same contour, used       */
                                 /* during drop-out control                  */
      };
    
      typedef PProfile   TProfileList;
      typedef PProfile*  PProfileList;
    
    
      /* Simple record used to implement a stack of bands, required */
      /* by the sub-banding mechanism                               */
      typedef struct  TBand_
      {
        Short  y_min;   /* band's minimum */
        Short  y_max;   /* band's maximum */
    
      } TBand;
    
    
    
    #define AlignProfileSize \
              (( sizeof(TProfile)+sizeof(long)-1 ) / sizeof(long))
    
    
    
    #ifdef TT_STATIC_RASTER
    
    #define  RAS_ARGS  /* void */
    #define  RAS_ARG   /* void */
    
    #define  RAS_VARS  /* void */
    #define  RAS_VAR   /* void */
    
    #else
    
    #define  RAS_ARGS  TRaster_Instance*  raster,
    #define  RAS_ARG   TRaster_Instance*  raster
    
    #define  RAS_VARS  raster,
    #define  RAS_VAR   raster
    
    #endif
    
    
      typedef struct TRaster_Instance_  TRaster_Instance;
    
    
      /* prototypes used for sweep function dispatch */
      typedef void  Function_Sweep_Init( RAS_ARGS Short*  min,
                                                  Short*  max );
    
      typedef void  Function_Sweep_Span( RAS_ARGS Short       y,
                                                  FT_F26Dot6  x1,
                                                  FT_F26Dot6  x2,
                                                  PProfile    left,
                                                  PProfile    right );
    
      typedef void  Function_Sweep_Step( RAS_ARG );
    
    
    /* NOTE: These operations are only valid on 2's complement processors */
    
    #define FLOOR( x )    ( (x) & -ras.precision )
    #define CEILING( x )  ( ((x) + ras.precision - 1) & -ras.precision )
    #define TRUNC( x )    ( (signed long)(x) >> ras.precision_bits )
    #define FRAC( x )     ( (x) & (ras.precision - 1) )
    #define SCALED( x )   ( ((x) << ras.scale_shift) - ras.precision_half )
    
      /* Note that I have moved the location of some fields in the */
      /* structure to ensure that the most used variables are used */
      /* at the top.  Thus, their offset can be coded with less    */
      /* opcodes, and it results in a smaller executable.          */
    
      struct  TRaster_Instance_
      {
        Int       precision_bits;       /* precision related variables */
        Int       precision;
        Int       precision_half;
        Long      precision_mask;
        Int       precision_shift;
        Int       precision_step;
        Int       precision_jitter;
    
        Int       scale_shift;        /* == precision_shift   for bitmaps */
                                      /* == precision_shift+1 for pixmaps */
    
        PLong     buff;                 /* The profiles buffer          */
        PLong     sizeBuff;             /* Render pool size             */
        PLong     maxBuff;              /* Profiles buffer size         */
        PLong     top;                  /* Current cursor in buffer     */
    
        FT_Error  error;
    
        Int       numTurns;             /* number of Y-turns in outline        */
    
        TPoint*   arc;                  /* current Bezier arc pointer */
    
        UShort    bWidth;               /* target bitmap width  */
        PByte     bTarget;              /* target bitmap buffer */
        PByte     gTarget;              /* target pixmap buffer */
    
        Long      lastX, lastY, minY, maxY;
    
        UShort    num_Profs;            /* current number of profiles */
    
        Bool      fresh;                /* signals a fresh new profile which */
                                        /* 'start' field must be completed   */
        Bool      joint;                /* signals that the last arc ended   */
                                        /* exactly on a scanline.  Allows    */
                                        /* removal of doublets               */
        PProfile  cProfile;             /* current profile                   */
        PProfile  fProfile;             /* head of linked list of profiles   */
        PProfile  gProfile;             /* contour's first profile in case   */
                                        /* of impact                         */
        TStates   state;                /* rendering state */
    
        FT_Bitmap  target;          /* description of target bit/pixmap */
        FT_Outline outline;
    
        Long      traceOfs;             /* current offset in target bitmap */
        Long      traceG;               /* current offset in target pixmap */
    
        Short     traceIncr;            /* sweep's increment in target bitmap */
    
        Short     gray_min_x;           /* current min x during gray rendering */
        Short     gray_max_x;           /* current max x during gray rendering */
    
        /* dispatch variables */
    
        Function_Sweep_Init*  Proc_Sweep_Init;
        Function_Sweep_Span*  Proc_Sweep_Span;
        Function_Sweep_Span*  Proc_Sweep_Drop;
        Function_Sweep_Step*  Proc_Sweep_Step;
    
        Byte      dropOutControl;       /* current drop_out control method */
    
        Bool      second_pass;      /* indicates wether a horizontal pass      */
                                    /* should be performed to control drop-out */
                                    /* accurately when calling Render_Glyph.   */
                                    /* Note that there is no horizontal pass   */
                                    /* during gray rendering.                  */
        TPoint    arcs[2 * MaxBezier + 1];      /* The Bezier stack */
    
        TBand     band_stack[16];       /* band stack used for sub-banding */
        Int       band_top;             /* band stack top                  */
    
        Int       count_table[256];     /* Look-up table used to quickly count */
                                        /* set bits in a gray 2x2 cell         */
    
        void*     memory;
    
    #ifdef FT_RASTER_OPTION_ANTI_ALIASING
    
        Byte      grays[5];         /* Palette of gray levels used for render */
    
        Byte      gray_lines[RASTER_GRAY_LINES];
                                    /* Intermediate table used to render the   */
                                    /* graylevels pixmaps.                     */
                                    /* gray_lines is a buffer holding two      */
                                    /* monochrome scanlines                    */
        Short     gray_width;       /* width in bytes of one monochrome        */
                                    /* intermediate scanline of gray_lines.    */
                                    /* Each gray pixel takes 2 bits long there */
    
                            /* The gray_lines must hold 2 lines, thus with size */
                            /* in bytes of at least 'gray_width*2'              */
    
    #endif /* FT_RASTER_ANTI_ALIASING */
    
    #if 0
        PByte     flags;                /* current flags table    */
        PUShort   outs;                 /* current outlines table */
        FT_Vector*  coords;
    
        UShort    nPoints;              /* number of points in current glyph   */
        Short     nContours;            /* number of contours in current glyph */
    #endif
    
    
      };
    
    
    #ifdef FT_CONFIG_OPTION_STATIC_RASTER
    
      static TRaster_Instance  cur_ras;
      #define ras  cur_ras
    
    #else
    
      #define ras  (*raster)
    
    #endif /* FT_STATIC_RASTER */
    
      /****************************************************************/
      /****************************************************************/
      /**                                                            **/
      /**  PROFILES COMPUTATION                                      **/
      /**                                                            **/
      /****************************************************************/
      /****************************************************************/
    
    
    /************************************************************************/
    /*                                                                      */
    /* Function:    Set_High_Precision                                      */
    /*                                                                      */
    /* Description: Sets precision variables according to param flag.       */
    /*                                                                      */
    /* Input:       High     set to True for high precision (typically for  */
    /*                       ppem < 18), false otherwise.                   */
    /*                                                                      */
    /************************************************************************/
    
      static void  Set_High_Precision( RAS_ARGS Int  High )
      {
        if ( High )
        {
          ras.precision_bits   = 10;
          ras.precision_step   = 128;
          ras.precision_jitter = 24;
        }
        else
        {
          ras.precision_bits   = 6;
          ras.precision_step   = 32;
          ras.precision_jitter = 2;
        }
    
        FT_TRACE7(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
    
        ras.precision       = 1L << ras.precision_bits;
        ras.precision_half  = ras.precision / 2;
        ras.precision_shift = ras.precision_bits - Pixel_Bits;
        ras.precision_mask  = -ras.precision;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    New_Profile                                                 */
    /*                                                                          */
    /* Description: Creates a new Profile in the render pool.                   */
    /*                                                                          */
    /* Input:       aState   state/orientation of the new Profile               */
    /*                                                                          */
    /* Returns:     SUCCESS on success.                                         */
    /*              FAILURE in case of overflow or of incoherent Profile.       */
    /*                                                                          */
    /****************************************************************************/
    
      static Bool  New_Profile( RAS_ARGS TStates  aState )
      {
        if ( !ras.fProfile )
        {
          ras.cProfile  = (PProfile)ras.top;
          ras.fProfile  = ras.cProfile;
          ras.top      += AlignProfileSize;
        }
    
        if ( ras.top >= ras.maxBuff )
        {
          ras.error = Raster_Err_Overflow;
          return FAILURE;
        }
    
        switch ( aState )
        {
        case Ascending:
          ras.cProfile->flow = Flow_Up;
          FT_TRACE7(( "New ascending profile = %lx\n", (long)ras.cProfile ));
          break;
    
        case Descending:
          ras.cProfile->flow = Flow_Down;
          FT_TRACE7(( "New descending profile = %lx\n", (long)ras.cProfile ));
          break;
    
        default:
          FT_ERROR(( "Invalid profile direction in Raster:New_Profile !!\n" ));
          ras.error = Raster_Err_Invalid;
          return FAILURE;
        }
    
        ras.cProfile->start  = 0;
        ras.cProfile->height = 0;
        ras.cProfile->offset = ras.top;
        ras.cProfile->link   = (PProfile)0;
        ras.cProfile->next   = (PProfile)0;
    
        if ( !ras.gProfile )
          ras.gProfile = ras.cProfile;
    
        ras.state = aState;
        ras.fresh = TRUE;
        ras.joint = FALSE;
    
        return SUCCESS;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    End_Profile                                                 */
    /*                                                                          */
    /* Description: Finalizes the current Profile.                              */
    /*                                                                          */
    /* Input:       None                                                        */
    /*                                                                          */
    /* Returns:     SUCCESS on success.                                         */
    /*              FAILURE in case of overflow or incoherency.                 */
    /*                                                                          */
    /****************************************************************************/
    
      static Bool  End_Profile( RAS_ARG )
      {
        Long      h;
        PProfile  oldProfile;
    
    
        h = ras.top - ras.cProfile->offset;
    
        if ( h < 0 )
        {
          FT_ERROR(( "Negative height encountered in End_Profile!\n" ));
          ras.error = Raster_Err_Neg_Height;
          return FAILURE;
        }
    
        if ( h > 0 )
        {
          FT_TRACE1(( "Ending profile %lx, start = %ld, height = %ld\n",
                    (long)ras.cProfile, ras.cProfile->start, h ));
    
          oldProfile           = ras.cProfile;
          ras.cProfile->height = h;
          ras.cProfile         = (PProfile)ras.top;
    
          ras.top             += AlignProfileSize;
    
          ras.cProfile->height = 0;
          ras.cProfile->offset = ras.top;
          oldProfile->next     = ras.cProfile;
          ras.num_Profs++;
        }
    
        if ( ras.top >= ras.maxBuff )
        {
          FT_TRACE1(( "overflow in End_Profile\n" ));
          ras.error = Raster_Err_Overflow;
          return FAILURE;
        }
    
        ras.joint = FALSE;
    
        return SUCCESS;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Insert_Y_Turn                                               */
    /*                                                                          */
    /* Description: Insert a salient into the sorted list placed on top         */
    /*              of the render pool                                          */
    /*                                                                          */
    /* Input:       New y scanline position                                     */
    /*                                                                          */
    /****************************************************************************/
    
      static
      Bool Insert_Y_Turn( RAS_ARGS  Int  y )
      {
        PLong     y_turns;
        Int       y2, n;
    
        n       = ras.numTurns-1;
        y_turns = ras.sizeBuff - ras.numTurns;
    
        /* look for first y value that is <= */
        while ( n >= 0 && y < y_turns[n] )
          n--;
    
        /* if it is <, simply insert it, ignore if == */
        if ( n >= 0 && y > y_turns[n] )
          while ( n >= 0 )
          {
            y2 = y_turns[n];
            y_turns[n] = y;
            y = y2;
            n--;
          }
    
        if ( n < 0 )
        {
          if (ras.maxBuff <= ras.top)
          {
            ras.error = Raster_Err_Overflow;
            return FAILURE;
          }
          ras.maxBuff--;
          ras.numTurns++;
          ras.sizeBuff[-ras.numTurns] = y;
        }
    
        return SUCCESS;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Finalize_Profile_Table                                      */
    /*                                                                          */
    /* Description: Adjusts all links in the Profiles list.                     */
    /*                                                                          */
    /* Input:       None                                                        */
    /*                                                                          */
    /* Returns:     None.                                                       */
    /*                                                                          */
    /****************************************************************************/
    
      static
      Bool Finalize_Profile_Table( RAS_ARG )
      {
        Int       bottom, top;
        UShort    n;
        PProfile  p;
    
    
        n = ras.num_Profs;
    
        if ( n > 1 )
        {
          p = ras.fProfile;
          while ( n > 0 )
          {
            if ( n > 1 )
              p->link = (PProfile)( p->offset + p->height );
            else
              p->link = NULL;
    
            switch ( p->flow )
            {
            case Flow_Down:
              bottom     = p->start - p->height+1;
              top        = p->start;
              p->start   = bottom;
              p->offset += p->height-1;
              break;
    
            case Flow_Up:
            default:
              bottom = p->start;
              top    = p->start + p->height-1;
            }
    
            if ( Insert_Y_Turn( RAS_VARS  bottom ) ||
                 Insert_Y_Turn( RAS_VARS  top+1 )  )
              return FAILURE;
    
            p = p->link;
            n--;
          }
        }
        else
          ras.fProfile = NULL;
    
        return SUCCESS;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Split_Conic                                                 */
    /*                                                                          */
    /* Description: Subdivides one conic bezier into two joint                  */
    /*              sub-arcs in the Bezier stack.                               */
    /*                                                                          */
    /* Input:       None (subdivided bezier is taken from the top of the        */
    /*              stack).                                                     */
    /*                                                                          */
    /* Returns:     None.                                                       */
    /*                                                                          */
    /*                                                                          */
    /* Note:  This routine is the 'beef' of this component. It is  _the_        */
    /*        inner loop that should be optimized to hell to get the            */
    /*        best performance.                                                 */
    /*                                                                          */
    /****************************************************************************/
    
      static void  Split_Conic( TPoint*  base )
      {
        Long     a, b;
    
    
        base[4].x = base[2].x;
        b = base[1].x;
        a = base[3].x = ( base[2].x + b ) / 2;
        b = base[1].x = ( base[0].x + b ) / 2;
        base[2].x = ( a + b ) / 2;
    
        base[4].y = base[2].y;
        b = base[1].y;
        a = base[3].y = ( base[2].y + b ) / 2;
        b = base[1].y = ( base[0].y + b ) / 2;
        base[2].y = ( a + b ) / 2;
    
        /* hand optimized.  gcc doesn't seem too good at common expression */
        /* substitution and instruction scheduling ;-)                     */
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>   Split_Cubic                                              */
      /*                                                                       */
      /* <Description>                                                         */
      /*     Subdivides a third-order Bezier arc into two joint sub-arcs in    */
      /*     the Bezier stack.                                                 */
      /*                                                                       */
      /* <Note>                                                                */
      /*    This routine is the `beef' of the component.  It is one of _the_   */
      /*    inner loops that should be optimized like hell to get the best     */
      /*    performance.                                                       */
      /*                                                                       */
      static
      void  Split_Cubic( TPoint*  base )
      {
        Long  a, b, c, d;
    
        base[6].x = base[3].x;
        c = base[1].x;
        d = base[2].x;
        base[1].x = a = ( base[0].x + c + 1 ) >> 1;
        base[5].x = b = ( base[3].x + d + 1 ) >> 1;
        c = ( c + d + 1 ) >> 1;
        base[2].x = a = ( a + c + 1 ) >> 1;
        base[4].x = b = ( b + c + 1 ) >> 1;
        base[3].x = ( a + b + 1 ) >> 1;
    
        base[6].y = base[3].y;
        c = base[1].y;
        d = base[2].y;
        base[1].y = a = ( base[0].y + c + 1 ) >> 1;
        base[5].y = b = ( base[3].y + d + 1 ) >> 1;
        c = ( c + d + 1 ) >> 1;
        base[2].y = a = ( a + c + 1 ) >> 1;
        base[4].y = b = ( b + c + 1 ) >> 1;
        base[3].y = ( a + b + 1 ) >> 1;
      }
    
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Line_Up                                                     */
    /*                                                                          */
    /* Description: Computes the x-coordinates of an ascending line segment     */
    /*              and stores them in the render pool.                         */
    /*                                                                          */
    /* Input:       x1,y1,x2,y2  Segment start (x1,y1) and end (x2,y2) points   */
    /*                                                                          */
    /* Returns:     SUCCESS on success.                                         */
    /*              FAILURE on Render Pool overflow.                            */
    /*                                                                          */
    /****************************************************************************/
    
      static Bool  Line_Up( RAS_ARGS Long  x1, Long  y1,
                                     Long  x2, Long  y2,
                                     Long  miny, Long  maxy )
      {
        Long  Dx, Dy;
        Int   e1, e2, f1, f2, size;     /* XXX: is `Short' sufficient? */
        Long  Ix, Rx, Ax;
    
        PLong  top;
    
    
        Dx = x2 - x1;
        Dy = y2 - y1;
    
        if ( Dy <= 0 || y2 < miny || y1 > maxy )
          return SUCCESS;
    
        if ( y1 < miny )
        {
          /* Take care : miny-y1 can be a very large value, we use     */
          /*             a slow MulDiv function to avoid clipping bugs */
          x1 += SMulDiv( Dx, miny - y1, Dy );
          e1  = TRUNC( miny );
          f1  = 0;
        }
        else
        {
          e1 = TRUNC( y1 );
          f1 = FRAC( y1 );
        }
    
        if ( y2 > maxy )
        {
          /* x2 += FMulDiv( Dx, maxy - y2, Dy );  UNNECESSARY */
          e2  = TRUNC( maxy );
          f2  = 0;
        }
        else
        {
          e2 = TRUNC( y2 );
          f2 = FRAC( y2 );
        }
    
        if ( f1 > 0 )
        {
          if ( e1 == e2 ) return SUCCESS;
          else
          {
            x1 += FMulDiv( Dx, ras.precision - f1, Dy );
            e1 += 1;
          }
        }
        else
          if ( ras.joint )
          {
            ras.top--;
            ras.joint = FALSE;
          }
    
        ras.joint = ( f2 == 0 );
    
        if ( ras.fresh )
        {
          ras.cProfile->start = e1;
          ras.fresh           = FALSE;
        }
    
        size = e2 - e1 + 1;
        if ( ras.top + size >= ras.maxBuff )
        {
          ras.error = Raster_Err_Overflow;
          return FAILURE;
        }
    
        if ( Dx > 0 )
        {
          Ix = (ras.precision*Dx) / Dy;
          Rx = (ras.precision*Dx) % Dy;
          Dx = 1;
        }
        else
        {
          Ix = -( (ras.precision*-Dx) / Dy );
          Rx =    (ras.precision*-Dx) % Dy;
          Dx = -1;
        }
    
        Ax  = -Dy;
        top = ras.top;
    
        while ( size > 0 )
        {
          *top++ = x1;
    
          x1 += Ix;
          Ax += Rx;
          if ( Ax >= 0 )
          {
            Ax -= Dy;
            x1 += Dx;
          }
          size--;
        }
    
        ras.top = top;
        return SUCCESS;
      }
    
    
      static Bool  Line_Down( RAS_ARGS Long  x1, Long  y1,
                                       Long  x2, Long  y2,
                                       Long  miny, Long  maxy )
      {
        Bool result, fresh;
    
    
        fresh  = ras.fresh;
    
        result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
    
        if ( fresh && !ras.fresh )
          ras.cProfile->start = -ras.cProfile->start;
    
        return result;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Bezier_Up                                                   */
    /*                                                                          */
    /* Description: Computes thes x-coordinates of an ascending bezier arc      */
    /*              and stores them in the render pool.                         */
    /*                                                                          */
    
     /* A function type describing the functions used to split bezier arcs */
      typedef  void  (*TSplitter)( TPoint*  base );
    
      static Bool  Bezier_Up( RAS_ARGS Int        degree,
                                       TSplitter  splitter,
                                       Long       miny,
                                       Long       maxy )
      {
        Long   y1, y2, e, e2, e0;
        Short  f1;
    
        TPoint*  arc;
        TPoint*  start_arc;
    
        PLong top;
    
    
        arc = ras.arc;
        y1  = arc[degree].y;
        y2  = arc[0].y;
        top = ras.top;
    
        if ( y2 < miny || y1 > maxy )
          goto Fin;
    
        e2 = FLOOR( y2 );
    
        if ( e2 > maxy )
          e2 = maxy;
    
        e0 = miny;
    
        if ( y1 < miny )
          e = miny;
        else
        {
          e  = CEILING( y1 );
          f1 = FRAC( y1 );
          e0 = e;
    
          if ( f1 == 0 )
          {
            if ( ras.joint )
            {
              top--;
              ras.joint = FALSE;
            }
    
            *top++ = arc[degree].x;
    
            e += ras.precision;
          }
        }
    
        if ( ras.fresh )
        {
          ras.cProfile->start = TRUNC( e0 );
          ras.fresh = FALSE;
        }
    
        if ( e2 < e )
          goto Fin;
    
        if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
        {
          ras.top   = top;
          ras.error = Raster_Err_Overflow;
          return FAILURE;
        }
    
        start_arc = arc;
    
        while ( arc >= start_arc && e <= e2 )
        {
          ras.joint = FALSE;
    
          y2 = arc[0].y;
    
          if ( y2 > e )
          {
            y1 = arc[degree].y;
            if ( y2 - y1 >= ras.precision_step )
            {
              splitter( arc );
              arc += degree;
            }
            else
            {
              *top++ = arc[degree].x + FMulDiv( arc[0].x-arc[degree].x,
                                                e - y1, y2 - y1 );
              arc -= degree;
              e   += ras.precision;
            }
          }
          else
          {
            if ( y2 == e )
            {
              ras.joint  = TRUE;
              *top++     = arc[0].x;
    
              e += ras.precision;
            }
            arc -= degree;
          }
        }
    
      Fin:
        ras.top  = top;
        ras.arc -= degree;
        return SUCCESS;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Bezier_Down                                                 */
    /*                                                                          */
    /* Description: Computes the x-coordinates of a descending bezier arc       */
    /*              and stores them in the render pool.                         */
    /*                                                                          */
    
      static Bool  Bezier_Down( RAS_ARGS Int        degree,
                                         TSplitter  splitter,
                                         Long       miny,
                                         Long       maxy )
      {
        TPoint*  arc = ras.arc;
        Bool     result, fresh;
    
    
        arc[0].y = -arc[0].y;
        arc[1].y = -arc[1].y;
        arc[2].y = -arc[2].y;
        if (degree > 2)
          arc[3].y = -arc[3].y;
    
        fresh = ras.fresh;
    
        result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
    
        if ( fresh && !ras.fresh )
          ras.cProfile->start = -ras.cProfile->start;
    
        arc[0].y = -arc[0].y;
        return result;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Line_To                                                     */
    /*                                                                          */
    /* Description: Injects a new line segment and adjusts Profiles list.       */
    /*                                                                          */
    /* Input:       x, y : segment endpoint (start point in LastX,LastY)        */
    /*                                                                          */
    /* Returns:     SUCCESS on success.                                         */
    /*              FAILURE on Render Pool overflow or Incorrect Profile.       */
    /*                                                                          */
    /****************************************************************************/
    
      static Bool  Line_To( RAS_ARGS Long  x, Long  y )
      {
        /* First, detect a change of direction */
    
        switch ( ras.state )
        {
        case Unknown:
          if ( y > ras.lastY )
          {
            if ( New_Profile( RAS_VARS  Ascending ) ) return FAILURE;
          }
          else
          {
            if ( y < ras.lastY )
              if ( New_Profile( RAS_VARS  Descending ) ) return FAILURE;
          }
          break;
    
        case Ascending:
          if ( y < ras.lastY )
          {
            if ( End_Profile( RAS_VAR ) ||
                 New_Profile( RAS_VARS  Descending ) ) return FAILURE;
          }
          break;
    
        case Descending:
          if ( y > ras.lastY )
          {
            if ( End_Profile( RAS_VAR ) ||
                 New_Profile( RAS_VARS  Ascending ) ) return FAILURE;
          }
          break;
    
        default:
          ;
        }
    
        /* Then compute the lines */
    
        switch ( ras.state )
        {
        case Ascending:
          if ( Line_Up ( RAS_VARS  ras.lastX, ras.lastY,
                         x, y, ras.minY, ras.maxY ) )
            return FAILURE;
          break;
    
        case Descending:
          if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
                          x, y, ras.minY, ras.maxY ) )
            return FAILURE;
          break;
    
        default:
          ;
        }
    
        ras.lastX = x;
        ras.lastY = y;
    
        return SUCCESS;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Conic_To                                                    */
    /*                                                                          */
    /* Description: Injects a new conic arc and adjusts the profile list.       */
    /*                                                                          */
    
      static Bool  Conic_To( RAS_ARGS Long  cx,
                                      Long  cy,
                                      Long  x,
                                      Long  y )
      {
        Long     y1, y2, y3, x3, ymin, ymax;
        TStates  state_bez;
    
    
        ras.arc      = ras.arcs;
        ras.arc[2].x = ras.lastX;
        ras.arc[2].y = ras.lastY;
        ras.arc[1].x = cx; ras.arc[1].y = cy;
        ras.arc[0].x = x;  ras.arc[0].y = y;
    
        do
        {
          y1 = ras.arc[2].y;
          y2 = ras.arc[1].y;
          y3 = ras.arc[0].y;
          x3 = ras.arc[0].x;
    
          /* first, categorize the Bezier arc */
    
          if ( y1 <= y3 )
          {
            ymin = y1;
            ymax = y3;
          }
          else
          {
            ymin = y3;
            ymax = y1;
          }
    
          if ( y2 < ymin || y2 > ymax )
          {
            /* this arc has no given direction, split it !! */
            Split_Conic( ras.arc );
            ras.arc += 2;
          }
          else if ( y1 == y3 )
          {
            /* this arc is flat, ignore it and pop it from the bezier stack */
            ras.arc -= 2;
          }
          else
          {
            /* the arc is y-monotonous, either ascending or descending */
            /* detect a change of direction                            */
            state_bez =  y1 < y3 ? Ascending : Descending;
            if ( ras.state != state_bez )
            {
              /* finalize current profile if any */
              if ( ras.state != Unknown   &&
                   End_Profile( RAS_VAR ) )
                goto Fail;
    
              /* create a new profile */
              if ( New_Profile( RAS_VARS state_bez ) )
                goto Fail;
            }
    
            /* now call the appropriate routine */
            if ( state_bez == Ascending )
            {
              if ( Bezier_Up( RAS_VARS  2, Split_Conic, ras.minY, ras.maxY ) )
                goto Fail;
            }
            else
              if ( Bezier_Down( RAS_VARS  2, Split_Conic, ras.minY, ras.maxY ) )
                goto Fail;
          }
    
        } while ( ras.arc >= ras.arcs );
    
        ras.lastX = x3;
        ras.lastY = y3;
    
        return SUCCESS;
      Fail:
        return FAILURE;
      }
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Cubic_To                                                    */
    /*                                                                          */
    /* Description: Injects a new cubic arc and adjusts the profile list.       */
    /*                                                                          */
    
      static Bool  Cubic_To( RAS_ARGS Long  cx1,
                                      Long  cy1,
                                      Long  cx2,
                                      Long  cy2,
                                      Long  x,
                                      Long  y )
      {
        Long     y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
        TStates  state_bez;
    
    
        ras.arc      = ras.arcs;
        ras.arc[3].x = ras.lastX;
        ras.arc[3].y = ras.lastY;
        ras.arc[2].x = cx1; ras.arc[2].y = cy1;
        ras.arc[1].x = cx2; ras.arc[1].y = cy2;
        ras.arc[0].x = x;   ras.arc[0].y = y;
    
        do
        {
          y1 = ras.arc[3].y;
          y2 = ras.arc[2].y;
          y3 = ras.arc[1].y;
          y4 = ras.arc[0].y;
          x4 = ras.arc[0].x;
    
          /* first, categorize the Bezier arc */
    
          if ( y1 <= y4 )
          {
            ymin1 = y1;
            ymax1 = y4;
          }
          else
          {
            ymin1 = y4;
            ymax1 = y1;
          }
    
          if ( y2 <= y3 )
          {
            ymin2 = y2;
            ymax2 = y3;
          }
          else
          {
            ymin2 = y3;
            ymax2 = y2;
          }
    
          if ( ymin2 < ymin1 || ymax2 > ymax1 )
          {
            /* this arc has no given direction, split it! */
            Split_Cubic( ras.arc );
            ras.arc += 3;
          }
          else if ( y1 == y4 )
          {
            /* this arc is flat, ignore it and pop it from the bezier stack */
            ras.arc -= 3;
          }
          else
          {
            state_bez = ( y1 <= y4 ) ? Ascending : Descending;
    
            /* detect a change of direction */
            if ( ras.state != state_bez )
            {
              if ( ras.state != Unknown   &&
                   End_Profile( RAS_VAR ) )
                goto Fail;
    
              if ( New_Profile( RAS_VARS state_bez ) )
                goto Fail;
            }
    
            /* compute intersections */
            if ( state_bez == Ascending )
            {
              if ( Bezier_Up ( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
                goto Fail;
            }
            else
              if ( Bezier_Down ( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
                goto Fail;
          }
    
        } while ( ras.arc >= ras.arcs );
    
        ras.lastX = x4;
        ras.lastY = y4;
    
        return SUCCESS;
      Fail:
        return FAILURE;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Decompose_Curve                                             */
    /*                                                                          */
    /* Description: Scans the outline arays in order to emit individual         */
    /*              segments and beziers by calling Line_To() and Bezier_To().  */
    /*              It handles all weird cases, like when the first point       */
    /*              is off the curve, or when there are simply no 'on'          */
    /*              points in the contour!                                      */
    /*                                                                          */
    /* Input:       first, last    : indexes of first and last point in         */
    /*                               contour.                                   */
    /*                                                                          */
    /* Returns:     SUCCESS on success.                                         */
    /*              FAILURE on error.                                           */
    /*                                                                          */
    /****************************************************************************/
    
    #undef  SWAP_
    #define SWAP_(x,y)  { Long swap = x; x = y; y = swap; }
    
    
      static Bool  Decompose_Curve( RAS_ARGS UShort  first,
                                             UShort  last,
                                             int     flipped )
      {
        FT_Vector  v_last;
        FT_Vector  v_control;
        FT_Vector  v_start;
    
        FT_Vector* points;
        FT_Vector* point;
        FT_Vector* limit;
        char*      tags;
    
        char   tag;       /* current point's state           */
    
        points = ras.outline.points;
        limit  = points + last;
    
        v_start.x = SCALED(points[first].x);
        v_start.y = SCALED(points[first].y);
        v_last.x  = SCALED(points[last].x);
        v_last.y  = SCALED(points[last].y);
        if (flipped)
        {
          SWAP_(v_start.x,v_start.y);
          SWAP_(v_last.x,v_last.y);
        }
    
        v_control = v_start;
    
        point = points + first;
        tags  = ras.outline.tags  + first;
        tag   = FT_CURVE_TAG( tags[0] );
    
        /* A contour cannot start with a cubic control point! */
        if ( tag == FT_Curve_Tag_Cubic )
          goto Invalid_Outline;
    
        /* check first point to determine origin */
        if ( tag == FT_Curve_Tag_Conic )
        {
          /* first point is conic control.  Yes, this happens. */
          if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_Curve_Tag_On )
          {
            /* start at last point if it is on the curve */
            v_start = v_last;
            limit--;
          }
          else
          {
            /* if both first and last points are conic,         */
            /* start at their middle and record its position    */
            /* for closure                                      */
            v_start.x = ( v_start.x + v_last.x ) / 2;
            v_start.y = ( v_start.y + v_last.y ) / 2;
    
            v_last = v_start;
          }
          point--;
          tags--;
        }
    
        ras.lastX = v_start.x;
        ras.lastY = v_start.y;
    
        while (point < limit)
        {
          point++;
          tags++;
    
          tag = FT_CURVE_TAG( tags[0] );
          switch (tag)
          {
            case FT_Curve_Tag_On:  /* emit a single line_to */
              {
                Long  x, y;
    
                x = SCALED(point->x);
                y = SCALED(point->y);
                if (flipped) SWAP_(x,y);
    
                if (Line_To( RAS_VARS x, y )) goto Fail;
                continue;
              }
    
    
            case FT_Curve_Tag_Conic:  /* consume conic arcs */
              {
                v_control.x = SCALED(point[0].x);
                v_control.y = SCALED(point[0].y);
                if (flipped) SWAP_(v_control.x,v_control.y);
    
              Do_Conic:
                if (point < limit)
                {
                  FT_Vector  v_middle;
                  Long       x, y;
    
                  point++;
                  tags++;
                  tag = FT_CURVE_TAG( tags[0] );
    
                  x = SCALED(point[0].x);
                  y = SCALED(point[0].y);
                  if (flipped) SWAP_(x,y);
    
                  if (tag == FT_Curve_Tag_On)
                  {
                    if (Conic_To( RAS_VARS v_control.x, v_control.y, x, y ))
                      goto Fail;
                    continue;
                  }
    
                  if (tag != FT_Curve_Tag_Conic)
                    goto Invalid_Outline;
    
                  v_middle.x = (v_control.x + x)/2;
                  v_middle.y = (v_control.y + y)/2;
    
                  if (Conic_To( RAS_VARS v_control.x, v_control.y,
                                         v_middle.x,  v_middle.y )) goto Fail;
    
                  v_control.x = x;
                  v_control.y = y;
                  goto Do_Conic;
                }
    
                if (Conic_To( RAS_VARS v_control.x, v_control.y,
                                       v_start.x,   v_start.y )) goto Fail;
                goto Close;
              }
    
            default:  /* FT_Curve_Tag_Cubic */
              {
                Long  x1, y1, x2, y2, x3, y3;
    
                if ( point+1 > limit         ||
                     FT_CURVE_TAG( tags[1] ) != FT_Curve_Tag_Cubic )
                  goto Invalid_Outline;
    
                point += 2;
                tags  += 2;
    
                x1 = SCALED(point[-2].x);
                y1 = SCALED(point[-2].y);
                x2 = SCALED(point[-1].x);
                y2 = SCALED(point[-1].y);
                x3 = SCALED(point[ 0].x);
                y3 = SCALED(point[ 0].y);
                if (flipped)
                {
                  SWAP_(x1,y1);
                  SWAP_(x2,y2);
                  SWAP_(x3,y3);
                }
                if (point <= limit)
                {
                  if (Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ))
                    goto Fail;
                  continue;
                }
    
                if (Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ))
                  goto Fail;
                goto Close;
              }
          }
        }
    
        /* close the contour with a line segment */
        if (Line_To( RAS_VARS v_start.x, v_start.y ))
          goto Fail;
    
      Close:
        return SUCCESS;
    
      Invalid_Outline:
        ras.error = Raster_Err_Invalid;
    
      Fail:
        return FAILURE;
      }
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Convert_Glyph                                               */
    /*                                                                          */
    /* Description: Converts a glyph into a series of segments and arcs         */
    /*              and makes a Profiles list with them.                        */
    /*                                                                          */
    /* Input:       _xCoord, _yCoord : coordinates tables.                      */
    /*                                                                          */
    /*              Uses the 'Flag' table too.                                  */
    /*                                                                          */
    /* Returns:     SUCCESS on success.                                         */
    /*              FAILURE if any error was encountered during rendering.      */
    /*                                                                          */
    /****************************************************************************/
    
      static Bool  Convert_Glyph( RAS_ARGS int  flipped )
      {
        Short     i;
        UShort    start;
    
        PProfile  lastProfile;
    
    
        ras.fProfile = NULL;
        ras.joint    = FALSE;
        ras.fresh    = FALSE;
    
        ras.maxBuff  = ras.sizeBuff - AlignProfileSize;
    
        ras.numTurns = 0;
    
        ras.cProfile         = (PProfile)ras.top;
        ras.cProfile->offset = ras.top;
        ras.num_Profs        = 0;
    
        start = 0;
    
        for ( i = 0; i < ras.outline.n_contours; i++ )
        {
          ras.state    = Unknown;
          ras.gProfile = NULL;
    
          if ( Decompose_Curve( RAS_VARS  start, ras.outline.contours[i], flipped ) )
            return FAILURE;
    
          start = ras.outline.contours[i] + 1;
    
          /* We must now see if the extreme arcs join or not */
          if ( ( FRAC( ras.lastY ) == 0 &&
                 ras.lastY >= ras.minY      &&
                 ras.lastY <= ras.maxY ) )
            if ( ras.gProfile && ras.gProfile->flow == ras.cProfile->flow )
              ras.top--;
            /* Note that ras.gProfile can be nil if the contour was too small */
            /* to be drawn.                                                   */
    
          lastProfile = ras.cProfile;
          if ( End_Profile( RAS_VAR ) ) return FAILURE;
    
          /* close the 'next profile in contour' linked list */
          if ( ras.gProfile )
            lastProfile->next = ras.gProfile;
        }
    
        if (Finalize_Profile_Table( RAS_VAR ))
          return FAILURE;
    
        return (ras.top < ras.maxBuff ? SUCCESS : FAILURE );
      }
    
    
      /****************************************************************/
      /****************************************************************/
      /**                                                            **/
      /**  SCAN-LINE SWEEPS AND DRAWING                              **/
      /**                                                            **/
      /****************************************************************/
      /****************************************************************/
    
    
    /************************************************/
    /*                                              */
    /*  Init_Linked                                 */
    /*                                              */
    /*    Inits an empty linked list.               */
    /*                                              */
    /************************************************/
    
      static void  Init_Linked( TProfileList*  l )
      {
        *l = NULL;
      }
    
    
    /************************************************/
    /*                                              */
    /*  InsNew :                                    */
    /*                                              */
    /*    Inserts a new Profile in a linked list.   */
    /*                                              */
    /************************************************/
    
      static void  InsNew( PProfileList  list,
                           PProfile      profile )
      {
        PProfile  *old, current;
        Long       x;
    
    
        old     = list;
        current = *old;
        x       = profile->X;
    
        while ( current )
        {
          if ( x < current->X )
            break;
          old     = &current->link;
          current = *old;
        }
    
        profile->link = current;
        *old          = profile;
      }
    
    
    /*************************************************/
    /*                                               */
    /*  DelOld :                                     */
    /*                                               */
    /*    Removes an old Profile from a linked list. */
    /*                                               */
    /*************************************************/
    
      static void  DelOld( PProfileList  list,
                           PProfile      profile )
      {
        PProfile  *old, current;
    
    
        old     = list;
        current = *old;
    
        while ( current )
        {
          if ( current == profile )
          {
            *old = current->link;
            return;
          }
    
          old     = &current->link;
          current = *old;
        }
    
        /* we should never get there, unless the Profile was not part of */
        /* the list.                                                     */
      }
    
    
    /************************************************/
    /*                                              */
    /*  Update :                                    */
    /*                                              */
    /*    Update all X offsets of a drawing list    */
    /*                                              */
    /************************************************/
    
      static void  Update( PProfile  first )
      {
        PProfile  current = first;
    
    
        while ( current )
        {
          current->X       = *current->offset;
          current->offset += current->flow;
          current->height--;
          current = current->link;
        }
      }
    
    
    /************************************************/
    /*                                              */
    /*  Sort :                                      */
    /*                                              */
    /*    Sorts a trace list.  In 95%, the list     */
    /*    is already sorted.  We need an algorithm  */
    /*    which is fast in this case.  Bubble sort  */
    /*    is enough and simple.                     */
    /*                                              */
    /************************************************/
    
      static void  Sort( PProfileList  list )
      {
        PProfile  *old, current, next;
    
    
        /* First, set the new X coordinate of each profile */
        Update( *list );
    
        /* Then sort them */
        old     = list;
        current = *old;
    
        if ( !current )
          return;
    
        next = current->link;
    
        while ( next )
        {
          if ( current->X <= next->X )
          {
            old     = &current->link;
            current = *old;
    
            if ( !current )
              return;
          }
          else
          {
            *old          = next;
            current->link = next->link;
            next->link    = current;
    
            old     = list;
            current = *old;
          }
    
          next = current->link;
        }
      }
    
    
    /***********************************************************************/
    /*                                                                     */
    /*  Vertical Sweep Procedure Set :                                     */
    /*                                                                     */
    /*  These three routines are used during the vertical black/white      */
    /*  sweep phase by the generic Draw_Sweep() function.                  */
    /*                                                                     */
    /***********************************************************************/
    
      static void  Vertical_Sweep_Init( RAS_ARGS Short*  min, Short*  max )
      {
        Long  pitch = ras.target.pitch;
    
        UNUSED(max);
    
        ras.traceIncr = (Short)- pitch;
        ras.traceOfs  = - *min * pitch;
        if (pitch > 0)
          ras.traceOfs += (ras.target.rows-1)*pitch;
    
        ras.gray_min_x = 0;
        ras.gray_max_x = 0;
      }
    
    
      static void  Vertical_Sweep_Span( RAS_ARGS Short       y,
                                                 FT_F26Dot6  x1,
                                                 FT_F26Dot6  x2,
                                                 PProfile    left,
                                                 PProfile    right )
      {
        Long   e1, e2;
        Short  c1, c2;
        Byte   f1, f2;
        Byte*  target;
    
        UNUSED(y);
        UNUSED(left);
        UNUSED(right);
    
        /* Drop-out control */
    
        e1 = TRUNC( CEILING( x1 ) );
    
        if ( x2-x1-ras.precision <= ras.precision_jitter )
          e2 = e1;
        else
          e2 = TRUNC( FLOOR( x2 ) );
    
        if ( e2 >= 0 && e1 < ras.bWidth )
        {
          if ( e1 < 0 )           e1 = 0;
          if ( e2 >= ras.bWidth ) e2 = ras.bWidth-1;
    
          c1 = (Short)(e1 >> 3);
          c2 = (Short)(e2 >> 3);
    
          f1 =  ((unsigned char)0xFF >> (e1 & 7));
          f2 = ~((unsigned char)0x7F >> (e2 & 7));
    
          if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
          if ( ras.gray_max_x < c2 ) ras.gray_max_x = c2;
    
          target = ras.bTarget + ras.traceOfs + c1;
          c2 -= c1;
    
          if ( c2 > 0 )
          {
            target[0] |= f1;
    
            /* memset() is slower than the following code on many platforms. */
            /* This is due to the fact that, in the vast majority of cases,  */
            /* the span length in bytes is relatively small.                 */
            c2--;
            while ( c2 > 0 )
            {
              *(++target) = 0xFF;
              c2--;
            }
            target[1] |= f2;
          }
          else
            *target |= ( f1 & f2 );
        }
      }
    
    
      static void  Vertical_Sweep_Drop( RAS_ARGS Short       y,
                                                 FT_F26Dot6  x1,
                                                 FT_F26Dot6  x2,
                                                 PProfile    left,
                                                 PProfile    right )
      {
        Long   e1, e2;
        Short  c1, f1;
    
    
        /* Drop-out control */
    
        e1 = CEILING( x1 );
        e2 = FLOOR  ( x2 );
    
        if ( e1 > e2 )
        {
          if ( e1 == e2 + ras.precision )
          {
            switch ( ras.dropOutControl )
            {
            case 1:
              e1 = e2;
              break;
    
            case 4:
              e1 = CEILING( (x1 + x2 + 1) / 2 );
              break;
    
            case 2:
            case 5:
              /* Drop-out Control Rule #4 */
    
              /* The spec is not very clear regarding rule #4.  It      */
              /* presents a method that is way too costly to implement  */
              /* while the general idea seems to get rid of 'stubs'.    */
              /*                                                        */
              /* Here, we only get rid of stubs recognized when:        */
              /*                                                        */
              /*  upper stub:                                           */
              /*                                                        */
              /*   - P_Left and P_Right are in the same contour         */
              /*   - P_Right is the successor of P_Left in that contour */
              /*   - y is the top of P_Left and P_Right                 */
              /*                                                        */
              /*  lower stub:                                           */
              /*                                                        */
              /*   - P_Left and P_Right are in the same contour         */
              /*   - P_Left is the successor of P_Right in that contour */
              /*   - y is the bottom of P_Left                          */
              /*                                                        */
    
              /* FIXXXME : uncommenting this line solves the disappearing */
              /*           bit problem in the '7' of verdana 10pts, but   */
              /*           makes a new one in the 'C' of arial 14pts      */
    
              /* if ( x2-x1 < ras.precision_half ) */
              {
                /* upper stub test */
    
                if ( left->next == right && left->height <= 0 ) return;
    
                /* lower stub test */
    
                if ( right->next == left && left->start == y ) return;
              }
    
              /* check that the rightmost pixel isn't set */
    
              e1 = TRUNC( e1 );
    
              c1 = (Short)(e1 >> 3);
              f1 = e1 &  7;
    
              if ( e1 >= 0 && e1 < ras.bWidth &&
                   ras.bTarget[ras.traceOfs + c1] & (0x80 >> f1) )
                return;
    
              if ( ras.dropOutControl == 2 )
                e1 = e2;
              else
                e1 = CEILING( (x1 + x2 + 1) / 2 );
    
              break;
    
            default:
              return;  /* unsupported mode */
            }
          }
          else
            return;
        }
    
        e1 = TRUNC( e1 );
    
        if ( e1 >= 0 && e1 < ras.bWidth )
        {
          c1 = (Short)(e1 >> 3);
          f1 = e1 & 7;
    
          if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
          if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1;
    
          ras.bTarget[ras.traceOfs + c1] |= (char)(0x80 >> f1);
        }
      }
    
    
      static void Vertical_Sweep_Step( RAS_ARG )
      {
        ras.traceOfs += ras.traceIncr;
      }
    
    
    /***********************************************************************/
    /*                                                                     */
    /*  Horizontal Sweep Procedure Set :                                   */
    /*                                                                     */
    /*  These three routines are used during the horizontal black/white    */
    /*  sweep phase by the generic Draw_Sweep() function.                  */
    /*                                                                     */
    /***********************************************************************/
    
      static void  Horizontal_Sweep_Init( RAS_ARGS Short*  min, Short*  max )
      {
        /* nothing, really */
        UNUSED(raster);
        UNUSED(min);
        UNUSED(max);
      }
    
    
      static void  Horizontal_Sweep_Span( RAS_ARGS Short       y,
                                                   FT_F26Dot6  x1,
                                                   FT_F26Dot6  x2,
                                                   PProfile    left,
                                                   PProfile    right )
      {
        Long  e1, e2;
        PByte bits;
        Byte  f1;
    
        UNUSED(left);
        UNUSED(right);
    
        if ( x2-x1 < ras.precision )
        {
          e1 = CEILING( x1 );
          e2 = FLOOR  ( x2 );
    
          if ( e1 == e2 )
          {
            bits = ras.bTarget + (y >> 3);
            f1   = (Byte)(0x80 >> (y  & 7));
    
            e1 = TRUNC( e1 );
    
            if ( e1 >= 0 && e1 < ras.target.rows )
            {
              PByte  p;
    
              p = bits - e1*ras.target.pitch;
              if (ras.target.pitch > 0)
                p += (ras.target.rows-1)*ras.target.pitch;
    
              p[0] |= f1;
            }
          }
        }
      }
    
    
      static void  Horizontal_Sweep_Drop( RAS_ARGS Short       y,
                                                   FT_F26Dot6  x1,
                                                   FT_F26Dot6  x2,
                                                   PProfile    left,
                                                   PProfile    right )
      {
        Long  e1, e2;
        PByte bits;
        Byte  f1;
    
    
        /* During the horizontal sweep, we only take care of drop-outs */
    
        e1 = CEILING( x1 );
        e2 = FLOOR  ( x2 );
    
        if ( e1 > e2 )
        {
          if ( e1 == e2 + ras.precision )
          {
            switch ( ras.dropOutControl )
            {
            case 1:
              e1 = e2;
              break;
    
            case 4:
              e1 = CEILING( (x1 + x2 + 1) / 2 );
              break;
    
            case 2:
            case 5:
    
              /* Drop-out Control Rule #4 */
    
              /* The spec is not very clear regarding rule #4.  It      */
              /* presents a method that is way too costly to implement  */
              /* while the general idea seems to get rid of 'stubs'.    */
              /*                                                        */
    
              /* rightmost stub test */
    
              if ( left->next == right && left->height <= 0 ) return;
    
              /* leftmost stub test */
    
              if ( right->next == left && left->start == y ) return;
    
              /* check that the rightmost pixel isn't set */
    
              e1 = TRUNC( e1 );
    
              bits = ras.bTarget + (y >> 3);
              f1   = (Byte)(0x80 >> (y &  7));
    
              bits -= e1*ras.target.pitch;
              if (ras.target.pitch > 0)
                bits += (ras.target.rows-1)*ras.target.pitch;
    
              if ( e1 >= 0              &&
                   e1 < ras.target.rows &&
                   *bits & f1 )
                return;
    
              if ( ras.dropOutControl == 2 )
                e1 = e2;
              else
                e1 = CEILING( (x1 + x2 + 1) / 2 );
    
              break;
    
            default:
              return;  /* unsupported mode */
            }
          }
          else
            return;
        }
    
        bits = ras.bTarget + (y >> 3);
        f1   = (Byte)(0x80 >> (y  & 7));
    
        e1 = TRUNC( e1 );
    
        if ( e1 >= 0 && e1 < ras.target.rows )
        {
          bits -= e1*ras.target.pitch;
          if (ras.target.pitch > 0)
            bits += (ras.target.rows-1)*ras.target.pitch;
    
          bits[0] |= f1;
        }
      }
    
    
      static void Horizontal_Sweep_Step( RAS_ARG )
      {
        /* Nothing, really */
        UNUSED(raster);
      }
    
    
    #ifdef FT_RASTER_OPTION_ANTI_ALIASING
    
    /***********************************************************************/
    /*                                                                     */
    /*  Vertical Gray Sweep Procedure Set:                                 */
    /*                                                                     */
    /*  These two routines are used during the vertical gray-levels        */
    /*  sweep phase by the generic Draw_Sweep() function.                  */
    /*                                                                     */
    /*                                                                     */
    /*  NOTES:                                                             */
    /*                                                                     */
    /*  - The target pixmap's width *must* be a multiple of 4.             */
    /*                                                                     */
    /*  - you have to use the function Vertical_Sweep_Span() for           */
    /*    the gray span call.                                              */
    /*                                                                     */
    /***********************************************************************/
    
      static void  Vertical_Gray_Sweep_Init( RAS_ARGS Short*  min, Short*  max )
      {
        Long  pitch, byte_len;
    
        *min = *min & -2;
        *max = ( *max + 3 ) & -2;
    
        ras.traceOfs  = 0;
        pitch         = ras.target.pitch;
        byte_len      = -pitch;
        ras.traceIncr = (Short)byte_len;
        ras.traceG    = (*min/2)*byte_len;
        if (pitch > 0)
        {
          ras.traceG += (ras.target.rows-1)*pitch;
          byte_len    = -byte_len;
        }
    
        ras.gray_min_x =  (Short)byte_len;
        ras.gray_max_x = -(Short)byte_len;
      }
    
    
      static void  Vertical_Gray_Sweep_Step( RAS_ARG )
      {
        Int    c1, c2;
        PByte  pix, bit, bit2;
        Int*   count = ras.count_table;
        Byte*  grays;
    
    
        ras.traceOfs += ras.gray_width;
    
        if ( ras.traceOfs > ras.gray_width )
        {
          pix   = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
          grays = ras.grays;
    
          if ( ras.gray_max_x >= 0 )
          {
            Long   last_pixel = ras.target.width-1;
            Int    last_cell  = last_pixel >> 2;
            Int    last_bit   = last_pixel & 3;
            Bool   over       = 0;
    
            if (ras.gray_max_x >= last_cell && last_bit != 3)
            {
              ras.gray_max_x = last_cell-1;
              over = 1;
            }
    
            if ( ras.gray_min_x < 0 )
              ras.gray_min_x = 0;
    
            bit   = ras.bTarget + ras.gray_min_x;
            bit2  = bit + ras.gray_width;
    
            c1 = ras.gray_max_x - ras.gray_min_x;
    
            while ( c1 >= 0 )
            {
              c2 = count[*bit] + count[*bit2];
    
              if ( c2 )
              {
                pix[0] = grays[(c2 >> 12) & 0x000F];
                pix[1] = grays[(c2 >> 8 ) & 0x000F];
                pix[2] = grays[(c2 >> 4 ) & 0x000F];
                pix[3] = grays[ c2        & 0x000F];
    
                *bit  = 0;
                *bit2 = 0;
              }
    
              bit ++;
              bit2++;
              pix += 4;
              c1  --;
            }
    
            if (over)
            {
              c2 = count[*bit] + count[*bit2];
              if (c2)
              {
                switch (last_bit)
                {
                  case 2:  pix[2] = grays[(c2 >> 4 ) & 0x000F];
                  case 1:  pix[1] = grays[(c2 >> 8 ) & 0x000F];
                  default: pix[0] = grays[(c2 >> 12) & 0x000F];
                }
    
                *bit  = 0;
                *bit2 = 0;
              }
            }
          }
    
          ras.traceOfs = 0;
          ras.traceG  += ras.traceIncr;
    
          ras.gray_min_x =  32000;
          ras.gray_max_x = -32000;
        }
      }
    
    
      static void  Horizontal_Gray_Sweep_Span( RAS_ARGS Short       y,
                                                        FT_F26Dot6  x1,
                                                        FT_F26Dot6  x2,
                                                        PProfile    left,
                                                        PProfile    right )
      {
        /* nothing, really */
        UNUSED(raster);
        UNUSED(y);
        UNUSED(x1);
        UNUSED(x2);
        UNUSED(left);
        UNUSED(right);
      }
    
      static void  Horizontal_Gray_Sweep_Drop( RAS_ARGS Short       y,
                                                        FT_F26Dot6  x1,
                                                        FT_F26Dot6  x2,
                                                        PProfile    left,
                                                        PProfile    right )
      {
        Long  e1, e2;
        PByte pixel;
        Byte  color;
    
    
        /* During the horizontal sweep, we only take care of drop-outs */
        e1 = CEILING( x1 );
        e2 = FLOOR  ( x2 );
    
        if ( e1 > e2 )
        {
          if ( e1 == e2 + ras.precision )
          {
            switch ( ras.dropOutControl )
            {
            case 1:
              e1 = e2;
              break;
    
            case 4:
              e1 = CEILING( (x1 + x2 + 1) / 2 );
              break;
    
            case 2:
            case 5:
    
              /* Drop-out Control Rule #4 */
    
              /* The spec is not very clear regarding rule #4.  It      */
              /* presents a method that is way too costly to implement  */
              /* while the general idea seems to get rid of 'stubs'.    */
              /*                                                        */
    
              /* rightmost stub test */
              if ( left->next == right && left->height <= 0 ) return;
    
              /* leftmost stub test */
              if ( right->next == left && left->start == y ) return;
    
              if ( ras.dropOutControl == 2 )
                e1 = e2;
              else
                e1 = CEILING( (x1 + x2 + 1) / 2 );
    
              break;
    
            default:
              return;  /* unsupported mode */
            }
          }
          else
            return;
        }
    
        if ( e1 >= 0 )
        {
          if ( x2 - x1 >= ras.precision_half )
            color = ras.grays[2];
          else
            color = ras.grays[1];
    
          e1 = TRUNC( e1 ) / 2;
          if ( e1 < ras.target.rows )
          {
            pixel = ras.gTarget - e1*ras.target.pitch + y/2;
            if (ras.target.pitch > 0)
              pixel += (ras.target.rows-1)*ras.target.pitch;
    
            if (pixel[0] == ras.grays[0])
              pixel[0] = color;
          }
        }
      }
    
    #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
    
    
    /********************************************************************/
    /*                                                                  */
    /*  Generic Sweep Drawing routine                                   */
    /*                                                                  */
    /********************************************************************/
    
      static Bool  Draw_Sweep( RAS_ARG )
      {
        Short  y, y_change, y_height;
    
        PProfile  P, Q, P_Left, P_Right;
    
        Short  min_Y, max_Y, top, bottom, dropouts;
    
        Long  x1, x2, xs, e1, e2;
    
        TProfileList  wait;
        TProfileList  draw_left, draw_right;
    
    
        /* Init empty linked lists */
    
        Init_Linked( &wait );
    
        Init_Linked( &draw_left  );
        Init_Linked( &draw_right );
    
        /* first, compute min and max Y */
    
        P     = ras.fProfile;
        max_Y = (short)TRUNC( ras.minY );
        min_Y = (short)TRUNC( ras.maxY );
    
        while ( P )
        {
          Q = P->link;
    
          bottom = (Short)P->start;
          top    = (Short)P->start + P->height-1;
    
          if ( min_Y > bottom ) min_Y = bottom;
          if ( max_Y < top    ) max_Y = top;
    
          P->X = 0;
          InsNew( &wait, P );
    
          P = Q;
        }
    
        /* Check the Y-turns */
        if ( ras.numTurns == 0 )
        {
          ras.error = Raster_Err_Invalid;
          return FAILURE;
        }
    
        /* Now inits the sweep */
    
        ras.Proc_Sweep_Init( RAS_VARS  &min_Y, &max_Y );
    
        /* Then compute the distance of each profile from min_Y */
    
        P = wait;
    
        while ( P )
        {
          P->countL = P->start - min_Y;
          P = P->link;
        }
    
        /* Let's go */
    
        y        = min_Y;
        y_height = 0;
    
        if ( ras.numTurns > 0 &&
             ras.sizeBuff[-ras.numTurns] == min_Y )
          ras.numTurns--;
    
        while ( ras.numTurns > 0 )
        {
          /* look in the wait list for new activations */
    
          P = wait;
    
          while ( P )
          {
            Q = P->link;
            P->countL -= y_height;
            if ( P->countL == 0 )
            {
              DelOld( &wait, P );
    
              switch ( P->flow )
              {
                case Flow_Up:    InsNew( &draw_left,  P ); break;
                case Flow_Down:  InsNew( &draw_right, P ); break;
              }
            }
    
            P = Q;
          }
    
          /* Sort the drawing lists */
    
          Sort( &draw_left );
          Sort( &draw_right );
    
          y_change = (Short)ras.sizeBuff[-ras.numTurns--];
          y_height = y_change - y;
    
          while ( y < y_change )
          {
    
            /* Let's trace */
    
            dropouts = 0;
    
            P_Left  = draw_left;
            P_Right = draw_right;
    
            while ( P_Left )
            {
              x1 = P_Left ->X;
              x2 = P_Right->X;
    
              if ( x1 > x2 )
              {
                xs = x1;
                x1 = x2;
                x2 = xs;
              }
    
              if ( x2-x1 <= ras.precision )
              {
                e1 = FLOOR( x1 );
                e2 = CEILING( x2 );
    
                if ( ras.dropOutControl != 0 &&
                     (e1 > e2 || e2 == e1 + ras.precision) )
                {
                  /* a drop out was detected */
    
                  P_Left ->X = x1;
                  P_Right->X = x2;
    
                  /* mark profile for drop-out processing */
                  P_Left->countL = 1;
                  dropouts++;
    
                  goto Skip_To_Next;
                }
              }
    
              ras.Proc_Sweep_Span( RAS_VARS  y, x1, x2, P_Left, P_Right );
    
       Skip_To_Next:
    
              P_Left  = P_Left->link;
              P_Right = P_Right->link;
            }
    
            /* now perform the dropouts _after_ the span drawing   */
            /* drop-outs processing has been moved out of the loop */
            /* for performance tuning                              */
            if (dropouts > 0)
              goto Scan_DropOuts;
    
       Next_Line:
    
            ras.Proc_Sweep_Step( RAS_VAR );
    
            y++;
    
            if ( y < y_change )
            {
              Sort( &draw_left  );
              Sort( &draw_right );
            }
    
          }
    
          /* Now finalize the profiles that needs it */
    
          {
            PProfile  Q, P;
            P = draw_left;
            while ( P )
            {
              Q = P->link;
              if ( P->height == 0 )
                DelOld( &draw_left, P );
              P = Q;
            }
          }
    
          {
            PProfile  Q, P = draw_right;
            while ( P )
            {
              Q = P->link;
              if ( P->height == 0 )
                DelOld( &draw_right, P );
              P = Q;
            }
          }
        }
    
        /* for gray-scaling, flushes the bitmap scanline cache */
        while ( y <= max_Y )
        {
          ras.Proc_Sweep_Step( RAS_VAR );
          y++;
        }
    
        return SUCCESS;
    
    Scan_DropOuts :
    
        P_Left  = draw_left;
        P_Right = draw_right;
    
        while ( P_Left )
        {
          if ( P_Left->countL )
          {
            P_Left->countL = 0;
            /* dropouts--;    -- this is useful when debugging only */
            ras.Proc_Sweep_Drop( RAS_VARS  y,
                                           P_Left->X,
                                           P_Right->X,
                                           P_Left,
                                           P_Right );
          }
    
          P_Left  = P_Left->link;
          P_Right = P_Right->link;
        }
    
        goto Next_Line;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Render_Single_Pass                                          */
    /*                                                                          */
    /* Description: Performs one sweep with sub-banding.                        */
    /*                                                                          */
    /* Input:       _XCoord, _YCoord : x and y coordinates arrays               */
    /*                                                                          */
    /* Returns:     SUCCESS on success                                          */
    /*              FAILURE if any error was encountered during render.         */
    /*                                                                          */
    /****************************************************************************/
    
      static FT_Error  Render_Single_Pass( RAS_ARGS Bool  flipped )
      {
        Short  i, j, k;
    
    
        while ( ras.band_top >= 0 )
        {
          ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
          ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
    
          ras.top = ras.buff;
    
          ras.error = Raster_Err_None;
    
          if ( Convert_Glyph( RAS_VARS  flipped ) )
          {
            if ( ras.error != Raster_Err_Overflow ) return FAILURE;
    
            ras.error = Raster_Err_None;
    
            /* sub-banding */
    
            #ifdef DEBUG_RASTER
            ClearBand( RAS_VARS  TRUNC( ras.minY ), TRUNC( ras.maxY ) );
            #endif
    
            i = ras.band_stack[ras.band_top].y_min;
            j = ras.band_stack[ras.band_top].y_max;
    
            k = ( i + j ) / 2;
    
            if ( ras.band_top >= 7 || k < i )
            {
              ras.band_top     = 0;
              ras.error = Raster_Err_Invalid;
              return ras.error;
            }
    
            ras.band_stack[ras.band_top+1].y_min = k;
            ras.band_stack[ras.band_top+1].y_max = j;
    
            ras.band_stack[ras.band_top].y_max = k - 1;
    
            ras.band_top++;
          }
          else
          {
            if ( ras.fProfile )
              if ( Draw_Sweep( RAS_VAR ) ) return ras.error;
            ras.band_top--;
          }
        }
    
        return FT_Err_Ok;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Render_Glyph                                                */
    /*                                                                          */
    /* Description: Renders a glyph in a bitmap.  Sub-banding if needed.        */
    /*                                                                          */
    /* Input:       AGlyph   Glyph record                                       */
    /*                                                                          */
    /* Returns:     SUCCESS on success.                                         */
    /*              FAILURE if any error was encountered during rendering.      */
    /*                                                                          */
    /****************************************************************************/
    
      LOCAL_FUNC
      FT_Error  Render_Glyph( RAS_ARG )
      {
        FT_Error  error;
    
        Set_High_Precision( RAS_VARS ras.outline.flags & ft_outline_high_precision );
        ras.scale_shift    = ras.precision_shift;
        ras.dropOutControl = 2;
        ras.second_pass    = !(ras.outline.flags & ft_outline_single_pass);
    
    
        /* Vertical Sweep */
        ras.Proc_Sweep_Init = Vertical_Sweep_Init;
        ras.Proc_Sweep_Span = Vertical_Sweep_Span;
        ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
        ras.Proc_Sweep_Step = Vertical_Sweep_Step;
    
        ras.band_top            = 0;
        ras.band_stack[0].y_min = 0;
        ras.band_stack[0].y_max = ras.target.rows - 1;
    
        ras.bWidth  = ras.target.width;
        ras.bTarget = (Byte*)ras.target.buffer;
    
        if ( (error = Render_Single_Pass( RAS_VARS 0 )) != 0 )
          return error;
    
        /* Horizontal Sweep */
        if ( ras.second_pass && ras.dropOutControl != 0 )
        {
          ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
          ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
          ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
          ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
    
          ras.band_top            = 0;
          ras.band_stack[0].y_min = 0;
          ras.band_stack[0].y_max = ras.target.width - 1;
    
          if ( (error = Render_Single_Pass( RAS_VARS  1 )) != 0 )
            return error;
        }
    
        return FT_Err_Ok;
      }
    
    
    /****************************************************************************/
    /*                                                                          */
    /* Function:    Render_Gray_Glyph                                           */
    /*                                                                          */
    /* Description: Renders a glyph with grayscaling. Sub-banding if needed.    */
    /*                                                                          */
    /* Input:       AGlyph   Glyph record                                       */
    /*                                                                          */
    /* Returns:     SUCCESS on success                                          */
    /*              FAILURE if any error was encountered during rendering.      */
    /*                                                                          */
    /****************************************************************************/
    
    #ifdef FT_RASTER_OPTION_ANTI_ALIASING
      LOCAL_FUNC
      FT_Error  Render_Gray_Glyph( RAS_ARG )
      {
        Long      pixel_width;
        FT_Error  error;
    
        Set_High_Precision( RAS_VARS ras.outline.flags & ft_outline_high_precision );
        ras.scale_shift    = ras.precision_shift+1;
        ras.dropOutControl = 2;
        ras.second_pass    = !(ras.outline.flags & ft_outline_single_pass);
    
    
        /* Vertical Sweep */
    
        ras.band_top            = 0;
        ras.band_stack[0].y_min = 0;
        ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
    
        ras.bWidth  = ras.gray_width;
        pixel_width = 2*((ras.target.width + 3) >> 2);
    
        if ( ras.bWidth > pixel_width )
          ras.bWidth = pixel_width;
    
        ras.bWidth  = ras.bWidth * 8;
        ras.bTarget = (Byte*)ras.gray_lines;
        ras.gTarget = (Byte*)ras.target.buffer;
    
        ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
        ras.Proc_Sweep_Span = Vertical_Sweep_Span;
        ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
        ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
    
        error = Render_Single_Pass( RAS_VARS  0 );
        if (error)
          return error;
    
        /* Horizontal Sweep */
        if ( ras.second_pass && ras.dropOutControl != 0 )
        {
          ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
          ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
          ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
          ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
    
          ras.band_top            = 0;
          ras.band_stack[0].y_min = 0;
          ras.band_stack[0].y_max = ras.target.width * 2 - 1;
    
          error = Render_Single_Pass( RAS_VARS  1 );
          if (error)
            return error;
        }
    
        return FT_Err_Ok;
      }
    #else
      LOCAL_FUNC
      FT_Error  Render_Gray_Glyph( RAS_ARG )
      {
        UNUSED_RASTER
        return Raster_Err_Unsupported;
      }
    #endif
    
    
      static  void  ft_black_init( TRaster_Instance*  raster )
      {
        FT_UInt  n;
        FT_ULong c;
    
        /* setup count table */
        for ( n = 0; n < 256; n++ )
        {
          c = (n & 0x55) + ((n & 0xAA) >> 1);
    
          c = ((c << 6) & 0x3000) |
              ((c << 4) & 0x0300) |
              ((c << 2) & 0x0030) |
                    (c  & 0x0003);
    
          raster->count_table[n] = c;
        }
    
        /* set default 5-levels gray palette */
        for ( n = 0; n < 5; n++ )
          raster->grays[n] = (n*255/4);
    
        raster->gray_width = RASTER_GRAY_LINES/2;
      }
    
      /**** RASTER OBJECT CREATION : in standalone mode, we simply use *****/
      /****                          a static object ..                *****/
    #ifdef _STANDALONE_
    
      static
      int  ft_black_new( void*  memory, FT_Raster *araster )
      {
         static FT_RasterRec_  the_raster;
         *araster = &the_raster;
         memset( &the_raster, sizeof(the_raster), 0 );
         ft_black_init( &the_raster );
         return 0;
      }
    
      static
      void  ft_black_done( FT_Raster  raster )
      {
        /* nothing */
        raster->init = 0;
      }
    
    #else
    
    #include <freetype/internal/ftobjs.h>
    
      static
      int  ft_black_new( FT_Memory  memory, TRaster_Instance*  *araster )
      {
        FT_Error           error;
        TRaster_Instance*  raster;
    
        *araster = 0;
        if ( !ALLOC( raster, sizeof(*raster) ))
        {
          raster->memory = memory;
          ft_black_init( raster );
    
          *araster = raster;
        }
    
        return error;
      }
    
      static
      void ft_black_done( TRaster_Instance*  raster )
      {
        FT_Memory  memory = (FT_Memory)raster->memory;
        FREE( raster );
      }
    
    #endif
    
    
      static void ft_black_reset( TRaster_Instance* raster,
                                  const char*       pool_base,
                                  long              pool_size )
      {
        if ( raster && pool_base && pool_size >= 4096 )
        {
          /* save the pool */
          raster->buff     = (PLong)pool_base;
          raster->sizeBuff = raster->buff + pool_size / sizeof (Long);
        }
      }
    
      static void ft_black_set_mode( TRaster_Instance* raster,
                                     unsigned long     mode,
                                     const char*       palette )
      {
        if (mode==FT_MAKE_TAG('p','a','l','5'))
        {
          /* set 5-levels gray palette */
          raster->grays[0] = palette[0];
          raster->grays[1] = palette[1];
          raster->grays[2] = palette[2];
          raster->grays[3] = palette[3];
          raster->grays[4] = palette[4];
        }
      }
    
    
      static
      int  ft_black_render( TRaster_Instance*  raster,
                            FT_Raster_Params*  params )
      {
        FT_Outline*  outline    = (FT_Outline*)params->source;
        FT_Bitmap*   target_map = params->target;
    
        if ( !raster || !raster->buff || !raster->sizeBuff )
          return Raster_Err_Not_Ini;
    
        if ( !outline || !outline->contours || !outline->points )
          return Raster_Err_Invalid;
    
        /* return immediately if the outline is empty */
        if ( outline->n_points == 0 || outline->n_contours <= 0 )
          return Raster_Err_None;
    
        if ( outline->n_points != outline->contours[outline->n_contours - 1] + 1 )
          return Raster_Err_Invalid;
    
        if ( !target_map || !target_map->buffer )
          return Raster_Err_Invalid;
    
        /* this version of the raster does not support direct rendering, sorry */
        if ( params->flags & ft_raster_flag_direct )
          return Raster_Err_Unsupported;
    
        ras.outline  = *outline;
        ras.target   = *target_map;
    
        return ( params->flags & ft_raster_flag_aa
               ? Render_Gray_Glyph( raster )
               : Render_Glyph( raster ) );
      }
    
    
      FT_Raster_Funcs      ft_default_raster =
      {
        ft_glyph_format_outline,
        (FT_Raster_New_Func)       ft_black_new,
        (FT_Raster_Reset_Func)     ft_black_reset,
        (FT_Raster_Set_Mode_Func)  ft_black_set_mode,
        (FT_Raster_Render_Func)    ft_black_render,
        (FT_Raster_Done_Func)      ft_black_done
      };
    
    
    /* END */