shithub: freetype+ttf2subf

Download patch

ref: d89489210a9a410fd93288b11813313374fe1502
parent: 06b73729c189ff3c206e6dc6d9c43f3e63df4197
author: David Turner <[email protected]>
date: Tue May 2 09:29:29 EDT 2000

ftrast.c

git/fs: mount .git/fs: mount/attach disallowed
--- /dev/null
+++ b/demos/src/ftrast.c
@@ -1,0 +1,2905 @@
+/*******************************************************************
+ *
+ *  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 "frast.h"
+#include <ftcalc.h>      /* for FT_MulDiv only */
+
+  /****************************************************************/
+  /****************************************************************/
+  /**                                                            **/
+  /**  CONFIGURATION MACROS                                      **/
+  /**                                                            **/
+  /****************************************************************/
+  /****************************************************************/
+
+/* define DEBUG_RASTER if you want to compile a debugging version */
+#define xxxDEBUG_RASTER
+
+/* required by the tracing mode */
+#undef  FT_COMPONENT
+#define FT_COMPONENT      trace_raster
+
+#include <ftdebug.h>
+
+
+/* The default render pool size */
+#define  RASTER_RENDER_POOL   8192
+
+/* The size of the two-lines intermediate bitmap used */
+/* for anti-aliasing                                  */
+#define  RASTER_GRAY_LINES    1024
+
+#define Raster_Err_None              TT_Err_Ok
+#define Raster_Err_Not_Ini           TT_Err_Raster_Not_Initialized
+#define Raster_Err_Overflow          TT_Err_Raster_Pool_Overflow
+#define Raster_Err_Neg_Height        TT_Err_Raster_Negative_Height
+#define Raster_Err_Invalid           TT_Err_Raster_Invalid_Value
+#define Raster_Err_Gray_Unsupported  TT_Err_Raster_Gray_Unsupported
+
+
+/* FMulDiv means "Fast MulDiv", it is uses 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 TT_MulDiv() function    */
+/* defined in "ttcalc.h"                                                  */
+/*                                                                        */
+/* So, the following definition fits the bill nicely, and we don't need   */
+/* to use the one in 'ttcalc' anymore, even for 16-bit systems...         */
+#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
+
+
+
+
+#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,
+    Flow_Down
+  
+  } 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   */
+    PStorage    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))
+
+
+  /* Left fill bitmask */
+  static const Byte  LMask[8] =
+    { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
+
+  /* Right fill bitmask */
+  static const Byte  RMask[8] =
+    { 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF };
+
+  /* We provide two different builds of the scan-line converter  */
+  /* The static build uses global variables and isn't            */
+  /* re-entrant.                                                 */
+  /* The indirect build is re-entrant but accesses all variables */
+  /* indirectly.                                                 */
+  /*                                                             */
+  /* As a consequence, the indirect build is about 10% slower    */
+  /* than the static one on a _Pentium_ (this could get worse    */
+  /* on older processors), but the code size is reduced by       */
+  /* more than 30% !                                             */
+  /*                                                             */
+  /* The indirect build is now the default, defined in           */
+  /* ttconfig.h.  Be careful if you experiment with this.        */
+
+  /* Note also that, though its code can be re-entrant, the      */
+  /* component is always used in thread-safe mode.  This is      */
+  /* simply due to the fact that we want to use a single         */
+  /* render pool (of 64 Kb), and not to waste memory.            */
+
+#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;
+
+    PByte     flags;                /* current flags table    */
+    PUShort   outs;                 /* current outlines table */
+
+    UShort    nPoints;              /* number of points in current glyph   */
+    Short     nContours;            /* number of contours in current glyph */
+    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 */
+
+    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;
+
+    FT_Vector*  coords;
+
+    Byte      dropOutControl;       /* current drop_out control method */
+
+    Byte      grays[5];         /* Palette of gray levels used for render */
+
+    Byte*     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'              */
+
+    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         */
+  };
+
+
+#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 Bool  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 = FT_Flow_Up;
+      FT_TRACE7(( "New ascending profile = %lx\n", (long)ras.cProfile ));
+      break;
+
+    case Descending:
+      ras.cProfile->flow = FT_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 )
+  {
+    PStorage  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 FT_Flow_Down:
+          bottom     = p->start - p->height+1;
+          top        = p->start;
+          p->start   = bottom;
+          p->offset += p->height-1;
+          break;
+
+        case FT_Flow_Up:
+        default:
+          bottom = p->start;
+          top    = p->start + p->height-1;
+        }
+
+        if ( Insert_Y_Turn( RAS_VARS  bottom ) ||
+             Insert_Y_Turn( RAS_VARS  top+1 )  )
+          return FAILURE;
+
+        p = p->link;
+        n--;
+      }
+    }
+    else
+      ras.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>                                                            */
+  /*    Push_Cubic                                                         */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Clears the Bezier stack and pushes a new third-order Bezier arc on */
+  /*    top of it.                                                         */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    p2 :: A pointer to the second (control) point.                     */
+  /*    p3 :: A pointer to the third (control) point.                      */
+  /*    p4 :: A pointer to the fourth (end) point.                         */
+  /*                                                                       */
+  /* <Note>                                                                */
+  /*    The first point is taken as `raster->last', so it doesn't appear   */
+  /*    in the signature.                                                  */
+  /*                                                                       */
+  /*    This is the same as Push_Conic(), except that it deals with        */
+  /*    third-order Beziers.                                               */
+  /*                                                                       */
+  static
+  static void  Push_Cubic( RAS_ARGS Long  x1, Long  y1,
+                                    Long  x2, Long  y2,
+                                    Long  x3, Long  y3,
+                                    Long  x4, Long  y4 )
+  {
+  }
+
+
+/****************************************************************************/
+/*                                                                          */
+/* 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;
+
+    PStorage  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;
+
+    PStorage 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  x,
+                                  Long  y,
+                                  Long  cx,
+                                  Long  cy )
+  {
+    Long     y1, y2, y3, x3;
+    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_VAR_ state_bez ) )
+            goto Fail;
+        }
+
+        /* now call the appropriate routine */
+        if ( state_bez == Ascending )
+        {
+          if ( Bezier_Up( RAS_VAR_  2, Split_Conic, ras.minY, ras.maxY ) )
+            goto Fail;
+        }
+        else
+          if ( Bezier_Down( RAS_VAR_  2, Split_Conic, ras.minY, ras.maxY ) )
+            goto Fail;
+      }
+
+    } while ( ras.arc >= ras.arcs );
+
+    ras.lastX = x3;
+    ras.lastY = y3;
+
+    return SUCCESS;
+  }
+
+/****************************************************************************/
+/*                                                                          */
+/* Function:    Cubic_To                                                    */
+/*                                                                          */
+/* Description: Injects a new cubic arc and adjusts the profile list.       */
+/*                                                                          */
+
+  static Bool  Cubic_To( RAS_ARGS Long  x,
+                                  Long  y,
+                                  Long  cx1,
+                                  Long  cy1,
+                                  Lonc  cx2,
+                                  Long  cy2 )
+  {
+    TPos     y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
+    Long     y0, y1, y2, y3, x3;
+    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_VAR_ state_bez ) )
+            goto Fail;
+        }
+
+        /* compute intersections */
+        if ( state_bez == Ascending )
+        {
+          if ( Bezier_Up ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) )
+            goto Fail;
+        }
+        else
+          if ( Bezier_Down ( RAS_VAR_ 3, Split_Cubic, ras.minY, ras.maxY ) )
+            goto Fail;
+      }
+
+    } while ( ras.arc >= ras.arcs );
+
+    ras.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,
+                                         Bool    flipped )
+  {
+    FT_Vector  v_last;
+    FT_Vector  v_control;
+    FT_Vector  v_start;
+
+    FT_Vector* points;
+    FT_Vector* point;
+    FT_Vector* limit;
+    char*      tags;
+
+    int    n;         /* index of contour in outline     */
+    int    first;     /* index of first point in contour */
+    int    error;
+    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--;
+    }
+
+    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.nContours; i++ )
+    {
+      ras.state    = Unknown;
+      ras.gProfile = NULL;
+
+      if ( Decompose_Curve( RAS_VARS  start, ras.outs[i], flipped ) )
+        return FAILURE;
+
+      start = ras.outs[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 )
+  {
+    switch ( ras.target.flow )
+    {
+    case FT_Flow_Up:
+      ras.traceOfs  = *min * ras.target.cols;
+      ras.traceIncr = ras.target.cols;
+      break;
+
+    default:
+      ras.traceOfs  = ( ras.target.rows - 1 - *min ) * ras.target.cols;
+      ras.traceIncr = -ras.target.cols;
+    }
+
+    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;
+    Short  f1, f2;
+    Byte*  target;
+
+
+    /* 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 = e1 & 7;
+      f2 = 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;
+
+      if ( c1 != c2 )
+      {
+        *target |= LMask[f1];
+
+        if ( c2 > c1 + 1 )
+          MEM_Set( target + 1, 0xFF, c2 - c1 - 1 );
+
+        target[c2 - c1] |= RMask[f2];
+      }
+      else
+        *target |= ( LMask[f1] & RMask[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 */
+  }
+
+
+  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;
+
+
+    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 )
+        {
+          if ( ras.target.flow == FT_Flow_Down )
+            bits[(ras.target.rows-1 - e1) * ras.target.cols] |= f1;
+          else
+            bits[e1 * ras.target.cols] |= f1;
+        }
+      }
+    }
+#if 0
+      e2 = TRUNC( e2 );
+
+      if ( e2 >= 0 && e2 < ras.target.rows )
+        if ( ras.target.flow == FT_Flow_Down )
+          bits[(ras.target.rows-1-e2) * ras.target.cols] |= f1;
+        else
+          bits[e2 * ras.target.cols] |= f1;
+#endif
+  }
+
+
+  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));
+
+          if ( ras.target.flow == FT_Flow_Down )
+            bits += (ras.target.rows-1-e1) * ras.target.cols;
+          else
+            bits += e1 * ras.target.cols;
+
+          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 )
+    {
+      if (ras.target.flow==FT_Flow_Down)
+        bits[(ras.target.rows-1-e1) * ras.target.cols] |= f1;
+      else
+        bits[e1 * ras.target.cols] |= f1;
+    }
+  }
+
+
+  static void Horizontal_Sweep_Step( RAS_ARG )
+  {
+    /* Nothing, really */
+  }
+
+
+#ifdef FT_CONFIG_OPTION_GRAY_SCALING
+
+/***********************************************************************/
+/*                                                                     */
+/*  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 )
+  {
+    *min = *min & -2;
+    *max = ( *max + 3 ) & -2;
+
+    ras.traceOfs = 0;
+
+    switch ( ras.target.flow )
+    {
+    case FT_Flow_Up:
+      ras.traceG  = (*min / 2) * ras.target.cols;
+      ras.traceIncr = ras.target.cols;
+      break;
+
+    default:
+      ras.traceG    = (ras.target.rows-1 - *min/2) * ras.target.cols;
+      ras.traceIncr = -ras.target.cols;
+    }
+
+    ras.gray_min_x =  ras.target.cols;
+    ras.gray_max_x = -ras.target.cols;
+  }
+
+
+  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 )
+      {
+        if ( ras.gray_max_x >= ras.target.width )
+          ras.gray_max_x = ras.target.width-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 & 0xF000) >> 12];
+            pix[1] = grays[(c2 & 0x0F00) >>  8];
+            pix[2] = grays[(c2 & 0x00F0) >>  4];
+            pix[3] = grays[(c2 & 0x000F)      ];
+
+            *bit  = 0;
+            *bit2 = 0;
+          }
+
+          bit ++;
+          bit2++;
+          pix += 4;
+          c1  --;
+        }
+      }
+
+      ras.traceOfs = 0;
+      ras.traceG  += ras.traceIncr;
+
+      ras.gray_min_x =  ras.target.cols;
+      ras.gray_max_x = -ras.target.cols;
+    }
+  }
+
+
+  static void  Horizontal_Gray_Sweep_Span( RAS_ARGS Short       y,
+                                                    FT_F26Dot6  x1,
+                                                    FT_F26Dot6  x2,
+                                                    PProfile    left,
+                                                    PProfile    right )
+  {
+    /* nothing, really */
+  }
+
+  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 )
+      {
+        if ( ras.target.flow == FT_Flow_Down )
+          pixel = ras.gTarget +
+                  (ras.target.rows - 1 - e1) * ras.target.cols + y / 2;
+        else
+          pixel = ras.gTarget +
+                  e1 * ras.target.cols + y / 2;
+
+        if (pixel[0] == ras.grays[0])
+          pixel[0] = color;
+      }
+    }
+  }
+
+#endif /* FT_CONFIG_OPTION_GRAY_SCALING */
+
+
+/********************************************************************/
+/*                                                                  */
+/*  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 = P->start;
+      top    = P->start + P->height-1;
+
+      if ( min_Y > bottom ) min_Y = bottom;
+      if ( max_Y < top    ) max_Y = top;
+
+      P->X = 0;
+      InsNew( &wait, P );
+
+      P = Q;
+    }
+
+    /* Check the Y-turns */
+    if ( ras.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 FT_Flow_Up:    InsNew( &draw_left,  P ); break;
+            case FT_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_ARGS FT_Outline*     glyph,
+                                   FT_Raster_Map*  target_map )
+  {
+    FT_Error  error;
+
+
+    if ( glyph->n_points == 0 || glyph->n_contours <= 0 )
+      return FT_Err_Ok;
+
+    if ( !ras.buff )
+    {
+      ras.error = Raster_Err_Not_Ini;
+      return ras.error;
+    }
+
+    if ( glyph->n_points < glyph->contours[glyph->n_contours - 1] )
+    {
+      ras.error = FT_Err_Too_Many_Points;
+      return ras.error;
+    }
+
+    if ( target_map )
+      ras.target = *target_map;
+
+    ras.outs      = glyph->contours;
+    ras.flags     = glyph->flags;
+    ras.nPoints   = glyph->n_points;
+    ras.nContours = glyph->n_contours;
+    ras.coords    = glyph->points;
+
+    Set_High_Precision( RAS_VARS glyph->high_precision );
+    ras.scale_shift    = ras.precision_shift;
+    ras.dropOutControl = glyph->dropout_mode;
+    ras.second_pass    = glyph->second_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.bitmap;
+
+    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;
+  }
+
+
+#ifdef FT_CONFIG_OPTION_GRAY_SCALING
+
+/****************************************************************************/
+/*                                                                          */
+/* 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.      */
+/*                                                                          */
+/****************************************************************************/
+
+  LOCAL_FUNC
+  FT_Error  Render_Gray_Glyph( RAS_ARGS  FT_Outline*     glyph,
+                                         FT_Raster_Map*  target_map,
+                                         Byte*           palette )
+  {
+    Int       i;
+    FT_Error  error;
+
+    if ( !ras.buff )
+    {
+      ras.error = Raster_Err_Not_Ini;
+      return ras.error;
+    }
+
+    if ( glyph->n_points == 0 || glyph->n_contours <= 0 )
+      return FT_Err_Ok;
+
+    if ( glyph->n_points < glyph->contours[glyph->n_contours - 1] )
+    {
+      ras.error = FT_Err_Too_Many_Points;
+      return ras.error;
+    }
+
+    if ( palette )
+    {
+      for ( i = 0; i < 5; i++ )
+        ras.grays[i] = palette[i];
+    }
+
+    if ( target_map )
+      ras.target = *target_map;
+
+    ras.outs      = glyph->contours;
+    ras.flags     = glyph->flags;
+    ras.nPoints   = glyph->n_points;
+    ras.nContours = glyph->n_contours;
+    ras.coords    = glyph->points;
+
+    Set_High_Precision( RAS_VARS glyph->high_precision );
+    ras.scale_shift    = ras.precision_shift+1;
+    ras.dropOutControl = glyph->dropout_mode;
+    ras.second_pass    = glyph->second_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;
+    if ( ras.bWidth > ras.target.cols/4 )
+      ras.bWidth = ras.target.cols/4;
+
+    ras.bWidth  = ras.bWidth * 8;
+    ras.bTarget = (Byte*)ras.gray_lines;
+    ras.gTarget = (Byte*)ras.target.bitmap;
+
+    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;
+  }
+
+#endif /* FT_CONFIG_OPTION_GRAY_SCALING */
+
+
+/************************************************/
+/*                                              */
+/* InitRasterizer                               */
+/*                                              */
+/*  Raster Initialization.                      */
+/*  Gets the bitmap description and render pool */
+/*  addresses.                                  */
+/*                                              */
+/************************************************/
+
+#undef ras
+
+  LOCAL_FUNC
+  FT_Error  TTRaster_Done( PEngine_Instance  engine )
+  {
+    TRaster_Instance*  ras = (TRaster_Instance*)engine->raster_component;
+
+
+    if ( !ras )
+      return FT_Err_Ok;
+
+    FREE( ras->buff );
+    FREE( ras->gray_lines );
+
+    return FT_Err_Ok;
+  }
+
+
+  LOCAL_FUNC
+  FT_Error  TTRaster_Init( PEngine_Instance  engine )
+  {
+    FT_Error  error;
+
+    Int  i, l, j, c;
+
+    TRaster_Instance*  ras;
+
+
+#ifdef FT_CONFIG_OPTION_STATIC_RASTER
+    ras = engine->raster_component = &cur_ras;
+#else
+    if ( ALLOC( engine->raster_component, sizeof ( TRaster_Instance ) ) )
+      return error;
+
+    ras = (TRaster_Instance*)engine->raster_component;
+#endif
+
+    if ( ALLOC( ras->buff,       RASTER_RENDER_POOL )  ||
+         ALLOC( ras->gray_lines, RASTER_GRAY_LINES ) )
+       return error;
+
+    ras->sizeBuff   = ras->buff + ( RASTER_RENDER_POOL/sizeof(long) );
+    ras->gray_width = RASTER_GRAY_LINES/2;
+
+    /* Initialization of Count_Table */
+
+    for ( i = 0; i < 256; i++ )
+    {
+      l = 0;
+      j = i;
+
+      for ( c = 0; c < 4; c++ )
+      {
+        l <<= 4;
+
+        if ( j & 0x80 ) l++;
+        if ( j & 0x40 ) l++;
+
+        j = ( j << 2 ) & 0xFF;
+      }
+
+      ras->count_table[i] = l;
+    }
+
+    ras->dropOutControl = 2;
+    ras->error          = Raster_Err_None;
+
+    return FT_Err_Ok;
+  }
+
+
+/* END */